mysqld-watcher.pl 3.17 KB
Newer Older
1
#!/usr/bin/perl
2 3 4
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
#
6 7
# This Source Code Form is "Incompatible With Secondary Licenses", as
# defined by the Mozilla Public License, v. 2.0.
8 9 10

# mysqld-watcher.pl - a script that watches the running instance of
# mysqld and kills off any long-running SELECTs against the shadow_db
11
#
12
use 5.10.1;
13
use strict;
14
use warnings;
15

16
# some configurables:
17 18 19

# length of time before a thread is eligible to be killed, in seconds
#
20
my $long_query_time = 180;
21 22 23
#
# the From header for any messages sent out
#
24 25 26 27 28
my $mail_from = "root\@mothra.mozilla.org";
#
# the To header for any messages sent out
#
my $mail_to = "root";
29 30 31 32 33
#
# mail transfer agent.  this should probably really be converted to a Param().
#
my $mta_program = "/usr/lib/sendmail -t -ODeliveryMode=deferred";

34
# The array of long-running queries
35
#
36
my $long = {};
37

38 39 40 41 42
# Run mysqladmin processlist twice, the first time getting complete queries
# and the second time getting just abbreviated queries.  We want complete
# queries so we know which queries are taking too long to run, but complete
# queries with line breaks get missed by this script, so we get abbreviated
# queries as well to make sure we don't miss any.
43 44 45 46
foreach my $command (
  "/opt/mysql/bin/mysqladmin --verbose processlist",
  "/opt/mysql/bin/mysqladmin processlist"
  )
47
{
48 49
  close(STDIN);
  open(STDIN, "$command |");
50

51 52 53 54
  # iterate through the running threads
  #
  while (<STDIN>) {
    my @F = split(/\|/);
55

56 57 58 59 60
    # if this line is not the correct number of fields, or if the thread-id
    # field contains Id, skip this line.  both these cases indicate that this
    # line contains pretty-printing gunk and not thread info.
    #
    next if ($#F != 9 || $F[1] =~ /Id/);
61

62 63 64 65 66 67 68 69 70 71
    if (
      $F[4] =~ /shadow_bugs/           # shadowbugs database in use
      && $F[5] =~ /Query/              # this is actually a query
      && $F[6] > $long_query_time      # this query has taken too long
      && $F[8] =~ /(select|SELECT)/    # only kill a select
      && !defined($long->{$F[1]})
      )                                # haven't seen this one already
    {
      $long->{$F[1]} = \@F;
      system("/opt/mysql/bin/mysqladmin", "kill", $F[1]);
72
    }
73
  }
74 75 76 77
}

# send an email message
#
78
# should perhaps be moved to somewhere more global for use in bugzilla as a
79 80 81
# whole; should also do more error-checking
#
sub sendEmail($$$$) {
82 83 84 85 86 87 88 89 90 91 92
  ($#_ == 3) || die("sendEmail: invalid number of arguments");
  my ($from, $to, $subject, $body) = @_;

  open(MTA, "|$mta_program");
  print MTA "From: $from\n";
  print MTA "To: $to\n";
  print MTA "Subject: $subject\n";
  print MTA "\n";
  print MTA $body;
  print MTA "\n";
  close(MTA);
93 94

}
95 96 97

# if we found anything, kill the database thread and send mail about it
#
98
if (scalar(keys(%$long))) {
99 100 101 102 103
  my $message = "";
  foreach my $process_id (keys(%$long)) {
    my $qry = $long->{$process_id};
    $message .= join(" ", @$qry) . "\n\n";
  }
104

105 106 107 108
  # fire off an email telling the maintainer that we had to kill some threads
  #
  sendEmail($mail_from, $mail_to, "long running MySQL thread(s) killed",
    $message);
109
}