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
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
|
# See the file LICENSE for redistribution information.
#
# Copyright (c) 1996-2003
# Sleepycat Software. All rights reserved.
#
# $Id: mdbscript.tcl,v 11.32 2003/09/29 18:19:19 sandstro 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"
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
}
# If we are renumbering, then each time we delete an item, the number of
# items in the file is temporarily decreased, so the highest record numbers
# do not exist. To make sure this doesn't happen, we never generate the
# highest few record numbers as keys.
#
# For record-based methods, record numbers begin at 1, while for other keys,
# we begin at 0 to index into an array.
proc rand_key { method nkeys renum procs} {
if { $renum == 1 } {
return [berkdb random_int 1 [expr $nkeys - $procs]]
} elseif { [is_record_based $method] == 1 } {
return [berkdb random_int 1 $nkeys]
} else {
return [berkdb random_int 0 [expr $nkeys - 1]]
}
}
# 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
|