AP4R,Rubyで非同期メッセージング

第3回 SAF機能とテストサポートによる安心非同期

この記事を読むのに必要な時間:およそ 10 分

SAF用テーブルに保管されているもの

さて,それでは変わっている部分をみてみましょう。

業務DBに作成したSAF用のテーブルを確認します。

% cd as_rails
% ruby script/console
>> y Ap4r::StoredMessage.find(:all)
--- []

=> nil

Storeされたはずのメッセージ情報がなにもありません。実は,SAF機能を利用した際には,Forwardされたメッセージ情報の後処理に2つのモードが選べます。すなわち,Forward後にメッセージを物理削除するモードと論理削除するモードです。Forwardが無事に成功した場合,SAF用テーブルのメッセージ情報は必要なくなります。残しておくと,どこかのタイミングでテーブルのお掃除が必要になるので,デフォルトでは物理削除のモードとなっています。そのため,テーブル内に何も残っていなかったのです。

今回は,SAF用テーブルに保管されているデータを確認したいので,論理削除モードにして再度実行してみましょう。モードの切り替えは,クラス変数で行います。environment.rbの最後に次の一行を加え,Railsを再起動してください。

config/environment.rb

Ap4r::AsyncHelper::Base.saf_delete_mode = :logical

画面より注文処理を実行し,script/consoleで確認します。

% ruby script/console
>> Ap4r::AsyncHelper::Base.saf_delete_mode
=> :logical
>> y Ap4r::StoredMessage.find(:all)
--- 
- !ruby/object:Ap4r::StoredMessage 
  attributes: 
    status: "1"
    updated_at: 2007-09-03 15:24:48
    duplication_check_id: 4b68b9c0-3c14-012a-cfa0-0016cb9ad524
    id: "13"
    queue: queue.async_shop.payment
    object: "\x04\b\"\x10order_id=13"
    created_at: 2007-09-03 15:24:48
    headers: "\x04\b{\n\
      :\rdelivery:\tonce:\x0Fqueue_name\"\x1Dqueue.async_shop.payment
      \x12dispatch_mode:\tHTTP:\x0Ftarget_url\"-http://localhost:3000/async_shop/payment
      :\x12target_method\"\tPOST"=> nil

SAF用テーブル内に1件のレコードがありました。statusが各メッセージのForward状況を示しています。

status = 0未処理(Forward前,およびForward失敗時)
status = 1処理済(Forward成功時)

最後に,⁠あとの話に備えて)メッセージの情報をお掃除しておきます。

>> Ap4r::StoredMessage.destroy_all
=> [#<Ap4r::StoredMessage:0x356748c @attr... (省略)

あわせて,メッセージDBのキューテーブルの情報も削除します。

% mysql -u ap4r -p ap4r
mysql> delete from reliable_msg_queues;

異常系での挙動確認

異常系でも安心してメッセージングができるよう拡張したので,その様子をみてみましょう。

一例として,業務DBのCRUDとメッセージ生成を終えてから,業務DBをコミットするまでになんらかの異常が発生したとします。業務DBとのネットワークに障害があったり,あるいは,業務DBやRailsプロセスに異常があった場合に該当します。第2回までのSAF機能を使っていない場合を図に示すと,以下のようになります。業務DBには アプリケーションによるCRUDが反映されていないにもかかわらず,メッセージの生成が行われてしまっています。

図4 データがないのに,メッセージがある場合

図4 データがないのに,メッセージがある場合

この状況を擬似的にコードでつくると,次のように ap4r.async_to メソッドのあとに例外処理をいれることで実現できます。ap4r.transaction を使用していない場合は,すでに書いたように,業務DBのデータとメッセージ生成のアトミック性が崩れてしまいます。

では,ap4r.transaction を利用した以下の例ではどうでしょう。

def order
  begin
    ap4r.transaction do
      @order = Order.new(params[:order])
      @order.save!

      ap4r.async_to({:action => 'payment'},
                    {:order_id => @order.id})

      raise "for SAF test"

   (省略)
  end
end

実際にコードに一行,例外発生を加えて実行してみます。

ユーザーの画面には,⁠Order was failed.」の文字が表示され,注文が失敗に終わったことがわかります。続いてテーブルを確認します。

業務DBのSAF用テーブル

% cd as_rails
% ruby script/console
>> Ap4r::AsyncHelper::Base.saf_delete_mode
=> :logical
>> y Ap4r::StoredMessage.find(:all)
--- []

=> nil

Storeされたメッセージはないようです。

メッセージ DB のキューテーブル

% mysql -u ap4r -p ap4r
mysql> select id, queue from reliable_msg_queues;
Empty set (0.00 sec)

SAF用テーブルに Storeされていなかったので,Forwardされているはずもありません(キューテーブルには論理/物理削除のモードはありません。非同期メッセージ処理のリクエストが成功すると格納されていたメッセージは消えるので,すでに非同期処理が実行されてしまっている場合と区別がつきません。厳密に確認したい場合はdispatcherスレッドの数を0にしておきます。あるいは,paymentsテーブルにレコードが作成されていないことを確認してもよいでしょう⁠⁠。

今回は,AP4RのSAF機能によるデータとメッセージのアトミック性の保証を確認するために,簡易的に例外発生のコードを追加することで異常系の状況をシミュレートしました。Forward失敗時の対応など,リカバリ方法については,次回にまた詳しくとりあげる予定です。

次ページからは安心非同期のもうひとつの柱,テストサポートについてお話しします。

著者プロフィール

加藤究(かとうきわむ)

フューチャーアーキテクト株式会社,シニアコンサルタント。土木専攻だった大学時代には,道路や橋の建設について学んできたが,社会に出てからは Javaのメッセージングミドルウェアの開発/保守などに従事。昨年,Rubyで書いたメッセージングライブラリ,AP4Rでオーンソースの世界に仲間入り。現在は,AP4Rの開発/導入サポート中。

URLhttp://d.hatena.ne.jp/kiwamu/


篠原俊一(しのはらしゅんいち)

フューチャーアーキテクト株式会社,シニアコンサルタント。大学時代は物理学を専攻,素粒子論を研究,10次元の世界に住んでいた。入社後まもなく,Martin FowlerのblikiでRubyを知り,"The Ruby Way"に学ぶ。 Rubyとオープンソースが大好きなプログラマとして,AP4Rを開発中。

URLhttp://d.hatena.ne.jp/ita-wasa/