Обработка двух всплесков на одном и том же объекте в космосе - PullRequest
0 голосов
/ 05 декабря 2018

Предположим, у меня есть документ с полем 'count', и я хочу увеличивать поле count при каждом вызове функции, например:

container.items.upsert ({id: "test",count: currentCount + 1})

Где 'currentCount' был последним значением, полученным из документа с идентификатором 'test'.

Если сделан асинхронный вызов для увеличения счетчика во время другого подсчета(с момента извлечения currentCount до наступления утечки) второй асинхронный вызов будет содержать неверные данные (т. е. currentCount до увеличения первого значения currentCount до первого вызова).

Как мне предотвратить такиесценарий?

1 Ответ

0 голосов
/ 05 декабря 2018

Как бы я мог предотвратить такой сценарий?

Чтобы предотвратить такие сценарии, вы должны использовать Optimistic Concurrency.По существу используйте свойство ETag документа для включения в ваши запросы upsert.Когда вы получаете документ, вы получаете его обратно ETag.Вы должны включить то же значение в запросе upsert.Если документ не изменился на сервере (т. Е. Значение ETag одинаково), то операция обновления будет выполнена успешно, в противном случае произойдет сбой.

С этого blog post, вотПример кода:

using System;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using Microsoft.Azure.Documents;
using Microsoft.Azure.Documents.Client;
using Shouldly;
using Xunit;

namespace Demo
{
    public class OptimtimisticConcurrencyTests
    {
        private readonly DocumentClient _client;
        private const string EndpointUrl = "https://localhost:8081";
        private const string AuthorizationKey = "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==";
        private const string DatabaseId = "ConcurrencyDemo";
        private const string CollectionId = "Customers";

        public OptimtimisticConcurrencyTests()
        {
            _client = new DocumentClient(new Uri(EndpointUrl), AuthorizationKey);
        }

        [Fact]
        public async Task Should_Throw_With_PreconditionFailed()
        {
            // Setup our Database and add a new Customer
            var dbSetup = new DatabaseSetup(_client);
            await dbSetup.Init(DatabaseId, CollectionId);
            var addCustomer = new Customer(Guid.NewGuid().ToString(), "Demo");
            await dbSetup.AddCustomer(addCustomer);

            // Fetch out the Document (Customer)
            var document = (from f in dbSetup.Client.CreateDocumentQuery(dbSetup.Collection.SelfLink)
                            where f.Id == addCustomer.Id
                            select f).AsEnumerable().FirstOrDefault();

            // Cast the Document to our Customer & make a data change
            var editCustomer = (Customer) (dynamic) document;
            editCustomer.Name = "Changed";

            // Using Access Conditions gives us the ability to use the ETag from our fetched document for optimistic concurrency.
            var ac = new AccessCondition {Condition = document.ETag, Type = AccessConditionType.IfMatch};

            // Replace our document, which will succeed with the correct ETag 
            await dbSetup.Client.ReplaceDocumentAsync(document.SelfLink, editCustomer,
                new RequestOptions {AccessCondition = ac});

            // Replace again, which will fail since our (same) ETag is now invalid
            var ex = await dbSetup.Client.ReplaceDocumentAsync(document.SelfLink, editCustomer,
                        new RequestOptions {AccessCondition = ac}).ShouldThrowAsync<DocumentClientException>();

            ex.StatusCode.ShouldBe(HttpStatusCode.PreconditionFailed);
        }
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...