# See the file LICENSE for redistribution information. # # Copyright (c) 1996, 1997, 1998, 1999, 2000 # Sleepycat Software. All rights reserved. # # $Id: recd008.tcl,v 1.22 2000/12/07 19:13:46 sue Exp $ # # Recovery Test 8. # Test deeply nested transactions and many-child transactions. proc recd008 { method {breadth 4} {depth 4} args} { global kvals source ./include.tcl set args [convert_args $method $args] set omethod [convert_method $method] if { [is_record_based $method] == 1 } { puts "Recd008 skipping for method $method" return } puts "Recd008: $method $breadth X $depth deeply nested transactions" # Create the database and environment. env_cleanup $testdir set dbfile recd008.db puts "\tRecd008.a: create database" set db [eval {berkdb_open -create} $args $omethod $testdir/$dbfile] error_check_good dbopen [is_valid_db $db] TRUE # Make sure that we have enough entries to span a couple of # different pages. set did [open $dict] set count 0 while { [gets $did str] != -1 && $count < 1000 } { if { [string compare $omethod "-recno"] == 0 } { set key [expr $count + 1] } else { set key $str } if { $count == 500} { set p1 $key set kvals($p1) $str } set ret [$db put $key $str] error_check_good put $ret 0 incr count } close $did error_check_good db_close [$db close] 0 set txn_max [expr int([expr pow($breadth,$depth)])] if { $txn_max < 20 } { set txn_max 20 } puts "\tRecd008.b: create environment for $txn_max transactions" set eflags "-mode 0644 -create -txn_max $txn_max \ -txn -home $testdir" set env_cmd "berkdb env $eflags" set dbenv [eval $env_cmd] error_check_good env_open [is_valid_env $dbenv] TRUE reset_env $dbenv set rlist { { {recd008_parent abort ENV DB $p1 TXNID 1 1 $breadth $depth} "Recd008.c: child abort parent" } { {recd008_parent commit ENV DB $p1 TXNID 1 1 $breadth $depth} "Recd008.d: child commit parent" } } foreach pair $rlist { set cmd [subst [lindex $pair 0]] set msg [lindex $pair 1] op_recover abort $testdir $env_cmd $dbfile $cmd $msg recd008_setkval $dbfile $p1 op_recover commit $testdir $env_cmd $dbfile $cmd $msg recd008_setkval $dbfile $p1 } puts "\tRecd008.e: Verify db_printlog can read logfile" set tmpfile $testdir/printlog.out set stat [catch {exec $util_path/db_printlog -h $testdir \ > $tmpfile} ret] error_check_good db_printlog $stat 0 fileremove $tmpfile } proc recd008_setkval { dbfile p1 } { global kvals source ./include.tcl set db [berkdb_open $testdir/$dbfile] error_check_good dbopen [is_valid_db $db] TRUE set ret [$db get $p1] set kvals($p1) [lindex [lindex $ret 0] 1] } # This is a lot like the op_recover procedure. We cannot use that # because it was not meant to be called recursively. This proc # knows about depth/breadth and file naming so that recursive calls # don't overwrite various initial and afterop files, etc. # # The basic flow of this is: # (Initial file) # Parent begin transaction (in op_recover) # Parent starts children # Recursively call recd008_recover # (children modify p1) # Parent modifies p1 # (Afterop file) # Parent commit/abort (in op_recover) # (Final file) # Recovery test (in op_recover) proc recd008_parent { op env db p1key parent b0 d0 breadth depth } { global kvals source ./include.tcl # # Save copy of original data # Acquire lock on data # set olddata $kvals($p1key) set ret [$db get -rmw -txn $parent $p1key] set Dret [lindex [lindex $ret 0] 1] error_check_good get_parent_RMW $Dret $olddata # # Parent spawns off children # set ret [recd008_txn $op $env $db $p1key $parent \ $b0 $d0 $breadth $depth] puts "Child runs complete. Parent modifies data." # # Parent modifies p1 # set newdata $olddata.parent set ret [$db put -txn $parent $p1key $newdata] error_check_good db_put $ret 0 # # Save value in kvals for later comparison # switch $op { "commit" { set kvals($p1key) $newdata } "abort" { set kvals($p1key) $olddata } } return 0 } proc recd008_txn { op env db p1key parent b0 d0 breadth depth } { global log_log_record_types global kvals source ./include.tcl for {set d 1} {$d < $d0} {incr d} { puts -nonewline "\t" } puts "Recd008_txn: $op parent:$parent $breadth $depth ($b0 $d0)" # Save the initial file and open the environment and the file for {set b $b0} {$b <= $breadth} {incr b} { # # Begin child transaction # set t [$env txn -parent $parent] error_check_bad txn_begin $t NULL error_check_good txn_begin [is_valid_txn $t $env] TRUE set startd [expr $d0 + 1] set child $b:$startd:$t set olddata $kvals($p1key) set newdata $olddata.$child set ret [$db get -rmw -txn $t $p1key] set Dret [lindex [lindex $ret 0] 1] error_check_good get_parent_RMW $Dret $olddata # # Recursively call to set up nested transactions/children # for {set d $startd} {$d <= $depth} {incr d} { set ret [recd008_txn commit $env $db $p1key $t \ $b $d $breadth $depth] set ret [recd008_txn abort $env $db $p1key $t \ $b $d $breadth $depth] } # # Modifies p1. # set ret [$db put -txn $t $p1key $newdata] error_check_good db_put $ret 0 # # Commit or abort # for {set d 1} {$d < $startd} {incr d} { puts -nonewline "\t" } puts "Executing txn_$op:$t" error_check_good txn_$op:$t [$t $op] 0 for {set d 1} {$d < $startd} {incr d} { puts -nonewline "\t" } set ret [$db get -rmw -txn $parent $p1key] set Dret [lindex [lindex $ret 0] 1] switch $op { "commit" { puts "Command executed and committed." error_check_good get_parent_RMW $Dret $newdata set kvals($p1key) $newdata } "abort" { puts "Command executed and aborted." error_check_good get_parent_RMW $Dret $olddata set kvals($p1key) $olddata } } } return 0 }