1# See the file LICENSE for redistribution information.
2#
3# Copyright (c) 2001-2009 Oracle.  All rights reserved.
4#
5# $Id$
6#
7# TEST	rep012
8# TEST	Replication and dead DB handles.
9# TEST
10# TEST	Run a modified version of test001 in a replicated master env.
11# TEST	Run in replicated environment with secondary indices too.
12# TEST	Make additional changes to master, but not to the client.
13# TEST	Downgrade the master and upgrade the client with open db handles.
14# TEST	Verify that the roll back on clients gives dead db handles.
15proc rep012 { method { niter 10 } { tnum "012" } args } {
16
17	source ./include.tcl
18	global databases_in_memory
19	global repfiles_in_memory
20
21	if { $is_windows9x_test == 1 } {
22		puts "Skipping replication test on Win 9x platform."
23		return
24	}
25
26	# Run for all access methods.
27	if { $checking_valid_methods } {
28		return "ALL"
29	}
30
31	set args [convert_args $method $args]
32	set logsets [create_logsets 3]
33
34	set msg "using on-disk databases"
35	if { $databases_in_memory } {
36		set msg "using named in-memory databases"
37		if { [is_queueext $method] } {
38			puts -nonewline "Skipping rep$tnum for method "
39			puts "$method with named in-memory databases."
40			return
41		}
42	}
43
44	set msg2 "and on-disk replication files"
45	if { $repfiles_in_memory } {
46		set msg2 "and in-memory replication files"
47	}
48
49	# Run the body of the test with and without recovery.
50	foreach r $test_recopts {
51		foreach l $logsets {
52			set logindex [lsearch -exact $l "in-memory"]
53			if { $r == "-recover" && $logindex != -1 } {
54				puts "Rep$tnum: Skipping\
55				    for in-memory logs with -recover."
56				continue
57			}
58			puts "Rep$tnum ($method $r):\
59			    Replication and dead db handles $msg $msg2."
60			puts "Rep$tnum: Master logs are [lindex $l 0]"
61			puts "Rep$tnum: Client 0 logs are [lindex $l 1]"
62			puts "Rep$tnum: Client 1 logs are [lindex $l 2]"
63			rep012_sub $method $niter $tnum $l $r $args
64		}
65	}
66}
67
68proc rep012_sub { method niter tnum logset recargs largs } {
69	global testdir
70	global databases_in_memory
71	global repfiles_in_memory
72	global verbose_check_secondaries
73	global rep_verbose
74	global verbose_type
75
76	set verbargs ""
77	if { $rep_verbose == 1 } {
78		set verbargs " -verbose {$verbose_type on} "
79	}
80
81	set repmemargs ""
82	if { $repfiles_in_memory } {
83		set repmemargs "-rep_inmem_files "
84	}
85
86	env_cleanup $testdir
87	set orig_tdir $testdir
88
89	replsetup $testdir/MSGQUEUEDIR
90
91	set masterdir $testdir/MASTERDIR
92	set clientdir $testdir/CLIENTDIR
93	set clientdir2 $testdir/CLIENTDIR.2
94	file mkdir $masterdir
95	file mkdir $clientdir
96	file mkdir $clientdir2
97
98	set m_logtype [lindex $logset 0]
99	set c_logtype [lindex $logset 1]
100	set c2_logtype [lindex $logset 2]
101
102	# In-memory logs require a large log buffer, and cannot
103	# be used with -txn nosync.
104	set m_logargs [adjust_logargs $m_logtype]
105	set c_logargs [adjust_logargs $c_logtype]
106	set c2_logargs [adjust_logargs $c2_logtype]
107	set m_txnargs [adjust_txnargs $m_logtype]
108	set c_txnargs [adjust_txnargs $c_logtype]
109	set c2_txnargs [adjust_txnargs $c2_logtype]
110
111	# Open a master.
112	repladd 1
113	set ma_envcmd "berkdb_env_noerr -create $m_txnargs \
114	    $m_logargs -errpfx ENV0 $verbargs $repmemargs \
115	    -home $masterdir -rep_transport \[list 1 replsend\]"
116	set env0 [eval $ma_envcmd $recargs -rep_master]
117	set masterenv $env0
118
119	# Open two clients
120	repladd 2
121	set cl_envcmd "berkdb_env_noerr -create $c_txnargs \
122	    $c_logargs -errpfx ENV1 $verbargs $repmemargs \
123	    -home $clientdir -rep_transport \[list 2 replsend\]"
124	set env1 [eval $cl_envcmd $recargs -rep_client]
125	set clientenv $env1
126
127	repladd 3
128	set cl2_envcmd "berkdb_env_noerr -create $c2_txnargs \
129	    $c2_logargs -errpfx ENV2 $verbargs $repmemargs \
130	    -home $clientdir2 -rep_transport \[list 3 replsend\]"
131	set cl2env [eval $cl2_envcmd $recargs -rep_client]
132
133	if { $databases_in_memory } {
134		set testfile { "" test$tnum.db }
135		set pname { "" primary$tnum.db }
136		set sname { "" secondary$tnum.db }
137	} else {
138		set testfile "test$tnum.db"
139		set pname "primary$tnum.db"
140		set sname "secondary$tnum.db"
141	}
142	set omethod [convert_method $method]
143	set env0db [eval {berkdb_open_noerr -env $env0 -auto_commit \
144	    -create -mode 0644} $largs $omethod $testfile]
145	error_check_good dbopen [is_valid_db $env0db] TRUE
146	set masterdb $env0db
147
148	set do_secondary 0
149	if { [is_btree $method] || [is_hash $method] } {
150		set do_secondary 1
151		# Open the primary
152		set mpdb [eval {berkdb_open_noerr -env $env0 -auto_commit \
153		    -create -mode 0644} $largs $omethod $pname]
154		error_check_good dbopen [is_valid_db $mpdb] TRUE
155
156		# Open the secondary
157		# Open a 2nd handle to the same secondary
158		set msdb [eval {berkdb_open_noerr -env $env0 -auto_commit \
159		    -create -mode 0644} $largs $omethod $sname]
160		error_check_good dbopen [is_valid_db $msdb] TRUE
161		error_check_good associate [$mpdb associate \
162		    [callback_n 0] $msdb] 0
163	}
164
165	# Bring the clients online by processing the startup messages.
166	set envlist "{$env0 1} {$env1 2} {$cl2env 3}"
167	process_msgs $envlist
168
169	set env1db [eval {berkdb_open_noerr -env $env1 -auto_commit \
170	     -mode 0644} $largs $omethod $testfile]
171	set clientdb $env1db
172	error_check_good dbopen [is_valid_db $env1db] TRUE
173	set env2db [eval {berkdb_open_noerr -env $cl2env -auto_commit \
174	     -mode 0644} $largs $omethod $testfile]
175	error_check_good dbopen [is_valid_db $env2db] TRUE
176
177	# Run a modified test001 in the master (and update clients).
178	puts "\tRep$tnum.a.0: Running rep_test in replicated env."
179	eval rep_test $method $masterenv $masterdb $niter 0 0 0 $largs
180	process_msgs $envlist
181
182	if { $do_secondary } {
183		# Put some data into the primary
184		puts "\tRep$tnum.a.1: Putting primary/secondary data on master."
185		eval rep012_sec $method $mpdb $niter keys data
186		process_msgs $envlist
187
188		set verbose_check_secondaries 1
189		check_secondaries $mpdb $msdb $niter keys data "Rep$tnum.b"
190	} else {
191		puts "\tRep$tnum.b: Skipping secondaries for method $method"
192	}
193
194	# Check that databases are in-memory or on-disk as expected.
195	# We can only check the secondaries if secondaries are allowed for
196	# this access method.
197	set names [list $testfile]
198	if { $do_secondary } {
199		lappend names $pname $sname
200	}
201	foreach name $names {
202		eval check_db_location $masterenv $name
203		eval check_db_location $clientenv $name
204		eval check_db_location $cl2env $name
205	}
206
207	puts "\tRep$tnum.c: Run test in master and client 2 only"
208	set nstart $niter
209	eval rep_test\
210	    $method $masterenv $masterdb $niter $nstart $nstart 0 $largs
211
212	# Ignore messages for $env1.
213	set envlist "{$env0 1} {$cl2env 3}"
214	process_msgs $envlist
215
216	# Nuke those for client about to become master.
217	replclear 2
218	tclsleep 3
219	puts "\tRep$tnum.d: Swap envs"
220	set tmp $masterenv
221	set masterenv $clientenv
222	set clientenv $tmp
223	error_check_good downgrade [$clientenv rep_start -client] 0
224	error_check_good upgrade [$masterenv rep_start -master] 0
225	set envlist "{$env0 1} {$env1 2} {$cl2env 3}"
226	process_msgs $envlist
227
228	#
229	# At this point, env0 should have rolled back across a txn commit.
230	# If we do any operation on env0db, we should get an error that
231	# the handle is dead.
232	puts "\tRep$tnum.e: Try to access db handle after rollback"
233	set stat1 [catch {$env0db stat} ret1]
234	error_check_good stat1 $stat1 1
235	error_check_good dead1 [is_substr $ret1 DB_REP_HANDLE_DEAD] 1
236
237	set stat3 [catch {$env2db stat} ret3]
238	error_check_good stat3 $stat3 1
239	error_check_good dead3 [is_substr $ret3 DB_REP_HANDLE_DEAD] 1
240
241	if { $do_secondary } {
242		#
243		# Check both secondary get and close to detect DEAD_HANDLE.
244		#
245		puts "\tRep$tnum.f: Try to access secondary db handles after rollback"
246		set verbose_check_secondaries 1
247		check_secondaries $mpdb $msdb $niter \
248		    keys data "Rep$tnum.f" errp errs errsg
249		error_check_good deadp [is_substr $errp DB_REP_HANDLE_DEAD] 1
250		error_check_good deads [is_substr $errs DB_REP_HANDLE_DEAD] 1
251		error_check_good deadsg [is_substr $errsg DB_REP_HANDLE_DEAD] 1
252		puts "\tRep$tnum.g: Closing"
253		error_check_good mpdb [$mpdb close] 0
254		error_check_good msdb [$msdb close] 0
255	} else {
256		puts "\tRep$tnum.f: Closing"
257	}
258
259	error_check_good env0db [$env0db close] 0
260	error_check_good env1db [$env1db close] 0
261	error_check_good cl2db [$env2db close] 0
262	error_check_good env0_close [$env0 close] 0
263	error_check_good env1_close [$env1 close] 0
264	error_check_good cl2_close [$cl2env close] 0
265	replclose $testdir/MSGQUEUEDIR
266	set verbose_check_secondaries 0
267	set testdir $orig_tdir
268	return
269}
270
271proc rep012_sec {method pdb niter keysp datap} {
272	source ./include.tcl
273
274	upvar $keysp keys
275	upvar $datap data
276	set did [open $dict]
277	for { set n 0 } { [gets $did str] != -1 && $n < $niter } { incr n } {
278		if { [is_record_based $method] == 1 } {
279			set key [expr $n + 1]
280			set datum $str
281		} else {
282			set key $str
283			gets $did datum
284		}
285		set keys($n) $key
286		set data($n) [pad_data $method $datum]
287
288		set ret [$pdb put $key [chop_data $method $datum]]
289		error_check_good put($n) $ret 0
290	}
291	close $did
292}
293