1# Copyright (c) 2012, 2020 Oracle and/or its affiliates.  All rights reserved.
2#
3# See the file LICENSE for license information.
4#
5# $Id$
6#
7# TEST  rep105
8# TEST	Replication and rollback on sync over multiple log files.
9# TEST
10# TEST	Run rep_test in a replicated master env.
11# TEST  Hold open various txns in various log files and make sure
12# TEST	that when synchronization happens, we rollback the correct set
13# TEST	of log files.
14proc rep105 { method { niter 4 } { tnum "105" } args } {
15	source ./include.tcl
16	global repfiles_in_memory
17
18	# Only Btree is needed.
19	if { $checking_valid_methods } {
20		set test_methods { btree }
21		return $test_methods
22	}
23
24	if { [is_btree $method] == 0 } {
25		puts "Skipping for method $method."
26		return
27	}
28
29	set msg2 "and on-disk replication files"
30	if { $repfiles_in_memory } {
31		set msg2 "and in-memory replication files"
32	}
33
34	set args [convert_args $method $args]
35	set logsets [create_logsets 3]
36
37	# Run the body of the test with and without recovery.
38	foreach r $test_recopts {
39		foreach l $logsets {
40			set logindex [lsearch -exact $l "in-memory"]
41			if { $r == "-recover" && $logindex != -1 } {
42				puts "Rep$tnum: Skipping for\
43				    in-memory logs with -recover."
44				continue
45			}
46			puts "Rep$tnum ($method $r): \
47			    Replication and multi-logfile rollback $msg2."
48			puts "Rep$tnum: Master logs are [lindex $l 0]"
49			puts "Rep$tnum: Client1 logs are [lindex $l 1]"
50			puts "Rep$tnum: Client2 logs are [lindex $l 2]"
51			rep105_sub $method $niter $tnum $l $r $args
52		}
53	}
54}
55
56proc rep105_sub { method niter tnum logset recargs largs } {
57	global repfiles_in_memory
58	global rep_verbose
59	global testdir
60	global verbose_type
61
62	set verbargs ""
63	if { $rep_verbose == 1 } {
64		set verbargs " -verbose {$verbose_type on} "
65	}
66
67	set repmemargs ""
68	if { $repfiles_in_memory } {
69		set repmemargs "-rep_inmem_files "
70	}
71
72	env_cleanup $testdir
73
74	set orig_tdir $testdir
75	set omethod [convert_method $method]
76
77	replsetup $testdir/MSGQUEUEDIR
78
79	set masterdir $testdir/MASTERDIR
80	set clientdir $testdir/CLIENTDIR
81	set clientdir2 $testdir/CLIENTDIR.2
82	file mkdir $masterdir
83	file mkdir $clientdir
84	file mkdir $clientdir2
85
86	set pagesize 4096
87	append largs " -pagesize $pagesize "
88	set log_max [expr $pagesize * 8]
89
90	set m_logtype [lindex $logset 0]
91	set m_logargs [adjust_logargs $m_logtype]
92	set m_txnargs [adjust_txnargs $m_logtype]
93
94	set c_logtype [lindex $logset 1]
95	set c_logargs [adjust_logargs $c_logtype]
96	set c_txnargs [adjust_txnargs $c_logtype]
97
98	set c2_logtype [lindex $logset 2]
99	set c2_logargs [adjust_logargs $c2_logtype]
100	set c2_txnargs [adjust_txnargs $c2_logtype]
101
102	# Open a master.
103	repladd 1
104	set ma_envcmd "berkdb_env_noerr -create $m_txnargs $m_logargs \
105	    -home $masterdir $verbargs -errpfx MASTER -log_max $log_max \
106	    -rep_transport \[list 1 replsend\] $repmemargs"
107	set masterenv [eval $ma_envcmd $recargs -rep_master]
108
109	# Open two clients
110	repladd 2
111	set cl_envcmd "berkdb_env_noerr -create $c_txnargs $c_logargs \
112	    -home $clientdir $verbargs -errpfx CLIENT1 -log_max $log_max \
113	    -rep_transport \[list 2 replsend\] $repmemargs"
114	set clientenv [eval $cl_envcmd $recargs -rep_client]
115
116	repladd 3
117	set cl2_envcmd "berkdb_env_noerr -create $c2_txnargs $c2_logargs \
118	    -home $clientdir2 $verbargs -errpfx CLIENT2 -log_max $log_max \
119	    -rep_transport \[list 3 replsend\] $repmemargs"
120	set cl2env [eval $cl2_envcmd $recargs -rep_client]
121
122	# Bring the clients online by processing the startup messages.
123	set envlist "{$masterenv 1} {$clientenv 2} {$cl2env 3}"
124	process_msgs $envlist
125
126	# Run rep_test in the master (and update clients).
127	#
128	# Set niter small so that no checkpoints are performed in
129	# rep_test.  We want to control when checkpoints happen.
130	#
131	set niter 4
132	puts "\tRep$tnum.a: Running rep_test in replicated env."
133	eval rep_test $method $masterenv NULL $niter 0 0 0 $largs
134	process_msgs $envlist
135
136	#
137	# We want to start several transactions and make sure
138	# that the correct log files are left based on outstanding
139	# txns after sync.
140	#
141	# The logfile sync LSN is in log file S.  Transactions
142	# are noted with T and their commit with #.
143	# We want:
144	#                    SYNC_LSN
145	# S-2.... S-1.... S......|...... S+1.... S+2....
146	# T1.................#   |
147	#   T2...................|..#
148	#          T3............|.........#
149	#                    T4..|..#
150	#                        | T5.#
151	#                        |        T6........#
152	#
153	#
154	# Create a few extra databases so we can hold these txns
155	# open and have operations on them outstanding.
156	#
157	# We close 'client' at the SYNC_LSN point.  Then run with
158	# the master and client2 only.  Then in S+2, we close the
159	# master and reopen 'client' as the master so that client2
160	# needs to rollback all the way to the SYNC_LSN.
161	#
162	set t1db "txn1.db"
163	set t2db "txn2.db"
164	set t3db "txn3.db"
165	set key1 "KEY1"
166	set key2 "KEY2"
167	set key3 "KEY3"
168	set key4 "KEY4"
169	set data1 "DATA1"
170	set data2 "DATA2"
171	set data3 "DATA3"
172	set data4 "DATA4"
173	set db1 [eval {berkdb_open_noerr} -env $masterenv -auto_commit\
174	    -create -mode 0644 $omethod $largs $t1db]
175	set db2 [eval {berkdb_open_noerr} -env $masterenv -auto_commit\
176	    -create -mode 0644 $omethod $largs $t2db]
177	set db3 [eval {berkdb_open_noerr} -env $masterenv -auto_commit\
178	    -create -mode 0644 $omethod $largs $t3db]
179	process_msgs $envlist
180
181	puts "\tRep$tnum.b: Set up T1 and T2 long running txns."
182	set t1 [$masterenv txn]
183	set ret [$db1 put -txn $t1 $key1 $data1]
184	error_check_good put $ret 0
185	set t2 [$masterenv txn]
186	set ret [$db2 put -txn $t2 $key1 $data1]
187	error_check_good put $ret 0
188
189	set logminus2 [get_logfile $masterenv last]
190	set start $niter
191
192	rep105_moveonelog $tnum $masterenv $method $niter $start $envlist \
193	    $logminus2 $largs
194
195	puts "\tRep$tnum.d: Set up T3 long running txn."
196	set t3 [$masterenv txn]
197	set ret [$db3 put -txn $t3 $key1 $data1]
198	error_check_good put $ret 0
199
200	set logminus1 [get_logfile $masterenv last]
201	set start [expr $niter * 10]
202	rep105_moveonelog $tnum $masterenv $method $niter $start $envlist \
203	    $logminus1 $largs
204
205	set logsync [get_logfile $masterenv last]
206	#
207	# We want to resolve T1 before the sync point.
208	# Write another part of that txn and then commit.
209	#
210	puts "\tRep$tnum.e: Resolve T1 and start T4."
211	set ret [$db1 put -txn $t1 $key2 $data2]
212	error_check_good put $ret 0
213	error_check_good commit [$t1 commit] 0
214	set t4 [$masterenv txn]
215	set ret [$db1 put -txn $t4 $key3 $data3]
216	error_check_good put $ret 0
217
218	# Run a couple more txns to get a sync point
219	set start [expr $niter * 20]
220	eval rep_test $method $masterenv NULL $niter $start $start 0 $largs
221	process_msgs $envlist
222
223	puts "\tRep$tnum.f: Close client 1 and make master changes."
224	error_check_good client_close [$clientenv close] 0
225	set envlist "{$masterenv 1} {$cl2env 3}"
226
227	puts "\tRep$tnum.g: Resolve T2 and T4.  Start and resolve T5."
228	set ret [$db2 put -txn $t2 $key2 $data2]
229	error_check_good put $ret 0
230	error_check_good commit [$t2 commit] 0
231	set ret [$db1 put -txn $t4 $key4 $data4]
232	error_check_good put $ret 0
233	error_check_good commit [$t4 commit] 0
234	set t5 [$masterenv txn]
235	set ret [$db2 put -txn $t5 $key3 $data3]
236	error_check_good put $ret 0
237	error_check_good commit [$t5 commit] 0
238
239	set start [expr $niter * 20]
240	rep105_moveonelog $tnum $masterenv $method $niter $start $envlist \
241	    $logsync $largs
242
243	set logplus1 [get_logfile $masterenv last]
244	puts "\tRep$tnum.h: Resolve T3.  Start T6."
245	set ret [$db3 put -txn $t3 $key2 $data2]
246	error_check_good put $ret 0
247	error_check_good commit [$t3 commit] 0
248	set t6 [$masterenv txn]
249	set ret [$db3 put -txn $t6 $key3 $data3]
250	error_check_good put $ret 0
251
252	set start [expr $niter * 30]
253	rep105_moveonelog $tnum $masterenv $method $niter $start $envlist \
254	    $logplus1 $largs
255
256	puts "\tRep$tnum.i: Resolve T6.  Close dbs"
257	set ret [$db3 put -txn $t6 $key4 $data4]
258	error_check_good put $ret 0
259	error_check_good commit [$t6 commit] 0
260
261	$db1 close
262	$db2 close
263	$db3 close
264	process_msgs $envlist
265
266	# Delete messages for closed client
267	replclear 2
268
269	puts "\tRep$tnum.j: Close master, reopen client as master."
270	error_check_good master_close [$masterenv close] 0
271
272	set newmasterenv [eval $cl_envcmd $recargs -rep_master]
273
274	puts "\tRep$tnum.k: Process messages to cause rollback in client2."
275	set lastlog [get_logfile $cl2env last]
276	set envlist "{$newmasterenv 2} {$cl2env 3}"
277	process_msgs $envlist
278	replclear 1
279
280	#
281	# Verify we rolled back to the expected log file.
282	# We know we're dealing with single digit log files nums so
283	# do the easy thing using lfname.  If that ever changes,
284	# this will need to be fixed.
285	#
286	# We expect to rollback to $logsync, and that $logplus1
287	# through $lastlog are gone after processing messages.
288	# All of the rollback verification is in clientdir2.
289	#
290	set cwd [pwd]
291	cd $clientdir2
292	set saved_lf [glob -nocomplain log.*]
293	cd $cwd
294	set lfname log.000000000
295
296	# For in-memory logs we just check the log file
297	# number of the first and last logs and assume that
298	# the logs in the middle are available.  For on-disk
299	# logs we check for physical existence of all logs.
300	if { $c2_logtype == "in-memory" } {
301		set last_lf [get_logfile $cl2env last]
302		set first_lf [get_logfile $cl2env first]
303		error_check_good first_inmem_log\
304		    [expr $first_lf <= $logminus2] 1
305		error_check_good last_inmem_log $last_lf $logsync
306	} else {
307		for { set i $logminus2 } { $i <= $logsync } { incr i } {
308			set lf $lfname$i
309			set present [lsearch -exact $saved_lf $lf]
310			error_check_bad lf.present.$i $present -1
311		}
312		for { set i $logplus1 } { $i <= $lastlog } { incr i } {
313			set lf $lfname$i
314			set present [lsearch -exact $saved_lf $lf]
315			error_check_good lf.notpresent.$i $present -1
316		}
317	}
318	error_check_good newmasterenv_close [$newmasterenv close] 0
319	error_check_good cl2_close [$cl2env close] 0
320	replclose $testdir/MSGQUEUEDIR
321	set testdir $orig_tdir
322	return
323}
324
325proc rep105_moveonelog { tnum env method niter start envlist lognum largs } {
326	set stop 0
327	while { $stop == 0 } {
328		puts "\t\tRep$tnum: Running rep_test until past log $lognum."
329		eval rep_test $method $env NULL $niter $start $start \
330		    0 $largs
331		process_msgs $envlist
332		incr start $niter
333		set newlog [get_logfile $env last]
334		if { $newlog > $lognum } {
335			set stop 1
336		}
337	}
338}
339