libdb/test/tcl/rep089.tcl
2011-09-13 13:44:24 -04:00

245 lines
6.9 KiB
Tcl

# See the file LICENSE for redistribution information.
#
# Copyright (c) 2009, 2011 Oracle and/or its affiliates. All rights reserved.
#
# TEST rep089
# TEST Test of proper clean-up of mpool during interrupted internal init.
# TEST
# TEST Have a client in the middle of internal init when a new master
# TEST generation comes along, forcing the client to interrupt the internal
# TEST init, including doing the clean-up. The client is in the middle of
# TEST retrieving database pages, so that we are forced to clean up mpool.
# TEST (Regression test for bug 17121)
proc rep089 { method { niter 200 } { tnum "089" } args } {
source ./include.tcl
global databases_in_memory
global repfiles_in_memory
global env_private
if { $checking_valid_methods } {
set test_methods {}
foreach method $valid_methods {
if { [is_btree $method] == 1 || \
[is_queue $method] == 1 } {
lappend test_methods $method
}
}
return $test_methods
}
if { [is_btree $method] == 0 && [is_queue $method] == 0 } {
puts "Rep$tnum: skipping for non-btree, non-queue method."
return
}
# It's possible to run this test with in-memory databases.
set msg "with named databases"
if { $databases_in_memory } {
set msg "with in-memory named databases"
if { [is_queueext $method] == 1 } {
puts "Skipping rep$tnum for method $method"
return
}
}
set msg2 "and on-disk replication files"
if { $repfiles_in_memory } {
set msg2 "and in-memory replication files"
}
set msg3 ""
if { $env_private } {
set msg3 "with private env"
}
# This test needs to set its own pagesize.
set pgindex [lsearch -exact $args "-pagesize"]
if { $pgindex != -1 } {
puts "Rep$tnum: skipping for specific pagesizes"
return
}
set args [convert_args $method $args]
puts -nonewline "Rep$tnum: Mpool cleanup on aborted"
puts " internal init $msg $msg2 $msg3."
rep089_sub $method $niter $tnum $args
}
proc rep089_sub { method niter tnum largs } {
global testdir
global rep_verbose
global verbose_type
global databases_in_memory
global repfiles_in_memory
global env_private
set verbargs ""
if { $rep_verbose == 1 } {
set verbargs " -verbose {$verbose_type on} "
}
set repmemargs ""
if { $repfiles_in_memory } {
set repmemargs "-rep_inmem_files "
}
set privargs ""
if { $env_private == 1 } {
set privargs " -private "
}
env_cleanup $testdir
replsetup $testdir/MSGQUEUEDIR
file mkdir [set masterdir $testdir/MASTERDIR]
file mkdir [set clientdir $testdir/CLIENTDIR]
# Use a small page size, so that it won't take too long to accumulate a
# large number of pages.
#
append largs " -pagesize 512"
puts "\tRep$tnum.a: Create a master and three databases."
# Note that we use a special version of the "send" call-back function
# here and in the client, during this first "set up" part of the test.
#
repladd 1
set env_cmd(M) "berkdb_env -create -txn $repmemargs $privargs \
$verbargs -home $masterdir -errpfx MASTER -rep_master"
set masterenv [eval $env_cmd(M) -rep_transport {{1 rep089_send}}]
# Create three databases. Later, we will interrupt an internal init
# during the middle of transferring pages for the second database. This
# will force the client to exercise clean-up for all three cases: a
# fully intact, already materialized database; one that's in the process
# of being materialized; and one that we haven't even touched yet (i.e.,
# totally nonexistent).
#
foreach n {1 2 3} {
rep089_make_data $masterenv test$n.db $method $niter $largs
}
# Turn off transmission limits, so that we can be sure that we're
# controlling the pace of progress through internal init from the test
# script.
#
$masterenv rep_limit 0 0
puts "\tRep$tnum.b: Create a client, and start internal init."
repladd 2
set env_cmd(C) "berkdb_env -create -txn $repmemargs $privargs \
$verbargs -home $clientdir -errpfx CLIENT -rep_client \
-rep_transport \[list 2 rep089_send\]"
set clientenv [eval $env_cmd(C)]
set envlist "{$masterenv 1} {$clientenv 2}"
# Go through just a single message processing cycle at a time, until we
# see that we've received some PAGE messages. Since we're using a
# special send() function that discards some PAGE messages, the client
# will then be left in the middle of REP_F_RECOVER_PAGE state.
#
# We want to interrupt the internal init in the middle of materializing
# the 2nd database. Note that queue databases require two PAGE_REQ
# cycles.
#
if {[is_queue $method]} {
set max_cycles 4
} else {
set max_cycles 2
}
# Do as many cycles as it takes till we see the first PAGE_REQ, then do
# enough additional cycles to bring the total page-related cycles up to
# "max_cycles". Then turn on message dropping for the following cycle.
#
global rep089_page_count
global rep089_pagereq_seen
set rep089_page_count -1
set rep089_pagereq_seen 0
while {! $rep089_pagereq_seen} {
proc_msgs_once $envlist
}
for { set i 1 } { $i < $max_cycles } { incr i } {
proc_msgs_once $envlist
}
set rep089_page_count 0
proc_msgs_once $envlist
error_check_bad assert_page_count $rep089_page_count 0
puts "\tRep$tnum.c: Restart master, to force internal init clean-up."
# Trigger an interrupted internal init clean-up, by having the master
# restart at a higher gen.
#
replclear 1
$masterenv close
set masterenv [eval $env_cmd(M) -rep_transport {{1 replsend}} -recover]
set envlist "{$masterenv 1} {$clientenv 2}"
process_msgs $envlist
# (If we get to this point without a crash, the bug has been fixed.)
puts "\tRep$tnum.d: Sanity-check the sync-interrupted stat."
set n [stat_field $clientenv mpool_stat "Number of syncs interrupted"]
error_check_good nsyncs $n 1
$clientenv close
$masterenv close
replclose $testdir/MSGQUEUEDIR
}
proc rep089_send { control rec fromid toid flags lsn } {
global rep089_page_count
global rep089_pagereq_seen
if {[berkdb msgtype $control] eq "page_req"} {
set rep089_pagereq_seen 1
} elseif {[berkdb msgtype $control] eq "page" && $rep089_page_count >= 0 } {
incr rep089_page_count
if { $rep089_page_count < 3 || $rep089_page_count > 10 } {
return 0
}
}
return [replsend $control $rec $fromid $toid $flags $lsn]
}
proc rep089_make_data { env dbname method niter largs } {
global databases_in_memory
if { $databases_in_memory } {
set dbname " {} $dbname "
}
set omethod [convert_method $method]
set db [eval berkdb_open_noerr -env $env -auto_commit\
-create -mode 0644 $omethod $largs $dbname]
# Make sure the database has at least 15 pages. That number is
# arbitrary, but it should be more than the 10 that we skip in
# rep089_send, above.
#
set page_target 15
if {[is_queue $method]} {
set descriptor "Number of pages"
} else {
set descriptor "Leaf pages"
}
set start 0
while true {
eval rep_test $omethod $env $db $niter $start 0 0 $largs
incr start $niter
set npages [stat_field $db stat $descriptor]
if {$npages >= $page_target} {
break
}
}
$db close
}