角待ちは対空

おもむろガウェイン

MySQLの外の世界も含んだトランザクション

トランザクションとは一般的な不可分な一連の処理という意味で使っているが、例えばS3に画像データをアップロードしMySQLにはそのキーを保存したいみたいな場合の話。

MySQLへの書き込みだけであればそれこそMySQL(ストレージエンジンか)に備わっているトランザクション機能を使えば良い。しかしながらS3のバケットの状態とも整合性を取りたいとなると少しむずかしい。S3へのアップロードは失敗するかもしれないし、失敗したのを検知して復帰処理を書いたとしてもそれが失敗するかもしれない。

簡単のためS3に画像をアップロードしテーブルに1レコード挿入することを考える。実際のアプリケーションでは非同期でやりたいこともあれば同期的に処理で十分な場合もあるが、簡単のため後者を考える。キーはuuid()とする。

このシチュエーションで考えられる作戦は

  • (MySQLの)トランザクションを張り、その中でS3へアップロード、コミットする。
    • S3の処理が失敗たらコミットしなければ良い
  • statusカラムを用意し状態を記録する
    • 例えば挿入した時点で0でinsertしS3の処理が終わったら1を書き込む
    • この場合S3の処理が失敗すると最悪0のままレコードが残ることになるが後処理は比較的簡単

どう考えても前者の作戦は嫌な予感がするので後者が王道かなと思っていたが、本質的に採番がしたいだけならばSELECT UUIDで採番しS3へアップロード、成功したらinsertでどうかと同僚から教わった。確かに。(この場合、S3へアップロードが失敗したら欠番になりinsertに失敗したらS3にゴミが残ることになる)

結局はどこまでちゃんとしたいかや要件の問題であり

  • S3にファイルは存在し、レコードのない状態を許容できるか
    • レコードだけ存在する状態は許容できないのが普通だと思う
  • statusカラムも見なくちゃいけない状態を許容できるか

あたりのトレードオフの話なのかなと言う感じがした。S3は画像をおいておくだけならば安いので、ゴミが残っていても特に問題ない要件ならば放っておけば良いという選択肢は普通にありえる。ゴミが許せないならばやはりステータスカラムを作り定期実行の処理でゴミを処理すれば良い。その代わり見るべきカラムが増えるという感じ。カラム増やさずテープルを増やすというアイデアもありうる。

共通するのは採番がちゃんとできればよいということであり、あとは要件次第という結論となる。