在多个Docker容器

时间:2016-09-13 17:09:44

标签: python azure stored-procedures docker azure-cosmosdb

我目前正在构建一个使用Azure存储来存储图像的python Tornado Web应用程序,以及用于在图像上存储元数据的DocumentDB。每当上传图像时,它都可以使用运行Tornado Web App的2个可能的Docker容器中的任何一个来异步执行POST方法。我遇到的错误是当我到达我在DocumentDB脚本中的存储过程时。 sproc在两个独立的Docker容器中同时在两个独立的线程中执行。存储过程旨在为每个上传的图像生成一个新的ReceiptID,方法是查询DocDB中的gen_receipt_id文档,如下所示:

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

然后,sproc将counter属性递增1,并且该新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正在两个不同的Docker容器407上执行并生成相同的ReceiptID,因此,由于我正在尝试创建两个文档,因此存在冲突错误相同的ID。我需要做的是阻止Sproc在两个单独的容器上生成相同的ID。我尝试在Sproc中使用Etags和“If-Match”标头,但它仍然会发生,因为每个容器在文档上都有相同的Etag,所以它没有看到错误。

1 个答案:

答案 0 :(得分:0)

这个常见问题的典型NoSQL解决方案是使用GUID而不是顺序ID。

但是,由于DocumentDB sprocs为您提供了ACID约束,因此应该可以使用乐观并发方法和重试来执行您想要的操作。

因此,如果您在同一时间从两个不同的位置运行此sproc两次,您将获得相同的ID(在此示例中为407)。其中一个sprocs应该能够写入该ID,另一个将失败。这里的关键是重试任何失败。 sproc将重新运行并获取下一个ID(408)。由于同时请求应该是罕见的,因此对中位响应时间的影响可以忽略不计。