summaryrefslogtreecommitdiff
path: root/test/reputilsnoenv.tcl
diff options
context:
space:
mode:
Diffstat (limited to 'test/reputilsnoenv.tcl')
-rw-r--r--test/reputilsnoenv.tcl509
1 files changed, 509 insertions, 0 deletions
diff --git a/test/reputilsnoenv.tcl b/test/reputilsnoenv.tcl
new file mode 100644
index 0000000..9ebea0f
--- /dev/null
+++ b/test/reputilsnoenv.tcl
@@ -0,0 +1,509 @@
+# See the file LICENSE for redistribution information.
+#
+# Copyright (c) 1996-2009 Oracle. All rights reserved.
+#
+# The procs in this file are used for replication messaging
+# ONLY when the default mechanism of setting up a queue of
+# messages in a environment is not possible. This situation
+# is fairly rare, but it is necessary when a replication
+# test simultaneously runs different versions of Berkeley DB,
+# because different versions cannot share an env.
+#
+# Note, all procs should be named with the suffix _noenv
+# so it's explicit that we are using them.
+#
+# Close up a replication group - close all message dbs.
+proc replclose_noenv { queuedir } {
+ global queuedbs machids
+
+ set dbs [array names queuedbs]
+ foreach tofrom $dbs {
+ set handle $queuedbs($tofrom)
+ error_check_good db_close [$handle close] 0
+ unset queuedbs($tofrom)
+ }
+
+ set machids {}
+}
+
+# Create a replication group for testing.
+proc replsetup_noenv { queuedir } {
+ global queuedbs machids
+
+ file mkdir $queuedir
+
+ # If there are any leftover handles, get rid of them.
+ set dbs [array names queuedbs]
+ foreach tofrom $dbs {
+ unset queuedbs($tofrom)
+ }
+ set machids {}
+}
+
+# Send function for replication.
+proc replsend_noenv { control rec fromid toid flags lsn } {
+ global is_repchild
+ global queuedbs machids
+ global drop drop_msg
+ global perm_sent_list
+ global anywhere
+ global qtestdir testdir
+
+ if { ![info exists qtestdir] } {
+ set qtestdir $testdir
+ }
+ set queuedir $qtestdir/MSGQUEUEDIR
+ set permflags [lsearch $flags "perm"]
+ if { [llength $perm_sent_list] != 0 && $permflags != -1 } {
+# puts "replsend_noenv sent perm message, LSN $lsn"
+ lappend perm_sent_list $lsn
+ }
+
+ #
+ # If we are testing with dropped messages, then we drop every
+ # $drop_msg time. If we do that just return 0 and don't do
+ # anything.
+ #
+ if { $drop != 0 } {
+ incr drop
+ if { $drop == $drop_msg } {
+ set drop 1
+ return 0
+ }
+ }
+ # XXX
+ # -1 is DB_BROADCAST_EID
+ if { $toid == -1 } {
+ set machlist $machids
+ } else {
+ set m NULL
+ # If we can send this anywhere, send it to the first id
+ # we find that is neither toid or fromid. If we don't
+ # find any other candidates, this falls back to the
+ # original toid.
+ if { $anywhere != 0 } {
+ set anyflags [lsearch $flags "any"]
+ if { $anyflags != -1 } {
+ foreach m $machids {
+ if { $m == $fromid || $m == $toid } {
+ continue
+ }
+ set machlist [list $m]
+ break
+ }
+ }
+ }
+ #
+ # If we didn't find a different site, fall back
+ # to the toid.
+ #
+ if { $m == "NULL" } {
+ set machlist [list $toid]
+ }
+ }
+ foreach m $machlist {
+ # Do not broadcast to self.
+ if { $m == $fromid } {
+ continue
+ }
+ # Find the handle for the right message file.
+ set pid [pid]
+ set db $queuedbs($m.$fromid.$pid)
+ set stat [catch {$db put -append [list $control $rec $fromid]} ret]
+ }
+ if { $is_repchild } {
+ replready_noenv $fromid from
+ }
+
+ return 0
+}
+
+#
+proc replmsglen_noenv { machid {tf "to"}} {
+ global queuedbs qtestdir testdir
+
+ if { ![info exists qtestdir] } {
+ set qtestdir $testdir
+ }
+ set queuedir $qtestdir/MSGQUEUEDIR
+ set orig [pwd]
+
+ cd $queuedir
+ if { $tf == "to" } {
+ set msgdbs [glob -nocomplain ready.$machid.*]
+ } else {
+ set msgdbs [glob -nocomplain ready.*.$machid.*]
+ }
+ cd $orig
+ return [llength $msgdbs]
+}
+
+# Discard all the pending messages for a particular site.
+proc replclear_noenv { machid {tf "to"}} {
+ global queuedbs qtestdir testdir
+
+ if { ![info exists qtestdir] } {
+ set qtestdir $testdir
+ }
+ set queuedir $qtestdir/MSGQUEUEDIR
+ set orig [pwd]
+
+ cd $queuedir
+ if { $tf == "to" } {
+ set msgdbs [glob -nocomplain ready.$machid.*]
+ } else {
+ set msgdbs [glob -nocomplain ready.*.$machid.*]
+ }
+ foreach m $msgdbs {
+ file delete -force $m
+ }
+ cd $orig
+ set dbs [array names queuedbs]
+ foreach tofrom $dbs {
+ # Process only messages _to_ the specified machid.
+ if { [string match $machid.* $tofrom] == 1 } {
+ set db $queuedbs($tofrom)
+ set dbc [$db cursor]
+ for { set dbt [$dbc get -first] } \
+ { [llength $dbt] > 0 } \
+ { set dbt [$dbc get -next] } {
+ error_check_good \
+ replclear($machid)_del [$dbc del] 0
+ }
+ error_check_good replclear($db)_dbc_close [$dbc close] 0
+ }
+ }
+ cd $queuedir
+ if { $tf == "to" } {
+ set msgdbs [glob -nocomplain temp.$machid.*]
+ } else {
+ set msgdbs [glob -nocomplain temp.*.$machid.*]
+ }
+ foreach m $msgdbs {
+# file delete -force $m
+ }
+ cd $orig
+}
+
+# Makes messages available to replprocessqueue by closing and
+# renaming the message files. We ready the files for one machine
+# ID at a time -- just those "to" or "from" the machine we want to
+# process, depending on 'tf'.
+proc replready_noenv { machid tf } {
+ global queuedbs machids
+ global counter
+ global qtestdir testdir
+
+ if { ![info exists qtestdir] } {
+ set qtestdir $testdir
+ }
+ set queuedir $qtestdir/MSGQUEUEDIR
+
+ set pid [pid]
+ #
+ # Close the temporary message files for the specified machine.
+ # Only close it if there are messages available.
+ #
+ set dbs [array names queuedbs]
+ set closed {}
+ foreach tofrom $dbs {
+ set toidx [string first . $tofrom]
+ set toid [string replace $tofrom $toidx end]
+ set fidx [expr $toidx + 1]
+ set fromidx [string first . $tofrom $fidx]
+ #
+ # First chop off the end, then chop off the toid
+ # in the beginning.
+ #
+ set fromid [string replace $tofrom $fromidx end]
+ set fromid [string replace $fromid 0 $toidx]
+ if { ($tf == "to" && $machid == $toid) || \
+ ($tf == "from" && $machid == $fromid) } {
+ set nkeys [stat_field $queuedbs($tofrom) \
+ stat "Number of keys"]
+ if { $nkeys != 0 } {
+ lappend closed \
+ [list $toid $fromid temp.$tofrom]
+ error_check_good temp_close \
+ [$queuedbs($tofrom) close] 0
+ }
+ }
+ }
+
+ # Rename the message files.
+ set cwd [pwd]
+ foreach filename $closed {
+ set toid [lindex $filename 0]
+ set fromid [lindex $filename 1]
+ set fname [lindex $filename 2]
+ set tofrom [string replace $fname 0 4]
+ incr counter($machid)
+ cd $queuedir
+# puts "$queuedir: Msg ready $fname to ready.$tofrom.$counter($machid)"
+ file rename -force $fname ready.$tofrom.$counter($machid)
+ cd $cwd
+ replsetuptempfile_noenv $toid $fromid $queuedir
+
+ }
+}
+
+# Add a machine to a replication environment. This checks
+# that we have not already established that machine id, and
+# adds the machid to the list of ids.
+proc repladd_noenv { machid } {
+ global queuedbs machids counter qtestdir testdir
+
+ if { ![info exists qtestdir] } {
+ set qtestdir $testdir
+ }
+ set queuedir $qtestdir/MSGQUEUEDIR
+ if { [info exists machids] } {
+ if { [lsearch -exact $machids $machid] >= 0 } {
+ error "FAIL: repladd_noenv: machid $machid already exists."
+ }
+ }
+
+ set counter($machid) 0
+ lappend machids $machid
+
+ # Create all the databases that receive messages sent _to_
+ # the new machid.
+ replcreatetofiles_noenv $machid $queuedir
+
+ # Create all the databases that receive messages sent _from_
+ # the new machid.
+ replcreatefromfiles_noenv $machid $queuedir
+}
+
+# Creates all the databases that a machid needs for receiving messages
+# from other participants in a replication group. Used when first
+# establishing the temp files, but also used whenever replready_noenv moves
+# the temp files away, because we'll need new files for any future messages.
+proc replcreatetofiles_noenv { toid queuedir } {
+ global machids
+
+ foreach m $machids {
+ # We don't need a file for a machid to send itself messages.
+ if { $m == $toid } {
+ continue
+ }
+ replsetuptempfile_noenv $toid $m $queuedir
+ }
+}
+
+# Creates all the databases that a machid needs for sending messages
+# to other participants in a replication group. Used when first
+# establishing the temp files only. Replready moves files based on
+# recipient, so we recreate files based on the recipient, also.
+proc replcreatefromfiles_noenv { fromid queuedir } {
+ global machids
+
+ foreach m $machids {
+ # We don't need a file for a machid to send itself messages.
+ if { $m == $fromid } {
+ continue
+ }
+ replsetuptempfile_noenv $m $fromid $queuedir
+ }
+}
+
+proc replsetuptempfile_noenv { to from queuedir } {
+ global queuedbs
+
+ set pid [pid]
+# puts "Open new temp.$to.$from.$pid"
+ set queuedbs($to.$from.$pid) [berkdb_open -create -excl -recno\
+ -renumber $queuedir/temp.$to.$from.$pid]
+ error_check_good open_queuedbs [is_valid_db $queuedbs($to.$from.$pid)] TRUE
+}
+
+# Process a queue of messages, skipping every "skip_interval" entry.
+# We traverse the entire queue, but since we skip some messages, we
+# may end up leaving things in the queue, which should get picked up
+# on a later run.
+proc replprocessqueue_noenv { dbenv machid { skip_interval 0 } { hold_electp NONE } \
+ { dupmasterp NONE } { errp NONE } } {
+ global errorCode
+ global perm_response_list
+ global qtestdir testdir
+
+ # hold_electp is a call-by-reference variable which lets our caller
+ # know we need to hold an election.
+ if { [string compare $hold_electp NONE] != 0 } {
+ upvar $hold_electp hold_elect
+ }
+ set hold_elect 0
+
+ # dupmasterp is a call-by-reference variable which lets our caller
+ # know we have a duplicate master.
+ if { [string compare $dupmasterp NONE] != 0 } {
+ upvar $dupmasterp dupmaster
+ }
+ set dupmaster 0
+
+ # errp is a call-by-reference variable which lets our caller
+ # know we have gotten an error (that they expect).
+ if { [string compare $errp NONE] != 0 } {
+ upvar $errp errorp
+ }
+ set errorp 0
+
+ set nproced 0
+
+ set queuedir $qtestdir/MSGQUEUEDIR
+# puts "replprocessqueue_noenv: Make ready messages to eid $machid"
+
+ # Change directories temporarily so we get just the msg file name.
+ set cwd [pwd]
+ cd $queuedir
+ set msgdbs [glob -nocomplain ready.$machid.*]
+# puts "$queuedir.$machid: My messages: $msgdbs"
+ cd $cwd
+
+ foreach msgdb $msgdbs {
+ set db [berkdb_open $queuedir/$msgdb]
+ set dbc [$db cursor]
+
+ error_check_good process_dbc($machid) \
+ [is_valid_cursor $dbc $db] TRUE
+
+ for { set dbt [$dbc get -first] } \
+ { [llength $dbt] != 0 } \
+ { set dbt [$dbc get -next] } {
+ set data [lindex [lindex $dbt 0] 1]
+ set recno [lindex [lindex $dbt 0] 0]
+
+ # If skip_interval is nonzero, we want to process
+ # messages out of order. We do this in a simple but
+ # slimy way -- continue walking with the cursor
+ # without processing the message or deleting it from
+ # the queue, but do increment "nproced". The way
+ # this proc is normally used, the precise value of
+ # nproced doesn't matter--we just don't assume the
+ # queues are empty if it's nonzero. Thus, if we
+ # contrive to make sure it's nonzero, we'll always
+ # come back to records we've skipped on a later call
+ # to replprocessqueue. (If there really are no records,
+ # we'll never get here.)
+ #
+ # Skip every skip_interval'th record (and use a
+ # remainder other than zero so that we're guaranteed
+ # to really process at least one record on every call).
+ if { $skip_interval != 0 } {
+ if { $nproced % $skip_interval == 1 } {
+ incr nproced
+ set dbt [$dbc get -next]
+ continue
+ }
+ }
+
+ # We need to remove the current message from the
+ # queue, because we're about to end the transaction
+ # and someone else processing messages might come in
+ # and reprocess this message which would be bad.
+ #
+ error_check_good queue_remove [$dbc del] 0
+
+ # We have to play an ugly cursor game here: we
+ # currently hold a lock on the page of messages, but
+ # rep_process_message might need to lock the page with
+ # a different cursor in order to send a response. So
+ # save the next recno, close the cursor, and then
+ # reopen and reset the cursor. If someone else is
+ # processing this queue, our entry might have gone
+ # away, and we need to be able to handle that.
+ #
+# error_check_good dbc_process_close [$dbc close] 0
+
+ set ret [catch {$dbenv rep_process_message \
+ [lindex $data 2] [lindex $data 0] \
+ [lindex $data 1]} res]
+
+ # Save all ISPERM and NOTPERM responses so we can
+ # compare their LSNs to the LSN in the log. The
+ # variable perm_response_list holds the entire
+ # response so we can extract responses and LSNs as
+ # needed.
+ #
+ if { [llength $perm_response_list] != 0 && \
+ ([is_substr $res ISPERM] || [is_substr $res NOTPERM]) } {
+ lappend perm_response_list $res
+ }
+
+ if { $ret != 0 } {
+ if { [string compare $errp NONE] != 0 } {
+ set errorp "$dbenv $machid $res"
+ } else {
+ error "FAIL:[timestamp]\
+ rep_process_message returned $res"
+ }
+ }
+
+ incr nproced
+ if { $ret == 0 } {
+ set rettype [lindex $res 0]
+ set retval [lindex $res 1]
+ #
+ # Do nothing for 0 and NEWSITE
+ #
+ if { [is_substr $rettype HOLDELECTION] } {
+ set hold_elect 1
+ }
+ if { [is_substr $rettype DUPMASTER] } {
+ set dupmaster "1 $dbenv $machid"
+ }
+ if { [is_substr $rettype NOTPERM] || \
+ [is_substr $rettype ISPERM] } {
+ set lsnfile [lindex $retval 0]
+ set lsnoff [lindex $retval 1]
+ }
+ }
+
+ if { $errorp != 0 } {
+ # Break on an error, caller wants to handle it.
+ break
+ }
+ if { $hold_elect == 1 } {
+ # Break on a HOLDELECTION, for the same reason.
+ break
+ }
+ if { $dupmaster == 1 } {
+ # Break on a DUPMASTER, for the same reason.
+ break
+ }
+
+ }
+ error_check_good dbc_close [$dbc close] 0
+
+ #
+ # Check the number of keys remaining because we only
+ # want to rename to done, message file that are
+ # fully processed. Some message types might break
+ # out of the loop early and we want to process
+ # the remaining messages the next time through.
+ #
+ set nkeys [stat_field $db stat "Number of keys"]
+ error_check_good db_close [$db close] 0
+
+ if { $nkeys == 0 } {
+ set dbname [string replace $msgdb 0 5 done.]
+ #
+ # We have to do a special dance to get rid of the
+ # empty messaging files because of the way Windows
+ # handles open files marked for deletion.
+ # On Windows, a file is marked for deletion but
+ # does not actually get deleted until the last handle
+ # is closed. This causes a problem when a test tries
+ # to create a new file with a previously-used name,
+ # and Windows believes the old file still exists.
+ # Therefore, we rename the files before deleting them,
+ # to guarantee they are out of the way.
+ #
+ file rename -force $queuedir/$msgdb $queuedir/$dbname
+ file delete -force $queuedir/$dbname
+ }
+ }
+ # Return the number of messages processed.
+ return $nproced
+}
+