1# See the file LICENSE for redistribution information. 2# 3# Copyright (c) 2009 Oracle. All rights reserved. 4# 5# TEST rep088 6# TEST Replication roll-back preserves checkpoint. 7# TEST 8# TEST Create a situation where a client has to roll back its 9# TEST log, discarding some existing transactions, in order to sync 10# TEST with a new master. 11# TEST 12# TEST 1. When the client still has its entire log file history, all 13# TEST the way back to log file #1, it's OK if the roll-back discards 14# TEST any/all checkpoints. 15# TEST 2. When old log files have been archived, if the roll-back would 16# TEST remove all existing checkpoints it must be forbidden. The log 17# TEST must always have a checkpoint (or all files back through #1). 18# TEST The client must do internal init or return JOIN_FAILURE. 19# TEST 3. (the normal case) Old log files archived, and a checkpoint 20# TEST still exists in the portion of the log which will remain after 21# TEST the roll-back: no internal-init/JOIN_FAILURE necessary. 22# 23# TODO: maybe just reject anything that doesn't comply with my simplified 24# rep_test clone, like fixed-length record methods, etc. 25 26proc rep088 { method { niter 20 } { tnum 088 } args } { 27 source ./include.tcl 28 29 if { $is_windows9x_test == 1 } { 30 puts "Skipping replication test on Win 9x platform." 31 return 32 } 33 34 # Run for btree only. 35 if { $checking_valid_methods } { 36 set test_methods { btree } 37 return $test_methods 38 } 39 if { [is_btree $method] == 0 } { 40 puts "\tRep$tnum: Skipping for method $method." 41 return 42 } 43 44 set args [convert_args $method $args] 45 46 puts "Rep$tnum ($method): Replication roll-back preserves checkpoint." 47 # Note: expected result = "sync" means the client should be allowed to 48 # synchronize normally (to the found sync point), without any need for 49 # internal init. 50 # 51 # Case #1. 52 puts "Rep$tnum: Rollback without checkpoint, with log file 1" 53 set archive false 54 set ckpt false 55 set result sync 56 rep088_sub $method $niter $tnum $archive $ckpt $result $args 57 58 # Case #2.(a). 59 # 60 puts "Rep$tnum: Forbid rollback over only chkp: join failure" 61 set archive true 62 set ckpt false 63 set result join_failure 64 rep088_sub $method $niter $tnum $archive $ckpt $result $args 65 66 # Case #2.(b): essentially the same, but allow the internal init to 67 # happen, so that we verify that the subsequent restart with recovery 68 # works fine. NB: this is the obvious failure case prior to bug fix 69 # #16732. 70 # 71 puts "Rep$tnum: Forbid rollback over only chkp: internal init" 72 set archive true 73 set ckpt false 74 set result internal_init 75 rep088_sub $method $niter $tnum $archive $ckpt $result $args 76 77 # Case #3. 78 puts "Rep$tnum: Rollback with sufficient extra checkpoints" 79 set archive true 80 set ckpt true 81 set result sync 82 rep088_sub $method $niter $tnum $archive $ckpt $result $args 83} 84 85proc rep088_sub { method niter tnum archive ckpt result largs } { 86 source ./include.tcl 87 global testdir 88 global util_path 89 global rep_verbose 90 global verbose_type 91 92 set verbargs "" 93 if { $rep_verbose == 1 } { 94 set verbargs " -verbose {$verbose_type on} " 95 } 96 97 env_cleanup $testdir 98 replsetup $testdir/MSGQUEUEDIR 99 100 file mkdir [set dirA $testdir/A] 101 file mkdir [set dirB $testdir/B] 102 file mkdir [set dirC $testdir/C] 103 104 set pagesize 4096 105 append largs " -pagesize $pagesize " 106 set log_buf [expr $pagesize * 2] 107 set log_max [expr $log_buf * 4] 108 109 puts "\tRep$tnum.a: Create master and two clients" 110 repladd 1 111 set env_A_cmd "berkdb_env -create -txn $verbargs \ 112 -log_buffer $log_buf -log_max $log_max \ 113 -errpfx SITE_A -errfile /dev/stderr \ 114 -home $dirA -rep_transport \[list 1 replsend\]" 115 set envs(A) [eval $env_A_cmd -rep_master] 116 117 repladd 2 118 set env_B_cmd "berkdb_env -create -txn $verbargs \ 119 -log_buffer $log_buf -log_max $log_max \ 120 -errpfx SITE_B -errfile /dev/stderr \ 121 -home $dirB -rep_transport \[list 2 replsend\]" 122 set envs(B) [eval $env_B_cmd -rep_client] 123 124 repladd 3 125 set env_C_cmd "berkdb_env -create -txn $verbargs \ 126 -log_buffer $log_buf -log_max $log_max \ 127 -errpfx SITE_C -errfile /dev/stderr \ 128 -home $dirC -rep_transport \[list 3 replsend\]" 129 set envs(C) [eval $env_C_cmd -rep_client] 130 131 set envlist "{$envs(A) 1} {$envs(B) 2} {$envs(C) 3}" 132 process_msgs $envlist 133 $envs(A) test force noarchive_timeout 134 135 # Using small log file size, push into the second log file. 136 # 137 puts "\tRep$tnum.b: Write enough txns to exceed 1 log file" 138 while { [lsn_file [next_expected_lsn $envs(C)]] == 1 } { 139 eval rep088_reptest $method $envs(A) $niter $largs 140 process_msgs $envlist 141 } 142 143 # To make sure everything still works in the normal case, put in a 144 # checkpoint here before writing the transactions that will have to be 145 # rolled back. Later, when the client sees that it must roll back over 146 # (and discard) the later checkpoint, the fact that this checkpoint is 147 # here will allow it to proceed. 148 # 149 if { $ckpt } { 150 puts "\tRep$tnum.c: put in an 'extra' checkpoint." 151 $envs(A) txn_checkpoint 152 process_msgs $envlist 153 } 154 155 # Turn off client TBM (the one that will become master later). 156 # 157 puts "\tRep$tnum.d: Turn off client B and write more txns" 158 $envs(B) close 159 set envlist "{$envs(A) 1} {$envs(C) 3}" 160 161 # Fill a bit more log, and then write a checkpoint. 162 # 163 eval rep088_reptest $method $envs(A) $niter $largs 164 $envs(A) txn_checkpoint 165 replclear 2 166 process_msgs $envlist 167 168 # At the client under test, archive away the first log file. 169 # 170 if { $archive } { 171 puts "\tRep$tnum.e: Archive log at client C" 172 exec $util_path/db_archive -d -h $dirC 173 } 174 175 # Maybe another cycle of filling and checkpoint. 176 # 177 eval rep088_reptest $method $envs(A) $niter $largs 178 $envs(A) txn_checkpoint 179 replclear 2 180 process_msgs $envlist 181 182 # Now turn off the master, and turn on the TBM site as master. The 183 # client under test has to sync with the new master. Just to make sure 184 # I understand what's going on, turn off auto-init. 185 # 186 187 if { $result != "internal_init" } { 188 $envs(C) rep_config {noautoinit on} 189 } 190 puts "\tRep$tnum.f: Switch master to site B, try to sync client C" 191 $envs(A) close 192 set envs(B) [eval $env_B_cmd -rep_master] 193 set envlist "{$envs(B) 2} {$envs(C) 3}" 194 replclear 1 195 set succeeded [catch { process_msgs $envlist } ret] 196 197 switch $result { 198 internal_init { 199 error_check_good inited $succeeded 0 200 201 # Now stop the client, and try restarting it with 202 # recovery. 203 # 204 $envs(C) close 205 set envs(C) [eval $env_C_cmd -rep_client -recover] 206 } 207 join_failure { 208 error_check_bad no_autoinit $succeeded 0 209 error_check_good join_fail \ 210 [is_substr $ret DB_REP_JOIN_FAILURE] 1 211 } 212 sync { 213 error_check_good sync_ok $succeeded 0 214 error_check_good not_outdated \ 215 [stat_field $envs(C) rep_stat \ 216 "Outdated conditions"] 0 217 } 218 default { 219 error "FAIL: unknown test result option $result" 220 } 221 } 222 223 $envs(C) close 224 $envs(B) close 225 replclose $testdir/MSGQUEUEDIR 226} 227 228# A simplified clone of proc rep_test, with the crucial distinction that it 229# doesn't do any of its own checkpointing. For this test we need explicit 230# control of when checkpoints should happen. This proc doesn't support 231# access methods using record numbers. 232proc rep088_reptest { method env niter args } { 233 source ./include.tcl 234 235 set omethod [convert_method $method] 236 set largs [convert_args $method $args] 237 set db [eval berkdb_open_noerr -env $env -auto_commit \ 238 -create $omethod $largs test.db] 239 240 set did [open $dict] 241 for { set i 0 } { $i < $niter && [gets $did str] >= 0 } { incr i } { 242 set key $str 243 set str [reverse $str] 244 $db put $key $str 245 } 246 close $did 247 $db close 248} 249