1# See the file LICENSE for redistribution information.
2#
3# Copyright (c) 2003,2008 Oracle.  All rights reserved.
4#
5# $Id: rep017.tcl,v 12.17 2008/01/08 20:58:53 bostic Exp $
6#
7# TEST	rep017
8# TEST	Concurrency with checkpoints.
9# TEST
10# TEST 	Verify that we achieve concurrency in the presence of checkpoints.
11# TEST 	Here are the checks that we wish to make:
12# TEST  	While dbenv1 is handling the checkpoint record:
13# TEST		Subsequent in-order log records are accepted.
14# TEST          	Accepted PERM log records get NOTPERM
15# TEST          	A subsequent checkpoint gets NOTPERM
16# TEST          	After checkpoint completes, next txn returns PERM
17proc rep017 { method { niter 10 } { tnum "017" } args } {
18
19	source ./include.tcl
20	if { $is_windows9x_test == 1 } {
21		puts "Skipping replication test on Win 9x platform."
22		return
23	}
24
25	# Run for all access methods.
26	if { $checking_valid_methods } {
27		return "ALL"
28	}
29
30	set args [convert_args $method $args]
31	set logsets [create_logsets 2]
32
33	# Run the body of the test with and without recovery.
34	foreach r $test_recopts {
35		foreach l $logsets {
36			set logindex [lsearch -exact $l "in-memory"]
37			if { $r == "-recover" && $logindex != -1 } {
38				puts "Rep$tnum: Skipping\
39				    for in-memory logs with -recover."
40				continue
41			}
42
43			puts "Rep$tnum ($method $r):\
44			    Concurrency with checkpoints."
45			puts "Rep$tnum: Master logs are [lindex $l 0]"
46			puts "Rep$tnum: Client logs are [lindex $l 1]"
47			rep017_sub $method $niter $tnum $l $r $args
48		}
49	}
50}
51
52proc rep017_sub { method niter tnum logset recargs largs } {
53	source ./include.tcl
54	global perm_response_list
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	env_cleanup $testdir
64	set omethod [convert_method $method]
65
66	replsetup $testdir/MSGQUEUEDIR
67
68	set masterdir $testdir/MASTERDIR
69	set clientdir $testdir/CLIENTDIR
70
71	file mkdir $masterdir
72	file mkdir $clientdir
73	set m_logtype [lindex $logset 0]
74	set c_logtype [lindex $logset 1]
75
76	# In-memory logs require a large log buffer, and cannot
77	# be used with -txn nosync.
78	set m_logargs [adjust_logargs $m_logtype]
79	set c_logargs [adjust_logargs $c_logtype]
80	set m_txnargs [adjust_txnargs $m_logtype]
81	set c_txnargs [adjust_txnargs $c_logtype]
82
83	# Open a master.
84	repladd 1
85	set ma_cmd "berkdb_env_noerr -create $verbargs \
86	    -log_max 1000000 $m_txnargs $m_logargs \
87	    -home $masterdir -rep_master -errpfx MASTER \
88	    -rep_transport \[list 1 replsend\]"
89	set masterenv [eval $ma_cmd $recargs]
90
91	# Open a client
92	repladd 2
93	set cl_cmd "berkdb_env_noerr -create -home $clientdir $verbargs \
94	    $c_txnargs $c_logargs -rep_client -errpfx CLIENT \
95	    -rep_transport \[list 2 replsend\]"
96	set clientenv [eval $cl_cmd $recargs]
97
98	# Bring the client online.
99	process_msgs "{$masterenv 1} {$clientenv 2}"
100
101	# Open database in master, make lots of changes so checkpoint
102	# will take a while, and propagate to client.
103	puts "\tRep$tnum.a: Create and populate database."
104	set dbname rep017.db
105	set db [eval "berkdb_open_noerr -create $omethod -auto_commit \
106	    -env $masterenv $largs $dbname"]
107	for { set i 1 } { $i <= $niter } { incr i } {
108		set t [$masterenv txn]
109		error_check_good db_put \
110		    [eval $db put -txn $t $i [chop_data $method data$i]] 0
111		error_check_good txn_commit [$t commit] 0
112	}
113	process_msgs "{$masterenv 1} {$clientenv 2}" 1
114
115	# Get the master's last LSN before the checkpoint
116	set pre_ckp_offset \
117		[stat_field $masterenv log_stat "Current log file offset"]
118
119	puts "\tRep$tnum.b: Checkpoint on master."
120	error_check_good checkpoint [$masterenv txn_checkpoint] 0
121
122	# Now get ckp LSN
123	set ckp_lsn [stat_field $masterenv txn_stat "LSN of last checkpoint"]
124	set ckp_offset [lindex $ckp_lsn 1]
125
126	# Fork child process on client.  It should process whatever
127	# it finds in the message queue -- just the checkpoint record,
128	# for now.  It's run in the background so the parent can
129	# test for whether we're checkpointing at the same time.
130	#
131	puts "\tRep$tnum.c: Fork child process on client."
132	set pid [exec $tclsh_path $test_path/wrap.tcl \
133	    rep017script.tcl $testdir/repscript.log \
134	    $masterdir $clientdir $rep_verbose $verbose_type &]
135
136
137	# We need to wait until we know that the client is processing a
138	# checkpoint.  The checkpoint will consist of some DBREG records
139	# followed by the actual checkpoint.  So, if we've gotten records
140	# later than the last LSN when the master took the checkpoint, we've
141	# begin the checkpoint.  By test design, we should not finish the
142	# checkpoint until this process has at least had a chance to run.
143	#
144	# In order to do this, we have handles open on the message
145	# queue from both this process and its child.  This is not
146	# normally legal behavior for an application using Berkeley DB,
147	# but this test depends on the parent process doing things while
148	# the child is pausing in the middle of the checkpoint.  We are
149	# very careful to control which process is handling which
150	# messages.
151
152 	puts "\tRep$tnum.d: Test whether client is in checkpoint."
153	while { 1 } {
154		set client_off \
155		    [stat_field $clientenv log_stat "Current log file offset"]
156
157		if { $client_off > $pre_ckp_offset } {
158			if { $client_off > $ckp_offset } {
159				# We already completed the checkpoint and
160				# never got out of here.  That's a bug in
161				# in the test.
162				error_check_good checkpoint_test \
163				    not_in_checkpoint should_be_in_checkpoint
164			} else {
165				break;
166			}
167		} else {
168			# Not yet up to checkpoint
169			tclsleep 1
170		}
171	}
172
173	# Main client processes checkpoint 2nd time and should get NOTPERM.
174	puts "\tRep$tnum.e: Commit and checkpoint return NOTPERM from client"
175	incr niter
176	set t [$masterenv txn]
177	error_check_good db_put [eval $db put \
178	    -txn $t $niter [chop_data $method data$niter]] 0
179	error_check_good txn_commit [$t commit] 0
180	error_check_good checkpoint [$masterenv txn_checkpoint] 0
181	set ckp2_lsn [stat_field $masterenv txn_stat "LSN of last checkpoint"]
182
183	process_msgs "{$clientenv 2}" 1
184
185	# Check that the checkpoint record got a NOTPERM
186	# Find the ckp LSN of the Master and then look for the response
187	# from that message in the client
188	set ckp_result ""
189	foreach i $perm_response_list {
190		# Everything in the list should be NOTPERM
191		if { [llength $i] == 0 } {
192			# Check for sentinel at beginning of list
193			continue;
194		}
195		set ckp_result [lindex $i 0]
196		error_check_good NOTPERM [is_substr $ckp_result NOTPERM] 1
197		if { [lindex $i 1] == $ckp2_lsn } {
198			break
199		}
200	}
201	error_check_bad perm_response $ckp_result ""
202
203	puts "\tRep$tnum.f: Waiting for child ..."
204	# Watch until the checkpoint is done.
205	watch_procs $pid 5
206
207	# Verify that the checkpoint is now complete on the client and
208	# that all later messages have been applied.
209	process_msgs "{$clientenv 2}" 1
210	set client_ckp [stat_field $clientenv txn_stat "LSN of last checkpoint"]
211	error_check_good matching_ckps $client_ckp $ckp2_lsn
212
213	set m_end [stat_field $masterenv log_stat "Current log file offset"]
214	set c_end [stat_field $clientenv log_stat "Current log file offset"]
215	error_check_good matching_lsn $c_end $m_end
216
217	# Finally, now that checkpoints are complete; perform another
218	# perm operation and make sure that it returns ISPERM.
219	puts "\tRep$tnum.g: No pending ckp; check for ISPERM"
220	incr niter
221	set t [$masterenv txn]
222	error_check_good db_put [eval $db put \
223	    -txn $t $niter [chop_data $method data$niter]] 0
224	error_check_good txn_commit [$t commit] 0
225	error_check_good checkpoint [$masterenv txn_checkpoint] 0
226	set ckp3_lsn [stat_field $masterenv txn_stat "LSN of last checkpoint"]
227
228	process_msgs "{$clientenv 2}" 1
229
230	# Check that the checkpoint and commit records got a ISPERM
231	# Find the ckp LSN of the Master and then look for the response
232	# from that message in the client
233	set ckp_result ""
234	foreach i $perm_response_list {
235		if { [llength $i] == 0 } {
236			# Check for sentinel at beginning of list
237			continue;
238		}
239
240		# Everything in the list should be ISPERM
241		set ckp_result [lindex $i 0]
242		error_check_good ISPERM [is_substr $ckp_result ISPERM] 1
243		if { [lindex $i 1] == $ckp3_lsn } {
244			break
245		}
246	}
247	error_check_bad perm_response $ckp_result ""
248
249	# Clean up.
250	error_check_good db_close [$db close] 0
251	error_check_good masterenv_close [$masterenv close] 0
252	error_check_good clientenv_close [$clientenv close] 0
253
254	replclose $testdir/MSGQUEUEDIR
255}
256