1# See the file LICENSE for redistribution information. 2# 3# Copyright (c) 2000-2009 Oracle. All rights reserved. 4# 5# $Id$ 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 or partitioned. 98 if { [berkdb random_int 0 1] || \ 99 [is_queue $method] || [is_partitioned $largs] } { 100 # not a subdb 101 set dbname recd$tnum-$i.db 102 } else { 103 # subdb 104 set dbname "recd$tnum-subdb.db s$i" 105 } 106 puts $f $dbname 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 110 } 111 close $f 112 error_check_good env_close [$dbenv close] 0 113 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 118 # 119 # ((O[OUC]+S)+R+V) 120 # 121 # We'll repeat the inner + a random number up to $niniter times, 122 # and the outer + a random number up to $noutiter times. 123 # 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] 129 puts \ 130 "\t\tRecd$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] 133 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/recd$tnum.tcl" 140 141 set rnd [expr $iter * 10000 + $i * 100 + $rand_init] 142 143 # Go. 144 berkdb debug_check 145 puts $child "recd012_dochild {$env_cmd} $rnd $i $niniter\ 146 $ndbs $tnum $method $ofname $largs" 147 close $child 148 149 # Run recovery 0-3 times. 150 set nrecs [berkdb random_int 0 3] 151 for { set j 0 } { $j < $nrecs } { incr j } { 152 berkdb debug_check 153 set ret [catch {exec $util_path/db_recover \ 154 -h $testdir} res] 155 if { $ret != 0 } { 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] 160 puts $fd $res 161 close $fd 162 } 163 error_check_good recover($j) $ret 0 164 } 165 } 166 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] 170 if { $ret != 0 } { 171 puts "FAIL: db_recover returned with nonzero\ 172 exit status, output as follows:" 173 puts $res 174 } 175 176 # Make sure each datum is the correct filename. 177 puts "\t\tRecd$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] 181 set i 0 182 while { [gets $f dbinfo] > 0 } { 183 set db [eval berkdb_open -env $dbenv $largs $dbinfo] 184 error_check_good dbopen($dbinfo) [is_valid_db $db] TRUE 185 186 set dbc [$db cursor] 187 error_check_good cursor [is_valid_cursor $dbc $db] TRUE 188 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] 193 } 194 error_check_good dbc_close [$dbc close] 0 195 error_check_good db_close [$db close] 0 196 } 197 close $f 198 error_check_good env_close_integrity [$dbenv close] 0 199 200 # Verify 201 error_check_good verify \ 202 [verify_dir $testdir "\t\tRecd$tnum.d: " 0 0 1] 0 203} 204 205proc recd012_dochild { env_cmd rnd outiter niniter ndbs tnum method\ 206 ofname args } { 207 global recd012_ofkey 208 source ./include.tcl 209 if { [is_record_based $method] } { 210 set keybase "" 211 } else { 212 set keybase .[repeat abcdefghijklmnopqrstuvwxyz 4] 213 } 214 215 # Initialize our random number generator, repeatably based on an arg. 216 berkdb srand $rnd 217 218 # Open our env. 219 set dbenv [eval $env_cmd] 220 error_check_good env_open [is_valid_env $dbenv] TRUE 221 222 # Find out how many databases appear to be open in the log--we 223 # don't want recovery to run out of filehandles. 224 set txn [$dbenv txn] 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 228 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] 234 235 error_check_good of_commit [$oftxn commit] 0 236 237 # Read our dbnames 238 set f [open $testdir/dblist r] 239 set i 0 240 while { [gets $f dbname($i)] > 0 } { 241 incr i 242 } 243 close $f 244 245 # We now have $ndbs extant databases. 246 # Open one of them, just to get us started. 247 set opendbs {} 248 set oflags "-env $dbenv $args" 249 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 253 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] 259 switch $op { 260 0 { 261 # Open. 262 recd012_open 263 } 264 1 { 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. 270 recd012_open 271 set num_open [llength $opendbs] 272 } 273 set n [berkdb random_int 0 [expr $num_open - 1]] 274 set pair [lindex $opendbs $n] 275 set udb [lindex $pair 0] 276 set uname [lindex $pair 1] 277 278 set key [berkdb random_int 1000 1999]$keybase 279 set data [chop_data $method $uname] 280 error_check_good put($uname,$udb,$key,$data) \ 281 [$udb put -txn $curtxn $key $data] 0 282 283 # One time in four, commit the transaction. 284 if { [berkdb random_int 0 3] == 0 && 0 } { 285 error_check_good txn_recommit \ 286 [$curtxn commit] 0 287 set curtxn [$dbenv txn] 288 error_check_good txn_reopen \ 289 [is_valid_txn $curtxn $dbenv] TRUE 290 } 291 } 292 2 { 293 # Close. 294 if { [llength $opendbs] == 0 } { 295 # If none are open, open instead of closing. 296 recd012_open 297 continue 298 } 299 300 # Commit curtxn first, lest we self-deadlock. 301 error_check_good txn_recommit [$curtxn commit] 0 302 303 # Do it. 304 set which [berkdb random_int 0 \ 305 [expr [llength $opendbs] - 1]] 306 307 set db [lindex [lindex $opendbs $which] 0] 308 error_check_good db_choice [is_valid_db $db] TRUE 309 global errorCode errorInfo 310 311 error_check_good db_close \ 312 [[lindex [lindex $opendbs $which] 0] close] 0 313 314 set opendbs [lreplace $opendbs $which $which] 315 incr nopenfiles -1 316 317 # Reopen txn. 318 set curtxn [$dbenv txn] 319 error_check_good txn_reopen \ 320 [is_valid_txn $curtxn $dbenv] TRUE 321 } 322 } 323 324 # One time in two hundred, checkpoint. 325 if { [berkdb random_int 0 199] == 0 } { 326 puts "\t\t\tRecd$tnum:\ 327 Random checkpoint after operation $outiter.$j." 328 error_check_good txn_ckpt \ 329 [$dbenv txn_checkpoint] 0 330 set nopenfiles \ 331 [recd012_nopenfiles_ckpt $dbenv $ofdb $nopenfiles] 332 } 333 } 334 335 # We have to commit curtxn. It'd be kind of nice not to, but 336 # if we start in again without running recovery, we may block 337 # ourselves. 338 error_check_good curtxn_commit [$curtxn commit] 0 339 340 # Put back the new number of open files. 341 set oftxn [$dbenv txn] 342 error_check_good of_txn [is_valid_txn $oftxn $dbenv] TRUE 343 error_check_good of_del [$ofdb del -txn $oftxn $recd012_ofkey] 0 344 error_check_good of_put \ 345 [$ofdb put -txn $oftxn $recd012_ofkey $nopenfiles] 0 346 error_check_good of_commit [$oftxn commit] 0 347 error_check_good ofdb_close [$ofdb close] 0 348} 349 350proc recd012_open { } { 351 # This is basically an inline and has to modify curtxn, 352 # so use upvars. 353 upvar curtxn curtxn 354 upvar ndbs ndbs 355 upvar dbname dbname 356 upvar dbenv dbenv 357 upvar oflags oflags 358 upvar opendbs opendbs 359 upvar nopenfiles nopenfiles 360 361 # Return without an open if we've already opened too many files-- 362 # we don't want to make recovery run out of filehandles. 363 if { $nopenfiles > 30 } { 364 #puts "skipping--too many open files" 365 return -code break 366 } 367 368 # Commit curtxn first, lest we self-deadlock. 369 error_check_good txn_recommit \ 370 [$curtxn commit] 0 371 372 # Do it. 373 set which [berkdb random_int 0 [expr $ndbs - 1]] 374 375 set db [eval berkdb_open -auto_commit $oflags $dbname($which)] 376 377 lappend opendbs [list $db $dbname($which)] 378 379 # Reopen txn. 380 set curtxn [$dbenv txn] 381 error_check_good txn_reopen [is_valid_txn $curtxn $dbenv] TRUE 382 383 incr nopenfiles 384} 385 386# Update the database containing the number of files that db_recover has 387# to contend with--we want to avoid letting it run out of file descriptors. 388# We do this by keeping track of the number of unclosed opens since the 389# checkpoint before last. 390# $recd012_ofkey stores this current value; the two dups available 391# at $recd012_ofckptkey store the number of opens since the last checkpoint 392# previous. 393# Thus, if the current value is 17 when we do a checkpoint, and the 394# stored values are 3 and 8, the new current value (which we return) 395# is 14, and the new stored values are 8 and 6. 396proc recd012_nopenfiles_ckpt { env db nopenfiles } { 397 global recd012_ofckptkey 398 set txn [$env txn] 399 error_check_good nopenfiles_ckpt_txn [is_valid_txn $txn $env] TRUE 400 401 set dbc [$db cursor -txn $txn] 402 error_check_good db_cursor [is_valid_cursor $dbc $db] TRUE 403 404 # Get the first ckpt value and delete it. 405 set dbt [$dbc get -set $recd012_ofckptkey] 406 error_check_good set [llength $dbt] 1 407 408 set discard [lindex [lindex $dbt 0] 1] 409 error_check_good del [$dbc del] 0 410 411 set nopenfiles [expr $nopenfiles - $discard] 412 413 # Get the next ckpt value 414 set dbt [$dbc get -nextdup] 415 error_check_good set2 [llength $dbt] 1 416 417 # Calculate how many opens we've had since this checkpoint before last. 418 set onlast [lindex [lindex $dbt 0] 1] 419 set sincelast [expr $nopenfiles - $onlast] 420 421 # Put this new number at the end of the dup set. 422 error_check_good put [$dbc put -keylast $recd012_ofckptkey $sincelast] 0 423 424 # We should never deadlock since we're the only one in this db. 425 error_check_good dbc_close [$dbc close] 0 426 error_check_good txn_commit [$txn commit] 0 427 428 return $nopenfiles 429} 430 431# globals -- it's not worth passing these around, as they're constants 432set recd012_ofkey OPENFILES 433set recd012_ofckptkey CKPTS 434