🐚

query-digesterでパスワード無しでログインできない旨のエラーが出た

2023/11/18に公開

query-digesterとは

query-digesterはMySQLのスロークエリログのローテートをめちゃ良い感じにやってくれるツールです

事象

ISUCON12の練習に利用していたところ、以下のエラーが出ました。

isucon@ip-192-168-0-11:~$ sudo query-digester -duration 10
exec mysql to change long_query_time and slow_query_log_file
save slowlog to /tmp/slow_query_20231017235438.log
wait 10 seconds
ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: NO)
Error: mysql exited with code: 0 at /usr/local/bin/query-digester line 95.
isucon@ip-192-168-0-11:~$

調査

95行目で出力されているようなのでコードをみてみます。2023年11月18日時点でのコードは以下でした。
(コードブロックに行数を表示することができないため、95行目にコメントで★を、後述の書き換える行にコメントで■を追加しています)

#!/usr/bin/perl

use strict;
use warnings;
use IO::Handle;
use Getopt::Long;
use File::Spec;

sub find_path {
    my $pg = shift;
    my $path;
    for ( split /:/, $ENV{PATH} ) {
        if ( -x "$_/$pg" ) {
            $path = "$_/$pg";
            last;
        }
    }
    $path;
}

my $limit = 40;
my $duration = 10;
Getopt::Long::Configure ("no_ignore_case");
GetOptions(
    "duration=s" => \$duration,
    "clear-log" => \my $clear_log,
    "stdout" => \my $stdout,
    "limit=s" => \$limit,
    "h|help" => \my $help,
);
my @mysqlopt = @ARGV;
$|=1;

die "duration does not seems numeric" unless $duration =~ m!^\d+$!;
$duration += 0;

my $pt_query_digest = find_path('pt-query-digest')
    or die "could not find pt-query-digest";
my $mysql = find_path('mysql')
    or die "could not find mysql";
my $tmpdir = "/tmp";#File::Spec->tmpdir();

my $before = <<'EOF';
SET @cur_long_query_time = @@long_query_time;
SET @cur_slow_query_log_file = @@slow_query_log_file;
SET @cur_slow_query_log = @@slow_query_log;
SET GLOBAL slow_query_log_file = "<TMP_DIR>/slow_query_<DATE>.log";
SET GLOBAL long_query_time = 0;
SET GLOBAL slow_query_log = 1;
EOF

my $after = <<'EOF';
SET GLOBAL long_query_time = @cur_long_query_time;
SET GLOBAL slow_query_log_file = @cur_slow_query_log_file;
SET GLOBAL slow_query_log = @cur_slow_query_log;
EOF

$before =~ s!<TMP_DIR>!$tmpdir!;
my @lt = localtime();
my $date = sprintf('%04d%02d%02d%02d%02d%02d',$lt[5]+1900,$lt[4],$lt[3],$lt[2],$lt[1],$lt[0]);
$before =~ s!<DATE>!$date!;

print STDERR "exec mysql to change long_query_time and slow_query_log_file\n";
print STDERR "save slowlog to $tmpdir/slow_query_$date.log\n";

#open(my $fh, ">", "$tmpdir/slow_query_$date.log");
#close($fh);
#chmod 0666, "$tmpdir/slow_query_$date.log";
my $pid = fork;
if ( defined $pid && $pid == 0 ) {
    my $stop = 0;
    local $SIG{INT} = sub {
        $stop++;
    };
    local $SIG{TERM} = sub {
        $stop++;
    };

    open(STDOUT,'>/dev/null');
    open(my $pipe, '|-', $mysql, @mysqlopt, '--sigint-ignore'); // ■ここが書き換え行!!
    $pipe->autoflush;
    $pipe->print($before);
    for my $i ( 0..$duration ) {
        last if $stop;
        $pipe->print("SELECT 1;\n") if $i % 7 == 0;
        sleep 1;
    }
    $pipe->print($after);
    exit;
}
print STDERR "wait $duration seconds\n";
while (wait == -1) {}
my $exit_code = $?;
if ( $exit_code != 0 ) {
    die sprintf("Error: mysql exited with code: %d", $exit_code >> 8); // ★ここが95行目!!
}

print STDERR "finished capturing slowlog.\n";
print STDERR "start query-digest\n";
my $digest = *STDOUT;
if ( !$stdout ) {
    open($digest, '>', "$tmpdir/slow_query_$date.digest");
}
open(my $pipe, '-|', $pt_query_digest, '--limit',$limit,"$tmpdir/slow_query_$date.log");
while(<$pipe>){
    $digest->print($_);
}
print STDERR "finished pt-query-digest.\n";
print STDERR "digest saved to $tmpdir/slow_query_$date.digest\n" if !$stdout;
unlink "$tmpdir/slow_query_$date.log" if $clear_log;

対処

95行目はログ出力部分で、根本原因はMySQLでパスワード無しでログインしようとしているのがダメそうでした。ログインを実際にする箇所はコメントで■で注釈した行なので、その行を以下に書き換えました。(パスワードを直接コードに書くのはセキュリティ観点でよくないと思われるので、実行時引数でもらうなりしたほうがいいかもしれません。)

open(my $pipe, '|-', $mysql, @mysqlopt, '-u', 'root', '-proot', '--sigint-ignore');

修正後に再度実行するとエラー無く実行できました!わーい

isucon@ip-192-168-0-11:~$ sudo query-digester -duration 10
exec mysql to change long_query_time and slow_query_log_file
save slowlog to /tmp/slow_query_20231018001054.log
wait 10 seconds
mysql: [Warning] Using a password on the command line interface can be insecure.
finished capturing slowlog.
start query-digest
finished pt-query-digest.
digest saved to /tmp/slow_query_20231018001054.digest
isucon@ip-192-168-0-11:~$

Discussion