# See the file LICENSE for redistribution information. # # Copyright (c) 2005,2007 Oracle. All rights reserved. # # $Id: rep045.tcl,v 12.17 2007/05/17 18:17:21 bostic Exp $ # # TEST rep045 # TEST # TEST Replication with versions. # TEST # TEST Mimic an application where a database is set up in the # TEST background and then put into a replication group for use. # TEST The "version database" identifies the current live # TEST version, the database against which queries are made. # TEST For example, the version database might say the current # TEST version is 3, and queries would then be sent to db.3. # TEST Version 4 is prepared for use while version 3 is in use. # TEST When version 4 is complete, the version database is updated # TEST to point to version 4 so queries can be directed there. # TEST # TEST This test has a master and two clients. One client swaps # TEST roles with the master, and the other client runs constantly # TEST in another process. proc rep045 { method { tnum "045" } args } { source ./include.tcl if { $is_windows9x_test == 1 } { puts "Skipping replication test on Win 9x platform." return } # Valid for all access methods. if { $checking_valid_methods } { return "ALL" } set args [convert_args $method $args] set logsets [create_logsets 3] foreach l $logsets { set logindex [lsearch -exact $l "in-memory"] puts "Rep$tnum ($method): Replication with version\ databases." puts "Rep$tnum: Master logs are [lindex $l 0]" puts "Rep$tnum: Client 0 logs are [lindex $l 1]" puts "Rep$tnum: Client 1 logs are [lindex $l 2]" rep045_sub $method $tnum $l $args } } proc rep045_sub { method tnum logset largs } { source ./include.tcl set orig_tdir $testdir global rep_verbose set verbargs "" if { $rep_verbose == 1 } { set verbargs " -verbose {rep on} " } set masterdir $testdir/MASTERDIR set clientdir0 $testdir/CLIENTDIR0 set clientdir1 $testdir/CLIENTDIR1 env_cleanup $testdir replsetup $testdir/MSGQUEUEDIR file mkdir $masterdir file mkdir $clientdir0 file mkdir $clientdir1 set m_logtype [lindex $logset 0] set c_logtype [lindex $logset 1] set c2_logtype [lindex $logset 2] # In-memory logs require a large log buffer, and cannot # be used with -txn nosync. set m_logargs [adjust_logargs $m_logtype] set c_logargs [adjust_logargs $c_logtype] set c2_logargs [adjust_logargs $c2_logtype] set m_txnargs [adjust_txnargs $m_logtype] set c_txnargs [adjust_txnargs $c_logtype] set c2_txnargs [adjust_txnargs $c2_logtype] set omethod [convert_method $method] # Open a master. repladd 1 set envcmd(M0) "berkdb_env_noerr -create $m_txnargs \ $m_logargs -errpfx ENV.M0 $verbargs \ -errfile /dev/stderr -lock_detect default \ -home $masterdir -rep_transport \[list 1 replsend\]" set menv [eval $envcmd(M0) -rep_master] # Open a client repladd 2 set envcmd(C0) "berkdb_env_noerr -create $c_txnargs \ $c_logargs -errpfx ENV.C0 $verbargs \ -errfile /dev/stderr -lock_detect default \ -home $clientdir0 -rep_transport \[list 2 replsend\]" set cenv0 [eval $envcmd(C0) -rep_client] # Open second client. repladd 3 set envcmd(C1) "berkdb_env_noerr -create $c2_txnargs \ $c2_logargs -errpfx ENV.C1 $verbargs \ -errfile /dev/stderr -lock_detect default \ -home $clientdir1 -rep_transport \[list 3 replsend\]" set cenv1 [eval $envcmd(C1) -rep_client] # Bring the clients online by processing the startup messages. set envlist "{$menv 1} {$cenv0 2} {$cenv1 3}" process_msgs $envlist # Clobber replication's 30-second anti-archive timer, which will have # been started by client sync-up internal init, so that we can do a # db_remove in a moment. # $menv test force noarchive_timeout puts "\tRep$tnum.a: Initialize version database." # Set up variables so we cycle through version numbers 1 # through maxversion several times. set vname "version.db" set version 0 set maxversion 5 set iter 12 set nentries 100 set start 0 # The version db is always btree. set vdb [eval {berkdb_open_noerr -env $menv -create \ -auto_commit -mode 0644} -btree $vname] error_check_good init_version [$vdb put VERSION $version] 0 error_check_good vdb_close [$vdb close] 0 process_msgs $envlist # Start up a separate process that constantly reads data # from the current official version. puts "\tRep$tnum.b: Spawn a child tclsh to do client work." set pid [exec $tclsh_path $test_path/wrap.tcl \ rep045script.tcl $testdir/rep045script.log \ $clientdir1 $vname &] # Main loop: update query database, process messages (or don't, # simulating a failure), announce the new version, process # messages (or don't), and swap masters. set version 1 for { set i 1 } { $i < $iter } { incr i } { # If database.N exists, clean it up. set dbname "db.$version" if { [file exists $masterdir/$dbname] == 1 } { puts "\tRep$tnum.c.$i: Removing old version $version." error_check_good dbremove \ [$menv dbremove -auto_commit $dbname] 0 } puts "\tRep$tnum.c.$i: Set up query database $version." set db [eval berkdb_open_noerr -create -env $menv\ -auto_commit -mode 0644 $largs $omethod $dbname] error_check_good db_open [is_valid_db $db] TRUE eval rep_test $method $menv $db $nentries $start $start 0 0 $largs incr start $nentries error_check_good db_close [$db close] 0 # We alternate between processing the messages and # clearing the messages to simulate a failure. set process [expr $i % 2] if { $process == 1 } { process_msgs $envlist } else { replclear 2 replclear 3 } # Announce new version. puts "\tRep$tnum.d.$i: Announce new version $version." set vdb [eval {berkdb_open_noerr -env $menv \ -auto_commit -mode 0644} $vname] error_check_good update_version [$vdb put VERSION $version] 0 error_check_good vdb_close [$vdb close] 0 # Process messages or simulate failure. if { $process == 1 } { process_msgs $envlist } else { replclear 2 replclear 3 } # Switch master, update envlist. puts "\tRep$tnum.e.$i: Switch masters." set envlist [switch_master $envlist] # Update values for next iteration. set menv [lindex [lindex $envlist 0] 0] set cenv0 [lindex [lindex $envlist 1] 0] incr version if { $version > $maxversion } { set version 1 } } # Signal to child that we are done. set vdb [eval {berkdb_open_noerr -env $menv \ -auto_commit -mode 0644} $vname] error_check_good version_done [$vdb put VERSION DONE] 0 error_check_good vdb_close [$vdb close] 0 process_msgs $envlist # Watch for child to finish. watch_procs $pid 5 puts "\tRep$tnum.f: Clean up." error_check_good menv_close [$menv close] 0 error_check_good cenv0_close [$cenv0 close] 0 error_check_good cenv1_close [$cenv1 close] 0 replclose $testdir/MSGQUEUEDIR # Check for failures in child's log file. set errstrings [eval findfail $testdir/rep045script.log] foreach str $errstrings { puts "FAIL: error message in log file: $str" } set testdir $orig_tdir return } proc switch_master { envlist } { # Find env handles and machine ids. set menv [lindex [lindex $envlist 0] 0] set mid [lindex [lindex $envlist 0] 1] set cenv [lindex [lindex $envlist 1] 0] set cid [lindex [lindex $envlist 1] 1] set cenv1 [lindex [lindex $envlist 2] 0] set cid1 [lindex [lindex $envlist 2] 1] # Downgrade master, upgrade client. error_check_good master_downgrade [$menv rep_start -client] 0 error_check_good client_upgrade [$cenv rep_start -master] 0 process_msgs $envlist # Adjust envlist. The former client env is the new master, # and vice versa. set newenvlist "{$cenv $cid} {$menv $mid} {$cenv1 $cid1}" return $newenvlist }