1# See the file LICENSE for redistribution information.
2#
3# Copyright (c) 2003,2008 Oracle.  All rights reserved.
4#
5# $Id: rep010.tcl,v 12.16 2008/01/08 20:58:53 bostic Exp $
6#
7# TEST  rep010
8# TEST	Replication and ISPERM
9# TEST
10# TEST	With consecutive message processing, make sure every
11# TEST	DB_REP_PERMANENT is responded to with an ISPERM when
12# TEST	processed.  With gaps in the processing, make sure
13# TEST	every DB_REP_PERMANENT is responded to with an ISPERM
14# TEST	or a NOTPERM.  Verify in both cases that the LSN returned
15# TEST	with ISPERM is found in the log.
16proc rep010 { method { niter 100 } { tnum "010" } args } {
17
18	source ./include.tcl
19	if { $is_windows9x_test == 1 } {
20		puts "Skipping replication test on Win 9x platform."
21		return
22	}
23
24	# Run for all access methods.
25	if { $checking_valid_methods } {
26		return "ALL"
27	}
28
29	set args [convert_args $method $args]
30	set logsets [create_logsets 2]
31
32	# Run the body of the test with and without recovery.
33	foreach r $test_recopts {
34		foreach l $logsets {
35			set logindex [lsearch -exact $l "in-memory"]
36			if { $r == "-recover" && $logindex != -1 } {
37				puts "Rep$tnum: Skipping\
38				    for in-memory logs with -recover."
39				continue
40			}
41			puts "Rep$tnum ($method $r): Replication and ISPERM."
42			puts "Rep$tnum: Master logs are [lindex $l 0]"
43			puts "Rep$tnum: Client logs are [lindex $l 1]"
44			rep010_sub $method $niter $tnum $l $r $args
45		}
46	}
47}
48
49proc rep010_sub { method niter tnum logset recargs largs } {
50	source ./include.tcl
51	global rand_init
52	berkdb srand $rand_init
53	global perm_sent_list
54	global rep_verbose
55	global verbose_type
56
57	set verbargs ""
58	if { $rep_verbose == 1 } {
59		set verbargs " -verbose {$verbose_type on} "
60	}
61
62	env_cleanup $testdir
63	set omethod [convert_method $method]
64
65	replsetup $testdir/MSGQUEUEDIR
66	set perm_sent_list {{}}
67
68	set masterdir $testdir/MASTERDIR
69	set clientdir $testdir/CLIENTDIR
70
71	file mkdir $masterdir
72	file mkdir $clientdir
73
74	set m_logtype [lindex $logset 0]
75	set c_logtype [lindex $logset 1]
76
77	# In-memory logs require a large log buffer, and cannot
78	# be used with -txn nosync.
79	set m_logargs [adjust_logargs $m_logtype]
80	set c_logargs [adjust_logargs $c_logtype]
81	set m_txnargs [adjust_txnargs $m_logtype]
82	set c_txnargs [adjust_txnargs $c_logtype]
83
84	# Open a master.
85	repladd 1
86	set env_cmd(M) "berkdb_env_noerr -create -log_max 1000000 \
87	    $m_logargs $verbargs -errpfx MASTER \
88	    -home $masterdir $m_txnargs -rep_master \
89	    -rep_transport \[list 1 replsend\]"
90	set masterenv [eval $env_cmd(M) $recargs]
91
92	# Open a client
93	repladd 2
94	set env_cmd(C) "berkdb_env_noerr -create -home $clientdir \
95	    $c_txnargs $c_logargs $verbargs -rep_client -errpfx CLIENT \
96	    -rep_transport \[list 2 replsend\]"
97	set clientenv [eval $env_cmd(C) $recargs]
98
99	# Bring the client online.  Since that now involves internal init, we
100	# have to avoid the special rep010_process_msgs here, because otherwise
101	# we would hang trying to open a log cursor.
102	#
103	process_msgs "{$masterenv 1} {$clientenv 2}"
104
105	# Open database in master, propagate to client.
106	set dbname rep010.db
107	set db1 [eval {berkdb_open_noerr -create} $omethod -auto_commit \
108	    -env $masterenv $largs $dbname]
109	rep010_process_msgs $masterenv $clientenv 1
110
111	puts "\tRep$tnum.a: Process messages with no gaps."
112	# Feed operations one at a time to master and immediately
113	# update client.
114	for { set i 1 } { $i <= $niter } { incr i } {
115		set t [$masterenv txn]
116		error_check_good db_put \
117		    [eval $db1 put -txn $t $i [chop_data $method data$i]] 0
118		error_check_good txn_commit [$t commit] 0
119		rep010_process_msgs $masterenv $clientenv 1
120	}
121
122	# Replace data.
123	for { set i 1 } { $i <= $niter } { incr i } {
124		set t [$masterenv txn]
125		set ret \
126		    [$db1 get -get_both -txn $t $i [pad_data $method data$i]]
127		error_check_good db_put \
128		    [$db1 put -txn $t $i [chop_data $method newdata$i]] 0
129		error_check_good txn_commit [$t commit] 0
130		rep010_process_msgs $masterenv $clientenv 1
131	}
132
133	# Try some aborts.  These do not write permanent messages.
134	for { set i 1 } { $i <= $niter } { incr i } {
135		set t [$masterenv txn]
136		error_check_good db_put [$db1 put -txn $t $i abort$i] 0
137		error_check_good txn_abort [$t abort] 0
138		rep010_process_msgs $masterenv $clientenv 0
139	}
140
141	puts "\tRep$tnum.b: Process messages with gaps."
142	# To test gaps in message processing, run and commit a whole
143	# bunch of transactions, then process the messages with skips.
144	for { set i 1 } { $i <= $niter } { incr i } {
145		set t [$masterenv txn]
146		error_check_good db_put [$db1 put -txn $t $i data$i] 0
147		error_check_good txn_commit [$t commit] 0
148	}
149	set skip [berkdb random_int 2 8]
150	rep010_process_msgs $masterenv $clientenv 1 $skip
151
152	# Clean up.
153	error_check_good db1_close [$db1 close] 0
154	error_check_good masterenv_close [$masterenv close] 0
155	error_check_good clientenv_close [$clientenv close] 0
156
157	replclose $testdir/MSGQUEUEDIR
158}
159
160proc rep010_process_msgs { masterenv clientenv check {skip_interval 0} } {
161	global perm_response_list
162	global perm_sent_list
163
164	set perm_response_list {{}}
165
166	while { 1 } {
167		set nproced 0
168
169		incr nproced [replprocessqueue $masterenv 1 $skip_interval]
170		incr nproced [replprocessqueue $clientenv 2 $skip_interval]
171
172		# In this test, the ISPERM and NOTPERM messages are
173		# sent by the client back to the master.  Verify that we
174		# get ISPERM when the client is caught up to the master
175		# (i.e. last client LSN in the log matches the LSN returned
176		# with the ISPERM), and that when we get NOTPERM, the client
177		# is not caught up.
178
179		# Create a list of the LSNs in the client log.
180		set lsnlist {}
181		set logc [$clientenv log_cursor]
182		error_check_good logc \
183		    [is_valid_logc $logc $clientenv] TRUE
184		for { set logrec [$logc get -first] } \
185		    { [llength $logrec] != 0 } \
186		    { set logrec [$logc get -next] } {
187			lappend lsnlist [lindex [lindex $logrec 0] 1]
188		}
189		set lastloglsn [lindex $lsnlist end]
190
191		# Parse perm_response_list to find the LSN returned with
192		# ISPERM or NOTPERM.
193		set response [lindex $perm_response_list end]
194		set permtype [lindex $response 0]
195		set messagelsn [lindex [lindex $response 1] 1]
196
197		if { [llength $response] != 0 } {
198			if { $permtype == "NOTPERM" } {
199				# If we got a NOTPERM, the returned LSN has to
200				# be greater than the last LSN in the log.
201				error_check_good notpermlsn \
202				    [expr $messagelsn > $lastloglsn] 1
203			} elseif { $permtype == "ISPERM" } {
204				# If we got an ISPERM, the returned LSN has to
205				# be in the log.
206				error_check_bad \
207				    ispermlsn [lsearch $lsnlist $messagelsn] -1
208			} else {
209				puts "FAIL: unexpected message type $permtype"
210			}
211		}
212
213		error_check_good logc_close [$logc close] 0
214
215		# If we've finished processing all the messages, check
216		# that the last received permanent message LSN matches the
217		# last sent permanent message LSN.
218		if { $nproced == 0 } {
219			if { $check != 0 } {
220				set last_sent [lindex $perm_sent_list end]
221				set last_rec_msg \
222				    [lindex $perm_response_list end]
223				set last_received [lindex $last_rec_msg 1]
224				error_check_good last_message \
225				    $last_sent $last_received
226			}
227
228			# If we check correctly; empty out the lists
229			set perm_response_list {{}}
230			set perm_sent_list {{}}
231			break
232		}
233	}
234}
235