MySQLトリガーを外部リソース無しで実装してみる
レスキューナウエンジニアの坂本です。
アーキテクチャ設計を行う際、DBにデータが入ることをトリガーとしたシステム設計はさまざまな場面で必要になってくる場合があります。
クラウドサービスとして運用する場合amazonSNSやPub/Subなどを使用しトリガーとすることは可能ですが、リソース縮小や予算圧縮と言った観点では使用しない場合もあります。
今回はMySQL単体+αで稼働できるDB起因のサービス構築についてまとめたので記載します。
手順
最初にデータが投入されたことを起因として処理を実行すると言うとストアドプロシージャが真っ先に思いつきますが、DB内部のクエリで完結してしまっているためシステムのトリガーとして使用することは難しいものとなります。
そのためシステム間で情報を連携するとなるとストアド + User-Defined Function(UDF)を使用し外部連携する方針となります。
User-Defined Functionは外部関数を呼び出すMySQL特有の処理になりますが、名前の通りユーザーが定義した外部ファイルの関数を実行するクエリとなります。
元々C/C++言語向けの技術ですがコンパイル後にShared Object Libraryとして管理ができればどの言語でも実行することが可能です。
Go言語でもCのパッケージが出ており、soファイルとしてコンパイルが可能です。
package main
import "C"
//export my_go_function
func my_go_function(args *C.char) *C.char {
// httpリクエストを送信する処理を実装する
// args にはMySQLから渡されるcharが渡される
// 戻り値としてMySQLに渡すcharを返す
}
func main() {
// コンパイルの為メイン関数が必要ですが、何も必要ありません。
}
次に共有ライブラリ (.so ファイル) を生成します。
go build -buildmode=c-shared -o my_go_function.so my_go_function.go
次に生成された共有ライブラリをMySQLに登録します。以下のようなSQL文を使用して、共有ライブラリをMySQLに登録することができます。
CREATE FUNCTION my_go_function RETURNS STRING SONAME 'パス/my_go_function.so';
CloudSQLの様なマネージドサービスでの実装の際はGCSバケットを指定することもできます。
バケットを上げる前の手順は同じです。
CREATE FUNCTION my_go_function RETURNS STRING SONAME 'gs://[BUCKET_NAME]/my_go_function.so';
これによりSQLと外部プログラムの結合が可能となり、リクエスト送信やソケットのコネクション作成など、外部連携が可能となります。
上記の設定したUFDをCALL my_go_function()
といった形でストアドに登録することでデータ投入をトリガーにすることも可能となります。
後書き
今回はUDFの手順が必要になるMySQLについて記載をしましたが、PostgreSQLには plpgsql や plperlなどのストアドから直接リクエストを送信する方法が存在する為、そう言った拡張機能が多いDBを選択するのも良いかもしれません。
プログラムとDBを共存させるSQLiteの様な使用も可能となっている為、heroku無料枠で個人開発などを行っている方にもおすすめできる内容となっています。
Discussion