1# See the file LICENSE for redistribution information.
2#
3# Copyright (c) 2004,2008 Oracle.  All rights reserved.
4#
5# $Id: rep060.tcl,v 12.15 2008/01/08 20:58:53 bostic Exp $
6#
7# TEST	rep060
8# TEST	Test of normally running clients and internal initialization.
9# TEST	Have a client running normally, but slow/far behind the master.
10# TEST	Then the master checkpoints and archives, causing the client
11# TEST	to suddenly be thrown into internal init.  This test tests
12# TEST	that we clean up the old files/pages in mpool and dbreg.
13# TEST	Also test same thing but the app holding an open dbp as well.
14#
15proc rep060 { method { niter 200 } { tnum "060" } args } {
16
17	source ./include.tcl
18
19	# Run for btree and queue only.
20	if { $checking_valid_methods } {
21		set test_methods {}
22		foreach method $valid_methods {
23			if { [is_btree $method] == 1 || \
24			    [is_queue $method] == 1 } {
25				lappend test_methods $method
26			}
27		}
28		return $test_methods
29	}
30	if { [is_btree $method] != 1 && [is_queue $method] != 1 } {
31		puts "Skipping rep060 for method $method."
32		return
33	}
34
35	set args [convert_args $method $args]
36
37	# This test needs to set its own pagesize.
38	set pgindex [lsearch -exact $args "-pagesize"]
39        if { $pgindex != -1 } {
40                puts "Rep$tnum: skipping for specific pagesizes"
41                return
42        }
43
44	set logsets [create_logsets 2]
45
46	# Run the body of the test with and without recovery,
47	# and with and without cleaning.  Skip recovery with in-memory
48	# logging - it doesn't make sense.
49	#
50	# 'user' means that the "app" (the test in this case) has
51	# its own handle open to the database.
52	set opts { "" user }
53	foreach r $test_recopts {
54		foreach o $opts {
55			foreach l $logsets {
56				set logindex [lsearch -exact $l "in-memory"]
57				if { $r == "-recover" && $logindex != -1 } {
58					puts "Skipping rep$tnum for -recover\
59					    with in-memory logs."
60					continue
61				}
62				puts "Rep$tnum ($method $r $o $args): Test of\
63				    internal initialization and slow client."
64				puts "Rep$tnum: Master logs are [lindex $l 0]"
65				puts "Rep$tnum: Client logs are [lindex $l 1]"
66				rep060_sub $method $niter $tnum $l $r $o $args
67			}
68		}
69	}
70}
71
72proc rep060_sub { method niter tnum logset recargs opt largs } {
73	source ./include.tcl
74	global rep_verbose
75	global verbose_type
76
77	set verbargs ""
78	if { $rep_verbose == 1 } {
79		set verbargs " -verbose {$verbose_type on} "
80	}
81
82	env_cleanup $testdir
83
84	replsetup $testdir/MSGQUEUEDIR
85
86	set masterdir $testdir/MASTERDIR
87	set clientdir $testdir/CLIENTDIR
88
89	file mkdir $masterdir
90	file mkdir $clientdir
91
92	# Log size is small so we quickly create more than one.
93	# The documentation says that the log file must be at least
94	# four times the size of the in-memory log buffer.
95	set pagesize 4096
96	append largs " -pagesize $pagesize "
97	set log_max [expr $pagesize * 4]
98
99	set m_logtype [lindex $logset 0]
100	set c_logtype [lindex $logset 1]
101
102	# In-memory logs cannot be used with -txn nosync.
103	set m_logargs [adjust_logargs $m_logtype]
104	set c_logargs [adjust_logargs $c_logtype]
105	set m_txnargs [adjust_txnargs $m_logtype]
106	set c_txnargs [adjust_txnargs $c_logtype]
107
108	# Open a master.
109	repladd 1
110	set ma_envcmd "berkdb_env_noerr -create $m_txnargs \
111	    $m_logargs -log_max $log_max -errpfx MASTER $verbargs \
112	    -home $masterdir -rep_transport \[list 1 replsend\]"
113	set masterenv [eval $ma_envcmd $recargs -rep_master]
114
115	# Open a client
116	puts "\tRep$tnum.a: Open client."
117	repladd 2
118	set cl_envcmd "berkdb_env_noerr -create $c_txnargs \
119	    $c_logargs -log_max $log_max -errpfx CLIENT $verbargs \
120	    -home $clientdir -rep_transport \[list 2 replsend\]"
121	set clientenv [eval $cl_envcmd $recargs -rep_client]
122
123	# Bring the client online by processing the startup messages.
124	set envlist "{$masterenv 1} {$clientenv 2}"
125	process_msgs $envlist
126
127	# Clobber replication's 30-second anti-archive timer, which will have
128	# been started by client sync-up internal init.
129	#
130	$masterenv test force noarchive_timeout
131
132	# Set a low limit so that there are lots of reps between
133	# master and client.  This allows greater control over
134	# the test.
135	error_check_good thr [$masterenv rep_limit 0 [expr 10 * 1024]] 0
136
137	# It is *key* to this test that we have a database handle
138	# open for the duration of the test.  The problem this
139	# test checks for regards internal init when there are open
140	# database handles around.
141	#
142	set dbname "test.db"
143	set omethod [convert_method $method]
144	set db [eval {berkdb_open_noerr -env $masterenv -auto_commit \
145	    -create -mode 0644} $largs $omethod $dbname]
146	error_check_good dbopen [is_valid_db $db] TRUE
147
148	# Put some data into the database, running the master up past
149	# log file 10, discarding messages to the client so that it will
150	# be forced to request them as a gap.
151	#
152	puts "\tRep$tnum.c: Run rep_test in master env."
153	set start 0
154
155	set stop 0
156	set endlog 10
157	while { $stop == 0 } {
158		# Run test in the master (don't update client).
159		eval rep_test $method \
160		    $masterenv $db $niter $start $start 0 0 $largs
161		incr start $niter
162		replclear 2
163
164		if { $m_logtype != "in-memory" } {
165			set res \
166			    [eval exec $util_path/db_archive -l -h $masterdir]
167		}
168		# Make sure the master has gone as far as we requested.
169		set last_master_log [get_logfile $masterenv last]
170		if { $last_master_log > $endlog } {
171			set stop 1
172		}
173	}
174
175	# Do one more set of txns at the master, replicating log records
176	# normally, to give the client a chance to notice how many messages
177	# it is missing.
178	#
179	eval rep_test $method $masterenv $db $niter $start $start 0 0 $largs
180	incr start $niter
181
182	set stop 0
183	set client_endlog 5
184	set last_client_log 0
185	set nproced 0
186	incr nproced [proc_msgs_once $envlist NONE err]
187	incr nproced [proc_msgs_once $envlist NONE err]
188
189	puts "\tRep$tnum.d: Client catches up partway."
190	error_check_good ckp [$masterenv txn_checkpoint] 0
191
192	# We have checkpointed on the master, but we want to get the
193	# client a healthy way through the logs before archiving on
194	# the master.
195	while { $stop == 0 } {
196		set nproced 0
197		incr nproced [proc_msgs_once $envlist NONE err]
198		if { $nproced == 0 } {
199			error_check_good \
200			    ckp [$masterenv txn_checkpoint -force] 0
201		}
202
203		# Stop processing when the client is partway through.
204		if { $c_logtype != "in-memory" } {
205			set res \
206			    [eval exec $util_path/db_archive -l -h $clientdir]
207		}
208		set last_client_log [get_logfile $clientenv last]
209		set first_client_log [get_logfile $clientenv first]
210		if { $last_client_log > $client_endlog } {
211			set stop 1
212		}
213	}
214
215	#
216	# The user may have the database open itself.
217	#
218	if { $opt == "user" } {
219		set cdb [eval {berkdb_open_noerr -env} $clientenv $dbname]
220		error_check_good dbopen [is_valid_db $cdb] TRUE
221		set ccur [$cdb cursor]
222		error_check_good curs [is_valid_cursor $ccur $cdb] TRUE
223		set ret [$ccur get -first]
224		set kd [lindex $ret 0]
225		set key [lindex $kd 0]
226		error_check_good cclose [$ccur close] 0
227	} else {
228		set cdb NULL
229	}
230
231	# Now that the client is well on its way of normal processing,
232	# simply fairly far behind the master, archive on the master,
233	# removing the log files the client needs, sending it into
234	# internal init with the database pages reflecting the client's
235	# current LSN.
236	#
237	puts "\tRep$tnum.e: Force internal initialization."
238	if { $m_logtype != "in-memory" } {
239		puts "\tRep$tnum.e1: Archive on master."
240		set res [eval exec $util_path/db_archive -d -h $masterdir]
241	} else {
242		# Master is in-memory, and we'll need a different
243		# technique to create the gap forcing internal init.
244		puts "\tRep$tnum.e1: Run rep_test until gap is created."
245		set stop 0
246		while { $stop == 0 } {
247			eval rep_test $method $masterenv \
248			    NULL $niter $start $start 0 0 $largs
249			incr start $niter
250			set first_master_log [get_logfile $masterenv first]
251			if { $first_master_log > $last_client_log } {
252				set stop 1
253			}
254		}
255	}
256
257	puts "\tRep$tnum.f: Process messages."
258	if { $opt == "user" } {
259		for { set loop 0 } { $loop < 5 } { incr loop } {
260			set nproced 0
261			incr nproced [proc_msgs_once $envlist]
262			if { $cdb == "NULL" } {
263				continue
264			}
265			puts "\tRep$tnum.g.$loop: Check user database."
266			set status [catch {$cdb get $key} ret]
267			if { $status != 0 } {
268				#
269				# For db operations, DB doesn't block, but
270				# returns DEADLOCK.
271				#
272				set is_lock [is_substr $ret DB_LOCK_DEADLOCK]
273				set is_dead [is_substr $ret DB_REP_HANDLE_DEAD]
274				error_check_good lock_dead \
275				    [expr $is_lock || $is_dead] 1
276				if { $is_dead } {
277					error_check_good cclose [$cdb close] 0
278					set cdb NULL
279				}
280			}
281		}
282	}
283	process_msgs $envlist
284
285	#
286	# If we get through the user loop with a valid db, then it better
287	# be a dead handle after we've completed processing all the
288	# messages and running recovery.
289	#
290	if { $cdb != "NULL" } {
291		puts "\tRep$tnum.h: Check dead handle."
292		set status [catch {$cdb get $key} ret]
293		error_check_good status $status 1
294		error_check_good is_dead [is_substr $ret DB_REP_HANDLE_DEAD] 1
295		error_check_good cclose [$cdb close] 0
296		puts "\tRep$tnum.i: Verify correct internal initialization."
297	} else {
298		puts "\tRep$tnum.h: Verify correct internal initialization."
299	}
300	error_check_good close [$db close] 0
301	process_msgs $envlist
302
303	# We have now forced an internal initialization.  Verify it is correct.
304	rep_verify $masterdir $masterenv $clientdir $clientenv 1
305
306	check_log_location $masterenv
307	check_log_location $clientenv
308
309	error_check_good masterenv_close [$masterenv close] 0
310	error_check_good clientenv_close [$clientenv close] 0
311	replclose $testdir/MSGQUEUEDIR
312}
313