AzureコスモスDBのためのCassandra APIを用いた耐故障性アプリケーションの構築


Azure Cosmos DB に基づいて、1秒あたりのオペレーションの特定の数を実行できるリソース管理システムですprovisioned throughput 設定してください.クライアントがその限界を超えてrequest units プロビジョニングされたものよりも、その後の要求とスローされた例外のレート制限につながる429 errors .
実際の例の助けを借りて、私はあなたのGo これらのレート制限エラーによって影響を受ける操作を取り扱って、再試行することによるアプリケーション.あなたが続くのを助けるために、このブログのためのサンプル・アプリケーション・コードは利用できますon GitHub - これはgocql driver for Apache Cassandra .
このポストでは、我々は行きます:
  • サンプルアプリケーションを実行する前の初期設定と設定
  • 各種負荷テストシナリオの実行と解析
  • リトライポリシー実装の簡単な概要.
  • レート制限に取り組む1つの方法は、アプリケーション要件を満たすためにプロビジョニングされたスループットを調整することです.があるmultiple ways to do this , azureポータル、azure cli、cqlを使用します.
    しかし、アプリケーション自体でこれらのエラーを処理したい場合はどうですか?
    良いことはAzure Cosmos DBのためのCassandra APIがCassandraのネイティブのプロトコルの上でオーバーロードされたエラーにレート制限例外を翻訳するということです.以来gocql ドライバを使用すると、プラグインのプラグインRetryPolicy , これらのエラーをインターセプトするカスタム実装を記述することができます.この政策はbe applied to each Query  または、グローバルレベルで  ClusterConfig .
    The  Azure Cosmos DB extension  ライブラリを使用すると、Javaアプリケーションでリトライポリシーを使用することは非常に簡単です.同等の囲碁版はavailable on GitHub また、このブログ記事のサンプルアプリケーションで使用されています.

    ポリシーを再試行する


    約束通り、あなたは、シンプルで実用的な例を使用して、全体のプロセスを歩いていきます.概念を実証するのに使用されるサンプルアプリケーションは、残りのエンドポイントを公開するサービスですPOST  AzureコスモスDBのCassandraテーブルに永続するデータを注文します.
    このAPIサービスでいくつかの負荷テストを実行すると、制限制限がどのように現れるか、どのように処理されるかを見ることができます.

    事前の要件


    インストール開始  hey , 負荷試験プログラムOS固有のバイナリ(64ビット)をダウンロードできますLinux , Mac and Windows (参照)  the GitHub repo  最新の情報については、ユーティリティをダウンロードして問題に直面する)

    You can use any other tool that allows you to generate load on an HTTP endpoint


    このgithub repoをクローンし、正しいディレクトリに変更します.
    git clone github.com/abhirockzz/cosmos-go-rate-limiting 
    cd cosmos-go-rate-limiting
    

    AzureコスモスDBの設定


    アジュールを作る  Cosmos DB account  と カサンドラ オプション
    keyspaceとtableを作成するには、次のようにしますCQL :
        CREATE KEYSPACE ordersapp WITH REPLICATION = {'class' : 'SimpleStrategy'};
    
        CREATE TABLE ordersapp.orders (
            id uuid PRIMARY KEY,
            amount int,
            state text,
            time timestamp
        );
    

    アプリケーションを起動する


    端末をオープンし、アプリケーションの環境変数を設定します.
    export COSMOSDB_CASSANDRA_CONTACT_POINT=.cassandra.cosmos.azure.com 
    export COSMOSDB_CASSANDRA_PORT=10350 
    export COSMOSDB_CASSANDRA_USER= 
    export COSMOSDB_CASSANDRA_PASSWORD= 
    #optional (default: 5) 
    #export MAX_RETRIES=
    
    アプリケーションを起動する
    go run main.go 
    
    //wait for this output 
    Connected to Azure Cosmos DB
    
    アプリケーションが予想通りに動作しているかどうかをテストするには、別の端末から残りのエンドポイント(各順序に1回)を呼び出していくつかの命令を挿入します.
    curl http://localhost:8080/orders
    
    あなたがエンドポイントを起動しながら、それを入力する必要はありませんので、アプリケーションはランダムなデータを生成します
    順序が正常に格納されていることを確認します.あなたはhosted CQL shell in the Azure portal 以下のクエリを実行します.
        select count(*) from ordersapp.orders;
    
        // you should see this output
        system.count(*) 
        ----------------- 
            1 
        (1 rows)
    
    あなたはすべて決めた.

    負荷テストを開始しましょう!


    残りのエンドポイントを300リクエストで呼び出します.これは、デフォルトで割り当てられた400 Ru/sしかないので、システムをオーバーロードするのに十分です.
    Loadテストを開始するには、次の手順に従います.
    hey -t 0 -n 300 http://localhost:8080/orders
    
    アプリケーション端末のログに注目してください.最初に、順序が正常に作成されていることがわかります.例えば、
    Added order ID 25a8cec1-e67a-11ea-9c17-7f242c2eeac0
    Added order ID 25a8f5ef-e67a-11ea-9c17-7f242c2eeac0
    Added order ID 25a8f5ea-e67a-11ea-9c17-7f242c2eeac0
    ...
    
    しばらくして、スループットが低下し、最終的にprovisioned limitを超えるので、Azure Cosmos DBはアプリケーションリクエストを制限します.これは、以下のようにエラーになります.
        Request rate is large: ActivityID=ac78fac3-5c36-4a20-8ad7-4b2d0768ffe4, RetryAfterMs=112, Additional details='Response status code does not indicate success: TooManyRequests (429); Substatus: 3200; ActivityId: ac78fac3-5c36-4a20-8ad7-4b2d0768ffe4; Reason: ({
          "Errors": [
            "Request rate is large. More Request Units may be needed, so no changes were made. Please retry this request later. Learn more: http://aka.ms/cosmosdb-error-429"
          ]
        });
    

    In the error message above, notice the following: TooManyRequests (429) and RetryAfterMs=112


    クエリエラーの観測
    簡単にするために、ログ出力をテスト/診断目的で使用します.クエリ実行中に発生したエラー(この場合は制限されます.)は、  gocql.QueryObserver . ランダムに生成された順序IDは、各エラーメッセージでログに記録され、失敗した順序が再試行されたかどうかを確認するためにログをチェックすることができます.
    以下はコードスニペットです.
        ....
        type OrderInsertErrorLogger struct {
           orderID string
        }
    
        // implements gocql.QueryObserver
        func (l OrderInsertErrorLogger) ObserveQuery(ctx context.Context, oq gocql.ObservedQuery) {
          err := oq.Err
          if err != nil {
             log.Printf("Query error for order ID %sn%v", l.orderID, err)
          }
        }
    
        ....
    
        // the Observer is associated with each query
        rid, _ := uuid.GenerateUUID()
        err := cs.Query(insertQuery).Bind(rid, rand.Intn(200)+50, fixedLocation, time.Now()).Observer(OrderInsertErrorLogger{orderID: rid}).Exec()
        ....
    
    どのように多くの注文を介して?
    ロードテスト端末に戻って、統計のいくつかをチェックしてください(出力を簡潔にするために出力されました).
        Summary: 
    
          Total:        2.8507 secs 
          Slowest:      1.3437 secs 
          Fastest:      0.2428 secs 
          Average:      0.5389 secs 
          Requests/sec: 70.1592 
        .... 
    
        Status code distribution: 
          [200] 300 responses
    

    The numbers will differ in your specific case depending on multiple factors.


    これは生のベンチマークテストではなく、我々は生産グレードのアプリケーションを持っていないので、あなたは無視することができます  Requests/sec  などが、我々の注意を引く  Status code distribution   属性は、我々のアプリケーションはHTTP 200   for すべてのリクエスト.
    最後の数字を確認しましょう.開ける カサンドラ azure cosmos db portalで同じクエリを実行します.
        select count(*) from ordersapp.orders;
    
        //output
    
        system.count(*)
        -----------------
            301
    
    あなたは300を見るべきです 追加の行(命令)が挿入されている.キーテイクアウトは、すべての命令が正常にAzureコスモスDB DEに格納されていたことです-私たちのアプリケーションコードが透過的に我々が設定した再試行方針(コードの1行で)に基づいてそれらを再試行したので、レート制限エラーにもかかわらず
    clusterConfig.RetryPolicy = retry.NewCosmosRetryPolicy(numRetries)
    

    動的スループット管理に関する一考察


    あなたのアプリケーションがそのスループットの約60 - 70 %で動作しているほとんどの時間を費やすならAutoscale provisioned throughput あなたのRu/Sとコスト使用量を最適化するのを助けることができないとき、使用中でないときにスケーリングすることによって-あなたは、あなたのワークロードが1時間単位で必要とする資源のためにだけ支払います.
    だから、リトライポリシーなしで何が起こるか?

    ポリシーを無効にする


    プレスを停止するcontrol+c 端末で環境変数を設定し、アプリケーションを再起動します.
        export USE_RETRY_POLICY=false 
        go run main.go
    
    ロードテストを再度実行する前に、  select count(*) from ordersapp.orders;
    hey -t 0 -n 300 http://localhost:8080/orders
    
    アプリケーションのログでは、同じレートのエラーを制限通知されます.ロードテストを実行した端末では、出力の要約の最後に、いくつかの要求が正常に完了しなかったか、つまり応答がHTTP 200
        ...
        Status code distribution: 
          [200] 240 responses 
          [429] 60 responses
    
    リトライポリシーが実施されなかったため、アプリケーションはもはや再-レート制限のために失敗した要求を試みた.

    増加するスループット


    Azureポータルを使用してリクエストユニットを増やすことができます  800  RU/s ) と同じロードテストを実行する

    hey -t 0 -n 300 http://localhost:8080/orders
    
    あなたは ない レート制限(HTTP 429 ) エラーは、現在のレイテンシーのための比較的低数、秒あたりのリクエストなど.
    リクエストの数を増やす-n  スループットが閾値を取得するアプリケーションのために違反したときに参照してください.予想通り、すべての命令は首尾よく(エラーやリトライなしで)持続されます
    次のセクションではcustom Retry Policy 作品

    This is an experimental implementation, and you should write custom policies to suit fault-tolerance and performance requirements of your applications.


    舞台裏で

    CosmosRetryPolicy に付着するgocql.RetryPolicy 実装によるインタフェースAttempt and GetRetry 関数.
        type CosmosRetryPolicy struct {
            MaxRetryCount         int
            FixedBackOffTimeMs    int
            GrowingBackOffTimeMs  int
            numAttempts           int
        }
    
    再試行は、その問い合わせの再試行回数がmax retry config以下の場合にのみ開始されます.
        func (crp *CosmosRetryPolicy) Attempt(rq gocql.RetryableQuery) bool { 
            crp.numAttempts = rq.Attempts() 
            return rq.Attempts() <= crp.MaxRetryCount || crp.MaxRetryCount == -1
        }
    
    GetRetryType 関数はエラーの種類を検出します.HTTP 429 ), の値を取り出すRetryAfterMs フィールド(エラーメッセージから)とクエリを再試行する前にスリープ状態に使用します.
        func (crp *CosmosRetryPolicy) GetRetryType(err error) gocql.RetryType {
    
           switch err.(type) {
           default:
                 retryAfterMs := crp.getRetryAfterMs(err.Error())
                 if retryAfterMs == -1 {
                     return gocql.Rethrow
                 }
                time.Sleep(retryAfterMs)
                return gocql.Retry
    
        //other case statements have been omitted for brevity
        }
    
    Azure Cosmos DBは柔軟性を提供して、様々な方法を使用してスループット要件を設定し、調整するだけでなく、アプリケーションが制限制限エラーを処理できるようにする基本的なプリミティブを提供します.このブログのポストでは、どのようにGOアプリケーションのためにこれを行うことができますが、その概念は、任意の言語とそのそれぞれに適用されるCQL compatible Azure Cosmos DB用のCassandra APIで作業するためのドライバです.

    詳しく知る


    公式ドキュメントからこれらのリソースをチェックしてください.
  • Use cases and benefits of Autoscale provisioned throughput
  • Details of the Cassandra API support in Azure Cosmos DB
  • Get up and running with a Go application and Cassandra API for Azure Cosmos DB
  • Frequently asked questions about the Cassandra API in Azure Cosmos DB
  • Request Units concepts