Merge pt-osc-pxc-tests.

This commit is contained in:
Daniel Nichter
2012-11-29 17:10:51 -07:00
20 changed files with 1560 additions and 282 deletions

View File

@@ -395,39 +395,38 @@ $ni = make_nibble_iter(
my $row = $ni->next();
is_deeply(
$row,
[25, 'da79784d'],
[25, 'd9c52498'],
"SELECT chunk checksum 1 FROM sakila.country"
) or diag(Dumper($row));
$row = $ni->next();
is_deeply(
$row,
[25, 'e860c4f9'],
[25, 'ebdc982c'],
"SELECT chunk checksum 2 FROM sakila.country"
) or diag(Dumper($row));
$row = $ni->next();
is_deeply(
$row,
[25, 'eb651f58'],
[25, 'e8d9438d'],
"SELECT chunk checksum 3 FROM sakila.country"
) or diag(Dumper($row));
$row = $ni->next();
is_deeply(
$row,
[25, '2d87d588'],
[25, '2e3b895d'],
"SELECT chunk checksum 4 FROM sakila.country"
) or diag(Dumper($row));
$row = $ni->next();
is_deeply(
$row,
[9, 'beb4a180'],
[9, 'bd08fd55'],
"SELECT chunk checksum 5 FROM sakila.country"
) or diag(Dumper($row));
# #########################################################################
# exec_nibble callback and explain_sth
# #########################################################################

View File

@@ -14,6 +14,7 @@ use Test::More;
use PerconaTest;
use Sandbox;
require "$trunk/bin/pt-online-schema-change";
require VersionParser;
use Time::HiRes qw(sleep);
use Data::Dumper;
@@ -72,23 +73,27 @@ sub get_ids {
my @lines = <$fh>;
close $fh;
my %ids;
my %ids = (
updated => '',
deleted => '',
inserted => '',
);
foreach my $line ( @lines ) {
my ($stmt, $ids) = split(':', $line);
chomp $ids;
$ids{$stmt} = $ids;
$ids{$stmt} = $ids || '';
}
return \%ids;
}
sub check_ids {
my ( $db, $tbl, $pkcol, $ids ) = @_;
my ( $db, $tbl, $pkcol, $ids, $test ) = @_;
my $rows;
my $n_updated = $ids->{updated} ? ($ids->{updated} =~ tr/,//) : 0;
my $n_deleted = $ids->{deleted} ? ($ids->{deleted} =~ tr/,//) : 0;
my $n_inserted = ($ids->{inserted} =~ tr/,//);
my $n_inserted = $ids->{inserted} ?($ids->{inserted} =~ tr/,//) : 0;
# "1,1"=~tr/,// returns 1 but is 2 values
$n_updated++ if $n_updated;
@@ -100,16 +105,16 @@ sub check_ids {
is(
$rows->[0],
500 + $n_inserted - $n_deleted,
"New table row count: 500 original + $n_inserted inserted - $n_deleted deleted"
) or print Dumper($rows);
"$test: new table rows: 500 original + $n_inserted inserted - $n_deleted deleted"
) or diag(Dumper($rows));
$rows = $master_dbh->selectall_arrayref(
"SELECT $pkcol FROM $db.$tbl WHERE $pkcol > 500 AND $pkcol NOT IN ($ids->{inserted})");
is_deeply(
$rows,
[],
"No extra rows inserted in new table"
) or print Dumper($rows);
"$test: no extra rows inserted in new table"
) or diag(Dumper($rows));
if ( $n_deleted ) {
$rows = $master_dbh->selectall_arrayref(
@@ -117,13 +122,13 @@ sub check_ids {
is_deeply(
$rows,
[],
"No deleted rows present in new table"
) or print Dumper($rows);
"$test: no deleted rows present in new table"
) or diag(Dumper($rows));
}
else {
ok(
1,
"No rows deleted"
"$test: no rows deleted"
);
};
@@ -134,13 +139,13 @@ sub check_ids {
is_deeply(
$rows,
[],
"Updated rows correct in new table"
) or print Dumper($rows);
"$test: updated rows correct in new table"
) or diag(Dumper($rows));
}
else {
ok(
1,
"No rows updated"
"$test: no rows updated"
);
}
@@ -151,18 +156,19 @@ sub check_ids {
# Attempt to alter a table while another process is changing it.
# #############################################################################
# Load 500 rows.
diag('Loading sample dataset...');
$sb->load_file('master', "$sample/basic_no_fks.sql");
my $db_flavor = VersionParser->new($master_dbh)->flavor();
if ( $db_flavor =~ m/XtraDB Cluster/ ) {
$sb->load_file('master', "$sample/basic_no_fks_innodb.sql");
}
else {
$sb->load_file('master', "$sample/basic_no_fks.sql");
}
$master_dbh->do("USE pt_osc");
$master_dbh->do("TRUNCATE TABLE t");
$master_dbh->do("LOAD DATA INFILE '$trunk/t/pt-online-schema-change/samples/basic_no_fks.data' INTO TABLE t");
$master_dbh->do("ANALYZE TABLE t");
$sb->wait_for_slaves();
$rows = $master_dbh->selectrow_hashref('show master status');
diag('Binlog position before altering table: ', $rows->{file}, '/', $rows->{position});
# Start inserting, updating, and deleting rows at random.
start_query_table(qw(pt_osc t id));
@@ -178,29 +184,31 @@ start_query_table(qw(pt_osc t id));
# Stop changing the table's data.
stop_query_table();
like($output, qr/Successfully altered `pt_osc`.`t`/, 'Altered OK');
like(
$output,
qr/Successfully altered `pt_osc`.`t`/,
'Change engine: altered OK'
);
$rows = $master_dbh->selectall_hashref('SHOW TABLE STATUS FROM pt_osc', 'name');
is(
$rows->{t}->{engine},
'InnoDB',
"New table ENGINE=InnoDB"
"Change engine: new table ENGINE=InnoDB"
) or warn Dumper($rows);
is(
scalar keys %$rows,
1,
"Dropped old table"
"Change engine: dropped old table"
);
is(
$exit,
0,
"Exit status 0"
"Change engine: exit status 0"
);
check_ids(qw(pt_osc t id), get_ids());
# #############################################################################
# Check that triggers work when renaming a column
# #############################################################################
@@ -211,8 +219,6 @@ $master_dbh->do("LOAD DATA INFILE '$trunk/t/pt-online-schema-change/samples/basi
$master_dbh->do("ANALYZE TABLE t");
$sb->wait_for_slaves();
my $orig_rows = $master_dbh->selectall_arrayref(qq{SELECT id,d FROM pt_osc.t});
# Start inserting, updating, and deleting rows at random.
start_query_table(qw(pt_osc t id));
@@ -230,33 +236,19 @@ start_query_table(qw(pt_osc t id));
# Stop changing the table's data.
stop_query_table();
like($output, qr/Successfully altered `pt_osc`.`t`/, 'Altered OK');
like(
$output,
qr/Successfully altered `pt_osc`.`t`/,
'Rename column: altered OK'
);
is(
$exit,
0,
"Exit status 0"
"Rename columnn: exit status 0"
);
my $ids = get_ids();
my %deleted_ids = map { $_ => 1 } split /,/, $ids->{deleted};
my %updated_ids = map { $_ => 1 } split /,/, $ids->{updated};
$rows = $master_dbh->selectall_arrayref(
qq{SELECT id,q FROM pt_osc.t WHERE id}
. ($ids->{updated} ? qq{ AND id NOT IN ($ids->{updated})} : '')
. ($ids->{inserted} ? qq{ AND id NOT IN ($ids->{inserted})} : '')
);
my @filtered_orig_rows = grep {
!$deleted_ids{$_->[0]} && !$updated_ids{$_->[0]}
} @$orig_rows;
is_deeply(
$rows,
\@filtered_orig_rows,
"Triggers work if renaming a column"
);
check_ids(qw(pt_osc t id), get_ids(), "Rename column");
# #############################################################################
# Done.

View File

@@ -15,6 +15,7 @@ use Time::HiRes qw(sleep);
use PerconaTest;
use Sandbox;
require "$trunk/bin/pt-online-schema-change";
require VersionParser;
use Data::Dumper;
$Data::Dumper::Indent = 1;
@@ -46,7 +47,7 @@ my $rows;
# Tool shouldn't run without --execute (bug 933232).
# #############################################################################
$sb->load_file('master', "$sample/basic_no_fks.sql");
$sb->load_file('master', "$sample/basic_no_fks_innodb.sql");
($output, $exit) = full_output(
sub { pt_online_schema_change::main(@args, "$dsn,D=pt_osc,t=t",
@@ -57,7 +58,7 @@ like(
$output,
qr/neither --dry-run nor --execute was specified/,
"Doesn't run without --execute (bug 933232)"
) or warn $output;
) or diag($output);
my $ddl = $master_dbh->selectrow_arrayref("show create table pt_osc.t");
like(
@@ -100,9 +101,9 @@ sub test_alter_table {
my $tbl_struct = $tp->parse($ddl);
my $cols = '*';
if ( $test_type eq 'drop_col' && !grep { $_ eq '--dry-run' } @$cmds ) {
if ( $test_type =~ m/(?:add|drop)_col/ && !grep { $_ eq '--dry-run' } @$cmds ) {
# Don't select the column being dropped.
my $col = $args{drop_col};
my $col = $args{drop_col} || $args{new_col};
die "I need a drop_col argument" unless $col;
$cols = join(', ', grep { $_ ne $col } @{$tbl_struct->{cols}});
}
@@ -147,6 +148,7 @@ sub test_alter_table {
);
my $new_ddl = $tp->get_create_table($master_dbh, $db, $tbl);
my $new_tbl_struct = $tp->parse($new_ddl);
my $fail = 0;
is(
@@ -164,7 +166,7 @@ sub test_alter_table {
) or $fail = 1;
# Rows in the original and new table should be identical.
my $new_rows = $master_dbh->selectall_arrayref("SELECT * FROM $table ORDER BY `$pk_col`");
my $new_rows = $master_dbh->selectall_arrayref("SELECT $cols FROM $table ORDER BY `$pk_col`");
is_deeply(
$new_rows,
$orig_rows,
@@ -173,7 +175,7 @@ sub test_alter_table {
if ( grep { $_ eq '--no-drop-new-table' } @$cmds ) {
$new_rows = $master_dbh->selectall_arrayref(
"SELECT * FROM `$db`.`$new_tbl` ORDER BY `$pk_col`");
"SELECT $cols FROM `$db`.`$new_tbl` ORDER BY `$pk_col`");
is_deeply(
$new_rows,
$orig_rows,
@@ -216,6 +218,18 @@ sub test_alter_table {
}
}
elsif ( $test_type eq 'add_col' ) {
if ( $args{no_change} ) {
ok(
!$new_tbl_struct->{is_col}->{$args{new_col}},
"$name $args{new_col} not added"
);
}
else {
ok(
$new_tbl_struct->{is_col}->{$args{new_col}},
"$name $args{new_col} added"
);
}
}
elsif ( $test_type eq 'new_engine' ) {
my $new_engine = lc($args{new_engine});
@@ -305,36 +319,71 @@ sub test_alter_table {
# The most basic: alter a small table with no fks that's not active.
# #############################################################################
test_alter_table(
name => "Basic no fks --dry-run",
table => "pt_osc.t",
file => "basic_no_fks.sql",
max_id => 20,
test_type => "new_engine",
new_engine => "MyISAM",
cmds => [qw(--dry-run --alter ENGINE=InnoDB)],
);
my $db_flavor = VersionParser->new($master_dbh)->flavor();
if ( $db_flavor =~ m/XtraDB Cluster/ ) {
test_alter_table(
name => "Basic no fks --dry-run",
table => "pt_osc.t",
file => "basic_no_fks_innodb.sql",
max_id => 20,
test_type => "drop_col",
drop_col => "d",
cmds => [qw(--dry-run --alter), 'DROP COLUMN d'],
);
test_alter_table(
name => "Basic no fks --execute",
table => "pt_osc.t",
# The previous test should not have modified the table.
# file => "basic_no_fks.sql",
# max_id => 20,
test_type => "new_engine",
new_engine => "InnoDB",
cmds => [qw(--execute --alter ENGINE=InnoDB)],
);
test_alter_table(
name => "Basic no fks --execute",
table => "pt_osc.t",
# The previous test should not have modified the table.
# file => "basic_no_fks_innodb.sql",
# max_id => 20,
test_type => "drop_col",
drop_col => "d",
cmds => [qw(--execute --alter), 'DROP COLUMN d'],
);
test_alter_table(
name => "--execute but no --alter",
table => "pt_osc.t",
file => "basic_no_fks.sql",
max_id => 20,
test_type => "new_engine",
new_engine => "MyISAM",
cmds => [qw(--execute)],
);
test_alter_table(
name => "--execute but no --alter",
table => "pt_osc.t",
file => "basic_no_fks_innodb.sql",
max_id => 20,
test_type => "new_engine", # When there's no change, we just check
new_engine => "InnoDB", # the engine as a NOP. Any other
cmds => [qw(--execute)], # unintended changes are still detected.
);
}
else {
test_alter_table(
name => "Basic no fks --dry-run",
table => "pt_osc.t",
file => "basic_no_fks.sql",
max_id => 20,
test_type => "new_engine",
new_engine => "MyISAM",
cmds => [qw(--dry-run --alter ENGINE=InnoDB)],
);
test_alter_table(
name => "Basic no fks --execute",
table => "pt_osc.t",
# The previous test should not have modified the table.
# file => "basic_no_fks.sql",
# max_id => 20,
test_type => "new_engine",
new_engine => "InnoDB",
cmds => [qw(--execute --alter ENGINE=InnoDB)],
);
test_alter_table(
name => "--execute but no --alter",
table => "pt_osc.t",
file => "basic_no_fks.sql",
max_id => 20,
test_type => "new_engine",
new_engine => "MyISAM",
cmds => [qw(--execute)],
);
}
# ############################################################################
# Alter a table with foreign keys.
@@ -520,7 +569,7 @@ SKIP: {
);
# Restore the original fks.
diag('Restoring original Sakila foreign keys...');
diag('Restoring sakila...');
diag(`$trunk/sandbox/load-sakila-db 12345`);
}
@@ -528,23 +577,18 @@ SKIP: {
# --alther-foreign-keys-method=none. This intentionally breaks fks because
# they're not updated so they'll point to the old table that is dropped.
# #############################################################################
diag('Loading file and waiting for replication...');
$sb->load_file('master', "$sample/basic_with_fks.sql");
# Specify --alter-foreign-keys-method for a table with no child tables.
test_alter_table(
name => "Update fk method none",
file => "basic_with_fks.sql",
table => "pt_osc.country",
pk_col => "country_id",
file => "basic_with_fks.sql",
max_id => 20,
test_type => "new_engine",
new_engine => "innodb",
cmds => [
qw(
--execute
--alter-foreign-keys-method none
),
'--alter', 'ENGINE=INNODB',
qw(--execute --alter-foreign-keys-method none --alter ENGINE=INNODB)
],
);
@@ -613,22 +657,28 @@ test_table(
test_alter_table(
name => "--no-swap-tables",
table => "pt_osc.t",
file => "basic_no_fks.sql",
file => "basic_no_fks_innodb.sql",
max_id => 20,
test_type => "new_engine", # Engine doesn't actually change
new_engine => "MyISAM", # because the tables aren't swapped
cmds => [qw(--execute --alter ENGINE=InnoDB --no-swap-tables)],
test_type => "add_col",
new_col => "foo",
no_change => 1,
cmds => [
qw(--execute --no-swap-tables), '--alter', 'ADD COLUMN foo INT'
],
);
test_alter_table(
name => "--no-swap-tables --no-drop-new-table",
table => "pt_osc.t",
file => "basic_no_fks.sql",
file => "basic_no_fks_innodb.sql",
max_id => 20,
test_type => "new_engine", # Engine doesn't actually change
new_engine => "MyISAM", # because the tables aren't swapped
cmds => [qw(--execute --alter ENGINE=InnoDB --no-swap-tables),
qw(--no-drop-new-table)],
test_type => "add_col",
new_col => "foo",
no_change => 1,
cmds => [
qw(--execute --no-swap-tables), '--alter', 'ADD COLUMN foo INT',
qw(--no-drop-new-table),
],
);
# #############################################################################
@@ -654,7 +704,7 @@ ok(
'--execute', '--statistics',
'--alter', "modify column val ENUM('M','E','H') NOT NULL")
},
($sandbox_version ge '5.5'
($sandbox_version ge '5.5' && $db_flavor !~ m/XtraDB Cluster/
? "$sample/stats-execute-5.5.txt"
: "$sample/stats-execute.txt"),
),
@@ -689,7 +739,6 @@ SKIP: {
# #############################################################################
# Done.
# #############################################################################
$master_dbh->do("UPDATE mysql.proc SET created='2012-06-05 00:00:00', modified='2012-06-05 00:00:00'");
$sb->wipe_clean($master_dbh);
ok($sb->ok(), "Sandbox servers") or BAIL_OUT(__FILE__ . " broke the sandbox");
done_testing;

View File

@@ -25,8 +25,11 @@ sub test_func {
die "No renamed_cols arg" unless $renamed_cols;
(my $show_alter = $alter) =~ s/\n/\\n/g;
my %got_renamed_cols = eval {
pt_online_schema_change::find_renamed_cols($alter, $tp);
my $got_renamed_cols = eval {
pt_online_schema_change::find_renamed_cols(
alter => $alter,
TableParser => $tp,
);
};
if ( $EVAL_ERROR ) {
is_deeply(
@@ -37,10 +40,10 @@ sub test_func {
}
else {
is_deeply(
\%got_renamed_cols,
$got_renamed_cols,
$renamed_cols,
$show_alter,
) or diag(Dumper(\%got_renamed_cols));
) or diag(Dumper($got_renamed_cols));
}
}
@@ -209,15 +212,13 @@ test_func(
},
);
TODO: {
local $::TODO = "We don't parse the entire alter statement, what looks like a CHANGE COLUMNS";
# Not really an alter, pathological
test_func(
"MODIFY `CHANGE a z VARCHAR(255) NOT NULL` FLOAT",
{
},
);
}
# TODO
## Not really an alter, pathological
#test_func(
# "MODIFY `CHANGE a z VARCHAR(255) NOT NULL` FLOAT",
# {
# },
#);
# #############################################################################
# Done.

View File

@@ -38,9 +38,6 @@ elsif ( !$slave2_dbh ) {
elsif ( !@{$master_dbh->selectall_arrayref("show databases like 'sakila'")} ) {
plan skip_all => 'sakila database is not loaded';
}
else {
plan tests => 3;
}
# The sandbox servers run with lock_wait_timeout=3 and it's not dynamic
# so we need to specify --lock-wait-timeout=3 else the tool will die.
@@ -58,7 +55,7 @@ my $sample = "t/pt-online-schema-change/samples/";
diag(`/tmp/12345/use -u root < $trunk/$sample/osc-user.sql`);
PerconaTest::wait_for_table($slave1_dbh, "mysql.tables_priv", "user='osc_user'");
$sb->load_file('master', "$sample/basic_no_fks.sql");
$sb->load_file('master', "$sample/basic_no_fks_innodb.sql");
($output, $exit_status) = full_output(
sub { $exit_status = pt_online_schema_change::main(@args,
@@ -97,4 +94,4 @@ wait_until(
# #############################################################################
$sb->wipe_clean($master_dbh);
ok($sb->ok(), "Sandbox servers") or BAIL_OUT(__FILE__ . " broke the sandbox");
exit;
done_testing;

View File

@@ -0,0 +1,143 @@
#!/usr/bin/env perl
BEGIN {
die "The PERCONA_TOOLKIT_BRANCH environment variable is not set.\n"
unless $ENV{PERCONA_TOOLKIT_BRANCH} && -d $ENV{PERCONA_TOOLKIT_BRANCH};
unshift @INC, "$ENV{PERCONA_TOOLKIT_BRANCH}/lib";
};
use strict;
use warnings FATAL => 'all';
use English qw(-no_match_vars);
use Test::More;
use Data::Dumper;
# Hostnames make testing less accurate. Tests need to see
# that such-and-such happened on specific slave hosts, but
# the sandbox servers are all on one host so all slaves have
# the same hostname.
$ENV{PERCONA_TOOLKIT_TEST_USE_DSN_NAMES} = 1;
use PerconaTest;
use Sandbox;
require "$trunk/bin/pt-online-schema-change";
# Do this after requiring ptc, since it uses Mo
require VersionParser;
my $dp = new DSNParser(opts=>$dsn_opts);
my $sb = new Sandbox(basedir => '/tmp', DSNParser => $dp);
my $node1 = $sb->get_dbh_for('node1');
my $node2 = $sb->get_dbh_for('node2');
my $node3 = $sb->get_dbh_for('node3');
if ( !$node1 ) {
plan skip_all => 'Cannot connect to cluster node1';
}
elsif ( !$node2 ) {
plan skip_all => 'Cannot connect to cluster node2';
}
elsif ( !$node3 ) {
plan skip_all => 'Cannot connect to cluster node3';
}
my $db_flavor = VersionParser->new($node1)->flavor();
if ( $db_flavor !~ /XtraDB Cluster/ ) {
plan skip_all => "PXC tests";
}
# The sandbox servers run with lock_wait_timeout=3 and it's not dynamic
# so we need to specify --lock-wait-timeout=3 else the tool will die.
my $node1_dsn = $sb->dsn_for('node1');
my $output;
my $exit;
my $sample = "t/pt-online-schema-change/samples/";
# #############################################################################
# Can't alter a MyISAM table.
# #############################################################################
$sb->load_file('node1', "$sample/basic_no_fks.sql");
($output, $exit) = full_output(
sub { pt_online_schema_change::main(
"$node1_dsn,D=pt_osc,t=t",
qw(--lock-wait-timeout 5),
qw(--print --execute --alter ENGINE=InnoDB)) },
stderr => 1,
);
ok(
$exit,
"Table is MyISAM: non-zero exit"
) or diag($output);
like(
$output,
qr/is a cluster node and the table is MyISAM/,
"Table is MyISAM: error message"
);
# #############################################################################
# Can't alter a table converted to MyISAM.
# #############################################################################
$sb->load_file('node1', "$sample/basic_no_fks_innodb.sql");
($output, $exit) = full_output(
sub { pt_online_schema_change::main(
"$node1_dsn,D=pt_osc,t=t",
qw(--lock-wait-timeout 5),
qw(--print --execute --alter ENGINE=MyISAM)) },
stderr => 1,
);
ok(
$exit,
"Convert table to MyISAM: non-zero exit"
) or diag($output);
like(
$output,
qr/is a cluster node and the table is being converted to MyISAM/,
"Convert table to MyISAM: error message"
);
# #############################################################################
# Require wsrep_OSU_method=TOI
# #############################################################################
$node1->do("SET GLOBAL wsrep_OSU_method='RSU'");
($output, $exit) = full_output(
sub { pt_online_schema_change::main(
"$node1_dsn,D=pt_osc,t=t",
qw(--lock-wait-timeout 5),
qw(--print --execute --alter ENGINE=MyISAM)) },
stderr => 1,
);
ok(
$exit,
"wsrep_OSU_method=RSU: non-zero exit"
) or diag($output);
print $output;
like(
$output,
qr/wsrep_OSU_method=TOI is required.+?currently set to RSU/,
"wsrep_OSU_method=RSU: error message"
);
$node1->do("SET GLOBAL wsrep_OSU_method='TOI'");
is_deeply(
$node1->selectrow_arrayref("SHOW VARIABLES LIKE 'wsrep_OSU_method'"),
[qw(wsrep_OSU_method TOI)],
"Restored wsrep_OSU_method=TOI"
) or BAIL_OUT("Failed to restore wsrep_OSU_method=TOI");
# #############################################################################
# Done.
# #############################################################################
$sb->wipe_clean($node1);
ok($sb->ok(), "Sandbox servers") or BAIL_OUT(__FILE__ . " broke the sandbox");
done_testing;

View File

@@ -51,11 +51,10 @@ $sb->load_file('master', "$sample/data-loss-bug-1068562.sql");
qw(--execute)) },
);
is(
ok(
$exit_status,
255,
"Die if --execute without --no-check-alter"
);
) or diag($output);
like(
$output,
@@ -95,7 +94,7 @@ is(
$exit_status,
0,
"sakila.city: Exit status 0",
);
) or diag($output);
my $mod = $master_dbh->selectall_arrayref(q{SELECT some_cities FROM sakila.city});
@@ -177,7 +176,7 @@ is(
like(
$output,
qr/first_name to first_name_mod, last_name to last_name_mod/ms,
qr/first_name to first_name_mod.+?last_name to last_name_mod/ms,
"--dry-run warns about renaming columns"
);

View File

@@ -8,23 +8,23 @@ CREATE TABLE t (
unique index (c(32))
) ENGINE=MyISAM;
INSERT INTO pt_osc.t VALUES
(null, 'a', now()),
(null, 'b', now()),
(null, 'c', now()),
(null, 'd', now()),
(null, 'e', now()),
(null, 'f', now()),
(null, 'g', now()),
(null, 'h', now()),
(null, 'i', now()),
(null, 'j', now()), -- 10
(null, 'k', now()),
(null, 'l', now()),
(null, 'm', now()),
(null, 'n', now()),
(null, 'o', now()),
(null, 'p', now()),
(null, 'q', now()),
(null, 'r', now()),
(null, 's', now()),
(null, 't', now()); -- 20
(1, 'a', now()),
(2, 'b', now()),
(3, 'c', now()),
(4, 'd', now()),
(5, 'e', now()),
(6, 'f', now()),
(7, 'g', now()),
(8, 'h', now()),
(9, 'i', now()),
(10, 'j', now()), -- 10
(11, 'k', now()),
(12, 'l', now()),
(13, 'm', now()),
(14, 'n', now()),
(15, 'o', now()),
(16, 'p', now()),
(17, 'q', now()),
(18, 'r', now()),
(19, 's', now()),
(20, 't', now()); -- 20

View File

@@ -0,0 +1,30 @@
DROP DATABASE IF EXISTS pt_osc;
CREATE DATABASE pt_osc;
USE pt_osc;
CREATE TABLE t (
id int auto_increment primary key,
c char(32),
d date,
unique index (c(32))
) ENGINE=InnoDB;
INSERT INTO pt_osc.t VALUES
(1, 'a', now()),
(2, 'b', now()),
(3, 'c', now()),
(4, 'd', now()),
(5, 'e', now()),
(6, 'f', now()),
(7, 'g', now()),
(8, 'h', now()),
(9, 'i', now()),
(10, 'j', now()), -- 10
(11, 'k', now()),
(12, 'l', now()),
(13, 'm', now()),
(14, 'n', now()),
(15, 'o', now()),
(16, 'p', now()),
(17, 'q', now()),
(18, 'r', now()),
(19, 's', now()),
(20, 't', now()); -- 20

View File

@@ -40,17 +40,17 @@ INSERT INTO pt_osc.country VALUES
(5, 'Spain', null);
INSERT INTO pt_osc.city VALUES
(null, 'Montréal', 1, null),
(null, 'New York', 2, null),
(null, 'Durango', 3, null),
(null, 'Paris', 4, null),
(null, 'Madrid', 5, null);
(1, 'Montréal', 1, null),
(2, 'New York', 2, null),
(3, 'Durango', 3, null),
(4, 'Paris', 4, null),
(5, 'Madrid', 5, null);
INSERT INTO pt_osc.address VALUES
(null, 'addy 1', 1, '10000', null),
(null, 'addy 2', 2, '20000', null),
(null, 'addy 3', 3, '30000', null),
(null, 'addy 4', 4, '40000', null),
(null, 'addy 5', 5, '50000', null);
(1, 'addy 1', 1, '10000', null),
(2, 'addy 2', 2, '20000', null),
(3, 'addy 3', 3, '30000', null),
(4, 'addy 4', 4, '40000', null),
(5, 'addy 5', 5, '50000', null);
SET foreign_key_checks=1;

View File

@@ -4,9 +4,12 @@ use strict;
use warnings FATAL => 'all';
use English qw(-no_match_vars);
use DBI;
use Time::HiRes qw(usleep time);
use Time::HiRes qw(sleep time);
use Test::More qw();
my ($host, $port, $db, $tbl, $pkcol, $stop_file, $pid_file, $sleep_time) = @ARGV;
use constant PTDEBUG => $ENV{PTDEBUG} || 0;
my ($host, $port, $db, $tbl, $pkcol, $stop_file, $pid_file, $sleep) = @ARGV;
die "I need a pid_file argument" unless $pid_file;
open my $fh, '>', $pid_file or die $OS_ERROR;
@@ -19,63 +22,109 @@ my $dbh = DBI->connect(
{RaiseError => 1, AutoCommit => 0, ShowErrorStatement => 1, PrintError => 0},
);
my $sleep = ($sleep_time || 0.001) * 1_000_000;
my $cnt = 0;
my @del;
my @upd;
my @ins;
my $del_sql = "DELETE FROM $db.$tbl WHERE $pkcol=?";
my $ins_sql = "INSERT INTO $db.$tbl ($pkcol, c) VALUES (?, ?)";
my $upd_sql = "UPDATE $db.$tbl SET c=? WHERE $pkcol=?";
my $start_xa = "START TRANSACTION /*!40108 WITH CONSISTENT SNAPSHOT */";
$dbh->do($start_xa);
my $del_sth = $dbh->prepare($del_sql);
my $ins_sth = $dbh->prepare($ins_sql);
my $upd_sth = $dbh->prepare($upd_sql);
for my $i ( 1..5_000 ) {
last if -f $stop_file;
$sleep ||= 0.01;
use constant TYPE_DELETE => 1;
use constant TYPE_UPDATE => 2;
my (@del, %del);
my (@upd, %upd);
my (@ins, %ins);
my $cnt = 0;
my $id = 0;
my $type = 0;
sub reset_counters {
@del = ();
@ins = ();
@upd = ();
$cnt = 0;
}
sub commit {
eval {
# We do roughly 25% DELETE, 25% UPDATE and 50% INSERT.
my $x = int(rand(5));
if ($x == 1) {
my $id = int(rand(500)) || 1;
$dbh->do("delete from $db.$tbl where $pkcol=$id");
# To challenge the tool, we *do* (or can) delete the same id twice.
# But to keep the numbers straight, we only record each deleted
# id once.
push @del, $id unless grep { $_ == $id } @del;
}
elsif ($x == 2) {
my $id = int(rand(500)) || 1;
if ( !grep { $_ == $id } @del ) {
my $t=time;
$dbh->do("update $db.$tbl set c='updated row $t' where $pkcol=$id");
push @upd, $id;
}
}
else {
my $id = 500 + $i;
my $t=time;
$dbh->do("insert ignore into $db.$tbl ($pkcol, c) values ($id, 'new row $t')");
push @ins, $id;
}
# COMMIT every N statements
if ( $cnt++ > 5 ) {
$dbh->do('COMMIT');
$cnt = 0;
usleep($sleep);
$dbh->do($start_xa);
}
$dbh->commit;
};
if ( $EVAL_ERROR ) {
warn $EVAL_ERROR;
last;
#Test::More::diag($EVAL_ERROR);
#Test::More::diag("lost deleted: @del");
#Test::More::diag("lost updated: @upd");
#Test::More::diag("lost inserted: @ins");
}
else {
map { $del{$_}++ } @del;
map { $ins{$_}++ } @ins;
map { $upd{$_}++ } @upd;
}
}
$dbh->do('COMMIT');
$dbh->do("START TRANSACTION");
for my $i ( 1..5_000 ) {
last if -f $stop_file;
eval {
my $type = int(rand(5)); # roughly 25% DELETE, 25% UPDATE, 50% INSERT
if ( $type == TYPE_DELETE ) {
$id = int(rand(500)) || 1;
$del_sth->execute($id);
push @del, $id if $del_sth->rows;
}
elsif ( $type == TYPE_UPDATE ) {
$id = int(rand(500)) || 1;
if ( !$del{$id} && ($id <= 500 || $ins{$id}) ) {
my $t = time;
$upd_sth->execute("updated row $t", $id);
push @upd, $id;
}
}
else { # INSERT
$id = 500 + $i;
my $t = time;
$ins_sth->execute($id, "new row $t");
push @ins, $id;
}
};
if ( $EVAL_ERROR ) {
#Test::More::diag($EVAL_ERROR);
#Test::More::diag("lost deleted: @del");
#Test::More::diag("lost updated: @upd");
#Test::More::diag("lost inserted: @ins");
reset_counters();
sleep $sleep;
$dbh->do("START TRANSACTION");
}
# COMMIT every N statements. With PXC this can fail.
if ( ++$cnt >= 5 ) {
commit();
reset_counters();
sleep $sleep;
# TODO: somehow this can fail if called very near when
# the old table is dropped.
eval { $dbh->do("START TRANSACTION"); };
if ( $EVAL_ERROR ) {
#Test::More::diag($EVAL_ERROR);
}
}
else {
sleep 0.001;
}
}
commit();
$dbh->disconnect();
print "deleted:" . join(',', @del) . "\n";
print "updated:" . join(',', @upd) . "\n";
print "inserted:" . join(',', @ins) . "\n";
print "deleted:" . join(',', sort keys %del) . "\n";
print "updated:" . join(',', sort keys %upd) . "\n";
print "inserted:" . join(',', sort keys %ins) . "\n";
exit 0;

View File

@@ -28,9 +28,6 @@ my $slave_dbh = $sb->get_dbh_for('slave1');
if ( !$master_dbh ) {
plan skip_all => 'Cannot connect to sandbox master';
}
else {
plan tests => 6;
}
my $q = new Quoter();
my $tp = new TableParser(Quoter => $q);
@@ -66,7 +63,7 @@ like( $output,
"Original table must exist"
);
$sb->load_file('master', "$sample/basic_no_fks.sql");
$sb->load_file('master', "$sample/basic_no_fks_innodb.sql");
$master_dbh->do("USE pt_osc");
$slave_dbh->do("USE pt_osc");
@@ -100,7 +97,7 @@ like( $output,
# Checks for the new table.
# #############################################################################
$sb->load_file('master', "$sample/basic_no_fks.sql");
$sb->load_file('master', "$sample/basic_no_fks_innodb.sql");
$master_dbh->do("USE pt_osc");
$slave_dbh->do("USE pt_osc");
@@ -126,4 +123,4 @@ like(
# #############################################################################
$sb->wipe_clean($master_dbh);
ok($sb->ok(), "Sandbox servers") or BAIL_OUT(__FILE__ . " broke the sandbox");
exit;
done_testing;

View File

@@ -10,7 +10,7 @@ ERRORS DIFFS ROWS SKIPPED TABLE
0 0 0 0 mysql.host
0 0 0 0 mysql.ndb_binlog_index
0 0 0 0 mysql.plugin
0 0 6 0 mysql.proc
0 0 0 0 mysql.proc
0 0 0 0 mysql.procs_priv
0 0 0 0 mysql.servers
0 0 0 0 mysql.tables_priv

View File

@@ -10,7 +10,7 @@ ERRORS DIFFS ROWS CHUNKS SKIPPED TABLE
0 0 0 1 0 mysql.host
0 0 0 1 0 mysql.ndb_binlog_index
0 0 0 1 0 mysql.plugin
0 0 6 1 0 mysql.proc
0 0 0 1 0 mysql.proc
0 0 0 1 0 mysql.procs_priv
0 0 0 1 0 mysql.servers
0 0 0 1 0 mysql.tables_priv