前回のおさらい
前回 は、ウェブ上のお店のアプリケーションを作成しました。店を訪れたお客さんはウェブブラウザから品物の名前を入力して、注文を行います。その後、サーバ側では注文情報の保存と決済の2つの処理を順次行います。処理が完了すると、ブラウザに注文完了の情報が表示されます。この同期アプリケーションをAP4Rを利用して非同期拡張しました。
すなわち async_to
メソッドを使って「重い」決済処理を注文処理から分離し、ユーザーの待ち時間を解消しました。リバースプロキシを利用した、バックエンドに複数の Railsプロセスが存在する構成では、「 重い」非同期処理とユーザーにすぐ応答したい処理をうまく両立させながら実行できます。記事のサンプルではリバースプロキシ構成にはせず、URL変換フィルタの機能を利用して、非同期メッセージ処理のリクエスト先を別のRailsプロセスに変える例をご紹介しました。
今回は、AP4Rの「堅牢さ」を支える SAF機能の使い方やテストサポートについて解説し、安心して非同期処理を導入できるようにします。
アプリケーションの拡張の方向性
2つの拡張を行います。
異常系に対する備え
メッセージングを利用してアプリケーションを非同期化する際には、異常系に対する備えが重要となります。通常の同期処理であれば、ユーザーは何か問題が起きたことに気づき再度処理を実行してくれるでしょう。しかし、非同期処理の場合、ユーザーの気づかないところで異常が発生していることになります。また、異常によりメッセージのやりとりに問題がでるかもしれません。
データベースやネットワークの障害、サーバの突然のダウンなど、異常は予期せず発生します。連載第1回目でも触れましたが 、異常の発生タイミングによってメッセージングにどんな影響があるかを復習してみましょう。
下図は、業務DBへのコミットとメッセージ生成の間で障害が発生した場合です。業務DBにCRUD(Create, Refer, Update, Delete)が反映されているものの、メッセージの生成が行われていないので、AP4Rを介して実行するはずの非同期処理が行われません。サンプルアプリケーションでは決済処理が実行されないことになります。
図1 データはあるのに、メッセージがない場合
また次の図は、業務DBへのコミットとメッセージ生成の順番を変えたものです。このようにすると、メッセージは生成されますが、業務DBへのコミット前に異常が発生するとCRUDの結果はロールバックされます。この結果、注文処理に失敗したにもかかわらず、決済処理だけ実行されてしまうという状況になります。
図2 データがないのに、メッセージがある場合
業務DBへのCRUDの反映とメッセージの生成をアトミックに行い、たとえなにがあろうとも、メッセージを死守する。それが信頼性のあるメッセージングに求められる必須要件となります。AP4Rでは、そのためにSAF機能を提供しています。
非同期処理もテストする
テスト駆動開発(Test-driven Development、以下TDD)や振舞駆動開発(Behaivior-driven Development, 以下BDD)という開発手法は、既に広く知られており、開発現場で適用されている方も多いのではないかと思います。
筆者はあるプロジェクトでTDDを実践し、自分の書いたコードに対するテストスイートを作成しました。その際、コードの保守、拡張に対する安心感、そしてリリースに対する自信に繋がるたいへん有効な方法だと感じました。
この連載では、TDD/BDDの説明には踏み込みませんが、BDDでは開発側だけに留まらず、「 仕様」に着目し、それを記述することで開発自体を牽引していくという面も強調されます。
Rubyには、Test::Unit
ラリブラリが付属しており、簡単にTDDが実践できます。また、Ruby on Railsでは、ウェブアプリケーションにTDDを適用するため、unit、functional、integrationという3種類のテスト方法をサポートしています。
非同期処理を含むアプリケーションでも、網羅的なテストが(比較的)簡単に実行できることは同じように重要です。メッセージング処理では、ネットワーク通信をともなうため、複数のプロセスが関係することと、通信により時間がかかることの2点を考慮する必要があります。そこで、AP4Rでは、2つのテスト方法を用いて、テストしやすさと、網羅性を補完的に実現します。
(Rails付属の)functionalテスト
アクション単体のテストとして、メッセージングでの通信無しにテストを行います。
asyncテスト
ネットワーク通信やメッセージDBへのアクセスを含め、実動作となるべく近いやり方でテストを行います。
次ページから、SAF機能を適用し、前回に続いて非同期アプリケーションを拡張します。その後、AP4Rでのテストサポートとして、それぞれの動作概要、テストの書き方と実行方法を説明します。
SAF機能を利用してみましょう
前回非同期化したアプリケーションをベースに拡張します。Rails、AP4Rプロセスが1つずつの構成です。URL変換フィルタの機能は利用しませんので、config/queues_mysql.cfgの modify_rules
の設定はコメントアウトしておいてください。
ベースとなるサンプルの一連のコードは以下からも取得できます。
% svn checkout http://ap4r.rubyforge.org/svn/tags/200709_gihyo_async_shop
準備
本題にはいる前にAP4Rのアップデートをしましょう。最新バージョンのap4r-0.3.3がリリースされています。テストサポート機能はこのリリースから含まれていますので、これからご紹介する内容を試すためには必須となります。
% gem update ap4r
また、async_shop/as_rails/vendor/plugins以下のプラグインも上書きします。as_rails/vendor/plugins/ap4r以下をいったん消したのち、以下のコマンドを実行してください。インターネット環境に接続できない場合は、RubyGemsでインストールされた AP4Rパッケージのgems/ap4r-0.3.3/rails_plugin/以下をコピーしてご利用ください。
% ruby script/plugin install http://ap4r.rubyforge.org/svn/tags/ap4r-0.3.3/samples/HelloWorld/vendor/plugins/ap4r
SAF用テーブルの準備
SAF機能でメッセージを保管するテーブルを用意します。すでにordersテーブルとpaymentsテーブルの作成に使ったmigrationファイルがあるので、以下のような結果になると思います。
% cd as_rails
% ruby script/generate migration create_table_for_saf
exists db/migrate
create db/migrate/003_create_table_for_saf.rb
db/migrate/003_create_table_for_saf.rbを以下のように編集します。
class CreateTableForSaf < ActiveRecord::Migration
def self.up
create_table :stored_messages do |t|
t.column :duplication_check_id, :string, :null => false
t.column :queue, :string, :null => false
t.column :headers, :binary, :null => false
t.column :object, :binary, :null => false
t.column :status, :integer, :null => false
t.column :created_at, :datetime, :null => false
t.column :updated_at, :datetime, :null => false
end
end
def self.down
drop_table :stored_messages
end
end
RubyGemsでインストールされたAP4Rのなかにもmigrationファイルのサンプルがあります(gems/ap4r-0.3.3/lib/ap4r/xxx_create_table_for_saf.rb) 。xxxの部分を003に変えてご利用ください。
ap4r.transaction
準備は整ったので、さっそくコードの拡張にはいりましょう。変更するのは、app/controllers/async_shop_controller.rbです。以下に修正前のものと修正後のものを並べます。
修正前
class AsyncShopController < ApplicationController
(省略)
def order
begin
Order.transaction do
@order = Order.new(params[:order])
@order.save!
ap4r.async_to({:action => 'payment'},
{:order_id => @order.id})
flash[:notice] = 'Order was successfully created.'
redirect_to :action => 'index'
end
rescue Exception
flash[:notice] = 'Order was failed.'
render :action => 'order_form'
end
end
(省略)
end
修正後
class AsyncShopController < ApplicationController
(省略)
def order
begin
ap4r.transaction do
@order = Order.new(params[:order])
@order.save!
ap4r.async_to({:action => 'payment'},
{:order_id => @order.id})
flash[:notice] = 'Order was successfully created.'
redirect_to :action => 'index'
end
rescue Exception
flash[:notice] = 'Order was failed.'
render :action => 'order_form'
end
end
(省略)
end
違いに気づいていただけたでしょうか?
def order
begin
Order.transaction do
が
def order
begin
ap4r.transaction do
に変わっているだけです。
修正は以上ですが、せっかくなので ap4r.transaction
メソッドの動きをもう少しみてみましょう。
図3 ap4r.transaction
の裏側
上の図から分かるように、処理は2段階で行なわれます。まず、ブロック内の ap4r.async_to
メソッドの部分では、アプリケーションによる業務DBへのCRUDと同様にSAF用テーブルにメッセージが保管されます。SAF用のテーブルはさきほどmigrationにて業務DB上に準備しました。したがって、トランザクションはアプリケーションのCRUDと一緒になります。ここがSAF(Store and Forward)のStoreにあたります。
次に、ブロックを抜けたところで業務DBがコミットされます。これで、業務DBへのCRUDの情報とともに、非同期連携用に作成されたメッセージの情報もいったん業務DB内に永続化されます。そして、そのメッセージの情報をAP4Rサービスに送信します。ここが、Forwardにあたります。
こうして、業務DBのCRUDとメッセージの生成がアトミックに処理されることになり、信頼性の高いメッセージングが実現されます。
実行
実行してみましょう。RailsとAP4Rを起動してください。
% cd as_rails
% ruby script/server
% cd as_ap4r
% ruby script/mongrel_ap4r start -A config/queues_mysql.cfg
以下のURLにアクセスします。
「New order」リンクから注文フォーム画面を開き、適当なアイテム名を入力して「Order」ボタンで注文します。注文が成功した場合は、注文結果のリストが表示されます。第2回までの作業で、「 重い」決済処理を注文処理から分離していますので、決済日時(Payed at)の項目が「Not yet.」として表示されるでしょう。タイミングによっては、ユーザーからの注文結果表示のリダイレクト処理より先に、AP4Rからの決済処理のリクエストが実行されてしまい、注文結果の画面がすぐに開かないかもしれません。その場合は、リバースプロキシを利用した構成や、第2回の最後 に紹介した、非同期処理を別プロセスに送信する構成をお試しください。
無事に注文結果が表示されたでしょうか?
ユーザーから見た動きは、第2回で非同期拡張したアプリケーションから変わっていませんね。
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 データがないのに、メッセージがある場合
この状況を擬似的にコードでつくると、次のように 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失敗時の対応など、リカバリ方法については、次回にまた詳しくとりあげる予定です。
次ページからは安心非同期のもうひとつの柱、テストサポートについてお話しします。
AP4R におけるテストサポート
AP4Rでのテストの方法には、functionalとasyncの2種類があることに触れました。ここからは、それぞれの動作概要を確認した後、テストの書き方と実行について説明します。
テストの動作概要
1つ目は、Rails付属の3種類の方法のひとつであるfunctionalテストを用います。アクション単体のテストを行う際に、メッセージのキューイングをスタブ化して、実際には通信無しでテストを行います(業務DBへの通信は残っています) 。これは、ネットワーク通信など、外部プロセスに関連するテストを行う際に良く用いられる手法です。
図5 functionalテストではキューイングをスタブ化して通信無しでテストを実行
このテスト方法の利点は、メモリ上でテストの実行が完結するため、テストを速く実行出来ることです。つまり繰り返し実行しやすいテストになっています。一方、実際の動作とは異なるため、ある種のエラー処理のテストが難しいことや、場合によっては、オブジェクト直列化(serialization)の制限など、通信ライブラリに特有の問題発見が出来ないこともあり、他のテスト方法にて補完する必要があります。
2つ目は、「 asyncテスト」という方法で実行するものです。この方法では、実際の通信を含め、実動作と(なるべく)近いテストを行います。ネットワーク通信や、メッセージDBへのメッセージ保存なども行います。この方法が、先程のfunctionalテストの弱点を補完するテストになります。
図6 asyncテストではプロセス間の通信を含めてテストを実行
この2種類のテストは、それぞれActionMailerのメール送信のスタブ化、およびRailsプロセスを起動して行うSeleniumでのテストと比較してもらうと理解しやすいかもしれません。
functionalテストの書き方と実行
functionalテストでAP4R特有なところは以下の2点です。
キューイングをスタブ化するコードを読み込む
キューイングされたメッセージに対してアサーションを書く
まず、スタブ化のコード読み込みのためにas_rails/test/test_helper.rbに以下の行を付け加えます。
require 'ap4r/queue_put_stub'
テストコードは、controllerを生成した際に作成されているas_rails/test/functional/async_shop_controller_test.rbに記述します。
ここでは、order
アクションを呼び出して、メッセージ1件のキューイングを確認するテストを書くことにしましょう。
def test_order
post :order, :order => {:item => "introduction to AP4R"}
assert_response :redirect
assert_redirected_to :action => 'index'
messages = @controller.ap4r.queued_messages # ... (1)
assert_equal 1, messages.keys.size, "should have messages in just ONE queue"
assert messages.key?("queue.async_shop.payment"), "queue name is INCORRECT" # ... (2)
assert_equal 1, messages["queue.async_shop.payment"].size,
"should have just ONE message for payment"
assert_match /order_id=\d+/, messages["queue.async_shop.payment"].first[:body],
"parameter order_id should be included with a numeric value" # ... (3)
end
(1)では、AP4Rのクライアント(@controller.ap4r
)を通じて、非同期処理のメッセージにアクセスしています。通信のスタブ化により、このクライアントはキューイングされるべきメッセージをメモリ上に保持するようになっているのです。このメッセージが詰った Hash
オブジェクトは、キュー名をキーとして、メッセージ本体(:body
)とヘッダー(:headers
)の組を配列として持つ構造となっています。具体的に、(1)で得られるオブジェクトをYAML 形式で表すと次のようになっています。
---
queue.async_shop.payment:
- :body: order_id=1
:headers:
:target_method: POST
:delivery: :once
:dispatch_mode: :HTTP
:queue_name: queue.async_shop.payment
:target_url: http://test.host/async_shop/payment
このオブジェクトに対し、正しい名前のキューに入っているか(2)、メッセージ本体に order_id
というキーを含むかどうか(3)を確認しています。
functionalテストの実行は、rakeを通じて、または直接テストクラスを読み込むことで行います。as_railsディレクトリにて、以下のコマンドを実行します。
% rake test:functionals
または
% ruby test/functional/async_shop_controller_test.rb
テストが成功した場合、以下のような表示になります。
Finished in 0.066875 seconds.
2 tests, 7 assertions, 0 failures, 0 errors
このようにキューイングをスタブ化することで、単一アクションのテストを行うことが出来ました。
asyncテストの書き方と実行
非同期処理のテストで実際に通信を行うには、複数のプロセスを起動する必要があります。AP4Rプロセスに加え、AP4RからHTTPで呼び出されるRailsのプロセスも起動しておく必要があります。
まとめると、asyncテストでは、以下の3つのプロセスが登場します。
テストのクライアントなるプロセス: Test::Unit
を実行
Railsプロセス: mongrel_railsを実行
AP4Rプロセス: mongrel_ap4rを実行
これらのプロセス間の関係は、前ページ図6になります。
テストの実行方法の前に、テストコードを見てみます。as_rails/test/asyncディレクトリを作成し、ap4r_test_helper.rb と async_shop_test.rbを以下の内容で作成します。
ap4r_test_helper.rb
ENV["RAILS_ENV"] = "test"
require File.expand_path(File.dirname(__FILE__) + "/../../config/environment")
require "ap4r/service_handler"
ap4r_test_helper = Ap4r::ServiceHandler.new
require 'test_help'
class Test::Unit::TestCase
self.use_transactional_fixtures = false
self.use_instantiated_fixtures = false
# Add more helper methods to be used by all tests here...
cattr_accessor :ap4r_helper
def ap4r_helper
@@ap4r_helper
end
def with_services(&block)
ap4r_helper.with_services(&block)
end
end
Test::Unit::TestCase.ap4r_helper = ap4r_test_helper
async_shop_test.rb
require "#{File.dirname(__FILE__)}/ap4r_test_helper"
require 'net/http'
# Test cases with ap4r integration.
# some comments:
# - Clearance of data in a database and queues should be included.
# - Workspaces of ap4r and rails should have some conventions for convinience.
# - Think about transition to RSpec.
# - HTTP session holding support is needed?
class AsyncShopTest < Test::Unit::TestCase
def test_http_dispatch
ap4r_helper.stop_dispatchers # ... (1)
assert_rows_added(Order, 1) { # ... (3)
do_order # ... (2)
}
assert_rows_added(Payment, 1) { # ... (6)
ap4r_helper.start_dispatchers # ... (4)
ap4r_helper.wait_all_done # ... (5)
}
end
private
# Requests to <tt>async_shop/order</tt>.
def do_order(item_name = "test item")
Net::HTTP.start("localhost", 3000, nil, nil) do |http|
http.request_post("/async_shop/order",
"order[item]=#{item_name}") do |res|
#nop
end
end
end
def assert_rows_added(model, rows)
rows_before = model.count
yield
rows_after = model.count
assert_equal rows, rows_after - rows_before, "table '#{model.table_name}' should count up by #{rows}"
end
end
テストメソッド test_http_dispatch
の中で行っていることは以下のようになります。番号は、コメントにある数字と対応しています。
AP4Rサービスを操作する ap4r_helper
を用いて、非同期処理の呼び出しスレッドを停止させます。
Railsへ、HTTP POSTのリクエストを発行し、
orders
テーブルに 1 行追加されたことを確認します。
非同期処理の呼び出しスレッドを開始させ、
非同期処理が終わるまで待機し、
payments
テーブルに 1 行追加されたことを確認します。
それでは、実行してみましょう。
Railsプロセス、AP4Rプロセスの起動
まずは、Railsプロセスと AP4Rプロセスを起動します。プロセス起動のまえに、これまで開発で用いていたRailsやAP4Rのプロセスが起動していないことを確認しておいてください。
Unix系のOSでは、AP4Rプラグインが提供するRakeのタスクで起動できます。設定ファイルを作成した後、Rakeコマンドを実行してください。
設定ファイルは下記のとおりです。
as_rails/config/test_async.yml
ap4r:
root_dir: ../as_ap4r
config_file: config/queues_mysql.cfg
rails:
% cd as_rails
% rake test:asyncs:arrange
Windowsでは、このRakeコマンドは使えませんので、以下の2つのコマンドを、別々のコマンドプロンプトで実行してください。
1つ目のコマンドプロンプト
% cd as_rails
% ruby script/server -e test
2つ目のコマンドプロンプト
% cd as_ap4r
% ruby script/mongrel_ap4r start -A config/test_queues.cfg
テストの実行
テストの実行は、rakeを通じて、またはテストクラスを直接読み込んで行います。
% rake test:asyncs:run
または
% ruby test/async/async_shop_test.rb
テストが成功すると、以下の表示となります。
Finished in 5.530491 seconds.
1 tests, 2 assertions, 0 failures, 0 errors
今回は、非同期処理である payment
アクションも実行されているため、( スリープ時間である)5秒程度の時間がかかっていることが分かります。
後片付け
起動しているプロセスを終了させます。Unix系のOSではRakeタスクで終了させることが出来ます。
% cd as_rails
% rake test:asyncs:cleanup
Windowsでは、RailsとAP4Rを実行している2つのコマンドプロンプトで Ctrl-C
を押すことで、それぞれのプロセスを終了させてください。
アクション単体のテストを手軽に行うためのfunctionalテストと、通信を含めて実動作に近いテストを実行するasyncテストの2つの方法を説明しました。このテストサポートでは、Rubyの柔軟さを活用させてもらっています。functionalテストでのスタブ化は、オープンクラスの性質を利用しています。またAP4R自体は(reliable-msgを通して)druby上に実装されているため、色々なところでdrubyの恩恵を得ています。asyncテストでは、AP4Rプロセスの制御のためにネットワーク越しにdrubyで通信を行いながらテストが実行されています。
テストサポートは追加されたばかりの機能で、まだ骨組みだけの状態です。テストコードの可読性を向上させるためのボキャブラリ(API)整備や、テスト自動化、異常系のテスト容易性などは今後向上させていく予定です。
第3回では、安心のためのSAF機能とテストサポートをみてきました。SAFによりat-least-onceという実行時の堅牢さが保証され、メッセージが消失しない安心を得ることができます。そして、非同期まで含めたアプリケーションの品質を保証するテストの仕組みにより、TDD/BDDと同じように開発、リリースに対する安心感を得ることができるのではないでしょうか。
最終回となる次回は、負荷分散のための設定と、SAF Forwardエラーのリカバリ、及び業務処理がエラーとなったメッセージのリカバリを説明する予定です。