summaryrefslogtreecommitdiff
path: root/db/test/test107.tcl
blob: dc95c736787262d0bb21dcb354e05eac7a87ae75 (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
# See the file LICENSE for redistribution information.
#
# Copyright (c) 2004,2007 Oracle.  All rights reserved.
#
# $Id: test107.tcl,v 12.8 2007/05/17 15:15:56 bostic Exp $
#
# TEST	test107
# TEST	Test of read-committed (degree 2 isolation). [#8689]
# TEST
# TEST	We set up a database.  Open a read-committed transactional cursor and
# TEST	a regular transactional cursor on it. Position each cursor on one page,
# TEST	and do a put to a different page.
# TEST
# TEST	Make sure that:
# TEST	- the put succeeds if we are using degree 2 isolation.
# TEST	- the put deadlocks within a regular transaction with
# TEST 	a regular cursor.
# TEST
proc test107 { method args } {
	source ./include.tcl
	global fixed_len
	global passwd
	set tnum "107"

	# If we are using an env, then skip this test.  It needs its own.
	set eindex [lsearch -exact $args "-env"]
	if { $eindex != -1 } {
		incr eindex
		set env [lindex $args $eindex]
		puts "Test$tnum skipping for env $env"
		return
	}

	# We'll make the data pretty good sized so we can easily
	# move to a different page.  Make the data size a little
	# smaller for fixed-length methods so it works with
	# pagesize 512 tests.
	set data_size 512
	set orig_fixed_len $fixed_len
	set fixed_len [expr $data_size - [expr $data_size / 8]]
	set args [convert_args $method $args]
	set encargs ""
	set ddargs ""
	set args [split_encargs $args encargs]
	if { $encargs != "" } {
		set ddargs " -P $passwd "
	}
	set omethod [convert_method $method]

	puts "Test$tnum: Degree 2 Isolation Test ($method $args)"
	set testfile test$tnum.db
	env_cleanup $testdir

	# Create the environment.
	set timeout 10
	set env [eval {berkdb_env -create -mode 0644 -lock \
	    -cachesize { 0 1048576 1 } \
	    -lock_timeout $timeout -txn} $encargs -home $testdir]
	error_check_good env_open [is_valid_env $env] TRUE

	# Create the database.
	set db [eval {berkdb_open -env $env -create -auto_commit\
	    -mode 0644 $omethod} $args {$testfile}]
	error_check_good dbopen [is_valid_db $db] TRUE

	puts "\tTest$tnum.a: put loop"
	# The data doesn't need to change from key to key.
	# Use numerical keys so we don't need special handling for
	# record-based methods.
	set origdata "data"
	set len [string length $origdata]
	set data [repeat $origdata [expr $data_size / $len]]
	set nentries 200
	set txn [$env txn]
	for { set i 1 } { $i <= $nentries } { incr i } {
		set key $i
		set ret [eval {$db put} \
		    -txn $txn {$key [chop_data $method $data]}]
		error_check_good put:$db $ret 0
	}
	error_check_good txn_commit [$txn commit] 0

	puts "\tTest$tnum.b: Start deadlock detector."
	# Start up a deadlock detector so we can break self-deadlocks.
	set dpid [eval {exec $util_path/db_deadlock} -v -ae -t 1.0 \
	    -h $testdir $ddargs >& $testdir/dd.out &]

	puts "\tTest$tnum.c: Open txns and cursors."
	# We can get degree 2 isolation with either a degree 2
	# txn or a degree 2 cursor or both.  However, the case
	# of a regular txn and regular cursor should deadlock.
	# We put this case last so it won't deadlock the cases
	# which should succeed.
	#
	# Cursors and transactions are named according to
	# whether they specify degree 2 (c2, t2) or not (c, t).
	# Set up all four possibilities.
	#
	set t [$env txn]
	error_check_good reg_txn_begin [is_valid_txn $t $env] TRUE
	set t2 [$env txn -read_committed]
	error_check_good deg2_txn_begin [is_valid_txn $t2 $env] TRUE

	set c2t [$db cursor -txn $t -read_committed]
	error_check_good valid_c2t [is_valid_cursor $c2t $db] TRUE
	set ct2 [$db cursor -txn $t2]
	error_check_good valid_ct2 [is_valid_cursor $ct2 $db] TRUE
	set c2t2 [$db cursor -txn $t2 -read_committed]
	error_check_good valid_c2t2 [is_valid_cursor $c2t2 $db] TRUE
	set ct [$db cursor -txn $t]
	error_check_good valid_ct [is_valid_cursor $ct $db] TRUE

	set curslist [list $c2t $ct2 $c2t2 $ct]
	set newdata newdata
	set offpagekey [expr $nentries - 1]

	# For one cursor at a time, read the first item in the
	# database, then move to an item on a different page.
	# Put a new value in the first item on the first page.  This
	# should work with degree 2 isolation and hang without it.
	#
	# Wrap the whole thing in a catch statement so we still get
	# around to killing the deadlock detector and cleaning up
	# even if the test fails.
	#
	puts "\tTest$tnum.d: Test for read-committed (degree 2 isolation)."
	set status [catch {
		foreach cursor $curslist {
			set retfirst [$cursor get -first]
			set firstkey [lindex [lindex $retfirst 0] 0]
			set ret [$cursor get -set $offpagekey]
			error_check_good cursor_off_page \
			    [lindex [lindex $ret 0] 0] $offpagekey
			if { [catch {eval {$db put} \
			    $firstkey [chop_data $method $newdata]} res]} {
				error_check_good error_is_deadlock \
				    [is_substr $res DB_LOCK_DEADLOCK] 1
				error_check_good right_cursor_failed $cursor $ct
			} else {
				set ret [lindex [lindex [$db get $firstkey] 0] 1]
				error_check_good data_changed \
				    $ret [pad_data $method $newdata]
				error_check_bad right_cursor_succeeded $cursor $ct
			}
			error_check_good close_cursor [$cursor close] 0
		}
	} res]
	if { $status != 0 } {
		puts $res
	}

	# Smoke test for db_stat -txn -read_committed.
	puts "\tTest$tnum.e: Smoke test for db_stat -txn -read_committed"
	if { [catch {set statret [$db stat -txn $t -read_committed]} res] } {
		puts "FAIL: db_stat -txn -read_committed returned $res"
	}

	# End deadlock detection and clean up handles
	puts "\tTest$tnum.f: Clean up."
	tclkill $dpid
	set fixed_len $orig_fixed_len
	error_check_good t_commit [$t commit] 0
	error_check_good t2_commit [$t2 commit] 0
	error_check_good dbclose [$db close] 0
	error_check_good envclose [$env close] 0
}