1# See the file LICENSE for redistribution information.
2#
3# Copyright (c) 2002-2009 Oracle.  All rights reserved.
4#
5# $Id$
6#
7# TEST  rep016
8# TEST	Replication election test with varying required nvotes.
9# TEST
10# TEST	Run a modified version of test001 in a replicated master environment;
11# TEST  hold an election among a group of clients to make sure they select
12# TEST  the master with varying required participants.
13
14proc rep016 { method args } {
15	global errorInfo
16	global databases_in_memory
17	global repfiles_in_memory
18
19	source ./include.tcl
20	if { $is_windows9x_test == 1 } {
21		puts "Skipping replication test on Win 9x platform."
22		return
23	}
24	set tnum "016"
25
26	# Skip for all methods except btree.
27	if { $checking_valid_methods } {
28		set test_methods { btree }
29		return $test_methods
30	}
31	if { [is_btree $method] == 0 } {
32		puts "Rep$tnum: Skipping for method $method."
33		return
34	}
35
36	set nclients 5
37	set logsets [create_logsets [expr $nclients + 1]]
38
39	# Set up for on-disk or in-memory databases.
40	set msg "using on-disk databases"
41	if { $databases_in_memory } {
42		set msg "using named in-memory databases"
43		if { [is_queueext $method] } {
44			puts -nonewline "Skipping rep$tnum for method "
45			puts "$method with named in-memory databases"
46			return
47		}
48	}
49
50	set msg2 "and on-disk replication files"
51	if { $repfiles_in_memory } {
52		set msg2 "and in-memory replication files"
53	}
54
55	# Run the body of the test with and without recovery.
56	foreach r $test_recopts {
57		foreach l $logsets {
58			set logindex [lsearch -exact $l "in-memory"]
59			if { $r == "-recover" && $logindex != -1 } {
60				puts "Rep$tnum: Skipping\
61				    for in-memory logs with -recover."
62				continue
63			}
64			puts "Rep$tnum ($method $r): Replication\
65			    elections with varying nvotes $msg $msg2."
66			puts "Rep$tnum: Master logs are [lindex $l 0]"
67			for { set i 0 } { $i < $nclients } { incr i } {
68				puts "Rep$tnum: Client $i logs are\
69				    [lindex $l [expr $i + 1]]"
70			}
71			rep016_sub $method $nclients $tnum $l $r $args
72		}
73	}
74}
75
76proc rep016_sub { method nclients tnum logset recargs largs } {
77	source ./include.tcl
78	global databases_in_memory
79	global repfiles_in_memory
80	global rep_verbose
81	global verbose_type
82
83	set verbargs ""
84	if { $rep_verbose == 1 } {
85		set verbargs " -verbose {$verbose_type on} "
86	}
87
88	set repmemargs ""
89	if { $repfiles_in_memory } {
90		set repmemargs "-rep_inmem_files "
91	}
92
93	env_cleanup $testdir
94
95	set niter 5
96	set qdir $testdir/MSGQUEUEDIR
97	replsetup $qdir
98
99	set masterdir $testdir/MASTERDIR
100	file mkdir $masterdir
101
102	set m_logtype [lindex $logset 0]
103	set m_logargs [adjust_logargs $m_logtype]
104	set m_txnargs [adjust_txnargs $m_logtype]
105
106	for { set i 0 } { $i < $nclients } { incr i } {
107		set clientdir($i) $testdir/CLIENTDIR.$i
108		file mkdir $clientdir($i)
109		set c_logtype($i) [lindex $logset [expr $i + 1]]
110		set c_logargs($i) [adjust_logargs $c_logtype($i)]
111		set c_txnargs($i) [adjust_txnargs $c_logtype($i)]
112	}
113
114	# Open a master.
115	set envlist {}
116	repladd 1
117	set env_cmd(M) "berkdb_env_noerr -create -log_max 1000000 \
118	    -event rep_event $repmemargs \
119	    -home $masterdir $m_txnargs $m_logargs -rep_master $verbargs \
120	    -errpfx MASTER -rep_transport \[list 1 replsend\]"
121	set masterenv [eval $env_cmd(M) $recargs]
122	lappend envlist "$masterenv 1"
123
124	# Open the clients.
125	# Don't set -errfile now -- wait until the error catching
126	# portion of the test is complete.
127	for { set i 0 } { $i < $nclients } { incr i } {
128		set envid [expr $i + 2]
129		repladd $envid
130		set env_cmd($i) "berkdb_env_noerr -create -home $clientdir($i) \
131		    -event rep_event $repmemargs \
132		    $c_txnargs($i) $c_logargs($i) -rep_client $verbargs \
133		    -rep_transport \[list $envid replsend\]"
134		set clientenv($i) [eval $env_cmd($i) $recargs]
135		lappend envlist "$clientenv($i) $envid"
136	}
137	# Bring the clients online by processing the startup messages.
138	process_msgs $envlist
139
140	# Run a modified test001 in the master.
141	puts "\tRep$tnum.a: Running rep_test in replicated env."
142	eval rep_test $method $masterenv NULL $niter 0 0 0 $largs
143	process_msgs $envlist
144
145	# Check that databases are in-memory or on-disk as expected.
146	if { $databases_in_memory } {
147		set dbname { "" "test.db" }
148	} else {
149		set dbname "test.db"
150	}
151	check_db_location $masterenv
152	for { set i 0 } { $i < $nclients } { incr i } {
153		check_db_location $clientenv($i)
154	}
155
156	error_check_good masterenv_close [$masterenv close] 0
157	set envlist [lreplace $envlist 0 0]
158
159	puts "\tRep$tnum.b: Error values for rep_elect"
160	#
161	# Do all the error catching in client0.  We do not need to call
162	# start_election here to fork a process because we never get
163	# far enough to send/receive any messages.  We just want to
164	# check the error message.
165	#
166	# !!!
167	# We cannot set -errpfx or -errfile or anything in the
168	# env_cmd above.  Otherwise the correct output won't be set
169	# in 'ret' below and the test will fail.
170	#
171	# First check negative nvotes.
172	#
173	set nsites [expr $nclients + 1]
174	set priority 2
175	set timeout 5000000
176	#
177	# Setting nsites to 0 acts as a signal for rep_elect to use
178	# the configured nsites, but since we haven't set that yet,
179	# this should still fail.  TODO: need another test verifying
180	# the proper operation when we *have* configured nsites.
181	#
182	set nsites 0
183	set nvotes 2
184	set res [catch {$clientenv(0) rep_elect $nsites $nvotes $priority \
185	    $timeout} ret]
186	error_check_bad catch $res 0
187	error_check_good ret [is_substr $ret "is larger than nsites"] 1
188
189	#
190	# Check nvotes > nsites.
191	#
192	set nsites $nclients
193	set nvotes [expr $nsites + 1]
194	set res [catch {$clientenv(0) rep_elect $nsites $nvotes $priority \
195	    $timeout} ret]
196	error_check_bad catch $res 0
197	error_check_good ret [is_substr $ret "is larger than nsites"] 1
198
199	for { set i 0 } { $i < $nclients } { incr i } {
200		replclear [expr $i + 2]
201		#
202		# This test doesn't use the testing hooks, so
203		# initialize err_cmd and crash appropriately.
204		#
205		set err_cmd($i) "none"
206		set crash($i) 0
207		#
208		# Initialize the array pri.  We'll set it to
209		# appropriate values when the winner is determined.
210 		#
211		set pri($i) 0
212		#
213		if { $rep_verbose == 1 } {
214			$clientenv($i) errpfx CLIENT.$i
215			$clientenv($i) verbose $verbose_type on
216			$clientenv($i) errfile /dev/stderr
217			set env_cmd($i) [concat $env_cmd($i) \
218			    "-errpfx CLIENT.$i -errfile /dev/stderr "]
219		}
220	}
221	set m "Rep$tnum.c"
222	puts "\t$m: Check single master/client can elect itself"
223	#
224	# 2 sites: 1 master, 1 client.  Allow lone client to elect itself.
225	# Adjust client env list to reflect the single client.
226	#
227	set oldenvlist $envlist
228	set envlist [lreplace $envlist 1 end]
229	set nsites 2
230	set nvotes 1
231	set orig_ncl $nclients
232	set nclients 1
233	set elector 0
234	set winner 0
235	setpriority pri $nclients $winner
236	run_election env_cmd envlist err_cmd pri crash\
237	    $qdir $m $elector $nsites $nvotes $nclients $winner 1 $dbname
238
239	#
240	# Now run with all clients.  Client0 should always get elected
241	# because it became master and should have a bigger LSN.
242	#
243	set nclients $orig_ncl
244	set envlist [lreplace $oldenvlist 0 0 [lindex $envlist 0]]
245
246	set m "Rep$tnum.d"
247	puts "\t$m: Elect with 100% client participation"
248	set nsites $nclients
249	set nvotes $nclients
250	set winner [rep016_selectwinner $nsites $nvotes $nclients]
251	setpriority pri $nclients $winner
252	run_election env_cmd envlist err_cmd pri crash\
253	    $qdir $m $elector $nsites $nvotes $nclients $winner 1 $dbname
254
255	#
256	# Elect with varying levels of participation.  Start with nsites
257	# as nclients+1 (simulating a down master) and require nclients,
258	# and fewer (by 1) until we get down to 2 clients.
259	#
260	set m "Rep$tnum.e"
261	puts "\t$m: Elect with varying participation"
262	set nsites [expr $nclients + 1]
263	set count 0
264	for {set n $nclients} {$n > 1} {incr n -1} {
265		set m "Rep$tnum.e.$count"
266		set winner [rep016_selectwinner $nsites $n $n]
267		setpriority pri $nclients $winner
268		run_election env_cmd envlist err_cmd pri crash\
269		    $qdir $m $elector $nsites $n $n $winner 1 $dbname
270		incr count
271	}
272
273	foreach pair $envlist {
274		set cenv [lindex $pair 0]
275		error_check_good cenv_close [$cenv close] 0
276	}
277	replclose $testdir/MSGQUEUEDIR
278}
279
280proc rep016_selectwinner { nsites nvotes nclients } {
281	#
282	# Special case:  When we test with 100% participation, we expect
283	# client 0 to always win because it has a bigger LSN than the
284	# rest due to earlier part of the test.  This special case is
285	# kinda gross.
286	#
287	if { $nsites != $nvotes } {
288		set win [berkdb random_int 0 [expr $nclients - 1]]
289	} else {
290		set win 0
291	}
292	return $win
293}
294