実践における IBM Cloudant

プラクティス文書内の IBM Cloudant は、このシリーズの 3 番目のベスト・プラクティス文書です。 以下のベスト・プラクティスが示されています。

  • 競合を回避する方法。
  • 文書の削除方法。
  • 更新について注意すべきこと。
  • 結果整合性のある環境で作業する方法。
  • 複製のセットアップ方法。
  • バルクAPIの使い方
  • Q、R、および N を変更してはならない理由。
  • レート制限がどのように機能するか。
  • どのロギングが追跡されるか。
  • HTTP トラフィックを圧縮する方法。

詳しくは、データ・モデリングまたは索引付けと照会を参照してください。

本書の内容は、2019 年 11 月 21 日に Stefan Kruger が「Best and worst practice」というブログ記事として執筆したものです。

競合の回避

IBM Cloudant は、競合を分散システム内のデータの自然な状態として扱うように設計されています。 これは、IBM Cloudant クラスターが常に高可用性を維持するのに役立つ強力な機能です。 ただし、競合は依然としてかなりまれであるという前提があります。 IBM Cloudant のコアでの競合の追跡には、それに関連する大きなコストが伴います。

競合を無視することは十分に可能です (ただし、お勧めしません)。 データベースは、競合する文書のランダムで決定論的なリビジョンを選択することによって、稼働し続けます。 ただし、未解決の競合の数が増えるにつれて、特に複製時に、データベースのパフォーマンスが低下します。

開発者には、競合が存在するかどうかを確認し、競合を解決する責任があります。さらには、競合を不可能にするデータ・モデルを使用する責任もあります。

競合が日常的に発生している場合は、モデルの変更を実際に検討する必要があります。競合をコツコツと解決しても、リビジョン・ツリー内の競合ブランチはそのまま残り、それを整理する簡単な方法はありません。 詳しくは、以下の Web サイトを参照してください。

文書を削除しても削除されない

IBM Cloudant データベースから文書を削除しても、その文書はパージされません。 削除は、削除対象の文書の新規リビジョンを作成し、それにフィールド _deleted: true を追加することで実装されされます。 この特別なリビジョンは tombstone と呼ばれます。 トゥームストーンは依然としてスペースを占有するだけでなく、レプリケーターによって渡されます。

文書の頻繁な削除に依存するモデルは、IBM Cloudant には適していません。 詳しくは、IBM Cloudant のトゥームストーンに関する資料を参照してください。

更新には注意

新しい文書を作成するよりも、既存の文書を変更する方が最終的にはコストが高くなります。IBM Cloudant は、常に文書ツリー構造を維持する必要があります。 このルールは、ツリー内の内部ノードのペイロードが除去されても適用されます。 長いリビジョン・ツリーを作成すると、複製のパフォーマンスが低下します。 さらに、更新頻度が、例えば数秒に 1 回から 2 回よりも高い場合、更新の競合が発生する可能性が高くなります。

モデルは不変であることが望ましいです。

以下のセクション「 文書を削除しても文書は削除されません 」および *「更新に注意してください」*を読むと、明らかな質問が生じます。 「モデルが不変である場合、データ・セットは無制限に拡大するのか」という明白な疑問を引き起こします。 削除しても削除されたデータは完全にはパージされないし、データ量の増加という点では更新はインプレースでの更新ではないということを受け入れるなら、それほど大きな違いはありません。 一定期間をとおしてデータ量を管理するには、さまざまな手法が必要です。

スペースを実際に再利用する唯一の方法は、文書ではなくデータベースを削除することです。 優先的なリビジョンだけを新しいデータベースに複製し、古いデータベースを削除することで、延々と続く削除と競合を解消することができます。 あるいは、定期的に新しいデータベース (例えば、「年次データ」) を起動し、古いデータをアーカイブ (または削除) するようなモデルを構築することもできます (ユース・ケースに適している場合)。

結果整合性 (「don’t read your writes」ともいう) は厳しい課題

結果整合性は、紙面上では素晴らしいアイデアであり、実践における IBM Cloudant のスケールアウト機能に大きく貢献しています。 しかし、結果整合性を採用したデータ・ストアを対象に開発を行うとき必要な考え方は、ほとんどの人にとって自然な考え方ではないでしょう。

次のようなテストを書くと、よく刺される:

  1. データベースの作成。
  2. データベースにテスト・データを取り込む。
  3. このテスト・データのサブセットをデータベースに照会する。
  4. 返されたデータが、予期したデータであることを確認する。

そのテストに問題はありませんでしたか。 それは、あなたがこれまで使ってきた他のすべてのデータベースで機能しますよね?

しかし、IBM Cloudant にはそうではありません。

というより、100 回のうち 99 回は有効です。

この違いの理由は、データベースにデータを書き込んでから、そのデータがクラスタの全ノードで利用できるようになるまでの間の(ほとんど)小さな不整合ウィンドウである。 クラスタ内のノードはすべて同じ大きさであるため、書き込みとそれに続く読み出しが同じノードで処理される保証はない。 そのため、状況によっては、書き込まれたデータがノードに届く前に、読み込まれたデータがノードに到達する場合があります。

では、書き込みと読み出しの間に短い遅延をテストに組み込んだらどうでしょうか。 遅延を組み込むことにより、テストが失敗する可能性は低くなりますが、問題がなくなるわけではありません。

IBM Cloudant には、トランザクションの保証はありません。 文書の書き込みはアトミックですが (文書全体が読み取られるか、まったく読み取れないことが保証される)、不整合ウィンドウをなくす方法はありません。 これは、設計によるものです。

すべての開発者が考慮しなければならない重大な問題は、自分が作成したデータを特定の時点で他の誰もが使用できることを安全に想定できないということです。 異なる種類のデータベースの伝統に慣れ親しんでいる場合は、この状態にも少し慣れる必要があります。

テストのヒント: テストで不整合ウィンドウを回避するためにできることは、 IBM Cloudant または CouchDB のシングルノードインスタンスに対して、 Docker (docker 情報 ) で実行するテストを行うことです。 1 つのノードにテストを実行すると、結果整合性の問題は排除できますが、実動環境とは異なる動作の環境にテストを実行しているということに注意してください。 禁物

複製は魔法ではない

“So let’s set up three clusters across the world, Dallas, London, Sydney, with bi-directional synchronization between them to provide real-time collaboration between our 100,000 clients.”

いいえ 単純に いいえ IBM Cloudant は複製を得意としています。 魔法のように見えるかもしれませんが、待ち時間の保証はないことに注意してください。 実際、システム全体が結果整合性を念頭に置いて設計されています。 IBM Cloudant の複製機能をリアルタイムのメッセージング・システムとして扱うと、問題が発生します。 このユース・ケースでは、Apache の Kafka などのようなこの目的のために設計されたシステムを間に挟みます。

複製のスループットを数値で表すのは困難です。 答えは常に「場合による」です。 複製のパフォーマンスに影響を与えるものには、以下のものがありますが、これらに限定されません。

  1. 変更頻度
  2. 文書サイズ
  3. クラスター全体における同時複製ジョブの数
  4. 幅の広い (競合する) 文書ツリー
  5. 予約されたスループット能力の設定

詳しくは、以下の Web サイトを参照してください。

バルク API の使用

IBM Cloudant には、1回のリクエストで多数のドキュメントを一括ロード(および読み込み)するための優れたAPIエンドポイントがある。 一度のリクエストで多くの文書を読むことは、一度に多くの文書を読み書きするよりもはるかに効率的である。 以下の例では、書き込みエンドポイントを示します。

${database}/_bulk_docs

その主な目的は、レプリケーター・アルゴリズムの中心的な部分となることですが、ユーザーもそれを使用することができ、非常に優れています。

_bulk_docs を使用すると、PouchDB の作成に加えて、単一の文書に対しても、より少ないコード・パスで、この方法で作成、更新、および削除を実装できます。

以下の例では、1 つの新規文書を作成し、2 つ目の既存文書を更新し、3 つ目の文書を削除します。

curl -XPOST 'https://ACCT.cloudant.com/DB/_bulk_docs' \
     -H "Content-Type: application/json" \
     -d '{"docs":[{"baz":"boo"}, \
         {"_id":"463bd...","foo":"bar"}, \
         {"_id":"ae52d...","_rev":"1-8147...","_deleted": true}]}'

また、 _all_docs_bulk_get という比較的新しいエンドポイントも存在するが、このエンドポイントはおそらくあなたが望むものではないだろう)にPOSTを発行することで、1回のリクエストで多くの文書を取得することもできる。 これは特定の内部目的のために存在します)。

keys 本体を指定した _all_docsPOST を使用して文書の固定セットをフェッチするには、次のコマンドを実行します。

curl -XPOST 'https://ACCT.cloudant.com/DB/_all_docs' \
     -H "Content-Type: application/json" \
     -d '{"keys":["ab234....","87addef...","76ccad..."]}'

IBM Cloudant (作成時点) では、最大要求サイズは 11 MB と定められています。このサイズを超える _bulk_docs 要求は、413: Payload Too Large error で拒否されます。

詳しくは、以下の Web サイトを参照してください。

変更の結果を理解していない限り Q、R、N は変更しない

Q、R、Nは、自分が何をしているのか本当に分かっていない限り、変更しないでください。 IBM Cloudant クォーラムとシャーディングのパラメータを発見した後は、データベースの動作を変更する魅力的なオプションのように思えます。

整合性を強化し、確実に書き込みクォーラムをレプリカ数に設定できるのでは?

いいえ。 クラスター内の不整合ウィンドウをなくす方法は存在しないことを思い出してください。

そこは変更しないでください。 特にネットワーク分割時に、動作を理解するのが難しくなる可能性があります。 Cloudant-the-service を使用している場合、ほとんどのユーザーにはデフォルト値で問題ありません。

可能な限り最高のパフォーマンスを得るためには、データベースのシャード数を微調整することが不可欠である場合があります。 しかし、その理由を理解していない場合は、状況が悪化する可能性があります。

IBM Cloudant 料金には制限があります

Cloudant-the-service (基本的な CouchDB とは異なる) は、「予約されたスループット能力」モデルで販売されています。 つまり、最終的に使用するスループットではなく、一定のスループットまでの使用権に対して料金を支払うということだ。 この使用権方式は、理解するのに時間がかかります。 ざっくりとした比較にはなりますが、使用するかどうかにかかわらず、決められた分数の料金を支払う携帯電話の契約と似ているかもしれません。

ただし、携帯電話の契約との比較では、状況全体を把握することはできないかもしれません。1 カ月に IBM Cloudant に対して行うことができる要求の合計に制限はありません。 制限は、要求を行う速度にかけられます。

これは、ユーザーが IBM Cloudant に対して行う約束であり、IBM Cloudant がユーザーに対して行う約束ではありません。 ユーザーは、1 秒当たりの要求数が、事前に合意した要求数を超えないことを約束します。 最大速度制限とでも言いましょうか。 もしユーザーが違反した場合、IBM Cloudant は、429: Too Many Requests というステータスでユーザーの要求を失敗させます。 このことに気を配り、対処するのはユーザーの責任ですが、複数のアプリケーション・サーバーが存在する場合は難しいかもしれません。 1秒あたりのリクエスト数を制限するために、どのように調整すればいいのだろうか?

IBM Cloudantの公式クライアント・ライブラリーには、「バックオフおよび再試行」戦略に従って有効にできる、このユース・ケース用のいくつかの組み込みプロビジョンがあります。

この組み込みプロビジョンは、考慮を強制するためにデフォルトでオフになっています。

ただし、この機能だけに頼ると、やがて失望するかもしれません。 「バックオフと再試行」戦略は、一時的な違反の場合にのみ役立ち、プロビジョン済みスループット能力の制限に対する永続的な対抗策にはにはなりません。

ビジネス・ロジックは、このような状況に対応できるものでなければなりません。 別の見方をすれば、ユーザーは支払った分の割り当てを受けることになります。 割り当てが十分でない場合、より高い割り当て分の料金を支払うしかありません。

プロビジョン済みスループット能力は、ルックアップ書き込み、および照会の 3 つの異なるバケットに分割されます。 ルックアップ は、「1 次キー」の読み取りであり、その _id に基づく文書の取り出しです。 書き込みは文書や添付ファイルのディスクへの保管で、照会は副次索引 (_design または _find を持つ API エンドポイント) を使用した文書の検索です。

それぞれの割り振りは異なり、それらの間の比率は固定されています。 この事実は、コストを最適化するために使用できます。 ユーザーは、1 つの照会に対して、20 のルックアップを取得します (1 秒あたり)。 主に照会が制限に達するかもしれませんが、ルックアップには十分な余裕があります。 照会への依存度は、データを再モデル化したり、クライアント・サイドでの作業を増やしたりすることで、下げることができるかもしれません。

ただし、サード・パーティーのライブラリーやフレームワークが、利便性よりもコストに対して最適化するとは想定できません。 プラグインを使用して複数のパーシスタンス・レイヤーをサポートするクライアント・サイドのフレームワークは、この状況を認識していない可能性が高いか、そのようなトレードオフを行うことができない可能性があります。

特定のツールにコミットする前に、サード・パーティーのライブラリーまたはフレームワークの互換性を確認することをお勧めします。

また、速度が HTTP API エンドポイント呼び出しと直接等価ではないことも理解しておくとよいでしょう。 例えば、一括更新は、その構成要素の文書書き込みに従ってカウントされることを想定する必要があります。

ロギングは何が起きているかを確認するのに役立つ

IBM Cloudant APIコールが行われ、何がリクエストされ、レスポンスにどのくらい時間がかかったかを示すログは、 IBM Cloud ベースのサービスの分析とレポート作成のために、 IBM Cloud Logs に自動的にスプールされる。 このデータは、要求のボリューム、パフォーマンス、およびアプリケーションが IBM Cloudant サービスのプロビジョン済み容量を超えているかどうかを監視するのに役立ちます。

IBM Cloud Logsは、さまざまな保持期間とログ消費階層を提供する従量制のサービスです。 階層によって、データの保持、COSへのアーカイブ、コールドサーチ、ホットサーチ、さまざまなコストでのアラートが可能になる。 データのスライスや集約をビジュアル・ダッシュボードに組み込むことで、IBM Cloudant トラフィックを一目で確認することができます。 詳しくは、以下の資料を参照してください。

HTTP トラフィックの圧縮

コードで JSON 形式のデータを処理できることを示す HTTP ヘッダーを要求に指定すると、IBM Cloudant はユーザーに対する JSON 応答を圧縮します。

Request:

> GET /cars/_all_docs?limit=5&include_docs=true HTTP/2
> Host: myhost.cloudant.com
> Accept: */*
> Accept-Encoding: deflate, gzip

Response:                                                                   

< HTTP/2 200
< content-type: application/json
< content-encoding: gzip

圧縮されたコンテンツのサイズは、そのコンテンツが解凍された状態のときと比べて数分の一であるため、IBM Cloudant のサーバーからご使用のアプリケーションへのデータ転送にかかる時間が短縮されます。

コンテンツ・エンコード・ヘッダーを使用して HTTP 要求本文を圧縮することもできます。 この方法は、 IBM Cloudant に文書を書き込む際のデータ転送時間を短縮するのに役立つ。