1
# See the file LICENSE for redistribution information.
3
# Copyright (c) 2000-2002
4
# Sleepycat Software. All rights reserved.
9
# TEST Test of log file ID management. [#2288]
10
# TEST Test recovery handling of file opens and closes.
11
proc recd012 { method {start 0} \
12
{niter 49} {noutiter 25} {niniter 100} {ndbs 5} args } {
22
puts "Recd0$tnum $method ($args): Test recovery file management."
23
set pgindex [lsearch -exact $args "-pagesize"]
24
if { $pgindex != -1 } {
25
puts "Recd012: skipping for specific pagesizes"
29
for { set i $start } { $i <= $niter } { incr i } {
32
# For repeatability, we pass in the iteration number
33
# as a parameter and use that in recd012_body to seed
34
# the random number generator to randomize our operations.
35
# This lets us re-run a potentially failing iteration
36
# without having to start from the beginning and work
39
# The number of databases ranges from 4 to 8 and is
40
# a function of $niter
41
# set ndbs [expr ($i % 5) + 4]
44
$method $ndbs $i $noutiter $niniter $pagesize $tnum $args
48
proc recd012_body { method {ndbs 5} iter noutiter niniter psz tnum {largs ""} } {
49
global alphabet rand_init fixed_len recd012_ofkey recd012_ofckptkey
52
set largs [convert_args $method $largs]
53
set omethod [convert_method $method]
55
puts "\tRecd0$tnum $method ($largs): Iteration $iter"
56
puts "\t\tRecd0$tnum.a: Create environment and $ndbs databases."
58
# We run out of lockers during some of the recovery runs, so
59
# we need to make sure that we specify a DB_CONFIG that will
60
# give us enough lockers.
61
set f [open $testdir/DB_CONFIG w]
62
puts $f "set_lk_max_lockers 5000"
65
set flags "-create -txn -home $testdir"
66
set env_cmd "berkdb_env $flags"
67
error_check_good env_remove [berkdb envremove -home $testdir] 0
68
set dbenv [eval $env_cmd]
69
error_check_good dbenv [is_valid_env $dbenv] TRUE
71
# Initialize random number generator based on $iter.
72
berkdb srand [expr $iter + $rand_init]
74
# Initialize database that keeps track of number of open files (so
75
# we don't run out of descriptors).
78
error_check_good open_txn_begin [is_valid_txn $txn $dbenv] TRUE
79
set ofdb [berkdb_open -env $dbenv -txn $txn\
80
-create -dup -mode 0644 -btree -pagesize 512 $ofname]
81
error_check_good of_open [is_valid_db $ofdb] TRUE
82
error_check_good open_txn_commit [$txn commit] 0
83
set oftxn [$dbenv txn]
84
error_check_good of_txn [is_valid_txn $oftxn $dbenv] TRUE
85
error_check_good of_put [$ofdb put -txn $oftxn $recd012_ofkey 1] 0
86
error_check_good of_put2 [$ofdb put -txn $oftxn $recd012_ofckptkey 0] 0
87
error_check_good of_put3 [$ofdb put -txn $oftxn $recd012_ofckptkey 0] 0
88
error_check_good of_txn_commit [$oftxn commit] 0
89
error_check_good of_close [$ofdb close] 0
91
# Create ndbs databases to work in, and a file listing db names to
93
set f [open $testdir/dblist w]
95
set oflags "-auto_commit -env $dbenv \
96
-create -mode 0644 -pagesize $psz $largs $omethod"
97
for { set i 0 } { $i < $ndbs } { incr i } {
98
# 50-50 chance of being a subdb, unless we're a queue.
99
if { [berkdb random_int 0 1] || [is_queue $method] } {
101
set dbname recd0$tnum-$i.db
104
set dbname "recd0$tnum-subdb.db s$i"
107
set db [eval berkdb_open $oflags $dbname]
108
error_check_good db($i) [is_valid_db $db] TRUE
109
error_check_good db($i)_close [$db close] 0
112
error_check_good env_close [$dbenv close] 0
114
# Now we get to the meat of things. Our goal is to do some number
115
# of opens, closes, updates, and shutdowns (simulated here by a
116
# close of all open handles and a close/reopen of the environment,
117
# with or without an envremove), matching the regular expression
121
# We'll repeat the inner + a random number up to $niniter times,
122
# and the outer + a random number up to $noutiter times.
124
# In order to simulate shutdowns, we'll perform the opens, closes,
125
# and updates in a separate process, which we'll exit without closing
126
# all handles properly. The environment will be left lying around
127
# before we run recovery 50% of the time.
128
set out [berkdb random_int 1 $noutiter]
130
"\t\tRecd0$tnum.b: Performing $out recoveries of up to $niniter ops."
131
for { set i 0 } { $i < $out } { incr i } {
132
set child [open "|$tclsh_path" w]
134
# For performance, don't source everything,
135
# just what we'll need.
136
puts $child "load $tcllib"
137
puts $child "set fixed_len $fixed_len"
138
puts $child "source $src_root/test/testutils.tcl"
139
puts $child "source $src_root/test/recd0$tnum.tcl"
141
set rnd [expr $iter * 10000 + $i * 100 + $rand_init]
145
puts $child "recd012_dochild {$env_cmd} $rnd $i $niniter\
146
$ndbs $tnum $method $ofname $largs"
149
# Run recovery 0-3 times.
150
set nrecs [berkdb random_int 0 3]
151
for { set j 0 } { $j < $nrecs } { incr j } {
153
set ret [catch {exec $util_path/db_recover \
156
puts "FAIL: db_recover returned with nonzero\
157
exit status, output as follows:"
158
file mkdir /tmp/12out
159
set fd [open /tmp/12out/[pid] w]
163
error_check_good recover($j) $ret 0
167
# Run recovery one final time; it doesn't make sense to
168
# check integrity if we do not.
169
set ret [catch {exec $util_path/db_recover -h $testdir} res]
171
puts "FAIL: db_recover returned with nonzero\
172
exit status, output as follows:"
176
# Make sure each datum is the correct filename.
177
puts "\t\tRecd0$tnum.c: Checking data integrity."
178
set dbenv [berkdb_env -create -private -home $testdir]
179
error_check_good env_open_integrity [is_valid_env $dbenv] TRUE
180
set f [open $testdir/dblist r]
182
while { [gets $f dbinfo] > 0 } {
183
set db [eval berkdb_open -env $dbenv $dbinfo]
184
error_check_good dbopen($dbinfo) [is_valid_db $db] TRUE
187
error_check_good cursor [is_valid_cursor $dbc $db] TRUE
189
for { set dbt [$dbc get -first] } { [llength $dbt] > 0 } \
190
{ set dbt [$dbc get -next] } {
191
error_check_good integrity [lindex [lindex $dbt 0] 1] \
192
[pad_data $method $dbinfo]
194
error_check_good dbc_close [$dbc close] 0
195
error_check_good db_close [$db close] 0
198
error_check_good env_close_integrity [$dbenv close] 0
201
error_check_good verify \
202
[verify_dir $testdir "\t\tRecd0$tnum.d: " 0 0 1] 0
205
proc recd012_dochild { env_cmd rnd outiter niniter ndbs tnum method\
209
if { [is_record_based $method] } {
212
set keybase .[repeat abcdefghijklmnopqrstuvwxyz 4]
215
# Initialize our random number generator, repeatably based on an arg.
219
set dbenv [eval $env_cmd]
220
error_check_good env_open [is_valid_env $dbenv] TRUE
222
# Find out how many databases appear to be open in the log--we
223
# don't want recovery to run out of filehandles.
225
error_check_good child_txn_begin [is_valid_txn $txn $dbenv] TRUE
226
set ofdb [berkdb_open -env $dbenv -txn $txn $ofname]
227
error_check_good child_txn_commit [$txn commit] 0
229
set oftxn [$dbenv txn]
230
error_check_good of_txn [is_valid_txn $oftxn $dbenv] TRUE
231
set dbt [$ofdb get -txn $oftxn $recd012_ofkey]
232
error_check_good of_get [lindex [lindex $dbt 0] 0] $recd012_ofkey
233
set nopenfiles [lindex [lindex $dbt 0] 1]
235
error_check_good of_commit [$oftxn commit] 0
238
set f [open $testdir/dblist r]
240
while { [gets $f dbname($i)] > 0 } {
245
# We now have $ndbs extant databases.
246
# Open one of them, just to get us started.
248
set oflags "-env $dbenv $args"
250
# Start a transaction, just to get us started.
251
set curtxn [$dbenv txn]
252
error_check_good txn [is_valid_txn $curtxn $dbenv] TRUE
254
# Inner loop. Do $in iterations of a random open, close, or
255
# update, where $in is between 1 and $niniter.
256
set in [berkdb random_int 1 $niniter]
257
for { set j 0 } { $j < $in } { incr j } {
258
set op [berkdb random_int 0 2]
265
# Update. Put random-number$keybase as key,
266
# filename as data, into random database.
267
set num_open [llength $opendbs]
268
if { $num_open == 0 } {
269
# If none are open, do an open first.
272
set n [berkdb random_int 0 [expr $num_open - 1]]
273
set pair [lindex $opendbs $n]
274
set udb [lindex $pair 0]
275
set uname [lindex $pair 1]
277
set key [berkdb random_int 1000 1999]$keybase
278
set data [chop_data $method $uname]
279
error_check_good put($uname,$udb,$key,$data) \
280
[$udb put -txn $curtxn $key $data] 0
282
# One time in four, commit the transaction.
283
if { [berkdb random_int 0 3] == 0 && 0 } {
284
error_check_good txn_recommit \
286
set curtxn [$dbenv txn]
287
error_check_good txn_reopen \
288
[is_valid_txn $curtxn $dbenv] TRUE
293
if { [llength $opendbs] == 0 } {
294
# If none are open, open instead of closing.
299
# Commit curtxn first, lest we self-deadlock.
300
error_check_good txn_recommit [$curtxn commit] 0
303
set which [berkdb random_int 0 \
304
[expr [llength $opendbs] - 1]]
306
set db [lindex [lindex $opendbs $which] 0]
307
error_check_good db_choice [is_valid_db $db] TRUE
308
global errorCode errorInfo
310
error_check_good db_close \
311
[[lindex [lindex $opendbs $which] 0] close] 0
313
set opendbs [lreplace $opendbs $which $which]
317
set curtxn [$dbenv txn]
318
error_check_good txn_reopen \
319
[is_valid_txn $curtxn $dbenv] TRUE
323
# One time in two hundred, checkpoint.
324
if { [berkdb random_int 0 199] == 0 } {
325
puts "\t\t\tRecd0$tnum:\
326
Random checkpoint after operation $outiter.$j."
327
error_check_good txn_ckpt \
328
[$dbenv txn_checkpoint] 0
330
[recd012_nopenfiles_ckpt $dbenv $ofdb $nopenfiles]
334
# We have to commit curtxn. It'd be kind of nice not to, but
335
# if we start in again without running recovery, we may block
337
error_check_good curtxn_commit [$curtxn commit] 0
339
# Put back the new number of open files.
340
set oftxn [$dbenv txn]
341
error_check_good of_txn [is_valid_txn $oftxn $dbenv] TRUE
342
error_check_good of_del [$ofdb del -txn $oftxn $recd012_ofkey] 0
343
error_check_good of_put \
344
[$ofdb put -txn $oftxn $recd012_ofkey $nopenfiles] 0
345
error_check_good of_commit [$oftxn commit] 0
346
error_check_good ofdb_close [$ofdb close] 0
349
proc recd012_open { } {
350
# This is basically an inline and has to modify curtxn,
357
upvar opendbs opendbs
358
upvar nopenfiles nopenfiles
360
# Return without an open if we've already opened too many files--
361
# we don't want to make recovery run out of filehandles.
362
if { $nopenfiles > 30 } {
363
#puts "skipping--too many open files"
367
# Commit curtxn first, lest we self-deadlock.
368
error_check_good txn_recommit \
372
set which [berkdb random_int 0 [expr $ndbs - 1]]
374
set db [eval berkdb_open -auto_commit $oflags $dbname($which)]
376
lappend opendbs [list $db $dbname($which)]
379
set curtxn [$dbenv txn]
380
error_check_good txn_reopen [is_valid_txn $curtxn $dbenv] TRUE
385
# Update the database containing the number of files that db_recover has
386
# to contend with--we want to avoid letting it run out of file descriptors.
387
# We do this by keeping track of the number of unclosed opens since the
388
# checkpoint before last.
389
# $recd012_ofkey stores this current value; the two dups available
390
# at $recd012_ofckptkey store the number of opens since the last checkpoint
392
# Thus, if the current value is 17 when we do a checkpoint, and the
393
# stored values are 3 and 8, the new current value (which we return)
394
# is 14, and the new stored values are 8 and 6.
395
proc recd012_nopenfiles_ckpt { env db nopenfiles } {
396
global recd012_ofckptkey
398
error_check_good nopenfiles_ckpt_txn [is_valid_txn $txn $env] TRUE
400
set dbc [$db cursor -txn $txn]
401
error_check_good db_cursor [is_valid_cursor $dbc $db] TRUE
403
# Get the first ckpt value and delete it.
404
set dbt [$dbc get -set $recd012_ofckptkey]
405
error_check_good set [llength $dbt] 1
407
set discard [lindex [lindex $dbt 0] 1]
408
error_check_good del [$dbc del] 0
410
set nopenfiles [expr $nopenfiles - $discard]
412
# Get the next ckpt value
413
set dbt [$dbc get -nextdup]
414
error_check_good set2 [llength $dbt] 1
416
# Calculate how many opens we've had since this checkpoint before last.
417
set onlast [lindex [lindex $dbt 0] 1]
418
set sincelast [expr $nopenfiles - $onlast]
420
# Put this new number at the end of the dup set.
421
error_check_good put [$dbc put -keylast $recd012_ofckptkey $sincelast] 0
423
# We should never deadlock since we're the only one in this db.
424
error_check_good dbc_close [$dbc close] 0
425
error_check_good txn_commit [$txn commit] 0
430
# globals -- it's not worth passing these around, as they're constants
431
set recd012_ofkey OPENFILES
432
set recd012_ofckptkey CKPTS