1# See the file LICENSE for redistribution information. 2# 3# Copyright (c) 2003,2008 Oracle. All rights reserved. 4# 5# $Id: rep017.tcl,v 12.17 2008/01/08 20:58:53 bostic Exp $ 6# 7# TEST rep017 8# TEST Concurrency with checkpoints. 9# TEST 10# TEST Verify that we achieve concurrency in the presence of checkpoints. 11# TEST Here are the checks that we wish to make: 12# TEST While dbenv1 is handling the checkpoint record: 13# TEST Subsequent in-order log records are accepted. 14# TEST Accepted PERM log records get NOTPERM 15# TEST A subsequent checkpoint gets NOTPERM 16# TEST After checkpoint completes, next txn returns PERM 17proc rep017 { method { niter 10 } { tnum "017" } args } { 18 19 source ./include.tcl 20 if { $is_windows9x_test == 1 } { 21 puts "Skipping replication test on Win 9x platform." 22 return 23 } 24 25 # Run for all access methods. 26 if { $checking_valid_methods } { 27 return "ALL" 28 } 29 30 set args [convert_args $method $args] 31 set logsets [create_logsets 2] 32 33 # Run the body of the test with and without recovery. 34 foreach r $test_recopts { 35 foreach l $logsets { 36 set logindex [lsearch -exact $l "in-memory"] 37 if { $r == "-recover" && $logindex != -1 } { 38 puts "Rep$tnum: Skipping\ 39 for in-memory logs with -recover." 40 continue 41 } 42 43 puts "Rep$tnum ($method $r):\ 44 Concurrency with checkpoints." 45 puts "Rep$tnum: Master logs are [lindex $l 0]" 46 puts "Rep$tnum: Client logs are [lindex $l 1]" 47 rep017_sub $method $niter $tnum $l $r $args 48 } 49 } 50} 51 52proc rep017_sub { method niter tnum logset recargs largs } { 53 source ./include.tcl 54 global perm_response_list 55 global rep_verbose 56 global verbose_type 57 58 set verbargs "" 59 if { $rep_verbose == 1 } { 60 set verbargs " -verbose {$verbose_type on} " 61 } 62 63 env_cleanup $testdir 64 set omethod [convert_method $method] 65 66 replsetup $testdir/MSGQUEUEDIR 67 68 set masterdir $testdir/MASTERDIR 69 set clientdir $testdir/CLIENTDIR 70 71 file mkdir $masterdir 72 file mkdir $clientdir 73 set m_logtype [lindex $logset 0] 74 set c_logtype [lindex $logset 1] 75 76 # In-memory logs require a large log buffer, and cannot 77 # be used with -txn nosync. 78 set m_logargs [adjust_logargs $m_logtype] 79 set c_logargs [adjust_logargs $c_logtype] 80 set m_txnargs [adjust_txnargs $m_logtype] 81 set c_txnargs [adjust_txnargs $c_logtype] 82 83 # Open a master. 84 repladd 1 85 set ma_cmd "berkdb_env_noerr -create $verbargs \ 86 -log_max 1000000 $m_txnargs $m_logargs \ 87 -home $masterdir -rep_master -errpfx MASTER \ 88 -rep_transport \[list 1 replsend\]" 89 set masterenv [eval $ma_cmd $recargs] 90 91 # Open a client 92 repladd 2 93 set cl_cmd "berkdb_env_noerr -create -home $clientdir $verbargs \ 94 $c_txnargs $c_logargs -rep_client -errpfx CLIENT \ 95 -rep_transport \[list 2 replsend\]" 96 set clientenv [eval $cl_cmd $recargs] 97 98 # Bring the client online. 99 process_msgs "{$masterenv 1} {$clientenv 2}" 100 101 # Open database in master, make lots of changes so checkpoint 102 # will take a while, and propagate to client. 103 puts "\tRep$tnum.a: Create and populate database." 104 set dbname rep017.db 105 set db [eval "berkdb_open_noerr -create $omethod -auto_commit \ 106 -env $masterenv $largs $dbname"] 107 for { set i 1 } { $i <= $niter } { incr i } { 108 set t [$masterenv txn] 109 error_check_good db_put \ 110 [eval $db put -txn $t $i [chop_data $method data$i]] 0 111 error_check_good txn_commit [$t commit] 0 112 } 113 process_msgs "{$masterenv 1} {$clientenv 2}" 1 114 115 # Get the master's last LSN before the checkpoint 116 set pre_ckp_offset \ 117 [stat_field $masterenv log_stat "Current log file offset"] 118 119 puts "\tRep$tnum.b: Checkpoint on master." 120 error_check_good checkpoint [$masterenv txn_checkpoint] 0 121 122 # Now get ckp LSN 123 set ckp_lsn [stat_field $masterenv txn_stat "LSN of last checkpoint"] 124 set ckp_offset [lindex $ckp_lsn 1] 125 126 # Fork child process on client. It should process whatever 127 # it finds in the message queue -- just the checkpoint record, 128 # for now. It's run in the background so the parent can 129 # test for whether we're checkpointing at the same time. 130 # 131 puts "\tRep$tnum.c: Fork child process on client." 132 set pid [exec $tclsh_path $test_path/wrap.tcl \ 133 rep017script.tcl $testdir/repscript.log \ 134 $masterdir $clientdir $rep_verbose $verbose_type &] 135 136 137 # We need to wait until we know that the client is processing a 138 # checkpoint. The checkpoint will consist of some DBREG records 139 # followed by the actual checkpoint. So, if we've gotten records 140 # later than the last LSN when the master took the checkpoint, we've 141 # begin the checkpoint. By test design, we should not finish the 142 # checkpoint until this process has at least had a chance to run. 143 # 144 # In order to do this, we have handles open on the message 145 # queue from both this process and its child. This is not 146 # normally legal behavior for an application using Berkeley DB, 147 # but this test depends on the parent process doing things while 148 # the child is pausing in the middle of the checkpoint. We are 149 # very careful to control which process is handling which 150 # messages. 151 152 puts "\tRep$tnum.d: Test whether client is in checkpoint." 153 while { 1 } { 154 set client_off \ 155 [stat_field $clientenv log_stat "Current log file offset"] 156 157 if { $client_off > $pre_ckp_offset } { 158 if { $client_off > $ckp_offset } { 159 # We already completed the checkpoint and 160 # never got out of here. That's a bug in 161 # in the test. 162 error_check_good checkpoint_test \ 163 not_in_checkpoint should_be_in_checkpoint 164 } else { 165 break; 166 } 167 } else { 168 # Not yet up to checkpoint 169 tclsleep 1 170 } 171 } 172 173 # Main client processes checkpoint 2nd time and should get NOTPERM. 174 puts "\tRep$tnum.e: Commit and checkpoint return NOTPERM from client" 175 incr niter 176 set t [$masterenv txn] 177 error_check_good db_put [eval $db put \ 178 -txn $t $niter [chop_data $method data$niter]] 0 179 error_check_good txn_commit [$t commit] 0 180 error_check_good checkpoint [$masterenv txn_checkpoint] 0 181 set ckp2_lsn [stat_field $masterenv txn_stat "LSN of last checkpoint"] 182 183 process_msgs "{$clientenv 2}" 1 184 185 # Check that the checkpoint record got a NOTPERM 186 # Find the ckp LSN of the Master and then look for the response 187 # from that message in the client 188 set ckp_result "" 189 foreach i $perm_response_list { 190 # Everything in the list should be NOTPERM 191 if { [llength $i] == 0 } { 192 # Check for sentinel at beginning of list 193 continue; 194 } 195 set ckp_result [lindex $i 0] 196 error_check_good NOTPERM [is_substr $ckp_result NOTPERM] 1 197 if { [lindex $i 1] == $ckp2_lsn } { 198 break 199 } 200 } 201 error_check_bad perm_response $ckp_result "" 202 203 puts "\tRep$tnum.f: Waiting for child ..." 204 # Watch until the checkpoint is done. 205 watch_procs $pid 5 206 207 # Verify that the checkpoint is now complete on the client and 208 # that all later messages have been applied. 209 process_msgs "{$clientenv 2}" 1 210 set client_ckp [stat_field $clientenv txn_stat "LSN of last checkpoint"] 211 error_check_good matching_ckps $client_ckp $ckp2_lsn 212 213 set m_end [stat_field $masterenv log_stat "Current log file offset"] 214 set c_end [stat_field $clientenv log_stat "Current log file offset"] 215 error_check_good matching_lsn $c_end $m_end 216 217 # Finally, now that checkpoints are complete; perform another 218 # perm operation and make sure that it returns ISPERM. 219 puts "\tRep$tnum.g: No pending ckp; check for ISPERM" 220 incr niter 221 set t [$masterenv txn] 222 error_check_good db_put [eval $db put \ 223 -txn $t $niter [chop_data $method data$niter]] 0 224 error_check_good txn_commit [$t commit] 0 225 error_check_good checkpoint [$masterenv txn_checkpoint] 0 226 set ckp3_lsn [stat_field $masterenv txn_stat "LSN of last checkpoint"] 227 228 process_msgs "{$clientenv 2}" 1 229 230 # Check that the checkpoint and commit records got a ISPERM 231 # Find the ckp LSN of the Master and then look for the response 232 # from that message in the client 233 set ckp_result "" 234 foreach i $perm_response_list { 235 if { [llength $i] == 0 } { 236 # Check for sentinel at beginning of list 237 continue; 238 } 239 240 # Everything in the list should be ISPERM 241 set ckp_result [lindex $i 0] 242 error_check_good ISPERM [is_substr $ckp_result ISPERM] 1 243 if { [lindex $i 1] == $ckp3_lsn } { 244 break 245 } 246 } 247 error_check_bad perm_response $ckp_result "" 248 249 # Clean up. 250 error_check_good db_close [$db close] 0 251 error_check_good masterenv_close [$masterenv close] 0 252 error_check_good clientenv_close [$clientenv close] 0 253 254 replclose $testdir/MSGQUEUEDIR 255} 256