Проблема в том, что соединение разрывается после того, как сервер отправил ответ о состоянии 200, но до того, как клиент получил ответ.Клиент не помечает заказ как синхронизированный.В следующий раз клиент снова отправляет этот заказ, а серверы снова записывают его в базу данных, поэтому у нас два раза один и тот же заказ.
Добро пожаловать в мир ненадежных сообщений.
Что такоехорошая практика для решения такого рода проблем?
Вам следует ознакомиться с «Никому не нужен надежный обмен сообщениями» от Марк де Грау (2010).
Краеугольный каменьнадежный обмен сообщениями - это идемпотентная обработка запросов.Идемпотент семантика описывается таким образом
Метод запроса считается «идемпотентом», если предполагаемый эффект на сервере нескольких идентичных запросов с помощью этого метода такой же, как эффект дляодин такой запрос.
Простое возмущение методом запроса, однако, ничего вам не даст.Во-первых, семантика other в сообщении может не совпадать с методами идемпотентного запроса, а во-вторых, сервер должен знать, как реализовать эффект , как предполагалось .
Существует два основных шаблона для обработки идемпотентных запросов.Более простым из них является set
, что означает «перезаписать текущее представление тем, которое я предоставляю».
// X == 6
server.setX(7)
// X == 7
server.setX(7) <- a second, identical request, but the _effect_ is the same.
// X == 7
Альтернатива - test and set
(иногда называемая compare and swap
);в этом шаблоне запрос состоит из двух частей: предикат, который определяет, выполняется ли какое-либо условие, и изменение, которое применяется, если условие выполняется.
// X == 6
server.testAndSetX(6,7)
// X == 7
server.testAndSetX(6,7) <- this is a no op, because 7 != 6
// X == 7
Это основная идея.
Из вашего описания, то, что вы делаете, это манипулирование коллекцией заказов.
Та же самая основная идея работает там.Если вы можете вычислить уникальный идентификатор на основе информации в запросе, то вы можете рассматривать свою коллекцию как хранилище набора / значения ключа.
// collection.get(Id.of(7)) == Nothing
collection.put(Id.of(7), 7)
// collection.get(Id.of(7)) == Just(7)
collection.put(Id.of(7), 7) <- a second, identical request, but the _effect_ is the same.
// collection.get(Id.of(7)) == Just(7)
Если это не вариант, тогда вам нужно немногосвойство коллекции, которое изменится, когда ваше редактирование будет выполнено, закодировано в запросе
if (collection.size() == 3) {
collection.append(7)
}
A универсальный способ управлять чем-то подобным, это учитывать номера версий - каждый раз, когдаПосле внесения изменений номер версии увеличивается в рамках одной и той же транзакции
// begin transaction
if (resource.version.get() == expectedVersion) {
resource.version.set(1 + expectedVersion)
resource.applyChange(request)
}
// end transaction
Для примера из реальной жизни рассмотрим исправление JSON, которое включает в себя тестовую операцию , которую можно использовать какусловие для предотвращения «одновременного» изменения документа.
Во всех этих test and set
сценариях мы описываем понятие условного запроса
Условными запросами являются HTTP-запросы [RFC7231], которые включают одно или несколько полей заголовка, указывающих предварительное условие, которое необходимо проверить перед применением семантики метода кцелевой ресурс.
Спецификация условных запросов дает вам универсальный способ описания условий в метаданных ваших запросов и ответов, так что generic http-компоненты могут помочь.
Обратите внимание: то, что это работает, не дает нам гарантии того, что сервер будет делать то, что хочет клиент.Вместо этого он слабее: клиент может безопасно повторять запрос, пока не получит подтверждение от сервера.