途中でコミットできるトランザクション

今まで何回かContinuationネタで使ったことがあるActiveRecord用の「途中でコミットできるトランザクション」をrailsプラグインにしてみた。なんか、意外と自分で実用することが判明してきたので。

ActiveRecord::Base.transactionを拡張して、途中で「ここまで保存」する機能を提供する。

使い方としては、こんな感じ。

Hoge.partially_commitable_transaction{|tx|
  loop
    (なんか処理)
    tx.commit if some_condition?
  end
}

一応、標準のActiveRecord::Base.transactionに対する下方互換性は持っているのだけれども、完全に変わりに使うには何かと問題があるので上書きはしていない。どうしても、という人はお好みで

ActiveRecord::Transactions::ClassMethods.module_eval do
  alias_method :transaction_without_partially_commitability, :transaction
  alias_method :transaction, :partially_commitable_transaction
end

とかしてくださいな。

で、お断りとしては「重要な機能には使わないでください。非常に危険です」

  • Continuationを使ってる

  • Rubyの中でも「落ちやすい」機能

  • Ruby 2.0では無くなるかもしれない機能だし。
  • Continuationによる再突入を想定していないC言語拡張ライブラリなんかがコールスタックに載ってると恐ろしい。
  • ネイティブスレッドとの相性も最悪と思われる。

  • コミット後に別のTransactionを開始しておいて元のものと偽るという荒業

  • そのDBMSのConnectionAdapterがローカル変数になんか情報を保存しているとアウトかも。

  • Rails標準のMysqlコネクタや、postgresql-prではとりあえず動いた。

私ゃ確かに、このテクニックを商売用のコードで使ったけれども、それは「たとえ落ちても、プロセスIDさえ分かっていればrecover可能な、重要性も低い短命なプロセス」だったからで。そういうのとか、せいぜいがMigrationによるデータ投入に使うとか、そういうのがおすすめ。

あとで、subversionリポジトリにも上げとく。