自社サイトのURLとキーワードのマッチングする必要する必要があり、フルマネージドで簡単に使える検索エンジンであるAWS CloudSearchを利用しています。
APIとして簡単に利用できる上、負荷に応じてオートスケールしてくれるため、「簡単に検索ドメインを作りたいし運用で楽したい」という要件にピッタリでした。
ところが、新しい地名が削除・追加されるたびに、必要な自社サイトのURLのリストも少しずつ変わってしまいます。そのため、それほど頻繁ではないものの、「定期的にドキュメントを全削除して入れ直す」という処理が必要でした。
しかし、公式ドキュメントを確認したところ、ドメイン内すべてのドキュメントを削除するAPIは用意されていません。
Amazon CloudSearch ドメイン内のすべてのドキュメントの削除
Amazon CloudSearch には現在、ドメイン内のすべてのドキュメントを削除するメカニズムが用意されていません。
ドメインを削除・再作成するRubyスクリプト
そこで、一度ドメイン自体を全削除して再作成することにしました。
いろいろ試行錯誤した結果、以下のようなメソッドを用意したクラスを作りました。Lib::CloudSearch
は、Rubyのaws-sdkライブラリのAws::CloudSearch::Client
の同名のメソッドに、通信が失敗した場合の再接続処理を若干付け足したものです。
# CloudSearchの指定のドメインを削除し、作成しなおす
# (この後ドキュメントをアップロードする別のプログラムが動く)
def rebuild_domain
_recreate_domain
Lib::CloudSearch.define_index_fields(@domain, @index_fields)
Lib::CloudSearch.update_service_access_policies(@domain, _access_policies)
_wait_until_indexing # もしドキュメントのアップロードの処理が必要なら
end
private
# ドメインを一度削除し、再び作成する
def _recreate_domain
Lib::CloudSearch.delete_domain(@domain)
loop do
resp = Lib::CloudSearch.create_domain(@domain)
break unless resp.domain_status.deleted
sleep(60)
end
end
# インデックスが終わるまで待つ
def _wait_until_indexing
loop do
resp = Lib::CloudSearch.describe_domains([@domain])
domain_status = resp.domain_status_list[0]
break unless domain_status.processing || domain_status.doc_service.endpoint.nil?
sleep(60)
end
end
# アクセスポリシーのファイルを読み込んで文字列として返す
# @return [String] アクセスポリシーのJSON文字列
def _access_policies
# 省略
end
ドメインの削除完了を待つ処理
# ドメインを一度削除し、再び作成する
# (この後)
def _recreate_domain
Lib::CloudSearch.delete_domain(@domain)
loop do
resp = Lib::CloudSearch.create_domain(@domain)
break unless resp.domain_status.deleted
sleep(60)
end
end
ちなみに、delete_domain
やcreate_domain
は、削除中や存在しない状態でもエラーを出さずに動作します。そのため、「最初に立ち上げる」場合でも問題なく処理が行われます。
最初、describe_domains
を使ってレスポンスの中のdomain_status_list
が空になるかで判断していたのですが、
resp = Lib::CloudSearch.describe_domains([@domain])
Lib::CloudSearch.create_domain(@domain) if resp.domain_status_list.empty?
空になった後でも、しばらく削除中のドメインが存在している状態で、create_domain
がきちんと動作しませんでした。
インデックスが終わるまで待つ処理
# インデックスが終わるまで待つ
def _wait_until_indexing
loop do
resp = Lib::CloudSearch.describe_domains([@domain])
domain_status = resp.domain_status_list[0]
break unless domain_status.processing || domain_status.doc_service.endpoint.nil?
sleep(60)
end
end
こちらも試行錯誤しており、当初はdomain_status.processing
(ドメインが処理中かどうか)のみで判定していたのですが、それだけでは後続のドキュメントのアップロードの実装でエラーが出てしまい、不十分でした。
具体的には「ドキュメント用のAPIのエンドポイントを調べて、そのエンドポイントのドキュメントアップロードAPIを叩く」という処理で、「ドキュメント用のエンドポイントが見つからない」ことが原因のエラーが出たため、domain_status.doc_service.endpoint.nil?
で判定しています。
(domain_status.doc_service
ではドメインが準備できた場合はエンドポイントの文字列が、無効な場合はnilが返されます。)
…正直、このあたりの実装は自分でも微妙に思うので、もっと良い判定方法を見落としているかもしれません…。
まとめ
ひとまず、Cloud Searchのドキュメントを全削除するためのプログラムを書き、定期的なドメインを作り直す作業を自動化することができました。
作り直すために30分~1時間程度の時間がかかるため、要件によってはドメインを作り直さずに、ドキュメントを削除する処理をループさせるなどの方法を取ったほうがいいかもしれません。