1# See the file LICENSE for redistribution information. 2# 3# Copyright (c) 2004,2008 Oracle. All rights reserved. 4# 5# $Id: rep060.tcl,v 12.15 2008/01/08 20:58:53 bostic Exp $ 6# 7# TEST rep060 8# TEST Test of normally running clients and internal initialization. 9# TEST Have a client running normally, but slow/far behind the master. 10# TEST Then the master checkpoints and archives, causing the client 11# TEST to suddenly be thrown into internal init. This test tests 12# TEST that we clean up the old files/pages in mpool and dbreg. 13# TEST Also test same thing but the app holding an open dbp as well. 14# 15proc rep060 { method { niter 200 } { tnum "060" } args } { 16 17 source ./include.tcl 18 19 # Run for btree and queue only. 20 if { $checking_valid_methods } { 21 set test_methods {} 22 foreach method $valid_methods { 23 if { [is_btree $method] == 1 || \ 24 [is_queue $method] == 1 } { 25 lappend test_methods $method 26 } 27 } 28 return $test_methods 29 } 30 if { [is_btree $method] != 1 && [is_queue $method] != 1 } { 31 puts "Skipping rep060 for method $method." 32 return 33 } 34 35 set args [convert_args $method $args] 36 37 # This test needs to set its own pagesize. 38 set pgindex [lsearch -exact $args "-pagesize"] 39 if { $pgindex != -1 } { 40 puts "Rep$tnum: skipping for specific pagesizes" 41 return 42 } 43 44 set logsets [create_logsets 2] 45 46 # Run the body of the test with and without recovery, 47 # and with and without cleaning. Skip recovery with in-memory 48 # logging - it doesn't make sense. 49 # 50 # 'user' means that the "app" (the test in this case) has 51 # its own handle open to the database. 52 set opts { "" user } 53 foreach r $test_recopts { 54 foreach o $opts { 55 foreach l $logsets { 56 set logindex [lsearch -exact $l "in-memory"] 57 if { $r == "-recover" && $logindex != -1 } { 58 puts "Skipping rep$tnum for -recover\ 59 with in-memory logs." 60 continue 61 } 62 puts "Rep$tnum ($method $r $o $args): Test of\ 63 internal initialization and slow client." 64 puts "Rep$tnum: Master logs are [lindex $l 0]" 65 puts "Rep$tnum: Client logs are [lindex $l 1]" 66 rep060_sub $method $niter $tnum $l $r $o $args 67 } 68 } 69 } 70} 71 72proc rep060_sub { method niter tnum logset recargs opt largs } { 73 source ./include.tcl 74 global rep_verbose 75 global verbose_type 76 77 set verbargs "" 78 if { $rep_verbose == 1 } { 79 set verbargs " -verbose {$verbose_type on} " 80 } 81 82 env_cleanup $testdir 83 84 replsetup $testdir/MSGQUEUEDIR 85 86 set masterdir $testdir/MASTERDIR 87 set clientdir $testdir/CLIENTDIR 88 89 file mkdir $masterdir 90 file mkdir $clientdir 91 92 # Log size is small so we quickly create more than one. 93 # The documentation says that the log file must be at least 94 # four times the size of the in-memory log buffer. 95 set pagesize 4096 96 append largs " -pagesize $pagesize " 97 set log_max [expr $pagesize * 4] 98 99 set m_logtype [lindex $logset 0] 100 set c_logtype [lindex $logset 1] 101 102 # In-memory logs cannot be used with -txn nosync. 103 set m_logargs [adjust_logargs $m_logtype] 104 set c_logargs [adjust_logargs $c_logtype] 105 set m_txnargs [adjust_txnargs $m_logtype] 106 set c_txnargs [adjust_txnargs $c_logtype] 107 108 # Open a master. 109 repladd 1 110 set ma_envcmd "berkdb_env_noerr -create $m_txnargs \ 111 $m_logargs -log_max $log_max -errpfx MASTER $verbargs \ 112 -home $masterdir -rep_transport \[list 1 replsend\]" 113 set masterenv [eval $ma_envcmd $recargs -rep_master] 114 115 # Open a client 116 puts "\tRep$tnum.a: Open client." 117 repladd 2 118 set cl_envcmd "berkdb_env_noerr -create $c_txnargs \ 119 $c_logargs -log_max $log_max -errpfx CLIENT $verbargs \ 120 -home $clientdir -rep_transport \[list 2 replsend\]" 121 set clientenv [eval $cl_envcmd $recargs -rep_client] 122 123 # Bring the client online by processing the startup messages. 124 set envlist "{$masterenv 1} {$clientenv 2}" 125 process_msgs $envlist 126 127 # Clobber replication's 30-second anti-archive timer, which will have 128 # been started by client sync-up internal init. 129 # 130 $masterenv test force noarchive_timeout 131 132 # Set a low limit so that there are lots of reps between 133 # master and client. This allows greater control over 134 # the test. 135 error_check_good thr [$masterenv rep_limit 0 [expr 10 * 1024]] 0 136 137 # It is *key* to this test that we have a database handle 138 # open for the duration of the test. The problem this 139 # test checks for regards internal init when there are open 140 # database handles around. 141 # 142 set dbname "test.db" 143 set omethod [convert_method $method] 144 set db [eval {berkdb_open_noerr -env $masterenv -auto_commit \ 145 -create -mode 0644} $largs $omethod $dbname] 146 error_check_good dbopen [is_valid_db $db] TRUE 147 148 # Put some data into the database, running the master up past 149 # log file 10, discarding messages to the client so that it will 150 # be forced to request them as a gap. 151 # 152 puts "\tRep$tnum.c: Run rep_test in master env." 153 set start 0 154 155 set stop 0 156 set endlog 10 157 while { $stop == 0 } { 158 # Run test in the master (don't update client). 159 eval rep_test $method \ 160 $masterenv $db $niter $start $start 0 0 $largs 161 incr start $niter 162 replclear 2 163 164 if { $m_logtype != "in-memory" } { 165 set res \ 166 [eval exec $util_path/db_archive -l -h $masterdir] 167 } 168 # Make sure the master has gone as far as we requested. 169 set last_master_log [get_logfile $masterenv last] 170 if { $last_master_log > $endlog } { 171 set stop 1 172 } 173 } 174 175 # Do one more set of txns at the master, replicating log records 176 # normally, to give the client a chance to notice how many messages 177 # it is missing. 178 # 179 eval rep_test $method $masterenv $db $niter $start $start 0 0 $largs 180 incr start $niter 181 182 set stop 0 183 set client_endlog 5 184 set last_client_log 0 185 set nproced 0 186 incr nproced [proc_msgs_once $envlist NONE err] 187 incr nproced [proc_msgs_once $envlist NONE err] 188 189 puts "\tRep$tnum.d: Client catches up partway." 190 error_check_good ckp [$masterenv txn_checkpoint] 0 191 192 # We have checkpointed on the master, but we want to get the 193 # client a healthy way through the logs before archiving on 194 # the master. 195 while { $stop == 0 } { 196 set nproced 0 197 incr nproced [proc_msgs_once $envlist NONE err] 198 if { $nproced == 0 } { 199 error_check_good \ 200 ckp [$masterenv txn_checkpoint -force] 0 201 } 202 203 # Stop processing when the client is partway through. 204 if { $c_logtype != "in-memory" } { 205 set res \ 206 [eval exec $util_path/db_archive -l -h $clientdir] 207 } 208 set last_client_log [get_logfile $clientenv last] 209 set first_client_log [get_logfile $clientenv first] 210 if { $last_client_log > $client_endlog } { 211 set stop 1 212 } 213 } 214 215 # 216 # The user may have the database open itself. 217 # 218 if { $opt == "user" } { 219 set cdb [eval {berkdb_open_noerr -env} $clientenv $dbname] 220 error_check_good dbopen [is_valid_db $cdb] TRUE 221 set ccur [$cdb cursor] 222 error_check_good curs [is_valid_cursor $ccur $cdb] TRUE 223 set ret [$ccur get -first] 224 set kd [lindex $ret 0] 225 set key [lindex $kd 0] 226 error_check_good cclose [$ccur close] 0 227 } else { 228 set cdb NULL 229 } 230 231 # Now that the client is well on its way of normal processing, 232 # simply fairly far behind the master, archive on the master, 233 # removing the log files the client needs, sending it into 234 # internal init with the database pages reflecting the client's 235 # current LSN. 236 # 237 puts "\tRep$tnum.e: Force internal initialization." 238 if { $m_logtype != "in-memory" } { 239 puts "\tRep$tnum.e1: Archive on master." 240 set res [eval exec $util_path/db_archive -d -h $masterdir] 241 } else { 242 # Master is in-memory, and we'll need a different 243 # technique to create the gap forcing internal init. 244 puts "\tRep$tnum.e1: Run rep_test until gap is created." 245 set stop 0 246 while { $stop == 0 } { 247 eval rep_test $method $masterenv \ 248 NULL $niter $start $start 0 0 $largs 249 incr start $niter 250 set first_master_log [get_logfile $masterenv first] 251 if { $first_master_log > $last_client_log } { 252 set stop 1 253 } 254 } 255 } 256 257 puts "\tRep$tnum.f: Process messages." 258 if { $opt == "user" } { 259 for { set loop 0 } { $loop < 5 } { incr loop } { 260 set nproced 0 261 incr nproced [proc_msgs_once $envlist] 262 if { $cdb == "NULL" } { 263 continue 264 } 265 puts "\tRep$tnum.g.$loop: Check user database." 266 set status [catch {$cdb get $key} ret] 267 if { $status != 0 } { 268 # 269 # For db operations, DB doesn't block, but 270 # returns DEADLOCK. 271 # 272 set is_lock [is_substr $ret DB_LOCK_DEADLOCK] 273 set is_dead [is_substr $ret DB_REP_HANDLE_DEAD] 274 error_check_good lock_dead \ 275 [expr $is_lock || $is_dead] 1 276 if { $is_dead } { 277 error_check_good cclose [$cdb close] 0 278 set cdb NULL 279 } 280 } 281 } 282 } 283 process_msgs $envlist 284 285 # 286 # If we get through the user loop with a valid db, then it better 287 # be a dead handle after we've completed processing all the 288 # messages and running recovery. 289 # 290 if { $cdb != "NULL" } { 291 puts "\tRep$tnum.h: Check dead handle." 292 set status [catch {$cdb get $key} ret] 293 error_check_good status $status 1 294 error_check_good is_dead [is_substr $ret DB_REP_HANDLE_DEAD] 1 295 error_check_good cclose [$cdb close] 0 296 puts "\tRep$tnum.i: Verify correct internal initialization." 297 } else { 298 puts "\tRep$tnum.h: Verify correct internal initialization." 299 } 300 error_check_good close [$db close] 0 301 process_msgs $envlist 302 303 # We have now forced an internal initialization. Verify it is correct. 304 rep_verify $masterdir $masterenv $clientdir $clientenv 1 305 306 check_log_location $masterenv 307 check_log_location $clientenv 308 309 error_check_good masterenv_close [$masterenv close] 0 310 error_check_good clientenv_close [$clientenv close] 0 311 replclose $testdir/MSGQUEUEDIR 312} 313