summaryrefslogtreecommitdiff
path: root/test/tcl/rep087.tcl
blob: b1a8254fd50ad1ef2300ded518535a455c29d73e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
# See the file LICENSE for redistribution information.
#
# Copyright (c) 2009, 2015 Oracle and/or its affiliates.  All rights reserved.
#
# TEST	rep087
# TEST  Abbreviated internal init with open file handles.
# TEST
# TEST  Client has open handle to an on-disk DB when abbreviated
# TEST  internal init starts.  Make sure we lock out access, and make sure
# TEST  it ends up as HANDLE_DEAD.  Also, make sure that if there are
# TEST  no NIMDBs, that we *don't* get HANDLE_DEAD.

proc rep087 { method { niter 200 } { tnum "087" } args } {
	source ./include.tcl

	# Run for btree and queue only.  Since this is a NIMDB test, 
	# explicitly exclude queueext. 
	if { $checking_valid_methods } {
		set test_methods {}
		foreach method $valid_methods {
			if { [is_btree $method] == 1 || [is_queue $method] == 1 } {
				if { [is_queueext $method] == 0 } {	
					lappend test_methods $method
				}
			}
		}
		return $test_methods
	}
	if { [is_btree $method] != 1 && [is_queue $method] != 1 } {
		puts "Skipping internal init test rep$tnum for method $method."
		return
	}
	if { [is_queueext $method] == 1 } {
		puts "Skipping in-memory database test rep$tnum for method $method."
		return
	}

	set args [convert_args $method $args]

	# Run test with and without a NIMDB present.
	rep087_sub $method $niter $tnum "true" $args
	rep087_sub $method $niter $tnum "false" $args
}	

proc rep087_sub { method niter tnum with_nimdb largs } {
	global testdir
	global env_private
	global rep_verbose
	global verbose_type

	set msg3 ""
	if { $env_private } {
		set msg3 "with private env"
	}

	set verbargs ""
	if { $rep_verbose == 1 } {
		set verbargs " -verbose {$verbose_type on} "
	}

	set privargs ""
	if { $env_private == 1 } {
		set privargs " -private "
	}

	if { $with_nimdb} {
		set msg "with"
	} else {
		set msg "without"
	}
	puts "Rep$tnum ($method): Abbreviated internal init\
	    and dead handles, $msg NIMDB $msg3."
	if { $niter < 3 } {
		set niter 3
		puts "\tRep$tnum: the minimum 'niter' value is 3."
	}

	set omethod [convert_method $method]

	env_cleanup $testdir
	replsetup $testdir/MSGQUEUEDIR

	file mkdir [set dirs(A) $testdir/SITE_A]
	file mkdir [set dirs(B) $testdir/SITE_B]

	puts "\tRep$tnum: Create master and client"
	repladd 1
	set env_A_cmd "berkdb_env_noerr -create -txn \
	    $verbargs $privargs \
	    -errpfx SITE_A \
	    -home $dirs(A) -rep_transport \[list 1 replsend\]"
	set envs(A) [eval $env_A_cmd -rep_master]

	# Open a client
	repladd 2
	set env_B_cmd "berkdb_env_noerr -create -txn \
	    $verbargs $privargs \
	    -errpfx SITE_B \
	    -home $dirs(B) -rep_transport \[list 2 replsend\]"
	set envs(B) [eval $env_B_cmd -rep_client]

	set envlist "{$envs(A) 1} {$envs(B) 2}"
	process_msgs $envlist

	if { $with_nimdb } {
		set msg "and a NIMDB"
	} else {
		set msg ""
	}
	puts "\tRep$tnum: Create a regular DB $msg"
	set start 0
	eval rep_test $method $envs(A) NULL $niter $start $start 0 $largs

	if { $with_nimdb } {
		set nimdb [eval {berkdb_open} -env $envs(A) -auto_commit \
			    -create $largs $omethod {"" "mynimdb"}]
		eval rep_test $method $envs(A) \
		    $nimdb $niter $start $start 0 $largs
		$nimdb close
	}
	process_msgs $envlist
	
	puts "\tRep$tnum: Restart client with recovery"
	#
	# In the NIMDB case, this forces the rematerialization of the NIMDB.
	# 
	$envs(B) close
	set envs(B) [eval $env_B_cmd -rep_client -recover]
	set envlist "{$envs(A) 1} {$envs(B) 2}"

	# Before seeking the master, open a DB handle onto the regular DB.
	# At this point, we should be allowed to read it.  
	#
	# Try reading a few records.  (How many?  We arbitrarily choose to try
	# reading three.)  Save one of the keys so that we can use it later in a
	# "$db get" call.  (Superstitiously skip over the first key, in deciding
	# which one to save, because it is usually a zero-length string.)
	# 
	set db [berkdb_open_noerr -env $envs(B) -auto_commit test.db]
	set c [$db cursor]
	$c get -next
	set pairs [$c get -next]
	set a_key [lindex $pairs 0 0]
	$c get -next
	$c close

	if { $with_nimdb} {
		# At this point, the NIMDB is obviously not available, since it
		# was blown away by the recovery/recreation of regions.  Let's
		# just make sure.
		#
		error_check_bad no_nimdb \
		    [catch {berkdb_open_noerr -env $envs(B) \
				-auto_commit "" "mynimdb"}] 0

		# Use the usual idiom of processing just one message cycle at a
		# time, so that we can check access during the middle of
		# internal init.  (If no NIMDB, there is no internal init, so
		# there's no point in doing this for that case.)

		# 1. NEWCLIENT -> NEWMASTER -> VERIFY_REQ (the checkpoint 
		#                 written by regular recovery)
		# 2.   -> VERIFY -> (no match) VERIFY_REQ (last txn commit in 
		#                 common)
		# 3.   -> VERIFY -> (match, but need NIMDBS) UPDATE_REQ
		# 4.   -> UPDATE -> PAGE_REQ
		# 5.   -> PAGE -> (limited to partial NIMDB content by 
		#                 rep_limit)
		
		proc_msgs_once $envlist
		proc_msgs_once $envlist
		proc_msgs_once $envlist
		proc_msgs_once $envlist
		
		# Before doing cycle # 5, set a ridiculously low limit, so that
		# only the first page of the database will be received on this
		# next cycle.
		# 
		$envs(A) rep_limit 0 4
		proc_msgs_once $envlist
		
		# Now we should be blocked from reading from our DB.
		puts "\tRep$tnum: Try blocked access (5 second delay)."
		error_check_bad should_block [catch {$db get $a_key} ret] 0
		error_check_good deadlock [is_substr $ret DB_LOCK_DEADLOCK] 1
		
		# Get rid of any limit for the remainder of the test.
		# 
		$envs(A) rep_limit 0 0
	}

	# Finish off all pending message processing.
	# 
	process_msgs $envlist
	
	if { $with_nimdb } { 
		# We should of course be able to open, and read a few
		# records from, the NIMDB, now that we've completed the
		# abbreviated internal init.
		# 
		set imdb [berkdb_open_noerr -env $envs(B) \
			      -auto_commit "" "mynimdb"]
		set c [$imdb cursor]
		$c get -next
		$c get -next
		$c get -next
		$c close
		$imdb close

		puts "\tRep$tnum: Try access to dead handle."
		error_check_bad handle_dead [catch {$db get $a_key} ret] 0
		error_check_good $ret [is_substr $ret DB_REP_HANDLE_DEAD] 1

		$db close
		set db [berkdb_open_noerr -env $envs(B) -auto_commit test.db]
		error_check_good reaccess_ok [catch {$db get $a_key} ret] 0
	} else {
		puts "\tRep$tnum: Try access to still-valid handle"
		error_check_good access_ok [catch {$db get $a_key} ret] 0
	}

	#
	# If we're a queue database, add in a check to make sure a client
	# cannot do a db get -consume because that is an update operation.
	#
	set dbtype [$db get_type]
	if { $dbtype == "queue" } {
		puts "\tRep$tnum: Try to consume a queue entry on a client."
		set ret [catch {$db get -consume} res]
		error_check_bad client_consume $ret 0
		error_check_good right_error \
		    [is_substr $res "permission denied"] 1 
	}
	puts "\tRep$tnum: Clean up."
	$db close
	$envs(A) close
	$envs(B) close
	replclose $testdir/MSGQUEUEDIR
}