GoでMySQLの監視コマンドを作成するのが楽になるライブラリ
本エントリはMySQL Advent Calendar 2020 の9日目の記事になります。
これまでいくつものMySQLの監視コマンドをつくってきたのですが、毎回同じような処理を書いているので、それらをまとめるライブラリを作ってみました
主な機能は
の2つです。
使い方 (コマンドラインオプション)
まずコマンドオプションの処理
オプションを定義した構造体に mysqlflags.MyOpts を追加します
type opts struct {
mysqlflags.MyOpts
Timeout time.Duration `long:"timeout" default:"10s" description:"Timeout to connect mysql"`
Version bool `short:"v" long:"version" description:"Show version"`
}
opts := opts{}
psr := flags.NewParser(&opts, flags.HelpFlag|flags.PassDoubleDash)
_, err := psr.Parse()
これだけで、helpを表示させると以下のようになります。
Application Options:
--defaults-extra-file= path to defaults-extra-file
--mysql-socket= path to mysql listen sock
-H, --host= Hostname (default: localhost)
-p, --port= Port (default: 3306)
-u, --user= Username (default: root)
-P, --password= Password
--database= database name connect to
--timeout= Timeout to connect mysql (default: 10s)
-v, --version Show version
MySQLの defaults-extra-file、socket、ホスト、ポート、ユーザなどのコマンドオプションが追加されてますね。
この構造体を利用してデータベースに接続するには次のようにします。
db, err := mysqlflags.OpenDB(opts.MyOpts, opts.Timeout, debug)
if err != nil {
log.Printf("couldn't connect DB: %v", err)
return 1
}
defer db.Close()
DSNだけ欲しい場合は、mysqlflags.CreateDSN()` を使います。
mysqlflagsはDSNを作る際に、my_print_defaults -s client コマンドを実行して my.cnf などに書かれた値を取得します。
% my_print_defaults -s client --host=127.0.0.1 --user=xxx --password=xxx
これらをデフォルト値として利用するので、$HOME/.my.cnf が設定されていればコマンドラインオプションが未指定でもコマンドが動きます。
また、接続する際のparameterを追加したい場合は、MySQLDSNParams にmapを渡します。
params := map[string]string{
"readTimeout": timeout.String(),
}
opts.MySQLDSNParams = params
使い方 (SHOWステートメントの構造体へのマッピング)
MySQLのSHOW STATUSやVARIABLESでは列方向に情報が並べられています。行によってstringやint、boolっぽいものが混じります。
mysql> SHOW VARIABLES; +-----------------------------+-------------------------------+ | Variable_name | Value | +-----------------------------+-------------------------------+ | activate_all_roles_on_login | OFF | | admin_address | | | admin_port | 33062 | | admin_tls_version | TLSv1,TLSv1.1,TLSv1.2,TLSv1.3 | ..
これらを良い感じにintやstringに自動で変換して構造体にマッピングしてくれると、監視コマンドのコードは短くなり、とても楽になります
また、SHOW SLAVE(REPLICA) STATUSではカラムが増え、行方向に情報が追加されています。情報が縦方向なのか横方向なのかあまり気にせず同じコードで必要とするカラムだけ良い感じにとれると嬉しいところです。
これをやるのが、mysqlflags.Query().Scan() です。内部では mapstructure というライブラリを利用しています。
thread関連の値をSHOW VARIABLES/SHOW GLOBAL STATUSから取得するのは以下のように書けます
type threads struct {
Running int64 `mysqlvar:"Threads_running"`
Connected int64 `mysqlvar:"Threads_connected"`
Cached int64 `mysqlvar:"Threads_cached"`
}
type connections struct {
Max int64 `mysqlvar:"max_connections"`
CacheSize int64 `mysqlvar:"thread_cache_size"`
}
var threads threads
err = mysqlflags.Query(db, "SHOW GLOBAL STATUS").Scan(&threads)
if err != nil {
log.Printf("couldn't fetch status: %v", err)
return 1
}
var connections connections
err = mysqlflags.Query(db, "SHOW VARIABLES").Scan(&connections)
if err != nil {
log.Printf("couldn't fetch variables: %v", err)
return 1
}
構造体のメタ情報のタグに、mysqlvar:"取得する変数名" を入れると自動的にマッピングを行います。
レプリケーションの情報を取得する際は次のようにします。
type replica struct {
IORunning bool `mysqlvar:"Slave_IO_Running"` // Replica_IO_Running
SQLRunning bool `mysqlvar:"Slave_SQL_Running"` // Replica_SQL_Running
LastSQLError string `mysqlvar:"Last_SQL_Error"`
}
var replica replica
err := mysqlflags.Query(db, "SHOW SLAVE(REPLICA) STATUS").Scan(&replica)
f !replica.IORunning || !replica.SQLRunning {
fmt.Errorf("something wrong in replication with %s", replica.LastSQLError);
}
bool へのマッピングは、値が Yes や On だった場合に true になり、それ以外は false になります。
Slave_IO_Running(Replica_IO_Running) は Connecting という文字列にもなりますが、こちらは false 扱いです。
もし、真偽値判定を楽にしたいけど、元の文字列も取得したいときは、mysqlflags.Bool 型が使えます。
type replica struct {
IORunning mysqlflags.Bool `mysqlvar:"Slave_IO_Running"`
SQLRunning mysqlflags.Bool `mysqlvar:"Slave_SQL_Running"`
ChannelName *string `mysqlvar:"Channel_Name"` // ポインタ型はオプショナルになる
Behind int64 `mysqlvar:"Seconds_Behind_Master"`
}
var replicas []replica
err := mysqlflags.Query(db, "SHOW SLAVE(REPLICA) STATUS").Scan(&replicas)
for _, replica := replicas {
f !replica.IORunning.Yes() || !replica.SQLRunning.Yes() {
if replica.ChannelName == nil {
*replica.ChannelName = "-"
}
fmt.Errorf("something wrong in replication Channel:%s IO:%s SQL:%s Error:%s",
*replica.ChannelName,
replica.IORunning, replica.SQLRunning,
replica.LastSQLError);
}
}
Yes() メソッドがbool値を返し、String() で元の文字列を返します。
使用例
いくつかのMackerelプラグイン、checkプラグインはこのライブラリを使って実装しています。
- GitHub - kazeburo/go-mackerel-plugin-mysql-lite: lite means Yet Another
- GitHub - kazeburo/go-check-mysql-slave-sql-error
- GitHub - kazeburo/go-mackerel-plugin-msr: mackerel plugin for mysql multi source replication rag
- GitHub - kazeburo/go-check-mysql-msr: check multi source replication
- GitHub - kazeburo/go-check-mysql-uptime
mkr plugin installでインストールできるようになってますので、ニーズが合えばご利用ください。
ライブラリや監視コマンドの機能追加要望などありましたらお気軽にどうぞ〜