summaryrefslogtreecommitdiff
path: root/test/tcl/t106script.tcl
blob: 2888390565dbaf98240f26c2a5049ea3e4bbcaa4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
# See the file LICENSE for redistribution information.
#
# Copyright (c) 1999, 2015 Oracle and/or its affiliates.  All rights reserved.
#
# $Id$
#

proc t106_initial { nitems nprod id tnum dbenv order args } {
	source ./include.tcl

	set pid [pid]
	puts "\tTest$tnum: Producer $pid initializing DBs"

	# Each producer initially loads a small number of items to
	# each btree database, then enters a RMW loop where it randomly
	# selects and executes a cursor operations which either:
	# 1.  Read-modify-write an item in db2; or
	# 2.  Read-modify-write an item in both db2 and db3, randomly
	# selecting between db2 and db3 on which to open first, which to
	# read first, which to write first, which to close first.  This
	# may create deadlocks so keep trying until it's successful.

	# Open queue database
	set dbq [eval {berkdb_open -create -queue -env $dbenv\
	    -auto_commit -len 32 queue.db} ]
	error_check_good dbq_open [is_valid_db $dbq] TRUE

	# Open four btree databases
	set db1 [berkdb_open \
	    -create -btree -env $dbenv -auto_commit testfile1.db]
	error_check_good db1_open [is_valid_db $db1] TRUE
	set db2 [berkdb_open \
	    -create -btree -env $dbenv -auto_commit testfile2.db]
	error_check_good db2_open [is_valid_db $db2] TRUE
	set db3 [berkdb_open \
	    -create -btree -env $dbenv -auto_commit testfile3.db]
	error_check_good db3_open [is_valid_db $db3] TRUE
	set db4 [berkdb_open \
	    -create -btree -env $dbenv -auto_commit testfile4.db]
	error_check_good db4_open [is_valid_db $db4] TRUE

	# Initialize databases with $nitems items from each producer.
	set did [open $dict]
	for { set i 1 } { $i <= $nitems } { incr i } {
		set db2data [read $did [berkdb random_int 300 700]]
		set db3data [read $did [berkdb random_int 500 1000]]
		set qdata [read $did 32]
		set suffix _0_$i
		set db23key "testclient$id$suffix"
		set suffix _$i
		set db4key key$id$suffix

		set t [$dbenv txn]
		set txn "-txn $t"
		error_check_good db2_put [eval {$db2 put} $txn\
		    {$db23key $db2data}] 0
		error_check_good db3_put [eval {$db3 put} $txn\
		    {$db23key $db3data}] 0
		error_check_good db4_put [eval {$db4 put} $txn\
		    {$db4key $db23key}] 0

		set c [$dbenv txn -parent $t]
		set ctxn "-txn $c"
		set qrecno [eval {$dbq put -append} $ctxn {$qdata}]
		error_check_good db1_put [eval {$db1 put} $ctxn\
		    {$qrecno $db2data}] 0
		error_check_good commit_child [$c commit] 0
		error_check_good commit_parent [$t commit] 0
	}
	close $did

	set ret [catch {$dbq close} res]
	error_check_good dbq_close:$pid $ret 0
	set ret [catch {$db1 close} res]
	error_check_good db1_close:$pid $ret 0
	set ret [catch {$db2 close} res]
	error_check_good db2_close:$pid $ret 0
	set ret [catch {$db3 close} res]
	error_check_good db3_close:$pid $ret 0
	set ret [catch {$db4 close} res]
	error_check_good db4_close:$pid $ret 0

	puts "\t\tTest$tnum: Initializer $pid finished."
}

proc t106_produce { nitems nprod id tnum dbenv order niter args } {
	source ./include.tcl

	set pid [pid]
	set did [open $dict]
	puts "\tTest$tnum: Producer $pid initializing DBs"

	# Open queue database
	set dbq [eval {berkdb_open -create -queue -env $dbenv\
	    -auto_commit -len 32 queue.db} ]
	error_check_good dbq_open [is_valid_db $dbq] TRUE

	# Open four btree databases
	set db1 [berkdb_open \
	    -create -btree -env $dbenv -auto_commit testfile1.db]
	error_check_good db1_open [is_valid_db $db1] TRUE
	set db2 [berkdb_open \
	    -create -btree -env $dbenv -auto_commit testfile2.db]
	error_check_good db2_open [is_valid_db $db2] TRUE
	set db3 [berkdb_open \
	    -create -btree -env $dbenv -auto_commit testfile3.db]
	error_check_good db3_open [is_valid_db $db3] TRUE
	set db4 [berkdb_open \
	    -create -btree -env $dbenv -auto_commit testfile4.db]
	error_check_good db4_open [is_valid_db $db4] TRUE

	# Now go into RMW phase.
	for { set i 1 } { $i <= $niter } { incr i } {

		set op [berkdb random_int 1 2]
		set newdb2data [read $did [berkdb random_int 300 700]]
		set qdata [read $did 32]

		if { $order == "ordered" } {
			set n [expr $i % $nitems]
			if { $n == 0 } {
				set n $nitems
			}
			set suffix _0_$n
		} else {
			# Retrieve a random key from the list
			set suffix _0_[berkdb random_int 1 $nitems]
		}
		set key "testclient$id$suffix"

		set t [$dbenv txn]
		set txn "-txn $t"

		# Now execute op1 or op2
		if { $op == 1 } {
			op1 $db2 $key $newdb2data $txn
		} elseif { $op == 2 } {
			set newdb3data [read $did [berkdb random_int 500 1000]]
			op2 $db2 $db3 $key $newdb2data $newdb3data $txn $dbenv
		} else {
			puts "FAIL: unrecogized op $op"
		}
		set c [$dbenv txn -parent $t]
		set ctxn "-txn $c"
		set qrecno [eval {$dbq put -append} $ctxn {$qdata}]
		error_check_good db1_put [eval {$db1 put} $ctxn\
		    {$qrecno $newdb2data}] 0
		error_check_good child_commit [$c commit] 0
		error_check_good parent_commit [$t commit] 0
	}
	close $did

	set ret [catch {$dbq close} res]
	error_check_good dbq_close:$pid $ret 0
	set ret [catch {$db1 close} res]
	error_check_good db1_close:$pid $ret 0
	set ret [catch {$db2 close} res]
	error_check_good db2_close:$pid $ret 0
	set ret [catch {$db3 close} res]
	error_check_good db3_close:$pid $ret 0
	set ret [catch {$db4 close} res]
	error_check_good db4_close:$pid $ret 0

	puts "\t\tTest$tnum: Producer $pid finished."
}

proc t106_consume { nitems tnum outputfile mode dbenv niter args } {
	source ./include.tcl
	set pid [pid]
	puts "\tTest$tnum: Consumer $pid starting ($niter iterations)."

	# Open queue database and btree database 1.
	set dbq [eval {berkdb_open \
	    -create -queue -env $dbenv -auto_commit -len 32 queue.db} ]
	error_check_good dbq_open:$pid [is_valid_db $dbq] TRUE

	set db1 [eval {berkdb_open \
	    -create -btree -env $dbenv -auto_commit testfile1.db} ]
	error_check_good db1_open:$pid [is_valid_db $db1] TRUE

	set oid [open $outputfile a]

	for { set i 1 } { $i <= $nitems } {incr i } {
		set t [$dbenv txn]
		set txn "-txn $t"
		set ret [eval {$dbq get $mode} $txn]
		set qrecno [lindex [lindex $ret 0] 0]
		set db1curs [eval {$db1 cursor} $txn]
		if {[catch {eval $db1curs get -set -rmw $qrecno} res]} {
			puts "FAIL: $db1curs get: $res"
		}
		error_check_good db1curs_del [$db1curs del] 0
		error_check_good db1curs_close [$db1curs close] 0
		error_check_good txn_commit [$t commit] 0
	}

	error_check_good output_close:$pid [close $oid] ""

	set ret [catch {$dbq close} res]
	error_check_good dbq_close:$pid $ret 0
	set ret [catch {$db1 close} res]
	error_check_good db1_close:$pid $ret 0
	puts "\t\tTest$tnum: Consumer $pid finished."
}

# op1 overwrites one data item in db2.
proc op1 { db2 key newdata txn } {

	set db2c [eval {$db2 cursor} $txn]
puts "in op1, key is $key"
	set ret [eval {$db2c get -set -rmw $key}]
	# Make sure we retrieved something
	error_check_good db2c_get [llength $ret] 1
	error_check_good db2c_put [eval {$db2c put} -current {$newdata}] 0
	error_check_good db2c_close [$db2c close] 0
}

# op 2
proc op2 { db2 db3 key newdata2 newdata3 txn dbenv } {

	# Randomly choose whether to work on db2 or db3 first for
	# each operation: open cursor, get, put, close.
	set open1 [berkdb random_int 0 1]
	set get1 [berkdb random_int 0 1]
	set put1 [berkdb random_int 0 1]
	set close1 [berkdb random_int 0 1]
puts "open [expr $open1 + 2] first, get [expr $get1 + 2] first,\
    put [expr $put1 + 2] first, close [expr $close1 + 2] first"
puts "in op2, key is $key"

	# Open cursor
	if { $open1 == 0 } {
		set db2c [eval {$db2 cursor} $txn]
		set db3c [eval {$db3 cursor} $txn]
	} else {
		set db3c [eval {$db3 cursor} $txn]
		set db2c [eval {$db2 cursor} $txn]
	}
	error_check_good db2_cursor [is_valid_cursor $db2c $db2] TRUE
	error_check_good db3_cursor [is_valid_cursor $db3c $db3] TRUE

	# Do the following until we succeed and don't get DB_DEADLOCK:
	if { $get1 == 0 } {
		get_set_rmw $db2c $key $dbenv
		get_set_rmw $db3c $key $dbenv
	} else {
		get_set_rmw $db3c $key $dbenv
		get_set_rmw $db2c $key $dbenv
	}

	# Put new data.
	if { $put1 == 0 } {
		error_check_good db2c_put [eval {$db2c put} \
		    -current {$newdata2}] 0
		error_check_good db3c_put [eval {$db3c put} \
		    -current {$newdata3}] 0
	} else {
		error_check_good db3c_put [eval {$db3c put} \
		    -current {$newdata3}] 0
		error_check_good db2c_put [eval {$db2c put} \
		    -current {$newdata2}] 0
	}
	if { $close1 == 0 } {
		error_check_good db2c_close [$db2c close] 0
		error_check_good db3c_close [$db3c close] 0
	} else {
		error_check_good db3c_close [$db3c close] 0
		error_check_good db2c_close [$db2c close] 0
	}
}

proc get_set_rmw { dbcursor key dbenv } {

	while { 1 } {
		if {[catch {set ret [eval {$dbcursor get -set -rmw} $key]}\
		    res ]} {
			# If the get failed, break if it failed for any
			# reason other than deadlock.  If we have deadlock,
			# the deadlock detector should break the deadlock
			# as we keep trying.
			if { [is_substr $res DB_LOCK_DEADLOCK] != 1 } {
				puts "FAIL: get_set_rmw: $res"
				break
			}
		} else {
			# We succeeded.  Go back to the body of the test.
			break
		}
	}
}

source ./include.tcl
source $test_path/test.tcl

# Verify usage
set usage "t106script.tcl dir runtype nitems nprod outputfile id tnum order"
if { $argc < 10 } {
	puts stderr "FAIL:[timestamp] Usage: $usage"
	exit
}

# Initialize arguments
set dir [lindex $argv 0]
set runtype [lindex $argv 1]
set nitems [lindex $argv 2]
set nprod [lindex $argv 3]
set outputfile [lindex $argv 4]
set id [lindex $argv 5]
set tnum [lindex $argv 6]
set order [lindex $argv 7]
set niter [lindex $argv 8]
# args is the string "{ -len 20 -pad 0}", so we need to extract the
# " -len 20 -pad 0" part.
set args [lindex [lrange $argv 9 end] 0]

# Open env
set dbenv [berkdb_env -home $dir -txn]
error_check_good dbenv_open [is_valid_env $dbenv] TRUE

# Invoke initial, produce or consume based on $runtype
if { $runtype == "INITIAL" } {
	t106_initial $nitems $nprod $id $tnum $dbenv $order $args
} elseif { $runtype == "PRODUCE" } {
	t106_produce $nitems $nprod $id $tnum $dbenv $order $niter $args
} elseif { $runtype == "WAIT" } {
	t106_consume $nitems $tnum $outputfile -consume_wait $dbenv $args
} else {
	error_check_good bad_args $runtype "either PRODUCE, or WAIT"
}
error_check_good env_close [$dbenv close] 0
exit