1# See the file LICENSE for redistribution information.
2#
3# Copyright (c) 2004,2008 Oracle.  All rights reserved.
4#
5# $Id: test107.tcl,v 12.9 2008/01/08 20:58:53 bostic Exp $
6#
7# TEST	test107
8# TEST	Test of read-committed (degree 2 isolation). [#8689]
9# TEST
10# TEST	We set up a database.  Open a read-committed transactional cursor and
11# TEST	a regular transactional cursor on it. Position each cursor on one page,
12# TEST	and do a put to a different page.
13# TEST
14# TEST	Make sure that:
15# TEST	- the put succeeds if we are using degree 2 isolation.
16# TEST	- the put deadlocks within a regular transaction with
17# TEST 	a regular cursor.
18# TEST
19proc test107 { method args } {
20	source ./include.tcl
21	global fixed_len
22	global passwd
23	set tnum "107"
24
25	# If we are using an env, then skip this test.  It needs its own.
26	set eindex [lsearch -exact $args "-env"]
27	if { $eindex != -1 } {
28		incr eindex
29		set env [lindex $args $eindex]
30		puts "Test$tnum skipping for env $env"
31		return
32	}
33
34	# We'll make the data pretty good sized so we can easily
35	# move to a different page.  Make the data size a little
36	# smaller for fixed-length methods so it works with
37	# pagesize 512 tests.
38	set data_size 512
39	set orig_fixed_len $fixed_len
40	set fixed_len [expr $data_size - [expr $data_size / 8]]
41	set args [convert_args $method $args]
42	set encargs ""
43	set ddargs ""
44	set args [split_encargs $args encargs]
45	if { $encargs != "" } {
46		set ddargs " -P $passwd "
47	}
48	set omethod [convert_method $method]
49
50	puts "Test$tnum: Degree 2 Isolation Test ($method $args)"
51	set testfile test$tnum.db
52	env_cleanup $testdir
53
54	# Create the environment.
55	set timeout 10
56	set env [eval {berkdb_env -create -mode 0644 -lock \
57	    -cachesize { 0 1048576 1 } \
58	    -lock_timeout $timeout -txn} $encargs -home $testdir]
59	error_check_good env_open [is_valid_env $env] TRUE
60
61	# Create the database.
62	set db [eval {berkdb_open -env $env -create -auto_commit\
63	    -mode 0644 $omethod} $args {$testfile}]
64	error_check_good dbopen [is_valid_db $db] TRUE
65
66	puts "\tTest$tnum.a: put loop"
67	# The data doesn't need to change from key to key.
68	# Use numerical keys so we don't need special handling for
69	# record-based methods.
70	set origdata "data"
71	set len [string length $origdata]
72	set data [repeat $origdata [expr $data_size / $len]]
73	set nentries 200
74	set txn [$env txn]
75	for { set i 1 } { $i <= $nentries } { incr i } {
76		set key $i
77		set ret [eval {$db put} \
78		    -txn $txn {$key [chop_data $method $data]}]
79		error_check_good put:$db $ret 0
80	}
81	error_check_good txn_commit [$txn commit] 0
82
83	puts "\tTest$tnum.b: Start deadlock detector."
84	# Start up a deadlock detector so we can break self-deadlocks.
85	set dpid [eval {exec $util_path/db_deadlock} -v -ae -t 1.0 \
86	    -h $testdir $ddargs >& $testdir/dd.out &]
87
88	puts "\tTest$tnum.c: Open txns and cursors."
89	# We can get degree 2 isolation with either a degree 2
90	# txn or a degree 2 cursor or both.  However, the case
91	# of a regular txn and regular cursor should deadlock.
92	# We put this case last so it won't deadlock the cases
93	# which should succeed.
94	#
95	# Cursors and transactions are named according to
96	# whether they specify degree 2 (c2, t2) or not (c, t).
97	# Set up all four possibilities.
98	#
99	set t [$env txn]
100	error_check_good reg_txn_begin [is_valid_txn $t $env] TRUE
101	set t2 [$env txn -read_committed]
102	error_check_good deg2_txn_begin [is_valid_txn $t2 $env] TRUE
103
104	set c2t [$db cursor -txn $t -read_committed]
105	error_check_good valid_c2t [is_valid_cursor $c2t $db] TRUE
106	set ct2 [$db cursor -txn $t2]
107	error_check_good valid_ct2 [is_valid_cursor $ct2 $db] TRUE
108	set c2t2 [$db cursor -txn $t2 -read_committed]
109	error_check_good valid_c2t2 [is_valid_cursor $c2t2 $db] TRUE
110	set ct [$db cursor -txn $t]
111	error_check_good valid_ct [is_valid_cursor $ct $db] TRUE
112
113	set curslist [list $c2t $ct2 $c2t2 $ct]
114	set newdata newdata
115	set offpagekey [expr $nentries - 1]
116
117	# For one cursor at a time, read the first item in the
118	# database, then move to an item on a different page.
119	# Put a new value in the first item on the first page.  This
120	# should work with degree 2 isolation and hang without it.
121	#
122	# Wrap the whole thing in a catch statement so we still get
123	# around to killing the deadlock detector and cleaning up
124	# even if the test fails.
125	#
126	puts "\tTest$tnum.d: Test for read-committed (degree 2 isolation)."
127	set status [catch {
128		foreach cursor $curslist {
129			set retfirst [$cursor get -first]
130			set firstkey [lindex [lindex $retfirst 0] 0]
131			set ret [$cursor get -set $offpagekey]
132			error_check_good cursor_off_page \
133			    [lindex [lindex $ret 0] 0] $offpagekey
134			if { [catch {eval {$db put} \
135			    $firstkey [chop_data $method $newdata]} res]} {
136				error_check_good error_is_deadlock \
137				    [is_substr $res DB_LOCK_DEADLOCK] 1
138				error_check_good right_cursor_failed $cursor $ct
139			} else {
140				set ret [lindex [lindex [$db get $firstkey] 0] 1]
141				error_check_good data_changed \
142				    $ret [pad_data $method $newdata]
143				error_check_bad right_cursor_succeeded $cursor $ct
144			}
145			error_check_good close_cursor [$cursor close] 0
146		}
147	} res]
148	if { $status != 0 } {
149		puts $res
150	}
151
152	# Smoke test for db_stat -txn -read_committed.
153	puts "\tTest$tnum.e: Smoke test for db_stat -txn -read_committed"
154	if { [catch {set statret [$db stat -txn $t -read_committed]} res] } {
155		puts "FAIL: db_stat -txn -read_committed returned $res"
156	}
157
158	# End deadlock detection and clean up handles
159	puts "\tTest$tnum.f: Clean up."
160	tclkill $dpid
161	set fixed_len $orig_fixed_len
162	error_check_good t_commit [$t commit] 0
163	error_check_good t2_commit [$t2 commit] 0
164	error_check_good dbclose [$db close] 0
165	error_check_good envclose [$env close] 0
166}
167