1# See the file LICENSE for redistribution information. 2# 3# Copyright (c) 2003,2008 Oracle. All rights reserved. 4# 5# $Id: rep015.tcl,v 12.14 2008/01/08 20:58:53 bostic Exp $ 6# 7# TEST rep015 8# TEST Locking across multiple pages with replication. 9# TEST 10# TEST Open master and client with small pagesize and 11# TEST generate more than one page and generate off-page 12# TEST dups on the first page (second key) and last page 13# TEST (next-to-last key). 14# TEST Within a single transaction, for each database, open 15# TEST 2 cursors and delete the first and last entries (this 16# TEST exercises locks on regular pages). Intermittently 17# TEST update client during the process. 18# TEST Within a single transaction, for each database, open 19# TEST 2 cursors. Walk to the off-page dups and delete one 20# TEST from each end (this exercises locks on off-page dups). 21# TEST Intermittently update client. 22# 23proc rep015 { method { nentries 100 } { tnum "015" } { ndb 3 } args } { 24 global rand_init 25 berkdb srand $rand_init 26 27 source ./include.tcl 28 if { $is_windows9x_test == 1 } { 29 puts "Skipping replication test on Win 9x platform." 30 return 31 } 32 33 # Run for btree only. 34 if { $checking_valid_methods } { 35 set test_methods { btree } 36 return $test_methods 37 } 38 if { [is_btree $method] == 0 } { 39 puts "Skipping rep$tnum for method $method." 40 return 41 } 42 43 set args [convert_args $method $args] 44 set logsets [create_logsets 2] 45 46 # Run the body of the test with and without recovery. 47 foreach r $test_recopts { 48 foreach l $logsets { 49 set logindex [lsearch -exact $l "in-memory"] 50 if { $r == "-recover" && $logindex != -1 } { 51 puts "Rep$tnum: \ 52 Skipping for in-memory logs with -recover." 53 continue 54 } 55 puts "Rep$tnum ($method $r):\ 56 Replication and locking." 57 puts "Rep$tnum: Master logs are [lindex $l 0]" 58 puts "Rep$tnum: Client logs are [lindex $l 1]" 59 rep015_sub $method $nentries $tnum $ndb $l $r $args 60 } 61 } 62} 63 64proc rep015_sub { method nentries tnum ndb logset recargs largs } { 65 global testdir 66 global rep_verbose 67 global verbose_type 68 69 set verbargs "" 70 if { $rep_verbose == 1 } { 71 set verbargs " -verbose {$verbose_type on} " 72 } 73 74 env_cleanup $testdir 75 set omethod [convert_method $method] 76 77 replsetup $testdir/MSGQUEUEDIR 78 79 set masterdir $testdir/MASTERDIR 80 set clientdir $testdir/CLIENTDIR 81 82 file mkdir $masterdir 83 file mkdir $clientdir 84 85 set m_logtype [lindex $logset 0] 86 set c_logtype [lindex $logset 1] 87 88 # In-memory logs require a large log buffer, and cannot 89 # be used with -txn nosync. 90 set m_logargs [adjust_logargs $m_logtype] 91 set c_logargs [adjust_logargs $c_logtype] 92 set m_txnargs [adjust_txnargs $m_logtype] 93 set c_txnargs [adjust_txnargs $c_logtype] 94 95 # Open a master. 96 repladd 1 97 set ma_envcmd "berkdb_env_noerr -create $m_txnargs $m_logargs \ 98 $verbargs -errpfx MASTER \ 99 -home $masterdir -rep_transport \[list 1 replsend\]" 100 set masterenv [eval $ma_envcmd $recargs -rep_master] 101 102 # Open a client 103 repladd 2 104 set cl_envcmd "berkdb_env_noerr -create $c_txnargs $c_logargs \ 105 $verbargs -errpfx CLIENT \ 106 -home $clientdir -rep_transport \[list 2 replsend\]" 107 set clientenv [eval $cl_envcmd $recargs -rep_client] 108 109 # Bring the clients online by processing the startup messages. 110 set envlist "{$masterenv 1} {$clientenv 2}" 111 process_msgs $envlist 112 113 # Set up the master databases. The small pagesize quickly 114 # generates multiple pages and off-page dups. 115 set pagesize 512 116 puts "\tRep$tnum.a: Create and populate databases in master." 117 for { set i 0 } { $i < $ndb } { incr i } { 118 set db [eval berkdb_open_noerr -create $omethod -auto_commit \ 119 -pagesize $pagesize -env $masterenv $largs -dup testdb$i.db] 120 set dblist($i) $db 121 # 122 # Populate, being sure to create multiple pages. 123 # The non-duplicate entries are pairs of the form 124 # {1, data1} {2, data2}. The duplicates are pairs of 125 # the form {2, dup1} {2, dup2}, {2, dup3}, etc. 126 # 127 for { set j 1 } { $j <= $nentries } { incr j } { 128 set t [$masterenv txn] 129 error_check_good put_$db [eval $db put -txn $t \ 130 $j [chop_data $method data$j]] 0 131 error_check_good txn_commit [$t commit] 0 132 } 133 # Create off-page dups on key 2 and next-to-last key. 134 set t [$masterenv txn] 135 for { set j 1 } { $j <= $nentries } { incr j } { 136 error_check_good put_second [eval $db put -txn $t \ 137 2 [chop_data $method dup$j]] 0 138 error_check_good put_next_to_last [eval $db put \ 139 -txn $t \ 140 [expr $nentries - 1] [chop_data $method dup$j]] 0 141 } 142 error_check_good txn_commit [$t commit] 0 143 # Make sure there are off-page dups. 144 set stat [$db stat] 145 error_check_bad stat:offpage \ 146 [is_substr $stat "{{Internal pages} 0}"] 1 147 } 148 149 puts "\tRep$tnum.b: Propagate setup to clients." 150 process_msgs $envlist 151 152 # Open client databases so we can exercise locking there too. 153 for { set i 0 } { $i < $ndb } { incr i } { 154 set cdb [eval {berkdb_open_noerr} -auto_commit \ 155 -env $clientenv $largs testdb$i.db] 156 set cdblist($i) $cdb 157 } 158 159 # Set up two cursors into each db. Randomly select a cursor 160 # and do the next thing: position, delete, or close. 161 foreach option { regular off-page } { 162 puts "\tRep$tnum.c: Transactional cursor deletes ($option)." 163 164 set t [$masterenv txn] 165 # Set up two cursors into each db, and initialize the next 166 # action to be done to POSITION. 167 for { set i 0 } { $i < [expr $ndb * 2] } { incr i } { 168 set db $dblist([expr $i / 2]) 169 set mcurs($i) [eval {$db cursor} -txn $t] 170 error_check_good mcurs$i \ 171 [is_valid_cursor $mcurs($i) $db] TRUE 172 set cnext($i) POSITION 173 } 174 175 set ct [$clientenv txn] 176 # Set up two cursors into each client db. 177 for { set i 0 } { $i < [expr $ndb * 2] } { incr i } { 178 set cdb $cdblist([expr $i / 2]) 179 set ccurs($i) [eval {$cdb cursor} -txn $ct] 180 error_check_good ccurs$i \ 181 [is_valid_cursor $ccurs($i) $cdb] TRUE 182 } 183 184 # Randomly pick a cursor to operate on and do the next thing. 185 # At POSITION, we position that cursor. At DELETE, we delete 186 # the current item. At CLOSE, we close the cursor. At DONE, 187 # we do nothing except check to see if all cursors have reached 188 # DONE, and quit when they have. 189 # On the off-page dup test, walk to reach an off-page entry, 190 # and delete that one. 191 set k 0 192 while { 1 } { 193 # Every nth time through, update the client. 194# set n 5 195# if {[expr $k % $n] == 0 } { 196# puts "Updating clients" 197# process_msgs $envlist 198# } 199# incr k 200 set i [berkdb random_int 0 [expr [expr $ndb * 2] - 1]] 201 set next $cnext($i) 202 switch -exact -- $next { 203 POSITION { 204 do_position $mcurs($i) \ 205 $i $nentries $option 206 set cnext($i) DELETE 207 # Position the client cursors too. 208 do_position $ccurs($i) \ 209 $i $nentries $option 210 } 211 DELETE { 212 error_check_good c_del \ 213 [$mcurs($i) del] 0 214 set cnext($i) CLOSE 215 # Update clients after a delete. 216 process_msgs $envlist 217 } 218 CLOSE { 219 error_check_good c_close.$i \ 220 [$mcurs($i) close] 0 221 set cnext($i) DONE 222 # Close the client cursor too. 223 error_check_good cc_close.$i \ 224 [$ccurs($i) close] 0 225 } 226 DONE { 227 set breakflag 1 228 for { set j 0 } \ 229 { $j < [expr $ndb * 2] } \ 230 { incr j } { 231 if { $cnext($j) != "DONE" } { 232 set breakflag 0 233 } 234 } 235 if { $breakflag == 1 } { 236 break 237 } 238 } 239 default { 240 puts "FAIL: Unrecognized \ 241 next action $next" 242 } 243 } 244 } 245 error_check_good txn_commit [$t commit] 0 246 error_check_good clienttxn_commit [$ct commit] 0 247 process_msgs $envlist 248 } 249 250 # Clean up. 251 for { set i 0 } { $i < $ndb } { incr i } { 252 set db $dblist($i) 253 error_check_good close_$db [$db close] 0 254 set cdb $cdblist($i) 255 error_check_good close_$cdb [$cdb close] 0 256 } 257 258 error_check_good masterenv_close [$masterenv close] 0 259 error_check_good clientenv_close [$clientenv close] 0 260 replclose $testdir/MSGQUEUEDIR 261 return 262} 263 264proc do_position { cursor i nentries option } { 265 if { [expr $i % 2] == 0 } { 266 if { $option == "regular" } { 267 set ret [$cursor get -first] 268 set key [lindex [lindex $ret 0] 0] 269 set data [lindex [lindex $ret 0] 1] 270 error_check_good get_first \ 271 [string range $data 4 end] $key 272 } elseif { $option == "off-page" } { 273 set ret [$cursor get -set 2] 274 error_check_good get_key_2 \ 275 [lindex [lindex $ret 0] 0] 2 276 error_check_good get_data_2 \ 277 [lindex [lindex $ret 0] 1] data2 278 for { set j 1 } { $j <= 95 } { incr j } { 279 set ret [$cursor get -nextdup] 280 error_check_good key_nextdup$j \ 281 [lindex [lindex $ret 0] 0] 2 282 error_check_good data_nextdup$j \ 283 [lindex [lindex $ret 0] 1] dup$j 284 } 285 } 286 } else { 287 if { $option == "regular" } { 288 set ret [$cursor get -set $nentries] 289 set key [lindex [lindex $ret 0] 0] 290 set data [lindex [lindex $ret 0] 1] 291 error_check_good get_set_$nentries \ 292 [string range $data 4 end] $key 293 } elseif { $option == "off-page" } { 294 set ret [$cursor get -last] 295 set key [lindex [lindex $ret 0] 0] 296 set data [lindex [lindex $ret 0] 1] 297 error_check_good get_last \ 298 [string range $data 3 end] [expr $key + 1] 299 for { set j 1 } { $j <= 5 } { incr j } { 300 set ret [$cursor get -prev] 301 set key [lindex [lindex $ret 0] 0] 302 set data [lindex [lindex $ret 0] 1] 303 error_check_good get_prev \ 304 [string range $data 3 end] \ 305 [expr [expr $key + 1] - $j] 306 } 307 } 308 } 309} 310