1# See the file LICENSE for redistribution information.
2#
3# Copyright (c) 2005,2008 Oracle.  All rights reserved.
4#
5# $Id: rep045.tcl,v 12.19 2008/01/08 20:58:53 bostic Exp $
6#
7# TEST	rep045
8# TEST
9# TEST	Replication with versions.
10# TEST
11# TEST	Mimic an application where a database is set up in the
12# TEST	background and then put into a replication group for use.
13# TEST	The "version database" identifies the current live
14# TEST	version, the database against which queries are made.
15# TEST	For example, the version database might say the current
16# TEST	version is 3, and queries would then be sent to db.3.
17# TEST	Version 4 is prepared for use while version 3 is in use.
18# TEST	When version 4 is complete, the version database is updated
19# TEST	to point to version 4 so queries can be directed there.
20# TEST
21# TEST	This test has a master and two clients.  One client swaps
22# TEST	roles with the master, and the other client runs constantly
23# TEST	in another process.
24
25proc rep045 { method { tnum "045" } args } {
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	# Valid for all access methods.
34	if { $checking_valid_methods } {
35		return "ALL"
36	}
37
38	set args [convert_args $method $args]
39	set logsets [create_logsets 3]
40
41	foreach l $logsets {
42		set logindex [lsearch -exact $l "in-memory"]
43		puts "Rep$tnum ($method): Replication with version\
44		    databases."
45		puts "Rep$tnum: Master logs are [lindex $l 0]"
46		puts "Rep$tnum: Client 0 logs are [lindex $l 1]"
47		puts "Rep$tnum: Client 1 logs are [lindex $l 2]"
48		rep045_sub $method $tnum $l $args
49	}
50}
51
52proc rep045_sub { method tnum logset largs } {
53	source ./include.tcl
54	set orig_tdir $testdir
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	set masterdir $testdir/MASTERDIR
64	set clientdir0 $testdir/CLIENTDIR0
65	set clientdir1 $testdir/CLIENTDIR1
66
67	env_cleanup $testdir
68	replsetup $testdir/MSGQUEUEDIR
69	file mkdir $masterdir
70	file mkdir $clientdir0
71	file mkdir $clientdir1
72
73	set m_logtype [lindex $logset 0]
74	set c_logtype [lindex $logset 1]
75	set c2_logtype [lindex $logset 2]
76
77	# In-memory logs require a large log buffer, and cannot
78	# be used with -txn nosync.
79	set m_logargs [adjust_logargs $m_logtype]
80	set c_logargs [adjust_logargs $c_logtype]
81	set c2_logargs [adjust_logargs $c2_logtype]
82	set m_txnargs [adjust_txnargs $m_logtype]
83	set c_txnargs [adjust_txnargs $c_logtype]
84	set c2_txnargs [adjust_txnargs $c2_logtype]
85
86	set omethod [convert_method $method]
87
88	# Open a master.
89	repladd 1
90	set envcmd(M0) "berkdb_env_noerr -create $m_txnargs \
91	    $m_logargs -errpfx ENV.M0 $verbargs \
92	    -errfile /dev/stderr -lock_detect default \
93	    -home $masterdir -rep_transport \[list 1 replsend\]"
94	set menv [eval $envcmd(M0) -rep_master]
95
96	# Open a client
97	repladd 2
98	set envcmd(C0) "berkdb_env_noerr -create $c_txnargs \
99	    $c_logargs -errpfx ENV.C0 $verbargs \
100	    -errfile /dev/stderr -lock_detect default \
101	    -home $clientdir0 -rep_transport \[list 2 replsend\]"
102	set cenv0 [eval $envcmd(C0) -rep_client]
103
104	# Open second client.
105	repladd 3
106	set envcmd(C1) "berkdb_env_noerr -create $c2_txnargs \
107	    $c2_logargs -errpfx ENV.C1 $verbargs \
108	    -errfile /dev/stderr -lock_detect default \
109	    -home $clientdir1 -rep_transport \[list 3 replsend\]"
110	set cenv1 [eval $envcmd(C1) -rep_client]
111
112	# Bring the clients online by processing the startup messages.
113	set envlist "{$menv 1} {$cenv0 2} {$cenv1 3}"
114	process_msgs $envlist
115
116	# Clobber replication's 30-second anti-archive timer, which will have
117	# been started by client sync-up internal init, so that we can do a
118	# db_remove in a moment.
119	#
120	$menv test force noarchive_timeout
121
122	puts "\tRep$tnum.a: Initialize version database."
123	# Set up variables so we cycle through version numbers 1
124	# through maxversion several times.
125	set vname "version.db"
126	set version 0
127	set maxversion 5
128	set iter 12
129	set nentries 100
130	set start 0
131
132	# The version db is always btree.
133	set vdb [eval {berkdb_open_noerr -env $menv -create \
134	    -auto_commit -mode 0644} -btree $vname]
135	error_check_good init_version [$vdb put VERSION $version] 0
136	error_check_good vdb_close [$vdb close] 0
137	process_msgs $envlist
138
139	# Start up a separate process that constantly reads data
140	# from the current official version.
141	puts "\tRep$tnum.b: Spawn a child tclsh to do client work."
142	set pid [exec $tclsh_path $test_path/wrap.tcl \
143	    rep045script.tcl $testdir/rep045script.log \
144		   $clientdir1 $vname &]
145
146	# Main loop: update query database, process messages (or don't,
147	# simulating a failure), announce the new version, process
148	# messages (or don't), and swap masters.
149	set version 1
150	for { set i 1 } { $i < $iter } { incr i } {
151
152		# If database.N exists, clean it up.
153		set dbname "db.$version"
154		if { [file exists $masterdir/$dbname] == 1 } {
155			puts "\tRep$tnum.c.$i: Removing old version $version."
156			error_check_good dbremove \
157			   [$menv dbremove -auto_commit $dbname] 0
158		}
159
160		puts "\tRep$tnum.c.$i: Set up query database $version."
161		set db [eval berkdb_open_noerr -create -env $menv\
162		    -auto_commit -mode 0644 $largs $omethod $dbname]
163		error_check_good db_open [is_valid_db $db] TRUE
164		eval rep_test $method $menv $db $nentries $start $start 0 0 $largs
165		incr start $nentries
166		error_check_good db_close [$db close] 0
167
168		# We alternate between processing the messages and
169		# clearing the messages to simulate a failure.
170
171		set process [expr $i % 2]
172		if { $process == 1 } {
173			process_msgs $envlist
174		} else {
175			replclear 2
176			replclear 3
177		}
178
179		# Announce new version.
180		puts "\tRep$tnum.d.$i: Announce new version $version."
181		set vdb [eval {berkdb_open_noerr -env $menv \
182		    -auto_commit -mode 0644} $vname]
183		error_check_good update_version [$vdb put VERSION $version] 0
184		error_check_good vdb_close [$vdb close] 0
185
186		# Process messages or simulate failure.
187		if { $process == 1 } {
188			process_msgs $envlist
189		} else {
190			replclear 2
191			replclear 3
192		}
193
194		# Switch master, update envlist.
195		puts "\tRep$tnum.e.$i: Switch masters."
196		set envlist [switch_master $envlist]
197
198		# Update values for next iteration.
199		set menv [lindex [lindex $envlist 0] 0]
200		set cenv0 [lindex [lindex $envlist 1] 0]
201		incr version
202		if { $version > $maxversion } {
203			set version 1
204		}
205	}
206
207	# Signal to child that we are done.
208	set vdb [eval {berkdb_open_noerr -env $menv \
209	    -auto_commit -mode 0644} $vname]
210	error_check_good version_done [$vdb put VERSION DONE] 0
211	error_check_good vdb_close [$vdb close] 0
212	process_msgs $envlist
213
214	# Watch for child to finish.
215	watch_procs $pid 5
216
217	puts "\tRep$tnum.f: Clean up."
218	error_check_good menv_close [$menv close] 0
219	error_check_good cenv0_close [$cenv0 close] 0
220	error_check_good cenv1_close [$cenv1 close] 0
221
222	replclose $testdir/MSGQUEUEDIR
223
224	# Check for failures in child's log file.
225	set errstrings [eval findfail $testdir/rep045script.log]
226	foreach str $errstrings {
227		puts "FAIL: error message in log file: $str"
228	}
229
230	set testdir $orig_tdir
231	return
232}
233
234proc switch_master { envlist } {
235	# Find env handles and machine ids.
236	set menv [lindex [lindex $envlist 0] 0]
237	set mid [lindex [lindex $envlist 0] 1]
238	set cenv [lindex [lindex $envlist 1] 0]
239	set cid [lindex [lindex $envlist 1] 1]
240	set cenv1 [lindex [lindex $envlist 2] 0]
241	set cid1 [lindex [lindex $envlist 2] 1]
242
243	# Downgrade master, upgrade client.
244	error_check_good master_downgrade [$menv rep_start -client] 0
245	error_check_good client_upgrade [$cenv rep_start -master] 0
246	process_msgs $envlist
247
248	# Adjust envlist.  The former client env is the new master,
249	# and vice versa.
250	set newenvlist "{$cenv $cid} {$menv $mid} {$cenv1 $cid1}"
251	return $newenvlist
252}
253