diff --git a/bin/pt-slave-delay b/bin/pt-slave-delay index 52193b03..f73bece5 100755 --- a/bin/pt-slave-delay +++ b/bin/pt-slave-delay @@ -26,6 +26,7 @@ BEGIN { Retry HTTP::Micro VersionCheck + VersionParser )); } @@ -4293,6 +4294,198 @@ sub _d { # End VersionCheck package # ########################################################################### +# ########################################################################### +# VersionParser package +# This package is a copy without comments from the original. The original +# with comments and its test file can be found in the GitHub repository at, +# lib/VersionParser.pm +# t/lib/VersionParser.t +# See https://github.com/percona/percona-toolkit for more information. +# ########################################################################### +{ +package VersionParser; + +use Lmo; +use Scalar::Util qw(blessed); +use English qw(-no_match_vars); +use constant PTDEBUG => $ENV{PTDEBUG} || 0; + +use overload ( + '""' => "version", + '<=>' => "cmp", + 'cmp' => "cmp", + fallback => 1, +); + +use Carp (); + +has major => ( + is => 'ro', + isa => 'Int', + required => 1, +); + +has [qw( minor revision )] => ( + is => 'ro', + isa => 'Num', +); + +has flavor => ( + is => 'ro', + isa => 'Str', + default => sub { 'Unknown' }, +); + +has innodb_version => ( + is => 'ro', + isa => 'Str', + default => sub { 'NO' }, +); + +sub series { + my $self = shift; + return $self->_join_version($self->major, $self->minor); +} + +sub version { + my $self = shift; + return $self->_join_version($self->major, $self->minor, $self->revision); +} + +sub is_in { + my ($self, $target) = @_; + + return $self eq $target; +} + +sub _join_version { + my ($self, @parts) = @_; + + return join ".", map { my $c = $_; $c =~ s/^0\./0/; $c } grep defined, @parts; +} +sub _split_version { + my ($self, $str) = @_; + my @version_parts = map { s/^0(?=\d)/0./; $_ } $str =~ m/(\d+)/g; + return @version_parts[0..2]; +} + +sub normalized_version { + my ( $self ) = @_; + my $result = sprintf('%d%02d%02d', map { $_ || 0 } $self->major, + $self->minor, + $self->revision); + PTDEBUG && _d($self->version, 'normalizes to', $result); + return $result; +} + +sub comment { + my ( $self, $cmd ) = @_; + my $v = $self->normalized_version(); + + return "/*!$v $cmd */" +} + +my @methods = qw(major minor revision); +sub cmp { + my ($left, $right) = @_; + my $right_obj = (blessed($right) && $right->isa(ref($left))) + ? $right + : ref($left)->new($right); + + my $retval = 0; + for my $m ( @methods ) { + last unless defined($left->$m) && defined($right_obj->$m); + $retval = $left->$m <=> $right_obj->$m; + last if $retval; + } + return $retval; +} + +sub BUILDARGS { + my $self = shift; + + if ( @_ == 1 ) { + my %args; + if ( blessed($_[0]) && $_[0]->can("selectrow_hashref") ) { + PTDEBUG && _d("VersionParser got a dbh, trying to get the version"); + my $dbh = $_[0]; + local $dbh->{FetchHashKeyName} = 'NAME_lc'; + my $query = eval { + $dbh->selectall_arrayref(q/SHOW VARIABLES LIKE 'version%'/, { Slice => {} }) + }; + if ( $query ) { + $query = { map { $_->{variable_name} => $_->{value} } @$query }; + @args{@methods} = $self->_split_version($query->{version}); + $args{flavor} = delete $query->{version_comment} + if $query->{version_comment}; + } + elsif ( eval { ($query) = $dbh->selectrow_array(q/SELECT VERSION()/) } ) { + @args{@methods} = $self->_split_version($query); + } + else { + Carp::confess("Couldn't get the version from the dbh while " + . "creating a VersionParser object: $@"); + } + $args{innodb_version} = eval { $self->_innodb_version($dbh) }; + } + elsif ( !ref($_[0]) ) { + @args{@methods} = $self->_split_version($_[0]); + } + + for my $method (@methods) { + delete $args{$method} unless defined $args{$method}; + } + @_ = %args if %args; + } + + return $self->SUPER::BUILDARGS(@_); +} + +sub _innodb_version { + my ( $self, $dbh ) = @_; + return unless $dbh; + my $innodb_version = "NO"; + + my ($innodb) = + grep { $_->{engine} =~ m/InnoDB/i } + map { + my %hash; + @hash{ map { lc $_ } keys %$_ } = values %$_; + \%hash; + } + @{ $dbh->selectall_arrayref("SHOW ENGINES", {Slice=>{}}) }; + if ( $innodb ) { + PTDEBUG && _d("InnoDB support:", $innodb->{support}); + if ( $innodb->{support} =~ m/YES|DEFAULT/i ) { + my $vars = $dbh->selectrow_hashref( + "SHOW VARIABLES LIKE 'innodb_version'"); + $innodb_version = !$vars ? "BUILTIN" + : ($vars->{Value} || $vars->{value}); + } + else { + $innodb_version = $innodb->{support}; # probably DISABLED or NO + } + } + + PTDEBUG && _d("InnoDB version:", $innodb_version); + return $innodb_version; +} + +sub _d { + my ($package, undef, $line) = caller 0; + @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; } + map { defined $_ ? $_ : 'undef' } + @_; + print STDERR "# $package:$line $PID ", join(' ', @_), "\n"; +} + +no Lmo; +1; +} +# ########################################################################### +# End VersionParser package +# ########################################################################### + # ########################################################################### # This is a combination of modules and programs in one -- a runnable module. # http://www.perl.com/pub/a/2006/07/13/lightning-articles.html?page=last @@ -4319,6 +4512,8 @@ my $oktorun = 1; sub main { local @ARGV = @_; # set global ARGV for this package + warn "This tool is deprecated and will be removed in future releases.\nUse built-in Delayed Replication instead: https://dev.mysql.com/doc/refman/8.4/en/replication-delayed.html\n\n"; + $o = new OptionParser(); $o->get_specs(); $o->get_opts(); @@ -4355,6 +4550,13 @@ sub main { # Connect before daemonizing, in case --ask-pass is needed. my $slave_dbh = get_dbh($dp, $slave_dsn); + + # Check version, refuse working with 8.4 + my $version = VersionParser->new($slave_dbh); + if ( $version ge '8.1' && $version->flavor() !~ m/maria/ ) { + die "This tool does not work with MySQL 8.1 and newer.\n"; + } + my $status = $slave_dbh->selectrow_hashref("SHOW SLAVE STATUS"); if ( !$status || ! %$status ) { die "No SLAVE STATUS found"; @@ -4654,6 +4856,10 @@ if ( !caller ) { exit main(@ARGV); } pt-slave-delay - Make a MySQL slave server lag behind its master. +This tool is deprecated and will be removed in future releases. It does not support MySQL 8.1 or newer. + +Use built-in Delayed Replication instead: https://dev.mysql.com/doc/refman/8.4/en/replication-delayed.html + =head1 SYNOPSIS Usage: pt-slave-delay [OPTIONS] SLAVE_DSN [MASTER_DSN] diff --git a/lib/PerconaTest.pm b/lib/PerconaTest.pm index 72006662..1bb230b3 100644 --- a/lib/PerconaTest.pm +++ b/lib/PerconaTest.pm @@ -88,7 +88,7 @@ our $source_status = 'binary log'; our $source_reset = 'binary logs and gtids'; our $source_change = 'replication source'; our $replica_name = 'replica'; -if ( $sandbox_version < '8.1' || ( $ENV{FORK} || "" eq 'mariadb' ) ) { +if ( $sandbox_version lt '8.1' || ( $ENV{FORK} || "" eq 'mariadb' ) ) { $source_name = 'master'; $source_status = 'master'; $source_reset = 'master'; diff --git a/t/pt-slave-delay/auto_restart.t b/t/pt-slave-delay/auto_restart.t index e255e8b5..27d366d6 100644 --- a/t/pt-slave-delay/auto_restart.t +++ b/t/pt-slave-delay/auto_restart.t @@ -22,8 +22,8 @@ my $sb = Sandbox->new(basedir => '/tmp', DSNParser => $dp); my $source_dbh = $sb->get_dbh_for('source'); my $dbh = $sb->get_dbh_for('replica1'); -if ($sandbox_version ge '5.7') { - plan skip_all => 'Use SQL_DELAY'; +if ($sandbox_version ge '8.1') { + plan skip_all => 'Tool is not supported. Use SQL_DELAY'; } if ( !$dbh ) { @@ -72,7 +72,7 @@ else { # Reap the child. waitpid ($pid, 0); -$sb->wait_for_slaves; +$sb->wait_for_replicas; # Do it all over again, but this time KILL instead of restart. $pid = fork(); @@ -92,7 +92,7 @@ if ( $pid ) { else { # child. Note that we'll kill the parent's 'mysql' connection sleep 1; - my $c_dbh = $sb->get_dbh_for('slave1'); + my $c_dbh = $sb->get_dbh_for('replica1'); my @cxn = @{$c_dbh->selectall_arrayref('show processlist', {Slice => {}})}; foreach my $c ( @cxn ) { # The parent's connection: diff --git a/t/pt-slave-delay/basics.t b/t/pt-slave-delay/basics.t index a968ff6d..ef0fabdf 100644 --- a/t/pt-slave-delay/basics.t +++ b/t/pt-slave-delay/basics.t @@ -21,8 +21,8 @@ my $master_dbh = $sb->get_dbh_for('source'); my $slave1_dbh = $sb->get_dbh_for('replica1'); my $slave2_dbh = $sb->get_dbh_for('replica2'); -if ($sandbox_version ge '5.7') { - plan skip_all => 'Use SQL_DELAY'; +if ($sandbox_version ge '8.1') { + plan skip_all => 'Tool is not supported. Use SQL_DELAY'; } if ( !$master_dbh ) { @@ -35,7 +35,7 @@ elsif ( !$slave2_dbh ) { plan skip_all => 'Cannot connect to sandbox slave2'; } else { - plan tests => 6; + plan tests => 7; } my $output; @@ -43,11 +43,14 @@ my $cnf = '/tmp/12346/my.sandbox.cnf'; my $cmd = "$trunk/bin/pt-slave-delay -F $cnf h=127.1"; # 1 -$output = `$cmd --help`; +$output = `$cmd --help 2>&1`; like($output, qr/Prompt for a password/, 'It compiles'); +# Check deprecation warning +like($output, qr/This tool is deprecated and will be removed in future releases/, 'Deprecation warning printed') or diag($output); + # ############################################################################# -# 2 Issue 149: h is required even with S, for slavehost argument +# Issue 149: h is required even with S, for slavehost argument # ############################################################################# $output = `$trunk/bin/pt-slave-delay --run-time 1s --delay 1s --interval 1s S=/tmp/12346/mysql_sandbox12346.sock 2>&1`; unlike($output, qr/Missing DSN part 'h'/, 'Does not require h DSN part'); @@ -58,34 +61,40 @@ unlike($output, qr/Missing DSN part 'h'/, 'Does not require h DSN part'); # easily when you connect to a SLAVE-HOST twice by accident.) To reproduce, # just disable log-bin and log-slave-updates on the slave. # #####1####################################################################### -diag(`cp /tmp/12346/my.sandbox.cnf /tmp/12346/my.sandbox.cnf-original`); -diag(`sed -i.bak -e '/log-bin/d' -e '/log_slave_updates/d' /tmp/12346/my.sandbox.cnf`); -diag(`/tmp/12346/stop >/dev/null`); -diag(`/tmp/12346/start >/dev/null`); -diag("Sleeping 3 seconds ..."); -sleep(3); -$slave2_dbh->do('STOP SLAVE'); -$slave2_dbh->do('RESET SLAVE'); -$slave2_dbh->do('START SLAVE'); +SKIP: { + if ($sandbox_version ge '8.0') { + skip 'Binary log is ON by default in 8.0+'; + } -# 3 -$output = `$trunk/bin/pt-slave-delay --delay 1s h=127.1,P=12346,u=msandbox,p=msandbox h=127.1 2>&1`; -like( - $output, - qr/Binary logging is disabled/, - 'Detects master that is not a master' -); + diag(`cp /tmp/12346/my.sandbox.cnf /tmp/12346/my.sandbox.cnf-original`); + diag(`sed -i.bak -e '/log-bin/d' -e '/log_slave_updates/d' /tmp/12346/my.sandbox.cnf`); + diag(`/tmp/12346/stop >/dev/null`); + diag(`/tmp/12346/start >/dev/null`); + diag("Sleeping 3 seconds ..."); + sleep(3); + $slave2_dbh->do('STOP SLAVE'); + $slave2_dbh->do('RESET SLAVE'); + $slave2_dbh->do('START SLAVE'); -diag(`/tmp/12346/stop >/dev/null`); -diag(`mv /tmp/12346/my.sandbox.cnf-original /tmp/12346/my.sandbox.cnf`); -diag(`/tmp/12346/start >/dev/null`); -diag(`/tmp/12346/use -e "set global read_only=1"`); + # 3 + $output = `$trunk/bin/pt-slave-delay --delay 1s h=127.1,P=12346,u=msandbox,p=msandbox h=127.1 2>&1`; + like( + $output, + qr/Binary logging is disabled/, + 'Detects master that is not a master' + ); -diag("Sleeping 3 seconds ..."); -sleep(3); -$slave2_dbh->do('STOP SLAVE'); -$slave2_dbh->do('RESET SLAVE'); -$slave2_dbh->do('START SLAVE'); + diag(`/tmp/12346/stop >/dev/null`); + diag(`mv /tmp/12346/my.sandbox.cnf-original /tmp/12346/my.sandbox.cnf`); + diag(`/tmp/12346/start >/dev/null`); + diag(`/tmp/12346/use -e "set global read_only=1"`); + + diag("Sleeping 3 seconds ..."); + sleep(3); + $slave2_dbh->do('STOP SLAVE'); + $slave2_dbh->do('RESET SLAVE'); + $slave2_dbh->do('START SLAVE'); +} # ############################################################################# # Check --use-master diff --git a/t/pt-slave-delay/deprecation.t b/t/pt-slave-delay/deprecation.t new file mode 100644 index 00000000..a1ff6ddc --- /dev/null +++ b/t/pt-slave-delay/deprecation.t @@ -0,0 +1,63 @@ +#!/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 PerconaTest; +use Sandbox; +require "$trunk/bin/pt-slave-delay"; + +my $dp = new DSNParser(opts=>$dsn_opts); +my $sb = new Sandbox(basedir => '/tmp', DSNParser => $dp); +my $master_dbh = $sb->get_dbh_for('source'); +my $slave1_dbh = $sb->get_dbh_for('replica1'); +my $slave2_dbh = $sb->get_dbh_for('replica2'); + +if ($sandbox_version lt '8.1') { + plan skip_all => 'Tool is supported.'; +} + +if ( !$master_dbh ) { + plan skip_all => 'Cannot connect to sandbox master'; +} +elsif ( !$slave1_dbh ) { + plan skip_all => 'Cannot connect to sandbox slave1'; +} +elsif ( !$slave2_dbh ) { + plan skip_all => 'Cannot connect to sandbox slave2'; +} +else { + plan tests => 3; +} + +my $output; +my $cnf = '/tmp/12346/my.sandbox.cnf'; +my $cmd = "$trunk/bin/pt-slave-delay -F $cnf h=127.1"; + +$output = `$cmd 2>&1`; +like( + $output, + qr/This tool does not work with MySQL 8.1 and newer/, + 'The tool does not work with 8.1 or newer' +); + +isnt( + $? >> 8, + 0, + "Tool died" +); + +# ############################################################################# +# Done. +# ############################################################################# +$sb->wipe_clean($master_dbh); +ok($sb->ok(), "Sandbox servers") or BAIL_OUT(__FILE__ . " broke the sandbox"); +exit; diff --git a/t/pt-slave-delay/issue_1169.t b/t/pt-slave-delay/issue_1169.t index d4582746..c5647b44 100644 --- a/t/pt-slave-delay/issue_1169.t +++ b/t/pt-slave-delay/issue_1169.t @@ -20,8 +20,8 @@ my $dp = DSNParser->new(opts => $dsn_opts); my $sb = Sandbox->new(basedir => '/tmp', DSNParser => $dp); my $dbh = $sb->get_dbh_for('replica1'); -if ($sandbox_version ge '5.7') { - plan skip_all => 'Use SQL_DELAY'; +if ($sandbox_version ge '8.1') { + plan skip_all => 'Tool is not supported. Use SQL_DELAY'; } if ( !$dbh ) { diff --git a/t/pt-slave-delay/standard_options.t b/t/pt-slave-delay/standard_options.t index 198674fd..ff47c644 100644 --- a/t/pt-slave-delay/standard_options.t +++ b/t/pt-slave-delay/standard_options.t @@ -20,8 +20,8 @@ my $sb = new Sandbox(basedir => '/tmp', DSNParser => $dp); my $master_dbh = $sb->get_dbh_for('source'); my $slave_dbh = $sb->get_dbh_for('replica1'); -if ($sandbox_version ge '5.7') { - plan skip_all => 'Use SQL_DELAY'; +if ($sandbox_version ge '8.1') { + plan skip_all => 'Tool is not supported. Use SQL_DELAY'; } if ( !$master_dbh ) {