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

第4回 システムは稼働してからがはじまり

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

メッセージのリカバリ

メッセージのリカバリには2つの種類があります。冒頭でも触れましたが,SAF Forwardエラーのリカバリ,および業務処理がエラーとなったメッセージのリカバリです。 簡単のため,前者をSAFリカバリ,後者をDLQリカバリと呼びます。DLQとはDead Letter Queueの略称で,メッセージングで配信不能のメッセージを格納する場所としてよく使われます。それぞれの発生状況,リカバリ方法についてはこのあと詳しく述べていきます。

SAF リカバリ

発生状況

SAF機能を利用しているときに発生します。メッセージは,SAF用テーブルにStoreされた後,AP4RサービスにForwardされますが,このタイミングでネットワークの障害や,Railsプロセス,あるいはAP4Rプロセスのダウンがあったとします。このとき,SAF Forwardエラーとなります。この場合,メッセージはAP4Rに渡っていませんので,非同期処理が実行されません。システムの復旧後,再度メッセージをForwardする必要があります。メッセージの情報はSAF用テーブルに残っていますので,そこからリカバリします。

図6 SAF Forward エラーの発生状況

図6 SAF Forward エラーの発生状況

状況の確認

SAF用テーブルに保管されているメッセージを確認します。最初に,前回からのメッセージが残っているかもしれないので,きれいにお掃除しましょう。

% cd as_rails
% ruby script/console
Loading development environment.
>> Ap4r::StoredMessage.destroy_all

次に,メッセージを確認したいのでSAF用テーブルが論理削除の設定になっていることを確認します。environment.rbの最後に以下のコードがあれば大丈夫です。

as_rails/config/environment.rb

(省略)
Ap4r::AsyncHelper::base.saf_delete_mode = :logical

RailsプロセスとAP4Rプロセスを起動します。

% cd as_rails
% ruby script/server
% cd as_ap4r
% ruby script/mongrel_ap4r start -A config/queues_mysql.cfg

これまで作成してきたアプリケーションの画面をひらき,注文処理を実行します。

「Order was successfully created.」と画面に表示されたら,SAF用テーブルを確認します。

% cd as_rails
% ruby script/console
Loading development environment.
>> y Ap4r::StoredMessage.find(:all)
---
- !ruby/object:Ap4r::StoredMessage
  attributes:
    status: "1"
    updated_at: 2007-09-14 10:21:30
    duplication_check_id: bf6627e0-448e-012a-cfbe-0016cb9ad524
    id: "29"
    queue: queue.async_shop.payment
    object: "\x04\b\"\x10order_id=29"
    created_at: 2007-09-14 10:21:30
    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

statusは"1"となっています。第3回のSAF機能の説明でも触れましたが,このステータスは次の意味があります。

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

したがって,このステータスはForwardが無事に成功したことをあらわしています。

次に,未処理のステータスをつくるために,AP4Rプロセスを Ctrl+C で終了した状態で注文処理を実行します。ステータスは未処理をあらわす"0"となっているでしょう。

>> y Ap4r::StoredMessage.find(:all)
--- 
- !ruby/object:Ap4r::StoredMessage 
  attributes: 
    status: "1"
    updated_at: 2007-09-14 10:21:30
  (省略)
- !ruby/object:Ap4r::StoredMessage
  attributes:
    status: "0"
    updated_at: 2007-09-14 10:26:58
    duplication_check_id: 82912650-448f-012a-cfbe-0016cb9ad524
    id: "30"
    queue: queue.async_shop.payment
    object: "\x04\b\"\x10order_id=30"
    created_at: 2007-09-14 10:26:58
    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用テーブルに保管されているメッセージが多い場合には,次のようにすると簡単に一覧が見られます。左から順に,ID,キューの名前,生成日時をあらわしています。

>> y Ap4r::StoredMessage.find_status_of(:all).map{|sm| sm.to_summary_string}
---
- 29, queue.async_shop.payment, Fri Sep 14 10:21:30 +0900 2007
- 30, queue.async_shop.payment, Fri Sep 14 10:26:58 +0900 2007
=> nil
>> y Ap4r::StoredMessage.find_status_of(:unforwarded).map{|sm| sm.to_summary_string}
---
- 30, queue.async_shop.payment, Fri Sep 14 10:26:58 +0900 2007
=> nil
リカバリ方法

メッセージを再度AP4Rに Forward します。AP4R プロセスを起動しておきましょう。

% cd as_ap4r
% ruby script/mongrel_ap4r start -A config/queues_mysql.cfg

SAF用テーブルに保管されている,すべての未処理のメッセージをリカバリするには次のようにします。戻り値は,リカバリに成功したメッセージの数と失敗した数の配列になります。ここでは,未処理のメッセージは1つだったので,無事にリカバリができたことになります。

% cd as_rails
% ruby script/console
Loading development environment.
>> Ap4r::StoredMessage.reforward_all
=> [1, 0]

リカバリ対象のメッセージが大量にある場合は,引数を指定することで,その数ごとにひとつのデータベーストランザクションとしてまとめて処理できます。引数なしの場合は,10メッセージごとに処理しています。下記の例では,未処理のメッセージを10用意しました。3つずつまとめて再送処理を実行していますが,そのなかでエラーが発生しています。エラーのあった回の3つは処理が失敗に終わり,それ以外の7つは無事に成功しています。失敗したメッセージはさきほどと同様にして確認できます。

>> Ap4r::StoredMessage.find_status_of(:unforwarded).size
=> 10         
>> Ap4r::StoredMessage.reforward_all 3
ActiveRecord::RecordNotFound: ...(省略)
=> [7, 3]
>> y Ap4r::StoredMessage.find_status_of(:unforwarded).map{|sm| sm.to_summary_string}
---
- 35, queue.async_shop.payment, Fri Sep 14 10:38:07 +0900 2007
- 36, queue.async_shop.payment, Fri Sep 14 10:38:19 +0900 2007
- 37, queue.async_shop.payment, Fri Sep 14 10:38:28 +0900 2007
=> nil

また,特定のメッセージのみリカバリするには,次のようにします。

>> Ap4r::StoredMessage.reforward 36
=> true

SAF用テーブルを管理するRailsアプリケーションを作成すれば,GUI環境でリカバリすることも可能です。RubyForgeからHelloWorldアプリケーションがダウンロードできますが,そちらにそのサンプル実装があります。HelloWorldアプリケーションの動かし方は,ホームページのGetting Startedを参考にしてください。

図7 HelloWorldサンプルアプリケーション

図7 HelloWorldサンプルアプリケーション

HelloWorldアプリケーションを起動し,以下のURLにアクセスすると,SAFリカバリの画面が表示されます。

各メッセージの中身も参照できますので,必要に応じて「Recovery」のリンクを押してください。

図8 SAFリカバリ画面

図8 SAFリカバリ画面

著者プロフィール

加藤究(かとうきわむ)

フューチャーアーキテクト株式会社,シニアコンサルタント。土木専攻だった大学時代には,道路や橋の建設について学んできたが,社会に出てからは 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/