1# See the file LICENSE for redistribution information.
2#
3# Copyright (c) 2005-2009 Oracle.  All rights reserved.
4#
5# $Id$
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	global databases_in_memory
29	global repfiles_in_memory
30
31	if { $is_windows9x_test == 1 } {
32		puts "Skipping replication test on Win 9x platform."
33		return
34	}
35
36	# Valid for all access methods.
37	if { $checking_valid_methods } {
38		return "ALL"
39	}
40
41	set args [convert_args $method $args]
42	set logsets [create_logsets 3]
43
44	# Set up for on-disk or in-memory databases.
45	set msg "using on-disk databases"
46	if { $databases_in_memory } {
47		set msg "using named in-memory databases"
48		if { [is_queueext $method] } {
49			puts -nonewline "Skipping rep$tnum for method "
50			puts "$method with named in-memory databases."
51			return
52		}
53	}
54
55	set msg2 "and on-disk replication files"
56	if { $repfiles_in_memory } {
57		set msg2 "and in-memory replication files"
58	}
59
60	foreach l $logsets {
61		set logindex [lsearch -exact $l "in-memory"]
62		puts "Rep$tnum ($method): Replication with version\
63		    databases $msg $msg2."
64		puts "Rep$tnum: Master logs are [lindex $l 0]"
65		puts "Rep$tnum: Client 0 logs are [lindex $l 1]"
66		puts "Rep$tnum: Client 1 logs are [lindex $l 2]"
67		rep045_sub $method $tnum $l $args
68	}
69}
70
71proc rep045_sub { method tnum logset largs } {
72	source ./include.tcl
73	set orig_tdir $testdir
74	global databases_in_memory
75	global repfiles_in_memory
76	global rep_verbose
77	global verbose_type
78
79	set verbargs ""
80	if { $rep_verbose == 1 } {
81		set verbargs " -verbose {$verbose_type on} "
82	}
83
84	set repmemargs ""
85	if { $repfiles_in_memory } {
86		set repmemargs "-rep_inmem_files "
87	}
88
89	set masterdir $testdir/MASTERDIR
90	set clientdir0 $testdir/CLIENTDIR0
91	set clientdir1 $testdir/CLIENTDIR1
92
93	env_cleanup $testdir
94	replsetup $testdir/MSGQUEUEDIR
95	file mkdir $masterdir
96	file mkdir $clientdir0
97	file mkdir $clientdir1
98
99	set m_logtype [lindex $logset 0]
100	set c_logtype [lindex $logset 1]
101	set c2_logtype [lindex $logset 2]
102
103	# In-memory logs require a large log buffer, and cannot
104	# be used with -txn nosync.
105	set m_logargs [adjust_logargs $m_logtype]
106	set c_logargs [adjust_logargs $c_logtype]
107	set c2_logargs [adjust_logargs $c2_logtype]
108	set m_txnargs [adjust_txnargs $m_logtype]
109	set c_txnargs [adjust_txnargs $c_logtype]
110	set c2_txnargs [adjust_txnargs $c2_logtype]
111
112	set omethod [convert_method $method]
113
114	# Open a master.
115	repladd 1
116	set envcmd(M0) "berkdb_env_noerr -create $m_txnargs \
117	    $m_logargs -errpfx ENV.M0 $verbargs $repmemargs \
118	    -errfile /dev/stderr -lock_detect default \
119	    -home $masterdir -rep_transport \[list 1 replsend\]"
120	set menv [eval $envcmd(M0) -rep_master]
121
122	# Open a client
123	repladd 2
124	set envcmd(C0) "berkdb_env_noerr -create $c_txnargs \
125	    $c_logargs -errpfx ENV.C0 $verbargs $repmemargs \
126	    -errfile /dev/stderr -lock_detect default \
127	    -home $clientdir0 -rep_transport \[list 2 replsend\]"
128	set cenv0 [eval $envcmd(C0) -rep_client]
129
130	# Open second client.
131	repladd 3
132	set envcmd(C1) "berkdb_env_noerr -create $c2_txnargs \
133	    $c2_logargs -errpfx ENV.C1 $verbargs $repmemargs \
134	    -errfile /dev/stderr -lock_detect default \
135	    -home $clientdir1 -rep_transport \[list 3 replsend\]"
136	set cenv1 [eval $envcmd(C1) -rep_client]
137
138	# Bring the clients online by processing the startup messages.
139	set envlist "{$menv 1} {$cenv0 2} {$cenv1 3}"
140	process_msgs $envlist
141
142	# Clobber replication's 30-second anti-archive timer, which will have
143	# been started by client sync-up internal init, so that we can do a
144	# db_remove in a moment.
145	#
146	$menv test force noarchive_timeout
147
148	puts "\tRep$tnum.a: Initialize version database."
149	# Set up variables so we cycle through version numbers 1
150	# through maxversion several times.
151	if { $databases_in_memory } {
152		set vname { "" "version.db" }
153	} else {
154		set vname "version.db"
155	}
156	set version 0
157	set maxversion 5
158	set iter 12
159	set nentries 100
160	set start 0
161
162	# The version db is always btree.
163	set vdb [eval {berkdb_open_noerr -env $menv -create \
164	    -auto_commit -mode 0644} -btree $vname]
165	error_check_good init_version [$vdb put VERSION $version] 0
166	error_check_good vdb_close [$vdb close] 0
167	process_msgs $envlist
168
169	# Start up a separate process that constantly reads data
170	# from the current official version.
171	puts "\tRep$tnum.b: Spawn a child tclsh to do client work."
172	set pid [exec $tclsh_path $test_path/wrap.tcl \
173	    rep045script.tcl $testdir/rep045script.log \
174		   $clientdir1 $vname $databases_in_memory &]
175
176	# Main loop: update query database, process messages (or don't,
177	# simulating a failure), announce the new version, process
178	# messages (or don't), and swap masters.
179	set version 1
180	for { set i 1 } { $i < $iter } { incr i } {
181
182		# If database.N exists on disk, clean it up.
183		if { $databases_in_memory } {
184			set dbname { "" "db.$version" }
185		} else {
186			set dbname "db.$version"
187		}
188		if { [file exists $masterdir/$dbname] == 1 } {
189			puts "\tRep$tnum.c.$i: Removing old version $version."
190			error_check_good dbremove \
191			   [$menv dbremove -auto_commit $dbname] 0
192		}
193
194		puts "\tRep$tnum.c.$i: Set up query database $version."
195		set db [eval berkdb_open_noerr -create -env $menv\
196		    -auto_commit -mode 0644 $largs $omethod $dbname]
197		error_check_good db_open [is_valid_db $db] TRUE
198		eval rep_test $method $menv $db $nentries $start $start 0 $largs
199		incr start $nentries
200		error_check_good db_close [$db close] 0
201
202		# We alternate between processing the messages and
203		# clearing the messages to simulate a failure.
204
205		set process [expr $i % 2]
206		if { $process == 1 } {
207			process_msgs $envlist
208		} else {
209			replclear 2
210			replclear 3
211		}
212
213		# Announce new version.
214		puts "\tRep$tnum.d.$i: Announce new version $version."
215		set vdb [eval {berkdb_open_noerr -env $menv \
216		    -auto_commit -mode 0644} $vname]
217		error_check_good update_version [$vdb put VERSION $version] 0
218		error_check_good vdb_close [$vdb close] 0
219
220		# Process messages or simulate failure.
221		if { $process == 1 } {
222			process_msgs $envlist
223		} else {
224			replclear 2
225			replclear 3
226		}
227
228		# Switch master, update envlist.
229		puts "\tRep$tnum.e.$i: Switch masters."
230		set envlist [switch_master $envlist]
231
232		# Update values for next iteration.
233		set menv [lindex [lindex $envlist 0] 0]
234		set cenv0 [lindex [lindex $envlist 1] 0]
235		incr version
236		if { $version > $maxversion } {
237			set version 1
238		}
239	}
240
241	# Signal to child that we are done.
242	set vdb [eval {berkdb_open_noerr -env $menv \
243	    -auto_commit -mode 0644} $vname]
244	error_check_good version_done [$vdb put VERSION DONE] 0
245	error_check_good vdb_close [$vdb close] 0
246	process_msgs $envlist
247
248	# Watch for child to finish.
249	watch_procs $pid 5
250
251	puts "\tRep$tnum.f: Clean up."
252	error_check_good menv_close [$menv close] 0
253	error_check_good cenv0_close [$cenv0 close] 0
254	error_check_good cenv1_close [$cenv1 close] 0
255
256	replclose $testdir/MSGQUEUEDIR
257
258	# Check for failures in child's log file.
259	set errstrings [eval findfail $testdir/rep045script.log]
260	foreach str $errstrings {
261		puts "FAIL: error message in log file: $str"
262	}
263
264	set testdir $orig_tdir
265	return
266}
267
268proc switch_master { envlist } {
269	# Find env handles and machine ids.
270	set menv [lindex [lindex $envlist 0] 0]
271	set mid [lindex [lindex $envlist 0] 1]
272	set cenv [lindex [lindex $envlist 1] 0]
273	set cid [lindex [lindex $envlist 1] 1]
274	set cenv1 [lindex [lindex $envlist 2] 0]
275	set cid1 [lindex [lindex $envlist 2] 1]
276
277	# Downgrade master, upgrade client.
278	error_check_good master_downgrade [$menv rep_start -client] 0
279	error_check_good client_upgrade [$cenv rep_start -master] 0
280	process_msgs $envlist
281
282	# Adjust envlist.  The former client env is the new master,
283	# and vice versa.
284	set newenvlist "{$cenv $cid} {$menv $mid} {$cenv1 $cid1}"
285	return $newenvlist
286}
287