mirror of
https://github.com/percona/percona-toolkit.git
synced 2025-09-18 17:58:55 +00:00
Implement MasterSlave::get_slaves() to get cxns from a DSN table. Add comments explaining use_repl_db().
This commit is contained in:
@@ -5265,14 +5265,47 @@ sub check_repl_table {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
# This sub should be called before any work is done with the
|
|
||||||
# --replicate table. It will USE the correct replicate db.
|
# Sub: use_repl_db
|
||||||
# If there's a tbl arg then its db will be used unless --replicate-database
|
# USE the correct database for the --replicate table.
|
||||||
# was specified. A tbl arg means we're checksumming that table,
|
# This sub must be called before any work is done with the --replicatte
|
||||||
# so we've been called from do_tbl_replicate(). Other callers
|
# table because replication filters can really complicate replicating the
|
||||||
# won't pass a tbl arg because they're just doing something to
|
# checksums. The originally issue is,
|
||||||
# the --replicate table.
|
# http://code.google.com/p/maatkit/issues/detail?id=982,
|
||||||
# See http://code.google.com/p/maatkit/issues/detail?id=982
|
# but here's what you need to know:
|
||||||
|
# - If there is no active DB, then if there's any do-db or ignore-db
|
||||||
|
# settings, the checksums will get filtered out of replication. So we
|
||||||
|
# have to have some DB be the current one.
|
||||||
|
# - Other places in the code may change the DB and we might not know it.
|
||||||
|
# Opportunity for bugs. The SHOW CREATE TABLE, for example. In the
|
||||||
|
# end, a bunch of USE statements isn't a big deal, it just looks noisy
|
||||||
|
# when you analyze the logs this tool creates. But it's better to just
|
||||||
|
# have them even if they're no-op.
|
||||||
|
# - We need to always let the user specify, because there are so many
|
||||||
|
# possibilities that the tool can't guess the right thing in all of
|
||||||
|
# them.
|
||||||
|
# - The right default behavior, which the user can override, is:
|
||||||
|
# * When running queries on the --replicate table itself, such as
|
||||||
|
# emptying it, USE that table's database.
|
||||||
|
# * When running checksum queries, USE the database of the table that's
|
||||||
|
# being checksummed.
|
||||||
|
# * When the user specifies --replicate-database, in contrast, always
|
||||||
|
# USE that database.
|
||||||
|
# - This behavior is the best compromise by default, because users who
|
||||||
|
# explicitly replicate some databases and filter out others will be
|
||||||
|
# very likely to run pt-table-checksum and limit its checksumming to
|
||||||
|
# only the databases that are replicated. I've seen people do this,
|
||||||
|
# including Peter. In this case, the tool will work okay even without
|
||||||
|
# an explicit --replicate-database setting.
|
||||||
|
#
|
||||||
|
# Required Arguments:
|
||||||
|
# dbh - dbh
|
||||||
|
# repl_table - Full quoted --replicate table name
|
||||||
|
# OptionParser - <OptionParser>
|
||||||
|
# Quoter - <Quoter>
|
||||||
|
#
|
||||||
|
# Returns:
|
||||||
|
# Nothing or dies on error
|
||||||
{
|
{
|
||||||
my $current_db;
|
my $current_db;
|
||||||
|
|
||||||
@@ -5286,8 +5319,11 @@ sub use_repl_db {
|
|||||||
|
|
||||||
my ($db, $tbl) = $q->split_unquote($repl_table);
|
my ($db, $tbl) = $q->split_unquote($repl_table);
|
||||||
if ( my $tbl = $args{tbl} ) {
|
if ( my $tbl = $args{tbl} ) {
|
||||||
# Caller is checksumming this table, USE its db unless
|
# If there's a tbl arg then its db will be used unless
|
||||||
# --replicate-database is in effect.
|
# --replicate-database was specified. A tbl arg means
|
||||||
|
# we're checksumming that table. Other callers won't
|
||||||
|
# pass a tbl arg when they're just doing something to
|
||||||
|
# the --replicate table.
|
||||||
$db = $o->get('replicate-database') ? $o->get('replicate-database')
|
$db = $o->get('replicate-database') ? $o->get('replicate-database')
|
||||||
: $tbl->{db};
|
: $tbl->{db};
|
||||||
}
|
}
|
||||||
|
@@ -36,6 +36,56 @@ sub new {
|
|||||||
return bless $self, $class;
|
return bless $self, $class;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub get_slaves {
|
||||||
|
my ($self, %args) = @_;
|
||||||
|
my @required_args = qw(OptionParser DSNParser Quoter);
|
||||||
|
foreach my $arg ( @required_args ) {
|
||||||
|
die "I need a $arg argument" unless $args{$arg};
|
||||||
|
}
|
||||||
|
my ($o, $dp) = @args{@required_args};
|
||||||
|
|
||||||
|
my $slaves = [];
|
||||||
|
my $method = $o->get('recursion-method');
|
||||||
|
MKDEBUG && _d('Slave recursion method:', $method);
|
||||||
|
if ( !$method || $method =~ m/proocesslist|hosts/i ) {
|
||||||
|
my @required_args = qw(dbh dsn);
|
||||||
|
foreach my $arg ( @required_args ) {
|
||||||
|
die "I need a $arg argument" unless $args{$arg};
|
||||||
|
}
|
||||||
|
my ($dbh, $dsn) = @args{@required_args};
|
||||||
|
$self->recurse_to_slaves(
|
||||||
|
{ dbh => $dbh,
|
||||||
|
dsn => $dsn,
|
||||||
|
dsn_parser => $dp,
|
||||||
|
recurse => $o->get('recurse'),
|
||||||
|
method => $o->get('recursion-method'),
|
||||||
|
callback => sub {
|
||||||
|
my ( $dsn, $dbh, $level, $parent ) = @_;
|
||||||
|
return unless $level;
|
||||||
|
MKDEBUG && _d('Found slave:', $dp->as_string($dsn));
|
||||||
|
$dbh->{InactiveDestroy} = 1; # Prevent destroying on fork.
|
||||||
|
$dbh->{FetchHashKeyName} = 'NAME_lc';
|
||||||
|
push @$slaves, { dsn=>$dsn, dbh=>$dbh };
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
elsif ( $method =~ m/^dsn=/i ) {
|
||||||
|
my ($dsn_table_dsn) = $method =~ m/^dsn=(.+)/i;
|
||||||
|
$slaves = $self->get_cxn_from_dsn_table(
|
||||||
|
%args,
|
||||||
|
dsn_table_dsn => $dsn_table_dsn,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
die "Invalid --recusion-method: $method. Valid values are: "
|
||||||
|
. "dsn=DSN, hosts, or processlist.\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
return $slaves;
|
||||||
|
}
|
||||||
|
|
||||||
# Sub: recurse_to_slaves
|
# Sub: recurse_to_slaves
|
||||||
# Descend to slaves by examining SHOW SLAVE HOSTS.
|
# Descend to slaves by examining SHOW SLAVE HOSTS.
|
||||||
# The callback gets the slave's DSN, dbh, parent, and the recursion level
|
# The callback gets the slave's DSN, dbh, parent, and the recursion level
|
||||||
@@ -797,6 +847,45 @@ sub reset_known_replication_threads {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub get_cxn_from_dsn_table {
|
||||||
|
my ($self, %args) = @_;
|
||||||
|
my @required_args = qw(dsn_table_dsn DSNParser Quoter);
|
||||||
|
foreach my $arg ( @required_args ) {
|
||||||
|
die "I need a $arg argument" unless $args{$arg};
|
||||||
|
}
|
||||||
|
my ($dsn_table_dsn, $dp, $q) = @args{@required_args};
|
||||||
|
MKDEBUG && _d('DSN table DSN:', $dsn_table_dsn);
|
||||||
|
|
||||||
|
my $dsn = $dp->parse($dsn_table_dsn);
|
||||||
|
my $dsn_table;
|
||||||
|
if ( $dsn->{D} && $dsn->{t} ) {
|
||||||
|
$dsn_table = $q->quote($dsn->{D}, $dsn->{t});
|
||||||
|
}
|
||||||
|
elsif ( $dsn->{t} && $dsn->{t} =~ m/\./ ) {
|
||||||
|
$dsn_table = $q->quote($q->split_unquote($dsn->{t}));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
die "DSN table DSN does not specify a database (D) "
|
||||||
|
. "or a database-qualified table (t)";
|
||||||
|
}
|
||||||
|
|
||||||
|
my @cxn;
|
||||||
|
my $dbh = $dp->get_dbh($dp->get_cxn_params($dsn));
|
||||||
|
my $sql = "SELECT dsn FROM $dsn_table ORDER BY id";
|
||||||
|
MKDEBUG && _d($sql);
|
||||||
|
my $dsns = $dbh->selectcol_arrayref($sql);
|
||||||
|
if ( $dsns ) {
|
||||||
|
foreach my $dsn ( @$dsns ) {
|
||||||
|
MKDEBUG && _d('DSN from DSN table:', $dsn);
|
||||||
|
my $dsn = $dp->parse($dsn);
|
||||||
|
my $dbh = $dp->get_dbh($dp->get_cxn_params($dsn));
|
||||||
|
push @cxn, {dsn=>$dsn, dbh=>$dbh};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$dbh->disconnect();
|
||||||
|
return \@cxn;
|
||||||
|
}
|
||||||
|
|
||||||
sub _d {
|
sub _d {
|
||||||
my ($package, undef, $line) = caller 0;
|
my ($package, undef, $line) = caller 0;
|
||||||
@_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; }
|
@_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; }
|
||||||
|
@@ -9,11 +9,13 @@ BEGIN {
|
|||||||
use strict;
|
use strict;
|
||||||
use warnings FATAL => 'all';
|
use warnings FATAL => 'all';
|
||||||
use English qw(-no_match_vars);
|
use English qw(-no_match_vars);
|
||||||
use Test::More tests => 42;
|
use Test::More tests => 46;
|
||||||
|
|
||||||
use MasterSlave;
|
use MasterSlave;
|
||||||
use DSNParser;
|
use DSNParser;
|
||||||
use VersionParser;
|
use VersionParser;
|
||||||
|
use OptionParser;
|
||||||
|
use Quoter;
|
||||||
use Sandbox;
|
use Sandbox;
|
||||||
use PerconaTest;
|
use PerconaTest;
|
||||||
|
|
||||||
@@ -22,10 +24,66 @@ my $ms = new MasterSlave(VersionParser => $vp);
|
|||||||
my $dp = new DSNParser(opts=>$dsn_opts);
|
my $dp = new DSNParser(opts=>$dsn_opts);
|
||||||
my $sb = new Sandbox(basedir => '/tmp', DSNParser => $dp);
|
my $sb = new Sandbox(basedir => '/tmp', DSNParser => $dp);
|
||||||
|
|
||||||
|
my $master_dbh = $sb->get_dbh_for('master');
|
||||||
|
my $slave_dbh = $sb->get_dbh_for('slave1');
|
||||||
|
|
||||||
|
# ############################################################################
|
||||||
|
# get_slaves() wrapper around recurse_to_slaves()
|
||||||
|
# ############################################################################
|
||||||
|
my $q = new Quoter;
|
||||||
|
my $o = new OptionParser(description => 'MasterSlave');
|
||||||
|
$o->get_specs("$trunk/bin/pt-table-checksum");
|
||||||
|
|
||||||
|
SKIP: {
|
||||||
|
skip "Cannot connect to sandbox master", 2 unless $master_dbh;
|
||||||
|
@ARGV = ();
|
||||||
|
$o->get_opts();
|
||||||
|
|
||||||
|
my $slaves = $ms->get_slaves(
|
||||||
|
dbh => $master_dbh,
|
||||||
|
dsn => {
|
||||||
|
h => '127.1',
|
||||||
|
P => '12345',
|
||||||
|
u => 'msandbox',
|
||||||
|
p => 'msandbox',
|
||||||
|
},
|
||||||
|
OptionParser => $o,
|
||||||
|
DSNParser => $dp,
|
||||||
|
Quoter => $q,
|
||||||
|
);
|
||||||
|
|
||||||
|
is_deeply(
|
||||||
|
$slaves->[0]->{dsn},
|
||||||
|
{ A => undef,
|
||||||
|
D => undef,
|
||||||
|
F => undef,
|
||||||
|
P => '12346',
|
||||||
|
S => undef,
|
||||||
|
h => '127.0.0.1',
|
||||||
|
p => 'msandbox',
|
||||||
|
t => undef,
|
||||||
|
u => 'msandbox',
|
||||||
|
server_id => 12346,
|
||||||
|
master_id => 12345,
|
||||||
|
source => 'hosts',
|
||||||
|
},
|
||||||
|
'get_slaves() from recurse_to_slaves()'
|
||||||
|
);
|
||||||
|
|
||||||
|
my ($id) = $slaves->[0]->{dbh}->selectrow_array('SELECT @@SERVER_ID');
|
||||||
|
is(
|
||||||
|
$id,
|
||||||
|
'12346',
|
||||||
|
'dbh created from get_slaves()'
|
||||||
|
);
|
||||||
|
|
||||||
|
$slaves->[0]->{dbh}->disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
# #############################################################################
|
# #############################################################################
|
||||||
# First we need to setup a special replication sandbox environment apart from
|
# First we need to setup a special replication sandbox environment apart from
|
||||||
# the usual persistent sandbox servers on ports 12345 and 12346.
|
# the usual persistent sandbox servers on ports 12345 and 12346.
|
||||||
# The tests in this script require a master with 3 slaves in a setup like:
|
# The tests in this script require a master with 3 slaves in a setup like:ggn
|
||||||
# 127.0.0.1:master
|
# 127.0.0.1:master
|
||||||
# +- 127.0.0.1:slave0
|
# +- 127.0.0.1:slave0
|
||||||
# | +- 127.0.0.1:slave1
|
# | +- 127.0.0.1:slave1
|
||||||
@@ -400,8 +458,6 @@ ok(
|
|||||||
# #############################################################################
|
# #############################################################################
|
||||||
# get_replication_filters()
|
# get_replication_filters()
|
||||||
# #############################################################################
|
# #############################################################################
|
||||||
my $master_dbh = $sb->get_dbh_for('master');
|
|
||||||
my $slave_dbh = $sb->get_dbh_for('slave1');
|
|
||||||
SKIP: {
|
SKIP: {
|
||||||
skip "Cannot connect to sandbox master", 3 unless $master_dbh;
|
skip "Cannot connect to sandbox master", 3 unless $master_dbh;
|
||||||
skip "Cannot connect to sandbox slave", 3 unless $slave_dbh;
|
skip "Cannot connect to sandbox slave", 3 unless $slave_dbh;
|
||||||
@@ -463,6 +519,44 @@ ok(
|
|||||||
"get_slave_lag() for slave"
|
"get_slave_lag() for slave"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
# ############################################################################
|
||||||
|
# get_slaves() and DSN table
|
||||||
|
# ############################################################################
|
||||||
|
$sb->load_file('master', "t/lib/samples/MasterSlave/dsn_table.sql");
|
||||||
|
|
||||||
|
@ARGV = ('--recursion-method', 'dsn=F=/tmp/12345/my.sandbox.cnf,D=dsn_t,t=dsns');
|
||||||
|
$o->get_opts();
|
||||||
|
|
||||||
|
my $slaves = $ms->get_slaves(
|
||||||
|
OptionParser => $o,
|
||||||
|
DSNParser => $dp,
|
||||||
|
Quoter => $q,
|
||||||
|
);
|
||||||
|
|
||||||
|
is_deeply(
|
||||||
|
$slaves->[0]->{dsn},
|
||||||
|
{ A => undef,
|
||||||
|
D => undef,
|
||||||
|
F => undef,
|
||||||
|
P => '12346',
|
||||||
|
S => undef,
|
||||||
|
h => '127.1',
|
||||||
|
p => 'msandbox',
|
||||||
|
t => undef,
|
||||||
|
u => 'msandbox'
|
||||||
|
},
|
||||||
|
'get_slaves() from DSN table'
|
||||||
|
);
|
||||||
|
|
||||||
|
my ($id) = $slaves->[0]->{dbh}->selectrow_array('SELECT @@SERVER_ID');
|
||||||
|
is(
|
||||||
|
$id,
|
||||||
|
'12346',
|
||||||
|
'dbh created from DSN table works'
|
||||||
|
);
|
||||||
|
|
||||||
|
$slaves->[0]->{dbh}->disconnect();
|
||||||
|
|
||||||
# #############################################################################
|
# #############################################################################
|
||||||
# Done.
|
# Done.
|
||||||
# #############################################################################
|
# #############################################################################
|
||||||
|
12
t/lib/samples/MasterSlave/dsn_table.sql
Normal file
12
t/lib/samples/MasterSlave/dsn_table.sql
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
DROP DATABASE IF EXISTS dsn_t;
|
||||||
|
CREATE DATABASE dsn_t;
|
||||||
|
USE dsn_t;
|
||||||
|
|
||||||
|
CREATE TABLE dsns (
|
||||||
|
id int auto_increment primary key,
|
||||||
|
parent_id int default null,
|
||||||
|
dsn varchar(255) not null
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO dsns VALUES
|
||||||
|
(null, null, 'h=127.1,P=12346,u=msandbox,p=msandbox');
|
Reference in New Issue
Block a user