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