2016-09-13 17 views
0

現在Azure Storageを使用して画像を格納するPython Tornado Webアプリケーションと、画像にメタデータを格納するDocumentDBを構築しています。画像がアップロードされるたびに、Tornado Web Appを実行している2つの可能なDockerコンテナのうち1つを使用して、POSTメソッドを非同期に実行できます。私が持っているエラーは、私がDocumentDBスクリプトに座っているストアドプロシージャに到達したときです。 sprocは、同時に2つの別々のDockerコンテナで2つのスレッドで実行されています。ストアドプロシージャは、次のようになりgen_receipt_id文書のDOCDBを照会することによってアップロード各画像のための新しいReceiptIDを生成することを意味する:複数のDockerコンテナでDocumentDBストアドプロシージャを実行中に同時実行エラーが発生する

{ 
    "id": "gen_receipt_id", 
    "counter": 406 
} 

をSPROC次いで1 counterプロパティをインクリメントし、その新たなIDをするために使用されメタデータの新しい領収書に添付されます。 SPROCは次のようになります。

function receiptIDSproc() { 
    var collection = getContext().getCollection(); 

    // Query documents and take 1st item. 
    var isAccepted = collection.queryDocuments(
     collection.getSelfLink(), 
     "SELECT * FROM root r WHERE r.id='gen_receipt_id'", 
     function(err, feed) { 
      if (err) throw err; 
      // Check the feed and if empty, set the body to 'no docs found', 
      // else take 1st element from feed 
      if (!feed || !feed.length) getContext().getResponse().setBody('no docs found'); 
      else { 
       tryUpdate(feed[0]); 
      } 
     }); 

    if (!isAccepted) throw new Error('The query was not accepted by the server.'); 

    function tryUpdate(document) { 

     var requestOptions = {"If-Match": document._etag, 
           etag: document._etag}; 
     document.counter += 1; 
     // Update the document. 
     var isAccepted = collection.replaceDocument(document._self, document, requestOptions, function (err, updatedDocument,responseOptions) { 
      if (err) throw err; 
      // If we have successfully updated the document - return it in the response body. 
      getContext().getResponse().setBody(updatedDocument); 
     }); 

     // If we hit execution bounds - throw an exception. 
     if (!isAccepted) { 
      throw new Error("The stored procedure timed out."); 
     } 
    } 
} 

をしかし、私は同時に複数の画像をアップロードするために行くとき、私は操作が非同期で起こっとの競合を取得: Fine-Uploader upload conflict

コンソールでのエラーは、次のようになります

[36mtornado2_1 |[0m ERROR:500 POST /v1.0/groups/1/receipts (172.18.0.4) 1684.98ms 
[33mtornado1_1 |[0m 407 //Here I'm printing the ID the Sproc generated 
[33mtornado1_1 |[0m 2016/9/13/000000000000407 
[36mtornado2_1 |[0m 407 //Here I'm printing the ID the Sproc generated 
[36mtornado2_1 |[0m 2016/9/13/000000000000407 
[32mnginx_1  |[0m 10.0.75.1 - - [13/Sep/2016:16:49:47 +0000] "POST /v1.0/groups/1/receipts HTTP/1.1" 200 17 "http://local.zenxpense.com/upload" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36" 
[33mtornado1_1 |[0m INFO:200 POST /v1.0/groups/1/receipts (172.18.0.4) 1132.49ms 
[36mtornado2_1 |[0m WARNING:500 POST /v1.0/groups/1/receipts (172.18.0.4): An error occured while uploading to Azure Storage: HTTP 500: Internal Server Error (An error occured while creating DocumentDB record: Status code: 409 
[36mtornado2_1 |[0m {"code":"Conflict","message":"Message: {\"Errors\":[\"Resource with specified id or name already exists\"]}\r\nActivityId: b226be91-f193-4c1b-9cc2-bcd8293bd36b, Request URI: /apps/8ae2ad5a-d261-42ac-aaa1-9ec0fd662d12/services/cc7fdf37-5f62-41db-a9d6-37626da67815/partitions/8063ad6c-33ad-4148-a60f-91c3acbfae6f/replicas/131171655602617741p"}) 

エラーからわかるように、Sprocは2つの異なるDockerコンテナ407で同じReceiptIDを実行して生成しています。そのため、競合エラーsincがありますe同じIDを持つ2つのドキュメントを作成しようとしています。私が起こる必要があるのは、Sprocが2つの別々の容器に同じIDを生成するのを防ぐことです。私はEtagsとSprocの "If-Match"ヘッダーを使用してみましたが、各コンテナは文書上に同じEtagを持っているので、エラーは表示されません。

答えて

0

この一般的な問題に対する典型的なNoSQLの解決策は、順次IDではなくGUIDを使用することです。

しかし、DocumentDB sprocsはACID制約を提供するので、再試行でオプティミスティックな並行性アプローチを使用して、必要な処理を実行できるはずです。

したがって、同じ時間に2つの異なる場所からこのsprocを2回実行すると、同じID(この例では407)が得られます。 sprocsの1つはそのIDに書き込むことができ、もう1つは失敗します。ここでの鍵は、失敗したものを再試行することです。 sprocは再実行し、次のIDを取得します(408)。同時リクエストは希少であるため、応答時間の中央値にはごくわずかな影響があります。

+0

お返事ありがとうございます。しかし、問題は、Sproc自身が必ずしもIDを生成したり、返すことさえしないということです。単にドキュメントに行き、1だけインクリメントします。そして、私のサーバーサイドのPythonはそのドキュメントに現在の 'counter'が何であるかを尋ねます。これが私の新しいIDです。このIDを持つDocDB文書を作成しようとするとエラーが発生します。このIDは、BLOBをAzure Storageにアップロードした後にのみ発生します。つまり、サーバ側のアップロード操作全体をやり直す必要があります。むしろ、Sprocが実行されるたびに常に新しいIDに増分されるようにしたいと思います。 –

+0

IDが逐次である必要がありますか?そうでない場合は、なぜGUIDを使用しないのですか? –

+0

私たちは意図的に独自のIDジェネレータを使用しています。なぜなら、GUIDは常にユニークで、乱雑で順序が乱れているからです。 'eceaeb07-3247-4e79-8999-7b2d83b4ca02'は' 15'に比べてユーザーを追跡するきれいな方法ではありません。 –

関連する問題