1# See the file LICENSE for redistribution information.
2#
3# Copyright (c) 1996,2008 Oracle.  All rights reserved.
4#
5# $Id: archive.tcl,v 12.6 2008/01/08 20:58:53 bostic Exp $
6#
7# Options are:
8# -checkrec <checkpoint frequency"
9# -dir <dbhome directory>
10# -maxfilesize <maxsize of log file>
11proc archive { { inmem 0 } args } {
12	global alphabet
13	source ./include.tcl
14
15	# Set defaults
16	if { $inmem == 1 } {
17		set maxbsize [expr 8 * [expr 1024 * 1024]]
18		set desc "in-memory"
19	} else {
20		set maxbsize [expr 8 * 1024]
21		set desc "on-disk"
22	}
23	set maxfile [expr 32 * 1024]
24	set checkrec 500
25	for { set i 0 } { $i < [llength $args] } {incr i} {
26		switch -regexp -- [lindex $args $i] {
27			-c.* { incr i; set checkrec [lindex $args $i] }
28			-d.* { incr i; set testdir [lindex $args $i] }
29			-m.* { incr i; set maxfile [lindex $args $i] }
30			default {
31				puts "FAIL:[timestamp] archive usage"
32	puts "usage: archive -checkrec <checkpt freq> \
33	    -dir <directory> -maxfilesize <max size of log files>"
34				return
35			}
36		}
37	}
38
39	# Clean out old log if it existed
40	puts "Archive: Log archive test (using $desc logging)."
41	puts "Unlinking log: error message OK"
42	env_cleanup $testdir
43
44	# Now run the various functionality tests
45	if { $inmem == 0 } {
46		set eflags "-create -txn -home $testdir \
47		    -log_buffer $maxbsize -log_max $maxfile"
48	} else {
49		set eflags "-create -txn -home $testdir -log_inmemory \
50		    -log_buffer $maxbsize -log_max $maxfile"
51	}
52	set dbenv [eval {berkdb_env} $eflags]
53	error_check_good dbenv [is_valid_env $dbenv] TRUE
54
55	set logc [$dbenv log_cursor]
56	error_check_good log_cursor [is_valid_logc $logc $dbenv] TRUE
57
58	# The basic test structure here is that we write a lot of log
59	# records (enough to fill up 100 log files; each log file it
60	# small).  We start with three txns and open a database in
61	# each transaction.  Then, in a loop, we take periodic
62	# checkpoints.  Between each pair of checkpoints, we end one
63	# transaction; when no transactions are left, we start up three
64	# new ones, letting them overlap checkpoints as well.
65	#
66	# The pattern that we create is:
67	# 1.  Create TXN1, TXN2, TXN3 and open dbs within the txns.
68	# 2.  Write a bunch of additional log records.
69	# 3.  Checkpoint.
70	# 4.  Archive, checking that we list the right files.
71	# 5.  Commit one transaction.
72	# 6.  If no txns left, start 3 new ones.
73	# 7.  Until we've gone through enough records, return to step 2.
74
75	set baserec "1:$alphabet:2:$alphabet:3:$alphabet:4:$alphabet"
76	puts "\tArchive.a: Writing log records; checkpoint every $checkrec records"
77	set nrecs $maxfile
78	set rec 0:$baserec
79
80	# Begin 1st transaction and record current log file.  Open
81	# a database in the transaction; the log file won't be
82	# removable until the transaction is aborted or committed.
83	set t1 [$dbenv txn]
84	error_check_good t1:txn_begin [is_valid_txn $t1 $dbenv] TRUE
85
86	set l1 [lindex [lindex [$logc get -last] 0] 0]
87	set lsnlist [list $l1]
88
89	set tdb1 [eval {berkdb_open -create -mode 0644} \
90	    -env $dbenv -txn $t1 -btree tdb1.db]
91	error_check_good dbopen [is_valid_db $tdb1] TRUE
92
93	# Do the same for a 2nd and 3rd transaction.
94	set t2 [$dbenv txn]
95	error_check_good t2:txn_begin [is_valid_txn $t2 $dbenv] TRUE
96	set l2 [lindex [lindex [$logc get -last] 0] 0]
97	lappend lsnlist $l2
98	set tdb2 [eval {berkdb_open -create -mode 0644} \
99	    -env $dbenv -txn $t2 -btree tdb2.db]
100	error_check_good dbopen [is_valid_db $tdb2] TRUE
101
102	set t3 [$dbenv txn]
103	error_check_good t3:txn_begin [is_valid_txn $t3 $dbenv] TRUE
104	set l3 [lindex [lindex [$logc get -last] 0] 0]
105	lappend lsnlist $l3
106	set tdb3 [eval {berkdb_open -create -mode 0644} \
107	    -env $dbenv -txn $t3 -btree tdb3.db]
108	error_check_good dbopen [is_valid_db $tdb3] TRUE
109
110	# Keep a list of active transactions and databases opened
111	# within those transactions.
112	set txnlist [list "$t1 $tdb1" "$t2 $tdb2" "$t3 $tdb3"]
113
114	# Loop through a large number of log records, checkpointing
115	# and checking db_archive periodically.
116	for { set i 1 } { $i <= $nrecs } { incr i } {
117		set rec $i:$baserec
118		set lsn [$dbenv log_put $rec]
119		error_check_bad log_put [llength $lsn] 0
120		if { [expr $i % $checkrec] == 0 } {
121
122			# Take a checkpoint
123			$dbenv txn_checkpoint
124			set ckp_file [lindex [lindex [$logc get -last] 0] 0]
125			catch { archive_command -h $testdir -a } res_log_full
126			if { [string first db_archive $res_log_full] == 0 } {
127				set res_log_full ""
128			}
129			catch { archive_command -h $testdir } res_log
130			if { [string first db_archive $res_log] == 0 } {
131				set res_log ""
132			}
133			catch { archive_command -h $testdir -l } res_alllog
134			catch { archive_command -h $testdir -a -s } \
135			    res_data_full
136			catch { archive_command -h $testdir -s } res_data
137
138			if { $inmem == 0 } {
139				error_check_good nlogfiles [llength $res_alllog] \
140				    [lindex [lindex [$logc get -last] 0] 0]
141			} else {
142				error_check_good nlogfiles [llength $res_alllog] 0
143			}
144
145			error_check_good logs_match [llength $res_log_full] \
146			    [llength $res_log]
147			error_check_good data_match [llength $res_data_full] \
148			    [llength $res_data]
149
150			# Check right number of log files
151			if { $inmem == 0 } {
152				set expected [min $ckp_file [expr [lindex $lsnlist 0] - 1]]
153				error_check_good nlogs [llength $res_log] $expected
154			}
155
156			# Check that the relative names are a subset of the
157			# full names
158			set n 0
159			foreach x $res_log {
160				error_check_bad log_name_match:$res_log \
161				    [string first $x \
162				    [lindex $res_log_full $n]] -1
163				incr n
164			}
165
166			set n 0
167			foreach x $res_data {
168				error_check_bad log_name_match:$res_data \
169				    [string first $x \
170				    [lindex $res_data_full $n]] -1
171				incr n
172			}
173
174			# Commit a transaction and close the associated db.
175			set t [lindex [lindex $txnlist 0] 0]
176			set tdb [lindex [lindex $txnlist 0] 1]
177			if { [string length $t] != 0 } {
178				error_check_good txn_commit:$t [$t commit] 0
179				error_check_good tdb_close:$tdb [$tdb close] 0
180				set txnlist [lrange $txnlist 1 end]
181				set lsnlist [lrange $lsnlist 1 end]
182			}
183
184			# If we're down to no transactions, start some new ones.
185			if { [llength $txnlist] == 0 } {
186				set t1 [$dbenv txn]
187				error_check_bad tx_begin $t1 NULL
188				error_check_good \
189				    tx_begin [is_substr $t1 $dbenv] 1
190				set tdb1 [eval {berkdb_open -create -mode 0644} \
191				    -env $dbenv -txn $t1 -btree tdb1.db]
192				error_check_good dbopen [is_valid_db $tdb1] TRUE
193				set l1 [lindex [lindex [$logc get -last] 0] 0]
194				lappend lsnlist $l1
195
196				set t2 [$dbenv txn]
197				error_check_bad tx_begin $t2 NULL
198				error_check_good \
199				    tx_begin [is_substr $t2 $dbenv] 1
200				set tdb2 [eval {berkdb_open -create -mode 0644} \
201				    -env $dbenv -txn $t2 -btree tdb2.db]
202				error_check_good dbopen [is_valid_db $tdb2] TRUE
203				set l2 [lindex [lindex [$logc get -last] 0] 0]
204				lappend lsnlist $l2
205
206				set t3 [$dbenv txn]
207				error_check_bad tx_begin $t3 NULL
208				error_check_good \
209				    tx_begin [is_substr $t3 $dbenv] 1
210				set tdb3 [eval {berkdb_open -create -mode 0644} \
211				    -env $dbenv -txn $t3 -btree tdb3.db]
212				error_check_good dbopen [is_valid_db $tdb3] TRUE
213				set l3 [lindex [lindex [$logc get -last] 0] 0]
214				lappend lsnlist $l3
215
216				set txnlist [list "$t1 $tdb1" "$t2 $tdb2" "$t3 $tdb3"]
217			}
218		}
219	}
220	# Commit any transactions still running.
221	puts "\tArchive.b: Commit any transactions still running."
222	foreach pair $txnlist {
223		set t [lindex $pair 0]
224		set tdb [lindex $pair 1]
225		error_check_good txn_commit:$t [$t commit] 0
226		error_check_good tdb_close:$tdb [$tdb close] 0
227	}
228
229	# Close and unlink the file
230	error_check_good log_cursor_close [$logc close] 0
231	reset_env $dbenv
232}
233
234proc archive_command { args } {
235	source ./include.tcl
236
237	# Catch a list of files output by db_archive.
238	catch { eval exec $util_path/db_archive $args } output
239
240	if { $is_windows_test == 1 || 1 } {
241		# On Windows, convert all filenames to use forward slashes.
242		regsub -all {[\\]} $output / output
243	}
244
245	# Output the [possibly-transformed] list.
246	return $output
247}
248
249proc min { a b } {
250	if {$a < $b} {
251		return $a
252	} else {
253		return $b
254	}
255}
256