Ruby/Railsでロックファイルによる排他制御

こんにちは、鈴木です。

バッチ処理を作成する時に、気を付けなければならないことの一つに、排他制御があります。

排他制御を行なう方法はいくつかありますが、今回はロックファイルによる排他制御を行なうコードを考えます。

 

排他制御を忘れると

排他制御を忘れていると、

  • cron で定期的にパッチ処理を起動するように設定した。
  • 前回起動されたバッチ処理がまだ終了していなかったので重複して起動された。
  • そんな状況は考慮していなかったので、バッチ処理中にエラーが発生した。データ不整合が発生した。

といったことになりかねません。

 

ロックファイルによる排他制御

ロックファイルによる排他制御とは、以下のような手順で排他的に処理を実行する方法のことを言います。

  1. バッチ処理の最初にファイルをロックする。( File#flock() を使用します )
  2. ロックに失敗したら、処理を終了する。( or ロックが取得できるまで待機する )
  3. 本来の処理を実行する。
  4. ロックを開放する。

そして、ロックをかける対象のファイルを「ロックファイル」と呼ぶことが多いです。

それではさっそく、ロックファイルによる排他制御を行なうサンプルコードを書いてみます。

上のコードをファイルに保存して実行、処理が終わる前にもう一回実行、とすれば排他制御が行なわれることを確認できます。

Linux であれば、上記コードを lock_file_sample.rb というファイル名で保存し、以下のように実行( & を付けてバックグラウンドで実行)すると、次のような動作をするはずです。

 

ブロックを排他的に実行するメソッドにまとめる

ロックファイルによる排他制御の方法は分かったので、次は使いやすいようにメソッドにまとめようと思います。

Ruby といえばブロック、ということで「指定されたブロックを排他的に実行するメソッド」を定義します。

メソッド名は synchronized としました。また、ファイルのロックに失敗したことを表す例外クラス Locked を定義しました。

synchronized メソッドを使用すると、以下のようになります。

排他制御を行なうコードと、排他的に実行したいコードを分離することができました。

 

Rails でバッチ処理を書く

ロックファイルで排他制御する方法は、Rails でバッチ処理を書く場合にも使用することができます。

バッチ処理は複数作成する場合もあるので、Batch::Base クラスに排他制御などの共通機能をまとめます。

Batch::Base を継承し、execute というインスタンスメソッドを定義します。

バッチ処理は以下のように起動します。

バッチ処理を起動すると、最初に Batch::Base で定義されたクラスメソッドの execute が呼び出されます。

Batch::Base.execute では synchronized の中で派生クラスで定義されてるインスタンスメソッドの execute を呼び出します。

 

まとめ

今回はロックファイルで排他制御する、というアプローチをご紹介しました。

単純に排他制御する方法に加えて、Rails でバッチ処理を作成する方法についてもお話ししました。

こんなやり方もあるんだ、と思っていただければ幸いです。

 

Comments are closed, but you can leave a trackback: Trackback URL.