Допустим, вызываем метод и он создает новый пункт заказа. Хотелось бы чтобы при n вызовах метода добавлялся только 1 пункт заказа (на случай повторных запросов при ошибке сетевой).
Варианты:
1. GUID.
2. Какая-нибудь метка времени + рандомное число, типа 220831215622121 — дата и время включая миллисекунды — уже тип int не вмещает, т.е. long. Ну и могут быть проблемы все-же, если запросы частые (что можно разрулить добавлением еще 3-5 разрядов рандомного числа).
3. Можно передавать ID пункта заказа, т.е. зная ID предыдущего делать инкремент.
Здравствуйте, Shmj, Вы писали:
S>Допустим, вызываем метод и он создает новый пункт заказа. Хотелось бы чтобы при n вызовах метода добавлялся только 1 пункт заказа (на случай повторных запросов при ошибке сетевой).
Откуда вообще взяться повторному запросу? Если в веб-функцию пришёл запрос, значит сервер уже получил полные данные и никаких "ошибке сетевой" там нет.
Ну и на всякий, поменьше юзать всякое г-но типа JS.
Здравствуйте, Shmj, Вы писали:
S>Допустим, вызываем метод и он создает новый пункт заказа. Хотелось бы чтобы при n вызовах метода добавлялся только 1 пункт заказа (на случай повторных запросов при ошибке сетевой).
S>Варианты:
Я бы взял GUID конечно, если никаких специальных требований нет, зачем изобретать велосипед
Здравствуйте, Shmj, Вы писали:
S>Варианты:
S>1. GUID. S>2. Какая-нибудь метка времени + рандомное число, типа 220831215622121 — дата и время включая миллисекунды — уже тип int не вмещает, т.е. long. Ну и могут быть проблемы все-же, если запросы частые (что можно разрулить добавлением еще 3-5 разрядов рандомного числа). S>3. Можно передавать ID пункта заказа, т.е. зная ID предыдущего делать инкремент.
S>Что еще?
Помечать состояние списка заказов номером, хэшем (fingerprint, ETag). По сути как коммит в GIT/оптимистическая блокировка. Это более общее решение, годится не только лишь для создания заказа, а вообще для работы со списком сущностей (создание, удаление, модификация)
Re[2]: WebAPI - защита от дублирования - ваш выбор
Здравствуйте, Baiker, Вы писали:
B>Откуда вообще взяться повторному запросу?
Ровно оттуда же, откуда возьмется первый запрос. Его отправит клиент, не получивший ответ на первый запрос.
Re[2]: WebAPI - защита от дублирования - ваш выбор
Здравствуйте, Baiker, Вы писали:
B>Откуда вообще взяться повторному запросу? Если в веб-функцию пришёл запрос, значит сервер уже получил полные данные и никаких "ошибке сетевой" там нет.
Сервер-то получил, но вот клиент может ответа от сервера не получить и получить разрыв соединения или таймаут и не знать — то ли это было до того как данные ушли на сервер, то ли уже после, и попробовать повторить операцию.
Здравствуйте, Shmj, Вы писали:
S>Допустим, вызываем метод и он создает новый пункт заказа. Хотелось бы чтобы при n вызовах метода добавлялся только 1 пункт заказа (на случай повторных запросов при ошибке сетевой).
Это называется идемпотентные операции, подходы к тому то что тебе нужно ищи по словам "ключ идемпотентности"
S>Варианты: S>1. GUID.
Да
S>2. Какая-нибудь метка времени + рандомное число, типа 220831215622121 — дата и время включая миллисекунды — уже тип int не вмещает, т.е. long. Ну и могут быть проблемы все-же, если запросы частые (что можно разрулить добавлением еще 3-5 разрядов рандомного числа).
Это создание "как бы guid" вручную. Если есть возможность просто сгенерировать guid — то зачем оно?
S>3. Можно передавать ID пункта заказа, т.е. зная ID предыдущего делать инкремент.
Это один из вариантов построения ключа идемпотентности из самих данных. Другой вариант — вычислять от них хэш. Могут быть свои плюсы в подходе, но как по мне это более сложное и хрупкое решение, чем синтетический ключ наподобие гуида. Надо очень обдуманно выбирать. Но guid тоже не решает волшебно всех проблем, надо применять осмысленно.
S>1. GUID. S>2. Какая-нибудь метка времени + рандомное число, типа 220831215622121 — дата и время включая миллисекунды — уже тип int не вмещает, т.е. long. Ну и могут быть проблемы все-же, если запросы частые (что можно разрулить добавлением еще 3-5 разрядов рандомного числа). S>3. Можно передавать ID пункта заказа, т.е. зная ID предыдущего делать инкремент.
Зависит от требований системы. Иногда имеет смысл использовать все три способа. Подробнее описано тут https://stripe.com/blog/idempotency: первый пункт — раздел Guaranteeing “exactly once” semantics, второй и третий — Being a good distributed citizen.
Здравствуйте, fmiracle, Вы писали:
S>>2. Какая-нибудь метка времени + рандомное число, типа 220831215622121 — дата и время включая миллисекунды — уже тип int не вмещает, т.е. long. Ну и могут быть проблемы все-же, если запросы частые (что можно разрулить добавлением еще 3-5 разрядов рандомного числа).
F>Это создание "как бы guid" вручную. Если есть возможность просто сгенерировать guid — то зачем оно?
Потому что Guid монструозно и уродливо выглядит.
Re[3]: WebAPI - защита от дублирования - ваш выбор
Здравствуйте, Shmj, Вы писали:
S>1. GUID. S>2. Какая-нибудь метка времени + рандомное число, типа 220831215622121 — дата и время включая миллисекунды — уже тип int не вмещает, т.е. long. Ну и могут быть проблемы все-же, если запросы частые (что можно разрулить добавлением еще 3-5 разрядов рандомного числа). S>3. Можно передавать ID пункта заказа, т.е. зная ID предыдущего делать инкремент. S>Что еще?
0. Сделать метод идемпотентным (грубо говоря, заменить POST на PUT)
Идея использовать для идентификатора семантически значимые поля приведет к тому, что в каждом конкретном случае тебе придется изобретать велосипед заново. Поэтому лучше синтетический ключ реквеста, формируемый автоматично в клиенте и передаваемый, с учетом ретраев, в хидере на сервер. Но вариант 0 лучше.
... << RSDN@Home 1.3.17 alpha 5 rev. 62>>
Re[2]: WebAPI - защита от дублирования - ваш выбор
Здравствуйте, bnk, Вы писали:
bnk>Я бы взял GUID конечно, если никаких специальных требований нет, зачем изобретать велосипед
Иногда важно восстатавливать исходную последовательность запросов (но, видимо, не в данном случае). Тогда нужен монотонно возрастающий ID, GUID не прокатит.
... << RSDN@Home 1.3.17 alpha 5 rev. 62>>
Re[3]: WebAPI - защита от дублирования - ваш выбор
Здравствуйте, Ночной Смотрящий, Вы писали:
НС>Здравствуйте, Shmj, Вы писали:
S>>1. GUID. S>>2. Какая-нибудь метка времени + рандомное число, типа 220831215622121 — дата и время включая миллисекунды — уже тип int не вмещает, т.е. long. Ну и могут быть проблемы все-же, если запросы частые (что можно разрулить добавлением еще 3-5 разрядов рандомного числа). S>>3. Можно передавать ID пункта заказа, т.е. зная ID предыдущего делать инкремент. S>>Что еще?
НС>0. Сделать метод идемпотентным (грубо говоря, заменить POST на PUT)
Как, если это пункт заказа? Может быть два одинаковых пункта заказа. Как отличить так и захотел клиент или же это ошибка из-за повторной отправки запроса?
Re[4]: WebAPI - защита от дублирования - ваш выбор
Здравствуйте, Ночной Смотрящий, Вы писали:
НС>Зачем тебе его смотреть, это ж не correlationid, он только системе нужен. Но если хочешь немонструозно — можешь использовать cuid.
Ну еще зачем 16 байт, если и 8 хватит более чем.
Re[5]: WebAPI - защита от дублирования - ваш выбор
Здравствуйте, Shmj, Вы писали:
НС>>Зачем тебе его смотреть, это ж не correlationid, он только системе нужен. Но если хочешь немонструозно — можешь использовать cuid. S>Ну еще зачем 16 байт, если и 8 хватит более чем.
cuid slug — от 7 до 10 байт, внутри счетчик, все как ты хочешь.
... << RSDN@Home 1.3.17 alpha 5 rev. 62>>
Re[3]: WebAPI - защита от дублирования - ваш выбор
Здравствуйте, Shmj, Вы писали:
НС>>0. Сделать метод идемпотентным (грубо говоря, заменить POST на PUT) S>Как, если это пункт заказа?
Так. Id пункта заказа генеришь на клиенте. При ретраях id меняться не будет.
S> Может быть два одинаковых пункта заказа. Как отличить так и захотел клиент или же это ошибка из-за повторной отправки запроса?
В коде клиента отличишь. При создании пункта генеришь новый id, при ретрае остается тот же самый.
Здравствуйте, Shmj, Вы писали:
S>Допустим, вызываем метод и он создает новый пункт заказа.
Клиента контроллируете вы или нет? Если вы не контроллируете, то просто говорите, что идентификатор пункта заказа — уникальная (в рамках заказа) строка. Дальше пусть клиент решает.
S>2. Какая-нибудь метка времени + рандомное число, типа 220831215622121 — дата и время включая миллисекунды — уже тип int не вмещает, т.е. long. Ну и могут быть проблемы все-же, если запросы частые (что можно разрулить добавлением еще 3-5 разрядов рандомного числа).
Я в ряде случаев использую инкрементальный счетчик, инициализируемый текущим временем (миллисекунды). Т.е. первый объект имеет время x = currentTimestamp(), второй — x+1 и т.д. Но зависит от сценария, в ряде случаев (много вкладок браузера открыты одновременно после восстановления/перезапуска) такой подход не работает. Я иногда в тестах использую. "Частые запросы" далеко не всегда проблема. Если у вас один запущенный клиент, идентификаторы позиций являются локальными в рамках заказа, то вероятность накликать две позиции с разницей меньше миллисекунды очень мала.
S>Что еще?
Put на /order/{orderId}/items со _всеми_ позициями заказа. Т.е. просто переписывать все. Иногда работает, иногда нет. Так как у вас был третий вариант (на основе предыдущего пункта заказа) — скорее всего работает.