summaryrefslogtreecommitdiff
path: root/storage/bdb/test/mdbscript.tcl
blob: 9f3c971ee3c1da80c4b1782bb8029bb82a61c616 (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
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
# See the file LICENSE for redistribution information.
#
# Copyright (c) 1996-2002
#	Sleepycat Software.  All rights reserved.
#
# $Id: mdbscript.tcl,v 11.29 2002/03/22 21:43:06 krinsky Exp $
#
# Process script for the multi-process db tester.

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

global dbenv
global klock
global l_keys
global procid
global alphabet

# In Tcl, when there are multiple catch handlers, *all* handlers
# are called, so we have to resort to this hack.
#
global exception_handled

set exception_handled 0

set datastr $alphabet$alphabet

# Usage: mdbscript dir file nentries iter procid procs seed
# dir: DBHOME directory
# file: db file on which to operate
# nentries: number of entries taken from dictionary
# iter: number of operations to run
# procid: this processes' id number
# procs: total number of processes running
set usage "mdbscript method dir file nentries iter procid procs"

# Verify usage
if { $argc != 7 } {
	puts "FAIL:[timestamp] test042: Usage: $usage"
	exit
}

# Initialize arguments
set method [lindex $argv 0]
set dir [lindex $argv 1]
set file [lindex $argv 2]
set nentries [ lindex $argv 3 ]
set iter [ lindex $argv 4 ]
set procid [ lindex $argv 5 ]
set procs [ lindex $argv 6 ]

set pflags ""
set gflags ""
set txn ""

set renum [is_rrecno $method]
set omethod [convert_method $method]

if { [is_record_based $method] == 1 } {
   append gflags " -recno"
}

# Initialize seed
global rand_init

# We want repeatable results, but we also want each instance of mdbscript
# to do something different.  So we add the procid to the fixed seed.
# (Note that this is a serial number given by the caller, not a pid.)
berkdb srand [expr $rand_init + $procid]

puts "Beginning execution for [pid] $method"
puts "$dir db_home"
puts "$file database"
puts "$nentries data elements"
puts "$iter iterations"
puts "$procid process id"
puts "$procs processes"

set klock NOLOCK

# Note: all I/O operations, and especially flush, are expensive
# on Win2000 at least with Tcl version 8.3.2.  So we'll avoid
# flushes in the main part of the loop below.
flush stdout

set dbenv [berkdb_env -create -cdb -home $dir]
#set dbenv [berkdb_env -create -cdb -log -home $dir]
error_check_good dbenv [is_valid_env $dbenv] TRUE

set locker [ $dbenv lock_id ]

set db [berkdb_open -env $dbenv -create -mode 0644 $omethod $file]
error_check_good dbopen [is_valid_db $db] TRUE

# Init globals (no data)
set nkeys [db_init $db 0]
puts "Initial number of keys: $nkeys"
error_check_good db_init $nkeys $nentries
tclsleep 5

proc get_lock { k } {
	global dbenv
	global procid
	global locker
	global klock
	global DB_LOCK_WRITE
	global DB_LOCK_NOWAIT
	global errorInfo
	global exception_handled
	# Make sure that the key isn't in the middle of
	# a delete operation
	if {[catch {$dbenv lock_get -nowait write $locker $k} klock] != 0 } {
		set exception_handled 1

		error_check_good \
		    get_lock [is_substr $errorInfo "DB_LOCK_NOTGRANTED"] 1
		puts "Warning: key $k locked"
		set klock NOLOCK
		return 1
	} else  {
		error_check_good get_lock [is_valid_lock $klock $dbenv] TRUE
	}
	return 0
}

# On each iteration we're going to randomly pick a key.
# 1. We'll either get it (verifying that its contents are reasonable).
# 2. Put it (using an overwrite to make the data be datastr:ID).
# 3. Get it and do a put through the cursor, tacking our ID on to
# 4. Get it, read forward some random number of keys.
# 5. Get it, read forward some random number of keys and do a put (replace).
# 6. Get it, read forward some random number of keys and do a del.  And then
#	do a put of the key.
set gets 0
set getput 0
set overwrite 0
set seqread 0
set seqput 0
set seqdel 0
set dlen [string length $datastr]

for { set i 0 } { $i < $iter } { incr i } {
	set op [berkdb random_int 0 5]
	puts "iteration $i operation $op"
	set close_cursor 0
	if {[catch {
	switch $op {
		0 {
			incr gets
			set k [rand_key $method $nkeys $renum $procs]
			if {[is_record_based $method] == 1} {
				set key $k
			} else  {
				set key [lindex $l_keys $k]
			}

			if { [get_lock $key] == 1 } {
				incr i -1
				continue;
			}

			set rec [eval {$db get} $txn $gflags {$key}]
			error_check_bad "$db get $key" [llength $rec] 0
			set partial [string range \
			    [lindex [lindex $rec 0] 1] 0 [expr $dlen - 1]]
			error_check_good \
			    "$db get $key" $partial [pad_data $method $datastr]
		}
		1 {
			incr overwrite
			set k [rand_key $method $nkeys $renum $procs]
			if {[is_record_based $method] == 1} {
				set key $k
			} else  {
				set key [lindex $l_keys $k]
			}

			set data $datastr:$procid
			set ret [eval {$db put} \
			    $txn $pflags {$key [chop_data $method $data]}]
			error_check_good "$db put $key" $ret 0
		}
		2 {
			incr getput
			set dbc [$db cursor -update]
			error_check_good "$db cursor" \
			    [is_valid_cursor $dbc $db] TRUE
			set close_cursor 1
			set k [rand_key $method $nkeys $renum $procs]
			if {[is_record_based $method] == 1} {
				set key $k
			} else  {
				set key [lindex $l_keys $k]
			}

			if { [get_lock  $key] == 1 } {
				incr i -1
				error_check_good "$dbc close" \
				    [$dbc close] 0
				set close_cursor 0
				continue;
			}

			set ret [$dbc get -set $key]
			error_check_good \
			    "$dbc get $key" [llength [lindex $ret 0]] 2
			set rec [lindex [lindex $ret 0] 1]
			set partial [string range $rec 0 [expr $dlen - 1]]
			error_check_good \
			    "$dbc get $key" $partial [pad_data $method $datastr]
			append rec ":$procid"
			set ret [$dbc put \
			    -current [chop_data $method $rec]]
			error_check_good "$dbc put $key" $ret 0
			error_check_good "$dbc close" [$dbc close] 0
			set close_cursor 0
		}
		3 -
		4 -
		5 {
			if { $op == 3 } {
				set flags ""
			} else {
				set flags -update
			}
			set dbc [eval {$db cursor} $flags]
			error_check_good "$db cursor" \
			    [is_valid_cursor $dbc $db] TRUE
			set close_cursor 1
			set k [rand_key $method $nkeys $renum $procs]
			if {[is_record_based $method] == 1} {
				set key $k
			} else  {
				set key [lindex $l_keys $k]
			}

			if { [get_lock $key] == 1 } {
				incr i -1
				error_check_good "$dbc close" \
				    [$dbc close] 0
				set close_cursor 0
				continue;
			}

			set ret [$dbc get -set $key]
			error_check_good \
			    "$dbc get $key" [llength [lindex $ret 0]] 2

			# Now read a few keys sequentially
			set nloop [berkdb random_int 0 10]
			if { [berkdb random_int 0 1] == 0 } {
				set flags -next
			} else {
				set flags -prev
			}
			while { $nloop > 0 } {
				set lastret $ret
				set ret [eval {$dbc get} $flags]
				# Might read beginning/end of file
				if { [llength $ret] == 0} {
					set ret $lastret
					break
				}
				incr nloop -1
			}
			switch $op {
				3 {
					incr seqread
				}
				4 {
					incr seqput
					set rec [lindex [lindex $ret 0] 1]
					set partial [string range $rec 0 \
					    [expr $dlen - 1]]
					error_check_good "$dbc get $key" \
					    $partial [pad_data $method $datastr]
					append rec ":$procid"
					set ret [$dbc put -current \
					    [chop_data $method $rec]]
					error_check_good \
					    "$dbc put $key" $ret 0
				}
				5 {
					incr seqdel
					set k [lindex [lindex $ret 0] 0]
					# We need to lock the item we're
					# deleting so that someone else can't
					# try to do a get while we're
					# deleting
					error_check_good "$klock put" \
					    [$klock put] 0
					set klock NOLOCK
					set cur [$dbc get -current]
					error_check_bad get_current \
					    [llength $cur] 0
					set key [lindex [lindex $cur 0] 0]
					if { [get_lock $key] == 1 } {
						incr i -1
						error_check_good "$dbc close" \
						     [$dbc close] 0
						set close_cursor 0
						continue
					}
					set ret [$dbc del]
					error_check_good "$dbc del" $ret 0
					set rec $datastr
					append rec ":$procid"
					if { $renum == 1 } {
						set ret [$dbc put -before \
						    [chop_data $method $rec]]
						error_check_good \
						    "$dbc put $k" $ret $k
					} elseif { \
					    [is_record_based $method] == 1 } {
						error_check_good "$dbc close" \
						    [$dbc close] 0
						set close_cursor 0
						set ret [$db put $k \
						    [chop_data $method $rec]]
						error_check_good \
						    "$db put $k" $ret 0
					} else {
						set ret [$dbc put -keylast $k \
						    [chop_data $method $rec]]
						error_check_good \
						    "$dbc put $k" $ret 0
					}
				}
			}
			if { $close_cursor == 1 } {
				error_check_good \
				    "$dbc close" [$dbc close] 0
				set close_cursor 0
			}
		}
	}
	} res] != 0} {
		global errorInfo;
		global exception_handled;

		puts $errorInfo

		set fnl [string first "\n" $errorInfo]
		set theError [string range $errorInfo 0 [expr $fnl - 1]]

		if { [string compare $klock NOLOCK] != 0 } {
			catch {$klock put}
		}
		if {$close_cursor == 1} {
			catch {$dbc close}
			set close_cursor 0
		}

		if {[string first FAIL $theError] == 0 && \
		    $exception_handled != 1} {
			flush stdout
			error "FAIL:[timestamp] test042: key $k: $theError"
		}
		set exception_handled 0
	} else {
		if { [string compare $klock NOLOCK] != 0 } {
			error_check_good "$klock put" [$klock put] 0
			set klock NOLOCK
		}
	}
}

error_check_good db_close_catch [catch {$db close} ret] 0
error_check_good db_close $ret 0
error_check_good dbenv_close [$dbenv close] 0

flush stdout
exit

puts "[timestamp] [pid] Complete"
puts "Successful ops: "
puts "\t$gets gets"
puts "\t$overwrite overwrites"
puts "\t$getput getputs"
puts "\t$seqread seqread"
puts "\t$seqput seqput"
puts "\t$seqdel seqdel"
flush stdout