可用性に関する検討事項
PaaSであるCosmos DBではSLAによって可用性が担保されていますが、アプリケーション全体としての可用性を高めるためにも検討すべき項目があります。
リトライ処理を組み込む
Cosmos DBのDocumentDB APIでは、コレクションに設定したRUを超える操作を行うと、スループットを平坦化するためにリクエストを処理せずにHTTPレスポンスコード429(RequestRateTooLarge)を返します。Cosmos DBのSDKを使用している場合は、HTTPレスポンスコード429でレスポンスを受けると暗黙的にリトライを実行してくれます。SDKによる暗黙的なリトライを数回行っても操作が終了しなかった場合は例外(DocumentClientException)を返します。そのためクライアント側では、例外とHTTPレスポンスコードをハンドリングし、失敗したリクエストをリトライするように実装することで、一時的な高負荷によるドキュメント操作の失敗を軽減することができます。
以下のコードは、SDKによる暗黙的なリトライでも操作が終了しなかった場合に、さらにリトライを実行するクライアントコードのサンプルです。
protected static async Task<T> ExecuteWithRetries<T>(Func<Task<T>> function)
{
var sleepTime = TimeSpan.Zero;
while (true)
{
try
{
// Cosmos DBに対する操作を実行する(DocumentDB APIの呼び出し)
return await function();
}
catch (DocumentClientException e)
{
// HTTPレスポンススコードが429であればリトライする
if ((int) e.StatusCode == 429)
{
// DocumentClientExceptionに含まれるリトライまでの待機時間を取得する
sleepTime = e.RetryAfter;
}
else
{
// HTTPレスポンスコードが429以外であればスローする
throw;
}
}
catch (AggregateException e)
{
// AggregateExceptionの内部例外にDocumentClientExceptionが含まれているか調べる
if (e.InnerException is DocumentClientException)
{
var de = (DocumentClientException) e.InnerException;
if ((int) de.StatusCode == 429)
{
// DocumentClientExceptionに含まれるリトライまでの待機時間を取得する
sleepTime = e.RetryAfter;
}
else
{
// HTTPレスポンスコードが429以外であればスローする
throw de;
}
}
else
{
// DocumentClientException以外の例外の場合はスローする
throw e;
}
}
// スリープ後にリトライする
await Task.Delay(sleepTime);
}
}
このメソッドでは、引数で受け取った何らかのDocumentDB APIの処理を実行し、例外とHTTPレスポンスコードの種類によってリトライの要否を決定しています。リトライが必要な場合は、リトライまでの待機時間が例外のプロパティ(RetryAfter)に含まれているのでそれを使って待機し、もう一度処理を実行するようにしています。
以下のコードは、リスト1のメソッドでラップしたアップサート処理の例です。
public static async Task UpsertDocumentAsync(T document)
{
try
{
// ドキュメントのアップサート処理をリトライ可能にする
await ExecuteWithRetries(() => client.UpsertDocumentAsync(
UriFactory.CreateDocumentCollectionUri(DatabaseId, CollectionId), document));
}
catch (Exception)
{
throw;
}
}
スケールアップ/ダウン
Cosmos DBはRUをダウンタイムなしでスケールアップ/ダウンすることができます。スケールは、Azureポータル画面かSDKを使用して設定します。Azureポータル画面でのスケールは、手動での即時スケールのみに対応しており、スケジュールやしきい値をトリガーとした、細やかなスケールの設定はできません。
現時点ではCosmos DBにオートスケールの機能がありませんが、他のAzureサービスと組み合わせてスケジュールやしきい値ベースのオートスケールを実現できます。
スケジュールベースの場合は、Azure WebJobsやAzure Functionsなどのスケジューラー機能を持ったサービスを使います。Azure WebJobsやAzure Functionsをタイマー起動で実行するように指定し、その中でCosmos DBのSDKのスループット変更APIを呼び出します。
しきい値ベースの場合は、Cosmos DBの「アラートルール」とSDKのスループット変更APIを組み合わせます。「アラートルール」は時間あたりのリクエスト数やRU消費量をしきい値に、アラートを発報させることができます。アラートの通知先としてWebhookが指定可能なため、スケールアップ/ダウンを行うAPIを用意し、そこへWebhookを設定することでスケールアップ/ダウンをコントロールします。
以下のコードは、引数で指定したRU/sにスループットを変更するコードの例です。
public static async Task ReplaseRequestUnitsAsync(int desiredRequestUnits)
{
// (1)コレクションのインスタンスを取得する
var collection = client.ReadDocumentCollectionAsync(
UriFactory.CreateDocumentCollectionUri(DatabaseId, CollectionId)).Result.Resource;
// (2)コレクションの現在のRU/s情報を保持するインスタンスを取得する
var offer = client.CreateOfferQuery()
.Where(r => r.ResourceLink == collection.SelfLink)
.AsEnumerable()
.SingleOrDefault();
// (3)新しいRU/sの値でインスタンスを置き換える
offer = new OfferV2(offer, desiredRequestUnits);
// (4)DocumentDB APIのスループット変更APIを呼び出す
await client.ReplaceOfferAsync(offer);
}
(2)でデータベースアカウントの中からスループットを変更したいコレクションを検索するために、(1)であらかじめコレクションのインスタンスを取得しています。(2)ではコレクションに割り当てられている現在のRU/sを保持するOfferインスタンスを取得し、(3)で新しいRU/sに置き換えます。最後に(4)でDocumentDB APIのメソッドを呼び出すことで、コレクションのスループット変更がCosmos DBに反映されます。
ストレージの拡張
Cosmos DBのストレージには固定と無制限のプランがあります。固定の場合は最大10GBまでのストレージ上限があり、それ以上の容量に拡張することができません。固定プランを利用する場合は、事前にアプリケーションのデータサイズの見積もりをして10GBを上回ることがないかをよく確認することをお薦めします。
また、無制限プランで10TB以上の容量を必要とする場合は、Azureサポートに容量引き上げリクエストを送る必要があります。
現在コレクションがどの程度ストレージを使用しているかは、Azureポータル上でCosmos DBの概要メニューから簡単に確認することができます。
より詳しい、コレクションのストレージ使用量の情報は「メトリック」画面の「ストレージ」タブから確認できます。
ドキュメントとインデックス別のストレージ使用量や各パーティション別のストレージ使用量・ドキュメント数をこの画面で見ることができます。
バックアップ
Cosmos DBは、以下の要件で全てのデータをバックアップします。
- 全てのデータを4時間ごとに自動的に取得
- 最新のスナップショットは90日間保持される
- 8時間以内であればAzureサポートへの問い合わせでデータの復元が可能
- バックアップはAzureの別ストレージサービス(Azure Blob Storage)に自動で保存され、その中でもレプリケートされる
- RUの消費やパフォーマンスに影響を与えない
さらに対象外性を高めるために、データを地理的に離れた別リージョンにレプリケートすることもできます。詳細はCosmos DBのグローバル分散に関するドキュメントをご確認してください。
