~percona-toolkit-dev/percona-toolkit/pt-deadlock-logger-introduces-a-noise-to-mysql-1258135

« back to all changes in this revision

Viewing changes to bin/pt-table-checksum

  • Committer: Daniel Nichter
  • Date: 2013-10-30 17:11:18 UTC
  • mfrom: (589.2.46 release-2.2.5)
  • Revision ID: daniel@percona.com-20131030171118-7pv7xp5aeinr04go
Merge release-2.2.5.

Show diffs side-by-side

added added

removed removed

Lines of Context:
57
57
{
58
58
package Percona::Toolkit;
59
59
 
60
 
our $VERSION = '2.2.4';
 
60
our $VERSION = '2.2.5';
61
61
 
62
62
use strict;
63
63
use warnings FATAL => 'all';
6930
6930
use strict;
6931
6931
use warnings FATAL => 'all';
6932
6932
use English qw(-no_match_vars);
 
6933
 
6933
6934
use constant PTDEBUG => $ENV{PTDEBUG} || 0;
6934
6935
 
6935
6936
use POSIX qw(setsid);
 
6937
use Fcntl qw(:DEFAULT);
6936
6938
 
6937
6939
sub new {
6938
 
   my ( $class, %args ) = @_;
6939
 
   foreach my $arg ( qw(o) ) {
6940
 
      die "I need a $arg argument" unless $args{$arg};
6941
 
   }
6942
 
   my $o = $args{o};
 
6940
   my ($class, %args) = @_;
6943
6941
   my $self = {
6944
 
      o        => $o,
6945
 
      log_file => $o->has('log') ? $o->get('log') : undef,
6946
 
      PID_file => $o->has('pid') ? $o->get('pid') : undef,
 
6942
      log_file       => $args{log_file},
 
6943
      pid_file       => $args{pid_file},
 
6944
      daemonize      => $args{daemonize},
 
6945
      force_log_file => $args{force_log_file},
 
6946
      parent_exit    => $args{parent_exit},
 
6947
      pid_file_owner => 0,
6947
6948
   };
6948
 
 
6949
 
   check_PID_file(undef, $self->{PID_file});
6950
 
 
6951
 
   PTDEBUG && _d('Daemonized child will log to', $self->{log_file});
6952
6949
   return bless $self, $class;
6953
6950
}
6954
6951
 
6955
 
sub daemonize {
6956
 
   my ( $self ) = @_;
6957
 
 
6958
 
   PTDEBUG && _d('About to fork and daemonize');
6959
 
   defined (my $pid = fork()) or die "Cannot fork: $OS_ERROR";
6960
 
   if ( $pid ) {
6961
 
      PTDEBUG && _d('Parent PID', $PID, 'exiting after forking child PID',$pid);
6962
 
      exit;
6963
 
   }
6964
 
 
6965
 
   PTDEBUG && _d('Daemonizing child PID', $PID);
6966
 
   $self->{PID_owner} = $PID;
6967
 
   $self->{child}     = 1;
6968
 
 
6969
 
   POSIX::setsid() or die "Cannot start a new session: $OS_ERROR";
6970
 
   chdir '/'       or die "Cannot chdir to /: $OS_ERROR";
6971
 
 
6972
 
   $self->_make_PID_file();
6973
 
 
6974
 
   $OUTPUT_AUTOFLUSH = 1;
6975
 
 
6976
 
   PTDEBUG && _d('Redirecting STDIN to /dev/null');
6977
 
   close STDIN;
6978
 
   open  STDIN, '/dev/null'
6979
 
      or die "Cannot reopen STDIN to /dev/null: $OS_ERROR";
6980
 
 
6981
 
   if ( $self->{log_file} ) {
6982
 
      PTDEBUG && _d('Redirecting STDOUT and STDERR to', $self->{log_file});
6983
 
      close STDOUT;
6984
 
      open  STDOUT, '>>', $self->{log_file}
6985
 
         or die "Cannot open log file $self->{log_file}: $OS_ERROR";
6986
 
 
6987
 
      close STDERR;
6988
 
      open  STDERR, ">&STDOUT"
6989
 
         or die "Cannot dupe STDERR to STDOUT: $OS_ERROR"; 
6990
 
   }
6991
 
   else {
6992
 
      if ( -t STDOUT ) {
6993
 
         PTDEBUG && _d('No log file and STDOUT is a terminal;',
6994
 
            'redirecting to /dev/null');
 
6952
sub run {
 
6953
   my ($self) = @_;
 
6954
 
 
6955
   my $daemonize      = $self->{daemonize};
 
6956
   my $pid_file       = $self->{pid_file};
 
6957
   my $log_file       = $self->{log_file};
 
6958
   my $force_log_file = $self->{force_log_file};
 
6959
   my $parent_exit    = $self->{parent_exit};
 
6960
 
 
6961
   PTDEBUG && _d('Starting daemon');
 
6962
 
 
6963
   if ( $pid_file ) {
 
6964
      eval {
 
6965
         $self->_make_pid_file(
 
6966
            pid      => $PID,  # parent's pid
 
6967
            pid_file => $pid_file,
 
6968
         );
 
6969
      };
 
6970
      die "$EVAL_ERROR\n" if $EVAL_ERROR;
 
6971
      if ( !$daemonize ) {
 
6972
         $self->{pid_file_owner} = $PID;  # parent's pid
 
6973
      }
 
6974
   }
 
6975
 
 
6976
   if ( $daemonize ) {
 
6977
      defined (my $child_pid = fork()) or die "Cannot fork: $OS_ERROR";
 
6978
      if ( $child_pid ) {
 
6979
         PTDEBUG && _d('Forked child', $child_pid);
 
6980
         $parent_exit->($child_pid) if $parent_exit;
 
6981
         exit 0;
 
6982
      }
 
6983
 
 
6984
      POSIX::setsid() or die "Cannot start a new session: $OS_ERROR";
 
6985
      chdir '/'       or die "Cannot chdir to /: $OS_ERROR";
 
6986
 
 
6987
      if ( $pid_file ) {
 
6988
         $self->_update_pid_file(
 
6989
            pid      => $PID,  # child's pid
 
6990
            pid_file => $pid_file,
 
6991
         );
 
6992
         $self->{pid_file_owner} = $PID;
 
6993
      }
 
6994
   }
 
6995
 
 
6996
   if ( $daemonize || $force_log_file ) {
 
6997
      PTDEBUG && _d('Redirecting STDIN to /dev/null');
 
6998
      close STDIN;
 
6999
      open  STDIN, '/dev/null'
 
7000
         or die "Cannot reopen STDIN to /dev/null: $OS_ERROR";
 
7001
      if ( $log_file ) {
 
7002
         PTDEBUG && _d('Redirecting STDOUT and STDERR to', $log_file);
6995
7003
         close STDOUT;
6996
 
         open  STDOUT, '>', '/dev/null'
6997
 
            or die "Cannot reopen STDOUT to /dev/null: $OS_ERROR";
6998
 
      }
6999
 
      if ( -t STDERR ) {
7000
 
         PTDEBUG && _d('No log file and STDERR is a terminal;',
7001
 
            'redirecting to /dev/null');
 
7004
         open  STDOUT, '>>', $log_file
 
7005
            or die "Cannot open log file $log_file: $OS_ERROR";
 
7006
 
7002
7007
         close STDERR;
7003
 
         open  STDERR, '>', '/dev/null'
7004
 
            or die "Cannot reopen STDERR to /dev/null: $OS_ERROR";
7005
 
      }
7006
 
   }
7007
 
 
7008
 
   return;
7009
 
}
7010
 
 
7011
 
sub check_PID_file {
7012
 
   my ( $self, $file ) = @_;
7013
 
   my $PID_file = $self ? $self->{PID_file} : $file;
7014
 
   PTDEBUG && _d('Checking PID file', $PID_file);
7015
 
   if ( $PID_file && -f $PID_file ) {
7016
 
      my $pid;
7017
 
      eval {
7018
 
         chomp($pid = (slurp_file($PID_file) || ''));
7019
 
      };
7020
 
      if ( $EVAL_ERROR ) {
7021
 
         die "The PID file $PID_file already exists but it cannot be read: "
7022
 
            . $EVAL_ERROR;
7023
 
      }
7024
 
      PTDEBUG && _d('PID file exists; it contains PID', $pid);
7025
 
      if ( $pid ) {
7026
 
         my $pid_is_alive = kill 0, $pid;
 
7008
         open  STDERR, ">&STDOUT"
 
7009
            or die "Cannot dupe STDERR to STDOUT: $OS_ERROR"; 
 
7010
      }
 
7011
      else {
 
7012
         if ( -t STDOUT ) {
 
7013
            PTDEBUG && _d('No log file and STDOUT is a terminal;',
 
7014
               'redirecting to /dev/null');
 
7015
            close STDOUT;
 
7016
            open  STDOUT, '>', '/dev/null'
 
7017
               or die "Cannot reopen STDOUT to /dev/null: $OS_ERROR";
 
7018
         }
 
7019
         if ( -t STDERR ) {
 
7020
            PTDEBUG && _d('No log file and STDERR is a terminal;',
 
7021
               'redirecting to /dev/null');
 
7022
            close STDERR;
 
7023
            open  STDERR, '>', '/dev/null'
 
7024
               or die "Cannot reopen STDERR to /dev/null: $OS_ERROR";
 
7025
         }
 
7026
      }
 
7027
 
 
7028
      $OUTPUT_AUTOFLUSH = 1;
 
7029
   }
 
7030
 
 
7031
   PTDEBUG && _d('Daemon running');
 
7032
   return;
 
7033
}
 
7034
 
 
7035
sub _make_pid_file {
 
7036
   my ($self, %args) = @_;
 
7037
   my @required_args = qw(pid pid_file);
 
7038
   foreach my $arg ( @required_args ) {
 
7039
      die "I need a $arg argument" unless $args{$arg};
 
7040
   };
 
7041
   my $pid      = $args{pid};
 
7042
   my $pid_file = $args{pid_file};
 
7043
 
 
7044
   eval {
 
7045
      sysopen(PID_FH, $pid_file, O_RDWR|O_CREAT|O_EXCL) or die $OS_ERROR;
 
7046
      print PID_FH $PID, "\n";
 
7047
      close PID_FH; 
 
7048
   };
 
7049
   if ( my $e = $EVAL_ERROR ) {
 
7050
      if ( $e =~ m/file exists/i ) {
 
7051
         my $old_pid = $self->_check_pid_file(
 
7052
            pid_file => $pid_file,
 
7053
            pid      => $PID,
 
7054
         );
 
7055
         if ( $old_pid ) {
 
7056
            warn "Overwriting PID file $pid_file because PID $old_pid "
 
7057
               . "is not running.\n";
 
7058
         }
 
7059
         $self->_update_pid_file(
 
7060
            pid      => $PID,
 
7061
            pid_file => $pid_file
 
7062
         );
 
7063
      }
 
7064
      else {
 
7065
         die "Error creating PID file $pid_file: $e\n";
 
7066
      }
 
7067
   }
 
7068
 
 
7069
   return;
 
7070
}
 
7071
 
 
7072
sub _check_pid_file {
 
7073
   my ($self, %args) = @_;
 
7074
   my @required_args = qw(pid_file pid);
 
7075
   foreach my $arg ( @required_args ) {
 
7076
      die "I need a $arg argument" unless $args{$arg};
 
7077
   };
 
7078
   my $pid_file = $args{pid_file};
 
7079
   my $pid      = $args{pid};
 
7080
 
 
7081
   PTDEBUG && _d('Checking if PID in', $pid_file, 'is running');
 
7082
 
 
7083
   if ( ! -f $pid_file ) {
 
7084
      PTDEBUG && _d('PID file', $pid_file, 'does not exist');
 
7085
      return;
 
7086
   }
 
7087
 
 
7088
   open my $fh, '<', $pid_file
 
7089
      or die "Error opening $pid_file: $OS_ERROR";
 
7090
   my $existing_pid = do { local $/; <$fh> };
 
7091
   chomp($existing_pid) if $existing_pid;
 
7092
   close $fh
 
7093
      or die "Error closing $pid_file: $OS_ERROR";
 
7094
 
 
7095
   if ( $existing_pid ) {
 
7096
      if ( $existing_pid == $pid ) {
 
7097
         warn "The current PID $pid already holds the PID file $pid_file\n";
 
7098
         return;
 
7099
      }
 
7100
      else {
 
7101
         PTDEBUG && _d('Checking if PID', $existing_pid, 'is running');
 
7102
         my $pid_is_alive = kill 0, $existing_pid;
7027
7103
         if ( $pid_is_alive ) {
7028
 
            die "The PID file $PID_file already exists "
7029
 
               . " and the PID that it contains, $pid, is running";
7030
 
         }
7031
 
         else {
7032
 
            warn "Overwriting PID file $PID_file because the PID that it "
7033
 
               . "contains, $pid, is not running";
7034
 
         }
7035
 
      }
7036
 
      else {
7037
 
         die "The PID file $PID_file already exists but it does not "
7038
 
            . "contain a PID";
 
7104
            die "PID file $pid_file exists and PID $existing_pid is running\n";
 
7105
         }
7039
7106
      }
7040
7107
   }
7041
7108
   else {
7042
 
      PTDEBUG && _d('No PID file');
7043
 
   }
7044
 
   return;
7045
 
}
7046
 
 
7047
 
sub make_PID_file {
7048
 
   my ( $self ) = @_;
7049
 
   if ( exists $self->{child} ) {
7050
 
      die "Do not call Daemon::make_PID_file() for daemonized scripts";
7051
 
   }
7052
 
   $self->_make_PID_file();
7053
 
   $self->{PID_owner} = $PID;
7054
 
   return;
7055
 
}
7056
 
 
7057
 
sub _make_PID_file {
7058
 
   my ( $self ) = @_;
7059
 
 
7060
 
   my $PID_file = $self->{PID_file};
7061
 
   if ( !$PID_file ) {
7062
 
      PTDEBUG && _d('No PID file to create');
7063
 
      return;
7064
 
   }
7065
 
 
7066
 
   $self->check_PID_file();
7067
 
 
7068
 
   open my $PID_FH, '>', $PID_file
7069
 
      or die "Cannot open PID file $PID_file: $OS_ERROR";
7070
 
   print $PID_FH $PID
7071
 
      or die "Cannot print to PID file $PID_file: $OS_ERROR";
7072
 
   close $PID_FH
7073
 
      or die "Cannot close PID file $PID_file: $OS_ERROR";
7074
 
 
7075
 
   PTDEBUG && _d('Created PID file:', $self->{PID_file});
7076
 
   return;
7077
 
}
7078
 
 
7079
 
sub _remove_PID_file {
7080
 
   my ( $self ) = @_;
7081
 
   if ( $self->{PID_file} && -f $self->{PID_file} ) {
7082
 
      unlink $self->{PID_file}
7083
 
         or warn "Cannot remove PID file $self->{PID_file}: $OS_ERROR";
 
7109
      die "PID file $pid_file exists but it is empty.  Remove the file "
 
7110
         . "if the process is no longer running.\n";
 
7111
   }
 
7112
 
 
7113
   return $existing_pid;
 
7114
}
 
7115
 
 
7116
sub _update_pid_file {
 
7117
   my ($self, %args) = @_;
 
7118
   my @required_args = qw(pid pid_file);
 
7119
   foreach my $arg ( @required_args ) {
 
7120
      die "I need a $arg argument" unless $args{$arg};
 
7121
   };
 
7122
   my $pid      = $args{pid};
 
7123
   my $pid_file = $args{pid_file};
 
7124
 
 
7125
   open my $fh, '>', $pid_file
 
7126
      or die "Cannot open $pid_file: $OS_ERROR";
 
7127
   print { $fh } $pid, "\n"
 
7128
      or die "Cannot print to $pid_file: $OS_ERROR";
 
7129
   close $fh
 
7130
      or warn "Cannot close $pid_file: $OS_ERROR";
 
7131
 
 
7132
   return;
 
7133
}
 
7134
 
 
7135
sub remove_pid_file {
 
7136
   my ($self, $pid_file) = @_;
 
7137
   $pid_file ||= $self->{pid_file};
 
7138
   if ( $pid_file && -f $pid_file ) {
 
7139
      unlink $self->{pid_file}
 
7140
         or warn "Cannot remove PID file $pid_file: $OS_ERROR";
7084
7141
      PTDEBUG && _d('Removed PID file');
7085
7142
   }
7086
7143
   else {
7090
7147
}
7091
7148
 
7092
7149
sub DESTROY {
7093
 
   my ( $self ) = @_;
 
7150
   my ($self) = @_;
7094
7151
 
7095
 
   $self->_remove_PID_file() if ($self->{PID_owner} || 0) == $PID;
 
7152
   if ( $self->{pid_file_owner} == $PID ) {
 
7153
      $self->remove_pid_file();
 
7154
   }
7096
7155
 
7097
7156
   return;
7098
7157
}
7099
7158
 
7100
 
sub slurp_file {
7101
 
   my ($file) = @_;
7102
 
   return unless $file;
7103
 
   open my $fh, "<", $file or die "Cannot open $file: $OS_ERROR";
7104
 
   return do { local $/; <$fh> };
7105
 
}
7106
 
 
7107
7159
sub _d {
7108
7160
   my ($package, undef, $line) = caller 0;
7109
7161
   @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; }
8882
8934
 
8883
8935
my $oktorun      = 1;
8884
8936
my $print_header = 1;
 
8937
my $exit_status  = 0;
 
8938
 
 
8939
# "exit codes 1 - 2, 126 - 165, and 255 [1] have special meanings,
 
8940
# and should therefore be avoided for user-specified exit parameters"
 
8941
# http://www.tldp.org/LDP/abs/html/exitcodes.html
 
8942
our %PTC_EXIT_STATUS = (
 
8943
   # General flags:
 
8944
   ERROR           => 1,
 
8945
   ALREADY_RUNNING => 2,
 
8946
   CAUGHT_SIGNAL   => 4,
 
8947
   NO_SLAVES_FOUND => 8,
 
8948
   # Tool-specific flags:
 
8949
   TABLE_DIFF      => 16,
 
8950
   SKIP_CHUNK      => 32,
 
8951
   SKIP_TABLE      => 64,
 
8952
);
8885
8953
 
8886
8954
# The following two hashes are used in exec_nibble().
8887
8955
# They're static, so they do not need to be reset in main().
8912
8980
   local @ARGV   = @_;
8913
8981
   $oktorun      = 1;  
8914
8982
   $print_header = 1;
8915
 
 
8916
 
   my $exit_status = 0;
 
8983
   $exit_status  = 0;
8917
8984
 
8918
8985
   # ########################################################################
8919
8986
   # Get configuration information.
9003
9070
   # ########################################################################
9004
9071
   # If --pid, check it first since we'll die if it already exists.
9005
9072
   # ########################################################################
9006
 
   my $daemon;
9007
 
   if ( $o->get('pid') ) {
9008
 
      # We're not daemoninzing, it just handles PID stuff.  Keep $daemon
9009
 
      # in the the scope of main() because when it's destroyed it automatically
9010
 
      # removes the PID file.
9011
 
      $daemon = new Daemon(o=>$o);
9012
 
      $daemon->make_PID_file();
 
9073
   # We're not daemoninzing, it just handles PID stuff.  Keep $daemon
 
9074
   # in the the scope of main() because when it's destroyed it automatically
 
9075
   # removes the PID file.
 
9076
   my $pid_file = $o->get('pid');
 
9077
   my $daemon = new Daemon(
 
9078
      pid_file => $pid_file,
 
9079
   );
 
9080
   eval {
 
9081
      $daemon->run();
 
9082
   };
 
9083
   if ( my $e = $EVAL_ERROR ) {
 
9084
      # TODO quite hackish but it should work for now
 
9085
      if ( $e =~ m/PID file $pid_file exists/ ) {
 
9086
         $exit_status |= $PTC_EXIT_STATUS{ALREADY_RUNNING};
 
9087
         warn "$e\n";
 
9088
         return $exit_status;
 
9089
      }
 
9090
      else {
 
9091
         die $e;
 
9092
      }
9013
9093
   }
9014
9094
 
9015
9095
   # ########################################################################
9208
9288
      }
9209
9289
 
9210
9290
      my $trimmed_nodes = Cxn->remove_duplicate_cxns(
9211
 
                                   cxns => [ $master_cxn, @$slaves ],
9212
 
                                );
 
9291
         cxns => [ $master_cxn, @$slaves ],
 
9292
      );
9213
9293
      ($master_cxn, @$slaves) = @$trimmed_nodes;
9214
9294
 
 
9295
      # If no slaves or nodes were found, and a recursion method was given
 
9296
      # (implicitly or explicitly), and that method is not none, then warn
 
9297
      # and continue but exit non-zero because there won't be any diffs but
 
9298
      # this could be a false-positive from having no slaves/nodes to check.
 
9299
      # https://bugs.launchpad.net/percona-toolkit/+bug/1210537
9215
9300
      PTDEBUG && _d(scalar @$slaves, 'slaves found');
9216
 
      if ( !@$slaves && $o->get('recursion-method')->[0] ne 'none' ) {
9217
 
         $exit_status |= 1;
 
9301
      if ( !@$slaves
 
9302
           && (($o->get('recursion-method')->[0] || '') ne 'none'
 
9303
               || $autodiscover_cluster))
 
9304
      {
 
9305
         $exit_status |= $PTC_EXIT_STATUS{NO_SLAVES_FOUND};
9218
9306
         if ( $o->get('quiet') < 2 ) {
9219
 
            warn "Diffs cannot be detected because no slaves were found.  "
 
9307
            my $type = $autodiscover_cluster ? 'cluster nodes' : 'slaves';
 
9308
            warn "Diffs cannot be detected because no $type were found.  "
9220
9309
               . "Please read the --recursion-method documentation for "
9221
9310
               . "information.\n";
9222
9311
         }
9385
9474
               $slave->name());
9386
9475
            $diffs = filter_tables_replicate_check_only($diffs, $o);
9387
9476
            if ( @$diffs ) {
9388
 
               $exit_status |= 1;
 
9477
               $exit_status |= $PTC_EXIT_STATUS{TABLE_DIFF};
9389
9478
               if ( $o->get('quiet') < 2 ) { 
9390
9479
                  print_checksum_diffs(
9391
9480
                     cxn   => $slave,
9689
9778
                  . "This can break replication.  If you understand the risks, "
9690
9779
                  . "specify --no-check-slave-tables to disable this check.\n";
9691
9780
               warn ts($msg);
9692
 
               $tbl->{checksum_results}->{errors}++;
 
9781
               $exit_status |= $PTC_EXIT_STATUS{SKIP_TABLE};
9693
9782
               $oktonibble = 0;
9694
9783
           }
9695
9784
         }
9750
9839
                           . " * chunk size limit=$chunk_size_limit).\n";
9751
9840
                     warn ts($msg);
9752
9841
                  }
9753
 
                  $tbl->{checksum_results}->{errors}++;
 
9842
                  $exit_status |= $PTC_EXIT_STATUS{SKIP_TABLE};
9754
9843
                  $oktonibble = 0;
9755
9844
               }
9756
9845
            }
9926
10015
         # Nibble time will be zero if the chunk was skipped.
9927
10016
         if ( !defined $tbl->{nibble_time} ) {
9928
10017
            PTDEBUG && _d('Skipping chunk', $chunk);
 
10018
            $exit_status |= $PTC_EXIT_STATUS{SKIP_CHUNK};
9929
10019
            $tbl->{checksum_results}->{skipped}++;
9930
10020
            return;
9931
10021
         }
10066
10156
                     # statement in RowChecksum::find_replication_differences()
10067
10157
                     # for the full list of columns.
10068
10158
                     map { $diff_chunks{ $_->{chunk} }++ } @$diffs;
 
10159
                     $exit_status |= $PTC_EXIT_STATUS{TABLE_DIFF};
10069
10160
                  }
10070
10161
               };
10071
10162
               if ($EVAL_ERROR) {
10132
10223
         if ( $EVAL_ERROR ) {
10133
10224
            warn ts("Skipping table $tbl->{db}.$tbl->{tbl} because "
10134
10225
                  . "$EVAL_ERROR\n");
 
10226
            $exit_status |= $PTC_EXIT_STATUS{SKIP_TABLE};
10135
10227
            return;
10136
10228
         }
10137
10229
 
10221
10313
      }
10222
10314
 
10223
10315
      # Update the tool's exit status.
10224
 
      if ( $tbl->{checksum_results}->{errors}
10225
 
           || $tbl->{checksum_results}->{diffs} ) {
10226
 
         $exit_status |= 1;
 
10316
      if ( $tbl->{checksum_results}->{errors} ) {
 
10317
         $exit_status |= $PTC_EXIT_STATUS{ERROR};
10227
10318
      }
10228
10319
   }
10229
10320
 
10272
10363
            . ($expl->{key} ? "the $expl->{key}" : "no") . " index "
10273
10364
            . " instead of the " . $nibble_iter->nibble_index() . "index.\n");
10274
10365
      }
 
10366
      $exit_status |= $PTC_EXIT_STATUS{SKIP_CHUNK};
10275
10367
      return 0; # not safe
10276
10368
   }
10277
10369
 
10295
10387
               . "that there are " . ($expl->{rows} || 0)
10296
10388
               . " rows in the chunk.\n");
10297
10389
         }
 
10390
         $exit_status |= $PTC_EXIT_STATUS{SKIP_CHUNK};
10298
10391
         return 0; # not safe
10299
10392
      }
10300
10393
   }
10313
10406
            . $tbl->{key_len} . ".  See the --[no]check-plan documentation "
10314
10407
            . "for more information.\n");
10315
10408
      }
 
10409
      $exit_status |= $PTC_EXIT_STATUS{SKIP_CHUNK};
10316
10410
      return 0; # not safe
10317
10411
   }
10318
10412
 
10447
10541
         ) {
10448
10542
            # These errors/warnings are not fatal but only cause this
10449
10543
            # nibble to be skipped.
10450
 
            if ( $o->get('quiet') < 2 ) {
10451
 
               warn "$error\n";
 
10544
            my $err = $error =~ /Lock wait timeout exceeded/
 
10545
                    ? 'lock_wait_timeout'
 
10546
                    : 'query_interrupted';
 
10547
            if ( !$tbl->{warned}->{$err}++ && $o->get('quiet') < 2 ) {
 
10548
               my $msg = "Skipping chunk " . ($nibble_iter->nibble_number() || '?')
 
10549
                       . " of $tbl->{db}.$tbl->{tbl} because $error.\n";
 
10550
               warn ts($msg);
10452
10551
            }
 
10552
            $exit_status |= $PTC_EXIT_STATUS{SKIP_CHUNK};
10453
10553
            return;  # skip this nibble
10454
10554
         }
10455
10555
 
11068
11168
# Catches signals so we can exit gracefully.
11069
11169
sub sig_int {
11070
11170
   my ( $signal ) = @_;
 
11171
   $exit_status |= $PTC_EXIT_STATUS{CAUGHT_SIGNAL};
11071
11172
   if ( $oktorun ) {
11072
 
      print STDERR "# Caught SIG$signal.\n";
 
11173
      warn "# Caught SIG$signal.\n";
11073
11174
      $oktorun = 0;
11074
11175
   }
11075
11176
   else {
11076
 
      print STDERR "# Exiting on SIG$signal.\n";
11077
 
      exit 1;
 
11177
      warn "# Exiting on SIG$signal.\n";
 
11178
      exit $exit_status;
11078
11179
   }
11079
11180
}
11080
11181
 
11121
11222
pt-table-checksum performs an online replication consistency check by executing
11122
11223
checksum queries on the master, which produces different results on replicas
11123
11224
that are inconsistent with the master.  The optional DSN specifies the master
11124
 
host.  The tool's exit status is nonzero if any differences are found, or if any
11125
 
warnings or errors occur.
 
11225
host.  The tool's L<"EXIT STATUS"> is non-zero if any differences are found,
 
11226
or if any warnings or errors occur.
11126
11227
 
11127
11228
The following command will connect to the replication master on localhost,
11128
11229
checksum every table, and report the results on every detected replica:
11433
11534
 
11434
11535
=item SKIPPED
11435
11536
 
11436
 
The number of chunks that were skipped due to errors or warnings, or because
11437
 
they were oversized.
 
11537
The number of chunks that were skipped due one or more of these problems:
 
11538
 
 
11539
   * MySQL not using the --chunk-index
 
11540
   * MySQL not using the full chunk index (--[no]check-plan)
 
11541
   * Chunk size is greater than --chunk-size * --chunk-size-limit
 
11542
   * Lock wait timeout exceeded (--retries)
 
11543
   * Checksum query killed (--retries)
 
11544
 
 
11545
As of pt-table-checksum 2.2.5, skipped chunks cause a non-zero L<"EXIT STATUS">.
11438
11546
 
11439
11547
=item TIME
11440
11548
 
11500
11608
 
11501
11609
=head1 EXIT STATUS
11502
11610
 
11503
 
A non-zero exit status indicates errors, warnings, or checksum differences.
 
11611
pt-table-checksum has three possible exit statuses: zero, 255, and any other
 
11612
value is a bitmask with flags for different problems.
 
11613
 
 
11614
A zero exit status indicates no errors, warnings, or checksum differences,
 
11615
or skipped chunks or tables.
 
11616
 
 
11617
A 255 exit status indicates a fatal error.  In other words: the tool died
 
11618
or crashed.  The error is printed to C<STDERR>.
 
11619
 
 
11620
If the exit status is not zero or 255, then its value functions as a bitmask
 
11621
with these flags:
 
11622
 
 
11623
   FLAG              BIT VALUE  MEANING
 
11624
   ================  =========  ==========================================
 
11625
   ERROR                     1  A non-fatal error occurred
 
11626
   ALREADY_RUNNING           2  --pid file exists and the PID is running
 
11627
   CAUGHT_SIGNAL             4  Caught SIGHUP, SIGINT, SIGPIPE, or SIGTERM
 
11628
   NO_SLAVES_FOUND           8  No replicas or cluster nodes were found
 
11629
   TABLE_DIFF               16  At least one diff was found
 
11630
   SKIP_CHUNK               32  At least one chunk was skipped
 
11631
   SKIP_TABLE               64  At least one table was skipped
 
11632
 
 
11633
If any flag is set, the exit status will be non-zero.  Use the bitwise C<AND>
 
11634
operation to check for a particular flag.  For example, if C<$exit_status & 16>
 
11635
is true, then at least one diff was found.
 
11636
 
 
11637
As of pt-table-checksum 2.2.5, skipped chunks cause a non-zero exit status.
 
11638
An exit status of zero or 32 is equivalent to a zero exit status with skipped
 
11639
chunks in previous versions of the tool.
11504
11640
 
11505
11641
=head1 OPTIONS
11506
11642
 
12445
12581
 
12446
12582
=head1 VERSION
12447
12583
 
12448
 
pt-table-checksum 2.2.4
 
12584
pt-table-checksum 2.2.5
12449
12585
 
12450
12586
=cut