#At file:///home/ksm/oracle/QA/nuts/ based on revid:serge.kozlov@stripped
386 Serge Kozlov 2011-01-19
QA for WL#5493: new test rep_func::crash_safe_master added
added:
suites/rep_func/crash_safe_master.pm
renamed:
suites/rep_exp/ => suites/rep_func/
modified:
lib/My.pm
lib/My/Nuts/Library/Kernel/Replication.pm
lib/My/Nuts/Library/Kernel/ServerResult.pm
suites/rep/client_redirect/client_redirect.pm
suites/rep_func/mts.pm
=== modified file 'lib/My.pm'
--- a/lib/My.pm 2009-04-13 19:41:13 +0000
+++ b/lib/My.pm 2011-01-18 23:02:07 +0000
@@ -13,7 +13,7 @@ use constant ROLE_MASTER => 'M
use constant ROLE_SLAVE => 'S';
use constant ROLE_BOTH => 'B';
use constant WAIT => 1;
-use constant EFFORT => 5;
+use constant EFFORT => 10;
use constant DIE => 0;
use constant CONTINUE => 1;
use constant UNDEPLOYED => 0;
=== modified file 'lib/My/Nuts/Library/Kernel/Replication.pm'
--- a/lib/My/Nuts/Library/Kernel/Replication.pm 2010-03-31 20:40:41 +0000
+++ b/lib/My/Nuts/Library/Kernel/Replication.pm 2011-01-18 23:02:07 +0000
@@ -483,7 +483,7 @@ sub get_changemaster
$command
. ( $param{master_connect_retry}
? ", MASTER_CONNECT_RETRY = " . $param{master_connect_retry}
- : "" );
+ : "20" );
$command =
$command
. ( $param{master_heartbeat_period}
=== modified file 'lib/My/Nuts/Library/Kernel/ServerResult.pm'
--- a/lib/My/Nuts/Library/Kernel/ServerResult.pm 2010-12-21 21:34:08 +0000
+++ b/lib/My/Nuts/Library/Kernel/ServerResult.pm 2011-01-18 23:02:07 +0000
@@ -2,7 +2,7 @@ package My::Nuts::Library::Kernel::Serve
use Exporter;
our @ISA = qw(Exporter);
our @EXPORT =
- qw(compare_dbs ok_compare_dbs get_supported_engines get_table_list get_variable get_status
+ qw(ok_diff_databases diff_databases compare_dbs ok_compare_dbs get_supported_engines get_table_list get_variable get_status
get_errmsg_by_num ok_wait_status ok_wait_sql);
use strict;
use warnings;
@@ -17,6 +17,124 @@ use My::Nuts::Report::Debug;
use Test::More;
my $errmsg = undef;
+my $diff = "/usr/bin/diff";
+
+sub diff_databases
+{
+ my $data = [];
+ my @result = ();
+ # Sort input
+ for (my $i = 0; $i < scalar(@_); $i=$i+2)
+ {
+ push(@$data, {"connection" => $_[$i], "database" => $_[$i+1]});
+ }
+ # Get the list of table names
+ my $tablelist_p;
+ my @tablelist;
+ foreach my $data_item ( @$data )
+ {
+ $data_item->{"tablelist"}= get_table_list($data_item->{"connection"}, $data_item->{"database"});
+ $data_item->{"tablelist_p"}= join(",", @{$data_item->{"tablelist"}});
+ if (! defined($tablelist_p))
+ {
+ $tablelist_p= $data_item->{"tablelist_p"};
+ @tablelist= @{$data_item->{"tablelist"}};
+ }
+ }
+ # Compare table names
+ foreach my $data_item ( @$data )
+ {
+ if ($data_item->{"tablelist_p"} ne $tablelist_p)
+ {
+ push(@result, 1, "Table names are different: $tablelist_p and " . $data_item->{"tablelist_p"});
+ last;
+ }
+ }
+ # Compare number of records
+ if (scalar(@result) == 0)
+ {
+ # Get number of records for each table
+ foreach my $data_item ( @$data )
+ {
+ foreach my $tname ( @tablelist )
+ {
+ $data_item->{"table_count"}->{$tname}= sql_result($data_item->{"connection"}, "SELECT COUNT(*) FROM $tname");
+
+ }
+ }
+ # Compare number of records for same tables for different connections
+ foreach my $data_item ( @$data )
+ {
+ foreach my $tname ( @tablelist )
+ {
+ if ($data->[0]->{"table_count"}->{$tname} != $data_item->{"table_count"}->{$tname})
+ {
+ push(@result, 1, "Query 'SELECT COUNT(*) FROM $tname' returns different number of rows: "
+ . $data->[0]->{"database"} . ".$tname: " . $data->[0]->{"table_count"}->{$tname} . ", "
+ . $data_item->{"database"} . ".$tname: " . $data_item->{"table_count"}->{$tname});
+ last;
+ }
+ }
+ if (scalar(@result) > 0)
+ {
+ last;
+ }
+ }
+ # Dump rows from tables to files
+ if (scalar(@result) == 0)
+ {
+ foreach my $data_item ( @$data )
+ {
+ foreach my $tname ( @tablelist )
+ {
+ my $order_list = get_table_pk_column_list($data_item->{"connection"}, $tname);
+ my$select_list = get_table_column_list($data_item->{"connection"}, $tname);
+ if (!defined($order_list))
+ {
+ $order_list = $select_list;
+ }
+ my $dumpfile = sql_result($data_item->{"connection"}, 'SELECT @@datadir')
+ . $data_item->{"database"} . ".$tname.dump";
+ # Dump data to file
+ my $rs= sql($data_item->{"connection"}, "SELECT " . join(",", @$select_list) . " FROM $tname ORDER BY " . join(",", @$order_list) . " INTO OUTFILE '$dumpfile'");
+ $data_item->{"table_dumpfile"}->{$tname}= $dumpfile;
+ }
+ }
+ }
+ # Run diff
+ foreach my $data_item ( @$data )
+ {
+ foreach my $tname ( @tablelist )
+ {
+ my $diff_line = $diff . " -q " . $data->[0]->{"table_dumpfile"}->{$tname} . " " . $data_item->{"table_dumpfile"}->{$tname};
+ my $diff_res = `$diff_line`;
+ if ($diff_res =~ m/^.+$/)
+ {
+ push(@result, 1, "Found diff for dump files of table $tname: "
+ . $data->[0]->{"table_dumpfile"}->{$tname} . ", "
+ . $data_item->{"table_dumpfile"}->{$tname});
+ last;
+ }
+ }
+ if (scalar(@result) > 0)
+ {
+ last;
+ }
+ }
+ # Final conclusion: databases have no diff
+ if (scalar(@result) == 0)
+ {
+ push(@result, 0, "Databases have no difference");
+ }
+ }
+ return @result;
+}
+
+sub ok_diff_databases
+{
+ my @result = diff_databases(@_);
+ ok ( $result[0] == 0, $result[1]) or diag ($result[1]);
+}
sub compare_dbs
{
@@ -137,10 +255,8 @@ sub get_supported_engines
sub get_table_list
{
- my $params = shift;
- my $conn = $params->{"conn"};
- my $dbname = $params->{"dbname"};
- my $tables = [];
+ my ($conn, $dbname) = @_;
+ my @tables = ();
my $rs;
if (defined $dbname)
{
@@ -153,10 +269,61 @@ sub get_table_list
my @rs_data = get_next($rs);
while (defined $rs_data[0])
{
- push(@$tables, $rs_data[0]);
+ push(@tables, $rs_data[0]);
@rs_data = get_next($rs);
}
- return $tables;
+ return [ sort @tables ];
+}
+
+sub get_show_create_table
+{
+ my ($conn, $tname) = @_;
+ my @cols = ();
+ my $rs = sql ( $conn, "SHOW CREATE TABLE $tname" );
+ my @rs_data = get_next($rs);
+ if (defined $rs_data[1])
+ {
+ return $rs_data[1];;
+ }
+ return undef;
+}
+
+sub get_table_column_list
+{
+ my @cols = ();
+ my $create = get_show_create_table(@_);
+ if (defined $create)
+ {
+ $create =~ s/(\r|\n|\`|\`)//g;
+ $create =~ s/^create.+?table.+?\(//gi;
+ $create =~ s/primary key.+//gi;
+ $create =~ s/\(\d+\)//gi;
+ $create =~ s/\).+?ENGINE.+//gi;
+ $create =~ s/\, +/,/gi;
+ $create =~ s/ +[a-z\_]+//gi;
+ @cols = split(",", $create);
+ return [@cols];
+ }
+ return undef;
+}
+
+sub get_table_pk_column_list
+{
+ my @cols = ();
+ my $create = get_show_create_table(@_);
+ if (defined $create)
+ {
+ $create =~ s/(\`|\`| )//g;
+ if ($create =~ m/primarykey\((.+?)\)/i)
+ {
+ @cols = split(",", $1);
+ }
+ if (scalar(@cols) > 0)
+ {
+ return [@cols];
+ }
+ }
+ return undef;
}
sub get_variable
@@ -286,14 +453,12 @@ dbname - name of database
=back
-=head3 get_table_list(params)
+=head3 get_table_list(conn, dbname)
Description: get list of tables for given server/database
Parameters:
-params - reference to hash that must have following keys:
-
conn - server (connection)
dbname - name of database
=== modified file 'suites/rep/client_redirect/client_redirect.pm'
--- a/suites/rep/client_redirect/client_redirect.pm 2010-08-17 20:30:52 +0000
+++ b/suites/rep/client_redirect/client_redirect.pm 2011-01-18 23:02:07 +0000
@@ -133,7 +133,7 @@ sub fire
my $cur_tables;
while (($cur_table_num < $orig_table_num) && ($timeout > 0))
{
- $cur_tables = get_table_list({"conn" => $master_middle, "dbname" => $dbname1});
+ $cur_tables = get_table_list($master_middle, $dbname1});
$cur_table_num = scalar(@$cur_tables);
sleep 1;
$timeout--;
=== renamed directory 'suites/rep_exp' => 'suites/rep_func'
=== added file 'suites/rep_func/crash_safe_master.pm'
--- a/suites/rep_func/crash_safe_master.pm 1970-01-01 00:00:00 +0000
+++ b/suites/rep_func/crash_safe_master.pm 2011-01-18 23:02:07 +0000
@@ -0,0 +1,254 @@
+package rep_func::crash_safe_master;
+use Exporter;
+our @ISA = qw(Exporter My::Nuts::Library::Tests::SimpleTest);
+use strict;
+use warnings;
+use DBI;
+use IO::File;
+use My;
+use My::Nuts::Library::Requirement;
+use My::Nuts::Library::Kernel::Server;
+use My::Nuts::Library::Kernel::ServerResult;
+use My::Nuts::Library::Kernel::Manager;
+use My::Nuts::Library::Kernel::Result;
+use My::Nuts::Library::Tests::SimpleTest;
+use My::Nuts::Library::Kernel::Replication;
+use My::Nuts::Library::Fault::FaultInjection;
+use My::Nuts::Library::DataSource;
+use Test::More;
+
+my @crash_points = (
+ "crash_commit_before",
+ "crash_commit_after_prepare",
+ "crash_commit_after_log",
+ "crash_commit_after",
+ "crash_before_writing_xid",
+ "half_binlogged_transaction"
+);
+
+sub combinations
+{
+ my @combinations = ();
+ foreach my $binlog_format ( ("row", "mixed", "stmt") )
+ {
+ my $idx= 0;
+ foreach my $crash_point ( @crash_points )
+ {
+ push (@combinations, $binlog_format . "-crashpoint" . $idx);
+ $idx++;
+ }
+ }
+ return @combinations;
+}
+
+sub prepare
+{
+ return;
+}
+
+sub startup
+{
+ return;
+}
+
+sub fire
+{
+ my ($test) = @_;
+
+ # Parameters
+ my $dbname = "test";
+ my $timeout= 30;
+
+ # Decode combination
+ my ($binlog_format, $crash_point) = split(/\-crashpoint/, get_combination($test));
+ $binlog_format = uc $binlog_format;
+ $binlog_format = "STATEMENT" if ($binlog_format eq "STMT");
+ $crash_point = $crash_points[$crash_point];
+
+ # Read list of statements from data source
+ my $data = get_data_from_source();
+
+ # Set number od subtests
+ my $subtests = 14;
+
+ # Skip test if servers compiled withot debug support
+ SKIP:
+ {
+ # Start master and check debubug support
+ my $master = server ($test);
+ if ( !has_dbug($master) )
+ {
+ skip "Master compiled without debug support ", $subtests;
+ }
+
+ # Start slave and check debug support
+ my $slave = server ($test);
+ if ( !has_dbug($master) )
+ {
+ skip "Slave compiled without debug support", $subtests;
+ }
+
+ # Set number of subtests
+ plan tests => $subtests;
+
+ # Setup topology, binlog format, prepare test db
+ ok_sql ( $master, "SET GLOBAL BINLOG_FORMAT='$binlog_format'");
+ ok_sql ( $master, "SET SESSION BINLOG_FORMAT='$binlog_format'");
+ ok_sql ( $slave, "SET GLOBAL BINLOG_FORMAT='$binlog_format'");
+ ok_attach ( $master, $slave );
+ ok_synchronize ( $master, $slave );
+ ok_sql ( $master, "CREATE DATABASE IF NOT EXISTS $dbname");
+ ok_sql ( $master, "USE $dbname");
+
+ # Main test
+
+ # Set common crash position inside range 25-75%
+ my $query_count= scalar(@$data);
+ my $crash_pos = int(rand($query_count/2) + $query_count/4);
+
+ # Fix position for special cases
+ if ($crash_point =~ m/(crash_commit_after|half_binlogged_transaction)/)
+ {
+ my ($b_pos, $c_pos) = find_rand_trans($data);
+ if (defined($c_pos))
+ {
+ if ($crash_point =~ m/crash_commit_after/)
+ {
+ $crash_pos = $c_pos;
+ }
+ elsif ($crash_point eq "half_binlogged_transaction")
+ {
+ $crash_pos = $b_pos + int(($c_pos - $b_pos)/2);
+ }
+ # Print via diag() where we will do a crash
+ for (my $pos = $b_pos; $pos <= $c_pos; $pos++)
+ {
+ if ($pos == $crash_pos)
+ {
+ diag("SET SESSION DEBUG='d,$crash_point'");
+ }
+ diag($data->[$pos]);
+ }
+ }
+ else
+ {
+ skip "Statement list has no transactions", 7;
+ }
+ }
+ else
+ {
+ diag($data->[$crash_pos-1]) if ($crash_pos > 0);
+ diag("SET SESSION DEBUG='d,$crash_point'");
+ diag($data->[$crash_pos+1]) if ($crash_pos < (scalar(@$data)-1));
+ }
+
+ # Run queries against master
+ # We do not stop executing even server crashed
+ my $query_num = 0;
+ foreach my $query (@$data)
+ {
+ if ( $query_num == $crash_pos )
+ {
+ ok_sql ( $master, "SET SESSION DEBUG='d,$crash_point'");
+ }
+ sql ( $master, $query );
+ $query_num++;
+ }
+
+ # Wait the crash of master
+ while (check($master) != My::FAILURE && $timeout > 0)
+ {
+ sleep 1;
+ $timeout--;
+ }
+
+ # Start master and restore replication
+ if (check($master) == My::FAILURE)
+ {
+ stop_server($master);
+ wait_stop_server($master);
+ start_server($master);
+ wait_start_server($master);
+ ok_sql ( $master, "SET GLOBAL BINLOG_FORMAT='$binlog_format'");
+ ok_sql ( $master, "SET SESSION BINLOG_FORMAT='$binlog_format'");
+ ok_start_replication($slave);
+ ok_wait_start_replication($slave);
+ }
+ else
+ {
+ skip "Master was not crashed at $crash_point.", 4;
+ }
+
+ # Sync slave with master
+ ok_synchronize($master, $slave);
+ # Compare databases
+ ok_diff_databases($master, "test", $slave, "test");
+ }
+}
+
+sub shutdown
+{
+ return;
+}
+
+sub find_rand_trans
+{
+ my ($data)= @_;
+ my @begin_pos_arr = ();
+ my @commit_pos_arr = ();
+ my $begin_pos = undef;
+ my $i = 0;
+ foreach my $query ( @$data )
+ {
+ if ($query =~ m/^BEGIN/i)
+ {
+ $begin_pos= $i;
+ }
+ elsif (defined($begin_pos) && ($query =~ m/COMMIT/i))
+ {
+ push(@begin_pos_arr, $begin_pos);
+ push(@commit_pos_arr, $i);
+ $begin_pos = undef;
+ }
+ $i++;
+ }
+ if (scalar(@begin_pos_arr) > 0)
+ {
+ my $pos = int(rand(scalar(@begin_pos_arr)));
+ return ($begin_pos_arr[$pos], $commit_pos_arr[$pos]);
+ }
+ else
+ {
+ return (undef, undef);
+ }
+}
+
+1;
+__END__;
+
+=head1 NAME
+
+rep::crash_safe_master - Crash safe master
+
+=head1 SYNOPSIS
+
+The test case emulates the crash at some points on master and possibility to restore the replication back without data loss.
+Test uses binlog format and crash point number for combinations. The list of statements provided by DataSource library.
+
+=head1 REFERENCES
+
+=over
+
+=item * WL#5439
+
+=back
+
+=head1 AUTHOR
+
+Serge Kozlov S<< <Serge.Kozlov@stripped> >>
+
+=head1 COPYRIGHT
+
+Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+
+=cut
\ No newline at end of file
=== modified file 'suites/rep_func/mts.pm'
--- a/suites/rep_exp/mts.pm 2010-12-21 21:34:08 +0000
+++ b/suites/rep_func/mts.pm 2011-01-18 23:02:07 +0000
@@ -1,4 +1,4 @@
-package rep_exp::mts;
+package rep_func::mts;
use Exporter;
our @ISA = qw(Exporter My::Nuts::Library::Tests::SimpleTest);
use strict;
Attachment: [text/bzr-bundle] bzr/serge.kozlov@oracle.com-20110118230207-4ujev1ekaqonfv70.bundle
| Thread |
|---|
| • bzr commit into nuts branch (Serge.Kozlov:386) WL#5493 | Serge Kozlov | 19 Jan |