1# See the file LICENSE for redistribution information. 2# 3# Copyright (c) 2000,2008 Oracle. All rights reserved. 4# 5# $Id: recd012.tcl,v 12.6 2008/01/08 20:58:53 bostic Exp $ 6# 7# TEST recd012 8# TEST Test of log file ID management. [#2288] 9# TEST Test recovery handling of file opens and closes. 10proc recd012 { method {start 0} \ 11 {niter 49} {noutiter 25} {niniter 100} {ndbs 5} args } { 12 source ./include.tcl 13 14 set tnum "012" 15 set pagesize 512 16 17 if { $is_qnx_test } { 18 set niter 40 19 } 20 21 puts "Recd$tnum $method ($args): Test recovery file management." 22 set pgindex [lsearch -exact $args "-pagesize"] 23 if { $pgindex != -1 } { 24 puts "Recd012: skipping for specific pagesizes" 25 return 26 } 27 28 for { set i $start } { $i <= $niter } { incr i } { 29 env_cleanup $testdir 30 31 # For repeatability, we pass in the iteration number 32 # as a parameter and use that in recd012_body to seed 33 # the random number generator to randomize our operations. 34 # This lets us re-run a potentially failing iteration 35 # without having to start from the beginning and work 36 # our way to it. 37 # 38 # The number of databases ranges from 4 to 8 and is 39 # a function of $niter 40 # set ndbs [expr ($i % 5) + 4] 41 42 recd012_body \ 43 $method $ndbs $i $noutiter $niniter $pagesize $tnum $args 44 } 45} 46 47proc recd012_body { method {ndbs 5} iter noutiter niniter psz tnum {largs ""} } { 48 global alphabet rand_init fixed_len recd012_ofkey recd012_ofckptkey 49 source ./include.tcl 50 51 set largs [convert_args $method $largs] 52 set omethod [convert_method $method] 53 54 puts "\tRecd$tnum $method ($largs): Iteration $iter" 55 puts "\t\tRecd$tnum.a: Create environment and $ndbs databases." 56 57 # We run out of lockers during some of the recovery runs, so 58 # we need to make sure that we specify a DB_CONFIG that will 59 # give us enough lockers. 60 set f [open $testdir/DB_CONFIG w] 61 puts $f "set_lk_max_lockers 5000" 62 close $f 63 64 set flags "-create -txn -home $testdir" 65 set env_cmd "berkdb_env $flags" 66 error_check_good env_remove [berkdb envremove -home $testdir] 0 67 set dbenv [eval $env_cmd] 68 error_check_good dbenv [is_valid_env $dbenv] TRUE 69 70 # Initialize random number generator based on $iter. 71 berkdb srand [expr $iter + $rand_init] 72 73 # Initialize database that keeps track of number of open files (so 74 # we don't run out of descriptors). 75 set ofname of.db 76 set txn [$dbenv txn] 77 error_check_good open_txn_begin [is_valid_txn $txn $dbenv] TRUE 78 set ofdb [berkdb_open -env $dbenv -txn $txn\ 79 -create -dup -mode 0644 -btree -pagesize 512 $ofname] 80 error_check_good of_open [is_valid_db $ofdb] TRUE 81 error_check_good open_txn_commit [$txn commit] 0 82 set oftxn [$dbenv txn] 83 error_check_good of_txn [is_valid_txn $oftxn $dbenv] TRUE 84 error_check_good of_put [$ofdb put -txn $oftxn $recd012_ofkey 1] 0 85 error_check_good of_put2 [$ofdb put -txn $oftxn $recd012_ofckptkey 0] 0 86 error_check_good of_put3 [$ofdb put -txn $oftxn $recd012_ofckptkey 0] 0 87 error_check_good of_txn_commit [$oftxn commit] 0 88 error_check_good of_close [$ofdb close] 0 89 90 # Create ndbs databases to work in, and a file listing db names to 91 # pick from. 92 set f [open $testdir/dblist w] 93 94 set oflags "-auto_commit -env $dbenv \ 95 -create -mode 0644 -pagesize $psz $largs $omethod" 96 for { set i 0 } { $i < $ndbs } { incr i } { 97 # 50-50 chance of being a subdb, unless we're a queue. 98 if { [berkdb random_int 0 1] || [is_queue $method] } { 99 # not a subdb 100 set dbname recd$tnum-$i.db 101 } else { 102 # subdb 103 set dbname "recd$tnum-subdb.db s$i" 104 } 105 puts $f $dbname 106 set db [eval berkdb_open $oflags $dbname] 107 error_check_good db($i) [is_valid_db $db] TRUE 108 error_check_good db($i)_close [$db close] 0 109 } 110 close $f 111 error_check_good env_close [$dbenv close] 0 112 113 # Now we get to the meat of things. Our goal is to do some number 114 # of opens, closes, updates, and shutdowns (simulated here by a 115 # close of all open handles and a close/reopen of the environment, 116 # with or without an envremove), matching the regular expression 117 # 118 # ((O[OUC]+S)+R+V) 119 # 120 # We'll repeat the inner + a random number up to $niniter times, 121 # and the outer + a random number up to $noutiter times. 122 # 123 # In order to simulate shutdowns, we'll perform the opens, closes, 124 # and updates in a separate process, which we'll exit without closing 125 # all handles properly. The environment will be left lying around 126 # before we run recovery 50% of the time. 127 set out [berkdb random_int 1 $noutiter] 128 puts \ 129 "\t\tRecd$tnum.b: Performing $out recoveries of up to $niniter ops." 130 for { set i 0 } { $i < $out } { incr i } { 131 set child [open "|$tclsh_path" w] 132 133 # For performance, don't source everything, 134 # just what we'll need. 135 puts $child "load $tcllib" 136 puts $child "set fixed_len $fixed_len" 137 puts $child "source $src_root/test/testutils.tcl" 138 puts $child "source $src_root/test/recd$tnum.tcl" 139 140 set rnd [expr $iter * 10000 + $i * 100 + $rand_init] 141 142 # Go. 143 berkdb debug_check 144 puts $child "recd012_dochild {$env_cmd} $rnd $i $niniter\ 145 $ndbs $tnum $method $ofname $largs" 146 close $child 147 148 # Run recovery 0-3 times. 149 set nrecs [berkdb random_int 0 3] 150 for { set j 0 } { $j < $nrecs } { incr j } { 151 berkdb debug_check 152 set ret [catch {exec $util_path/db_recover \ 153 -h $testdir} res] 154 if { $ret != 0 } { 155 puts "FAIL: db_recover returned with nonzero\ 156 exit status, output as follows:" 157 file mkdir /tmp/12out 158 set fd [open /tmp/12out/[pid] w] 159 puts $fd $res 160 close $fd 161 } 162 error_check_good recover($j) $ret 0 163 } 164 } 165 166 # Run recovery one final time; it doesn't make sense to 167 # check integrity if we do not. 168 set ret [catch {exec $util_path/db_recover -h $testdir} res] 169 if { $ret != 0 } { 170 puts "FAIL: db_recover returned with nonzero\ 171 exit status, output as follows:" 172 puts $res 173 } 174 175 # Make sure each datum is the correct filename. 176 puts "\t\tRecd$tnum.c: Checking data integrity." 177 set dbenv [berkdb_env -create -private -home $testdir] 178 error_check_good env_open_integrity [is_valid_env $dbenv] TRUE 179 set f [open $testdir/dblist r] 180 set i 0 181 while { [gets $f dbinfo] > 0 } { 182 set db [eval berkdb_open -env $dbenv $dbinfo] 183 error_check_good dbopen($dbinfo) [is_valid_db $db] TRUE 184 185 set dbc [$db cursor] 186 error_check_good cursor [is_valid_cursor $dbc $db] TRUE 187 188 for { set dbt [$dbc get -first] } { [llength $dbt] > 0 } \ 189 { set dbt [$dbc get -next] } { 190 error_check_good integrity [lindex [lindex $dbt 0] 1] \ 191 [pad_data $method $dbinfo] 192 } 193 error_check_good dbc_close [$dbc close] 0 194 error_check_good db_close [$db close] 0 195 } 196 close $f 197 error_check_good env_close_integrity [$dbenv close] 0 198 199 # Verify 200 error_check_good verify \ 201 [verify_dir $testdir "\t\tRecd$tnum.d: " 0 0 1] 0 202} 203 204proc recd012_dochild { env_cmd rnd outiter niniter ndbs tnum method\ 205 ofname args } { 206 global recd012_ofkey 207 source ./include.tcl 208 if { [is_record_based $method] } { 209 set keybase "" 210 } else { 211 set keybase .[repeat abcdefghijklmnopqrstuvwxyz 4] 212 } 213 214 # Initialize our random number generator, repeatably based on an arg. 215 berkdb srand $rnd 216 217 # Open our env. 218 set dbenv [eval $env_cmd] 219 error_check_good env_open [is_valid_env $dbenv] TRUE 220 221 # Find out how many databases appear to be open in the log--we 222 # don't want recovery to run out of filehandles. 223 set txn [$dbenv txn] 224 error_check_good child_txn_begin [is_valid_txn $txn $dbenv] TRUE 225 set ofdb [berkdb_open -env $dbenv -txn $txn $ofname] 226 error_check_good child_txn_commit [$txn commit] 0 227 228 set oftxn [$dbenv txn] 229 error_check_good of_txn [is_valid_txn $oftxn $dbenv] TRUE 230 set dbt [$ofdb get -txn $oftxn $recd012_ofkey] 231 error_check_good of_get [lindex [lindex $dbt 0] 0] $recd012_ofkey 232 set nopenfiles [lindex [lindex $dbt 0] 1] 233 234 error_check_good of_commit [$oftxn commit] 0 235 236 # Read our dbnames 237 set f [open $testdir/dblist r] 238 set i 0 239 while { [gets $f dbname($i)] > 0 } { 240 incr i 241 } 242 close $f 243 244 # We now have $ndbs extant databases. 245 # Open one of them, just to get us started. 246 set opendbs {} 247 set oflags "-env $dbenv $args" 248 249 # Start a transaction, just to get us started. 250 set curtxn [$dbenv txn] 251 error_check_good txn [is_valid_txn $curtxn $dbenv] TRUE 252 253 # Inner loop. Do $in iterations of a random open, close, or 254 # update, where $in is between 1 and $niniter. 255 set in [berkdb random_int 1 $niniter] 256 for { set j 0 } { $j < $in } { incr j } { 257 set op [berkdb random_int 0 2] 258 switch $op { 259 0 { 260 # Open. 261 recd012_open 262 } 263 1 { 264 # Update. Put random-number$keybase as key, 265 # filename as data, into random database. 266 set num_open [llength $opendbs] 267 if { $num_open == 0 } { 268 # If none are open, do an open first. 269 recd012_open 270 set num_open [llength $opendbs] 271 } 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] 276 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 281 282 # One time in four, commit the transaction. 283 if { [berkdb random_int 0 3] == 0 && 0 } { 284 error_check_good txn_recommit \ 285 [$curtxn commit] 0 286 set curtxn [$dbenv txn] 287 error_check_good txn_reopen \ 288 [is_valid_txn $curtxn $dbenv] TRUE 289 } 290 } 291 2 { 292 # Close. 293 if { [llength $opendbs] == 0 } { 294 # If none are open, open instead of closing. 295 recd012_open 296 continue 297 } 298 299 # Commit curtxn first, lest we self-deadlock. 300 error_check_good txn_recommit [$curtxn commit] 0 301 302 # Do it. 303 set which [berkdb random_int 0 \ 304 [expr [llength $opendbs] - 1]] 305 306 set db [lindex [lindex $opendbs $which] 0] 307 error_check_good db_choice [is_valid_db $db] TRUE 308 global errorCode errorInfo 309 310 error_check_good db_close \ 311 [[lindex [lindex $opendbs $which] 0] close] 0 312 313 set opendbs [lreplace $opendbs $which $which] 314 incr nopenfiles -1 315 316 # Reopen txn. 317 set curtxn [$dbenv txn] 318 error_check_good txn_reopen \ 319 [is_valid_txn $curtxn $dbenv] TRUE 320 } 321 } 322 323 # One time in two hundred, checkpoint. 324 if { [berkdb random_int 0 199] == 0 } { 325 puts "\t\t\tRecd$tnum:\ 326 Random checkpoint after operation $outiter.$j." 327 error_check_good txn_ckpt \ 328 [$dbenv txn_checkpoint] 0 329 set nopenfiles \ 330 [recd012_nopenfiles_ckpt $dbenv $ofdb $nopenfiles] 331 } 332 } 333 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 336 # ourselves. 337 error_check_good curtxn_commit [$curtxn commit] 0 338 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 347} 348 349proc recd012_open { } { 350 # This is basically an inline and has to modify curtxn, 351 # so use upvars. 352 upvar curtxn curtxn 353 upvar ndbs ndbs 354 upvar dbname dbname 355 upvar dbenv dbenv 356 upvar oflags oflags 357 upvar opendbs opendbs 358 upvar nopenfiles nopenfiles 359 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" 364 return -code break 365 } 366 367 # Commit curtxn first, lest we self-deadlock. 368 error_check_good txn_recommit \ 369 [$curtxn commit] 0 370 371 # Do it. 372 set which [berkdb random_int 0 [expr $ndbs - 1]] 373 374 set db [eval berkdb_open -auto_commit $oflags $dbname($which)] 375 376 lappend opendbs [list $db $dbname($which)] 377 378 # Reopen txn. 379 set curtxn [$dbenv txn] 380 error_check_good txn_reopen [is_valid_txn $curtxn $dbenv] TRUE 381 382 incr nopenfiles 383} 384 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 391# previous. 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. 395proc recd012_nopenfiles_ckpt { env db nopenfiles } { 396 global recd012_ofckptkey 397 set txn [$env txn] 398 error_check_good nopenfiles_ckpt_txn [is_valid_txn $txn $env] TRUE 399 400 set dbc [$db cursor -txn $txn] 401 error_check_good db_cursor [is_valid_cursor $dbc $db] TRUE 402 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 406 407 set discard [lindex [lindex $dbt 0] 1] 408 error_check_good del [$dbc del] 0 409 410 set nopenfiles [expr $nopenfiles - $discard] 411 412 # Get the next ckpt value 413 set dbt [$dbc get -nextdup] 414 error_check_good set2 [llength $dbt] 1 415 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] 419 420 # Put this new number at the end of the dup set. 421 error_check_good put [$dbc put -keylast $recd012_ofckptkey $sincelast] 0 422 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 426 427 return $nopenfiles 428} 429 430# globals -- it's not worth passing these around, as they're constants 431set recd012_ofkey OPENFILES 432set recd012_ofckptkey CKPTS 433