プロセスの排他制御
今作ってるGithub風のものはデータストアとしてRDBMSも使っているんですが、レポジトリ本体はファイルシステムとして、素のgitレポジトリで管理しています。
で、ファイルでデータを持つというのはウェブアプリではあまり聞かないような気がしますが(あっても画像だと思いますが),よくある軽量のスクリプトなどでデータベースを持つ程でもないものについては頻繁にtmpファイルを作成してデータを持つなどが普通ですから、意外に簡単に実装できます。
その上で怖いのはデータの整合性についてです。例えばあるユーザがstashを作成しようとしたときに、別のユーザがブランチを切り替えたらどうなるか。
アトミックな処理中は他の処理はそれを待つか、待っても終わらない場合は例外を返すようにしたい。
◯app/models/repository.rb
class Repository < ActiveRecord::Base def lock(&block) Timeout::timeout(10) do Dir.chdir(self.working_dir) do File.open(".lock","w") do |f| begin f.flock(File::LOCK_EX) block.call ensure f.flock(File::LOCK_UN) File.unlink(f) end end end end rescue Exception => ex raise AccessDenied.new("timeout") end end end
でもってこのレポジトリにロックをかけて一連の処理を保証したいときはlockにブロックを渡す。
@repository.lock do system("git stash") system("git checkout master") system("git stash pop") end
とりあえずの保証としてはこれで良いと思うのだが、これはあるプロセスがレポジトリを操作している間に他のプロセスがそれに影響を与えないことを保証するわけで、一連の処理がアトミック=トランザクション処理になるわけではないはず。
つまりsystem("git stash")を実行しているときにプロセスが死んだらstashに不整合が起きる。どうしたものか。。。