トランザクションとファイル処理の連携

circuits

最近めちゃくちゃ忙しくて・・・今年初めての更新。今回は、仕事中に実際困った話から。

例えば、ファイルのアップロード処理などである程度まとまったデータベース処理とファイルの処理をうまく連携させたいことがあります。

単純に正常系だけを考えれば別にどうってことはないんですが、例えばトランザクションをロールバックさせた場合にファイル処理も「なかったこと」にしたい時ってどうしましょう・・・。意外と厄介なもんです。

ファイルを処理したことにする

「なかったこと」にするのではなく、処理したことにしてみました。

やりたいことは、ロールバック時には何も処理されていない状態であること、コミット時にはきちんとファイルが処理されていることです。処理したことにした場合に困るのが、exists()とかのファイルの存在確認メソッドを使った場合のふるまいです。この辺も考慮にいれてエミュレーション(?)させます。

LazyFile

上のクラス図は、Commandパターンを利用したものでファイル処理をコマンドという概念でクラス化しそれらのコマンドをLazyFileクラスが呼び出していることを表しています。

分かりにくいかもしれないんですが、Fileクラスと全く同じインターフェイスを持つLazyFileクラスが各ファイルオペレーション(図で言えばdelete, mkdir, mkdirs, renameTo)を実行する際に該当するコマンドクラスをインスタンス化して、とりあえずvirtualOperation()を実行し、FileCommandManagerにコマンドを追加します。

これでどうなるかというと、virtualOperation()実行の際にFileCommandManagerが保持している「削除ファイルリスト」と「追加ファイルリスト」が更新され、LazyFile.exists()ではこのFileCommandManagerにファイルの存在を問い合わせます。

FileCommandManagerでは、ファイルの存在確認時には

  • 削除ファイルリストに含まれていないか
  • 追加ファイルリストに含まれているか
  • 実ファイルが存在しているか

という3点を確認します。

・・と、内部的には相当ややこしいことをしているわけですが、実際のコードでは遅延ファイル処理を行いたい箇所で、Fileクラスの代わりにLazyFileクラスのインスタンスを作るだけでOKです。トランザクションをロールバックしたい場合、FileCommandManager.clear()を、コミットしたい場合にはFileCommandManager.execute()を呼び出します。FileCommandManager.test()を呼ぶと追加したコマンドを順次実行していった場合にエラーが発生しないかどうかを確認することもできます。

まぁ、なんというか疲れているのもあり、言葉では全然説明できている気がしないんで・・。詳しくはソースで。あ、今回はJavaですが、他の言語でも同様のことは大体できるはずです。

LazyFile.zip

ご意見ご感想やご質問あれば、なんでもどーぞ。

LINEで送る
Pocket

トランザクションとファイル処理の連携」への5件のフィードバック

  1. こんにちは。
    処理の内容を詳しく知らないし、環境(フレームワーク等)も決まっているので、的外れかもしれませんが、Seasar2, Spring等のDIコンテナを
    採用すれば、トランザクションの管理はDI側でやってくれるので、
    対応できるかもしれません。。。

  2. はじめまして、こんにちは。
    コメントありがとうございます。

    すみません、説明が悪すぎたようで…。
    トランザクション管理はできているのが前提の話で、その中で例えばレコードのdeleteと同じタイミングでFile.delete()する必要がある場合、ロールバック時に削除したファイルも戻したいという要求をどうやって満たすか、というテーマでした。

    DIコンテナにひょっとしてそんな機能があるんですかね・・!?

  3. お疲れさまです。
    あまり理解せず、コメントして申し訳なかったです。
    私、今開発しているシステムで、SeasarのプロダクトでTeedaというフレームワークを
    使用しています。Teedaではhtmlに対応したPageクラスを作成するのですが、その中でDB削除成功後、ファイルの削除をしています。トランザクションはDIコンテナで管理されているので、DB削除処理に失敗した場合、例外をスローすれば、自動的にロールバックが行われます。
    トランザクション管理されているメソッド内でDB削除後、ファイルを削除する処理を書いておいて、DB削除に失敗した場合、例外がスローされるとファイルの削除が行われず、ロールバックが行われるという仕組みです。
    実際の処理とは違いますが、以下のような感じです。

    public Class doOnceDelete() {

    // 該当するコンテンツ情報(テーブル)を削除
      // *もしここで例外が発生すれば、以下のファイル削除処理は
      // 行われず、自動的にロールバックが行われる。
    contentLogic.delete(content);

     // ファイルを削除
    File file = new File(content.getFilePath());
    if (file.exists()) {
    file.delete();
    }

    }

    また、的外れであるかもしれませんね。本当にでしゃばってすみません。
    時々、拝見させていただいています。
    宜しくお願いします。(実は1回お会いしたことがあります。。。)
    応援していますので、頑張ってください。

  4. お疲れ様です。
    いえいえ、とんでもないです。色々とご意見頂けるのはありがたいと思っています!

    例外が発生すればファイル処理が行われない、ということですがまさにそうですね。
    ただ、自分は結構面倒くさがりなので、処理順序に依存したくなかったので、今回のクラスを作った次第です。

    1回会った事があるとのことですが・・・全然分かりません(^^;
    こういう話が出来る人とはどんどんお会いしたいと思っていますので、
    よろしければ、また飲みにでも行きましょう!!

    よろしくお願いします!(^^)

  5. お疲れ様です。
    >ただ、自分は結構面倒くさがりなので、処理順序に依存したくなかったので、今回のクラスを作っ
    た次第です。
    なるほど、そうですね。DIを使わなくても、例外を発生させれば、
    ファイル処理を行わないようにできますもんね。
    勉強になりますので、サンプルソース参考にさせていただきます!

    お会いしたのは、去年の9月か、10月だったと思います。
    NEXTLENのある方の送別会の時だと思います。その時は遅れて参加しましたし、
    酔われていたので、覚えてないと思います。
    はい、ぜひ飲みに行きましょう!
    宜しくお願いします!

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です