Сообщество / DevOps

Нажала дважды — это две заявки или одна нервная рука?

N
nina_test
Junior
Сообщения: 4
Репутация: 2
Дата регистрации:
31.10.2026
АВТОР ТЕМЫ31 окт. 2026 г., 09:00 (GMT+3)
Гоняю учебную форму заявки: имя, телефон, кнопка «Отправить». Нажала два раза подряд, как сделал бы человек с медленным интернетом. В таблице появились две одинаковые строки с разницей меньше секунды. С этим всё понятно: дубль.

А если человек через минуту отправит то же самое снова, потому что не увидел подтверждения? Или потому что правда хочет вторую заявку? Как вы отличаете новое намерение: по токену при открытии формы, по окну времени или по совпадению полей?
1
ruslan_krd
Junior
Сообщения: 72
Репутация: 71
Дата регистрации:
16.08.2026
31 окт. 2026 г., 15:07 (GMT+3)
Двойной клик за секунду — это действительно техника, тут токен при открытии формы закрывает вопрос, и спорить не о чем.

А вот повтор через минуту я бы не отдавал коду на окончательное решение. Картина знакомая: человек отправил заявку, не увидел «спасибо», отправил ещё раз. Менеджер видит две строки и звонит дважды. Во второй раз ему отвечают: «Я же только что вам всё сказал». Неловко, но никто ничего не потерял.

Обратная ошибка хуже. Если система сама решила, что это дубль, и молча выкинула строку, а человек на самом деле хотел вторую заявку, например для коллеги со своего телефона, об этом никто не узнает. Клиент просто не дождётся звонка.

Поэтому я бы разделил так. Токен отсекает то, что точно дубль. Совпадение телефона в каком-нибудь окне, хоть час, хоть сутки, ничего не удаляет, а ставит пометку «возможный повтор» рядом с первой заявкой. Объединяет их уже человек, у которого обе перед глазами. Окно подберёте под то, как быстро у вас реально перезванивают.

И отдельно: если люди жмут второй раз, потому что не видят подтверждения, начинать стоит с экрана после отправки. Это дешевле любой дедупликации)
2
S
sasha_par
Junior
Сообщения: 15
Репутация: 16
Дата регистрации:
24.09.2026
31 окт. 2026 г., 16:00 (GMT+3)

ruslan_krd писал:
если люди жмут второй раз, потому что не видят подтверждения, начинать стоит с экрана после отправки



вот это да. я лендинги собираю, и самый частый дубль у меня делаю я сам)) жму «отправить», ничего не происходит, жму ещё раз, а потом в таблице две строки «тест тест 123». если даже автор формы не верит своей кнопке, посетитель тем более не поверит.

что я теперь ставлю почти по умолчанию, без всякой магии:
— сразу после клика кнопка неактивна и пишет «отправляем…», чтобы было видно, что нажатие засчитано;
— после ответа сервера форма пропадает целиком, а на её месте крупное «спасибо, перезвоним» с тем номером, который человек ввёл. так он видит, что номер записался правильно, и опечатку заметит сам;
— если сервер не ответил, честно пишу «не отправилось, попробуйте ещё раз», а не оставляю тишину.

с токеном это друг другу не мешает: токен ловит то, что всё-таки проскочило. но после такого экрана повторов через минуту обычно становится заметно меньше, а пометка «возможный повтор» уже ловит редкие случаи.

у меня пара франкоязычных клиентов, там ещё отдельно слежу, чтобы «merci» не осталось английским «thank you» после копипасты. а то подтверждение есть, но человек сомневается, туда ли попал)
3
V
vlad_dv
Junior
Сообщения: 14
Репутация: 12
Дата регистрации:
26.09.2026
31 окт. 2026 г., 16:02 (GMT+3)

sasha_par писал:
если сервер не ответил, честно пишу «не отправилось, попробуйте ещё раз»



Вот тут я бы поправил формулировку. «Сервер не ответил» и «не отправилось» — не одно и то же, а отсюда у меня этих дублей больше всего.

У вас сейчас вечер, у меня одиннадцать, почти ночь, и я как раз из тех посетителей, на которых такие формы ломаются. До европейских серверов отсюда обычно двести с чем-то миллисекунд, по мобильному в автобусе бывает заметно хуже, с провалами. Типичная картина: жму «отправить», кнопка честно пишет «отправляем…», запрос уходит, сервер его принимает и пишет строку в таблицу. А ответ обратно теряется где-то между тоннелем и сменой вышки. Фронт ждёт, срабатывает таймаут, и мне показывают «не отправилось, попробуйте ещё раз». Я пробую. В таблице две строки, и вторую я сделал не нервной рукой, а потому что форма сама меня попросила.

Поэтому я бы разделил два сообщения:
— сервер ответил ошибкой — тогда да, «не отправилось»;
— ответа не дождались — «не удалось подтвердить отправку», и повторная кнопка отправляет тот же токен, который выдали при открытии формы, а не новый.

Тогда повтор после таймаута безопасен. Если первая попытка дошла, сервер видит знакомый токен и просто возвращает «уже есть, спасибо». Если не дошла, записывает как новую. Человек ничего про это не знает, ему показывают одно и то же «спасибо, перезвоним».

Главное — не генерировать новый токен на каждый клик «попробовать ещё раз». Я видел, как токен перевыпускали при каждом сабмите, «чтобы был свежий», и вся защита от двойного клика переставала работать ровно там, где она нужна, на плохой связи. Токен живёт, пока живёт эта форма на экране, и меняется после успешной отправки, когда форма уже исчезла.

И пометка «возможный повтор» от ruslan_krd тут хорошо дополняет: токен ловит технические повторы, а через минуту с новой вкладки — уже человеческий случай, его пусть смотрит менеджер.

Проверить у себя легко, не надо ехать во Владивосток) В devtools браузера включить медленную сеть, нажать отправить и на середине запроса отключить сеть совсем. Если после этого в таблице появилась строка, а на экране «не отправилось» — ровно мой случай из автобуса.
2
L
leo_msk
Junior
Сообщения: 27
Репутация: 13
Дата регистрации:
12.09.2026
31 окт. 2026 г., 18:04 (GMT+3)

vlad_dv писал:
повторная кнопка отправляет тот же токен, который выдали при открытии формы



Добавлю со стороны приёмника, потому что там легко сделать половину работы. Сервер узнал токен, строку второй раз не записал. А что он ответил? Если 409 или «дубликат», фронт снова покажет ошибку, и человек в автобусе решит, что не дошло ни разу.

У меня такие формы приходят в n8n, сценарий примерно такой:

1. Webhook принимает имя, телефон, токен.
2. Поиск токена в таблице заявок.
3. Нашёлся: Respond to Webhook с тем же «спасибо, перезвоним на +7…», что и в первый раз. Новую строку не создаём.
4. Не нашёлся: пишем строку и сразу отвечаем «спасибо».
5. Только после ответа идёт тяжёлое: ollama раскладывает заявку по теме, проверяет телефон на «возможный повтор» за сутки, шлёт менеджеру в чат.

Пятый пункт я однажды поставил до ответа. Локальная модель думала свои секунд восемь, фронт отваливался по таймауту и сам плодил ваши «не удалось подтвердить». То есть дубли делала не нервная рука, а моя же красивая классификация. Сейчас фронт ждёт только записи в таблицу, всё умное идёт после.

Повтор по токену для пользователя должен выглядеть как успех, а не как отказ. Тогда и дедупликация, и честный экран после отправки работают вместе.
0
M
mira_games
Junior
Сообщения: 18
Репутация: 10
Дата регистрации:
23.10.2026
31 окт. 2026 г., 18:05 (GMT+3)

leo_msk писал:
Повтор по токену для пользователя должен выглядеть как успех, а не как отказ.



Да! У меня запись на игровые вечера, и нервнее всех жмут те, кто боится не попасть за стол. Мне кажется, тут мало просто «спасибо». Лучше сразу показать: «вы шестой из восьми, суббота, 19:00». Человек видит себя в списке и успокаивается. А если видит себя там дважды, сам пишет: «уберите второго меня»)
1
R
robert_sound
Junior
Сообщения: 8
Репутация: 3
Дата регистрации:
30.10.2026
31 окт. 2026 г., 20:00 (GMT+3)

mira_games писал:
вы шестой из восьми



Только пусть повтор с тем же токеном снова показывает «шестой», а не «седьмой». Иначе человек увидит, что место сдвинулось, и решит одно из двух: записал себя ещё раз или кто-то влез.

Со сдачей учебных работ та же история. Прислал mix_v3 дважды, получил два номера в очереди и пришёл спрашивать, какой настоящий. Одна попытка — один номер.

Эхо хорошо в записи. В списке оно лишнее.
1
A
andrei_brest
Junior
Сообщения: 18
Репутация: 11
Дата регистрации:
19.09.2026
31 окт. 2026 г., 20:03 (GMT+3)

robert_sound писал:
Одна попытка — один номер.



С оплатой это правило становится строже. Там второй номер означает не просто путаницу в очереди, а возможное второе списание.

У нас в Бресте небольшой сайт с предоплатой картой. Типичная картина: человек нажал «оплатить», ушёл на страницу банка, подтвердил кодом и ждёт. Страница возврата грузится долго, он жмёт «назад», видит свою корзину и снова «оплатить». Если сайт на каждый клик заводит новый заказ, в платёжку уходит новый номер, и для неё это честная вторая покупка. Если заказ уже существует и в платёжку снова идёт его номер, всё зависит от того, как платёжный сервис обрабатывает повтор. Это надо читать в его документации, а не предполагать.

Поэтому номер заказа я создаю до перехода к оплате и привязываю к тому же токену формы. Повторный клик ведёт не на новую оплату, а на страницу этого заказа: «№1042, ждём подтверждения от банка». Пока статус не финальный, второй кнопки «оплатить» там просто нет.

И одно уточнение к «шестому из восьми»: номер должен оставаться прежним, а место — нет. Если оплата не прошла и бронь сняли, честнее показать «место освободилось, попробуйте снова», чем держать человека шестым в списке, где его уже нет.
3
D
dima_radio
Junior
Сообщения: 8
Репутация: 3
Дата регистрации:
26.10.2026
01 нояб. 2026 г., 08:00 (GMT+3)

andrei_brest писал:
Пока статус не финальный, второй кнопки «оплатить» там просто нет.



С этим согласен, но у такой страницы есть слабое место: статус может не стать финальным никогда. Уведомление от платёжки не дошло, обработчик упал, сервер перезагрузили в неудачную секунду. Тогда человек остаётся перед надписью «№1042, ждём подтверждения от банка» без кнопки и без выхода. Деньги, может быть, уже списались, а может, и нет. Он не знает, вы тоже.

У радиолюбителей эта задача старая. В пакетной связи кадры нумеруются. Если подтверждение потерялось, передатчик повторяет кадр с тем же номером, а приёмник отвечает «уже есть» и второй раз его не принимает. Это ваш токен один в один. Но у протокола есть и вторая половина: число повторов ограничено. Исчерпал — канал объявляется потерянным, и дальше решает оператор. Бесконечно ждать ответа там не принято.

Я это в своё время усвоил на собственном датчике. Поставил на балкон термометр с передатчиком и научил его ждать подтверждения от приёмника. Аккуратно, по-взрослому. Приёмник однажды завис, и датчик честно ждал ответа, пока не посадил батарейку. Надёжнейшая схема: ни одного дубля, ни одного показания.

Для страницы заказа я бы добавил три вещи:
— страница сама спрашивает статус у платёжного сервиса по номеру заказа, если его API это позволяет. Об этом опять же надо читать в документации, а не надеяться на уведомление;
— после разумного срока надпись меняется на честную: «банк пока не подтвердил оплату, повторно не платите, мы проверим заказ №1042 и сообщим». Кнопки оплаты по-прежнему нет, зато человек понимает, что делать дальше: ничего;
— зависшие заказы раз в день кто-то сверяет руками с выпиской платёжки. Это скучно, зато в итоге остаются только два исхода: «оплачен» или «не оплачен, можно пробовать снова».

Кнопку убрать правильно. Плохо только, когда вместе с кнопкой пропадает и понимание, что будет дальше. Тогда человек идёт искать другую кнопку, например открывает сайт в соседней вкладке и заводит новую корзину. И вот там уже новый токен, а дубль опять честный.
0
S
sasha_par
Junior
Сообщения: 15
Репутация: 16
Дата регистрации:
24.09.2026
01 нояб. 2026 г., 12:00 (GMT+3)

vlad_dv писал:
не удалось подтвердить отправку


принято, переписываю. думал, что пишу честно) а про автобус с провалами связи вообще не подумал.
0
V
vlad_dv
Junior
Сообщения: 14
Репутация: 12
Дата регистрации:
26.09.2026
01 нояб. 2026 г., 15:00 (GMT+3)

leo_msk писал:
фронт ждёт только записи в таблицу



Да, порядок правильный. Только ваши восемь секунд модели складываются с моей дорогой: сначала до Европы, потом обратно, плюс мобильный в автобусе на Русский. Из офиса таймаут кажется щедрым, а мне от него остаётся заметно меньше. Я бы проверял его с телефона на плохой связи, а не по своему вайфаю.
0
M
mira_games
Junior
Сообщения: 18
Репутация: 10
Дата регистрации:
23.10.2026
01 нояб. 2026 г., 18:09 (GMT+3)

robert_sound писал:
Только пусть повтор с тем же токеном снова показывает «шестой», а не «седьмой».



Вот тут меня поймали. Я полезла смотреть, откуда у меня берётся «шестой», и это просто номер строки в таблице. Сколько строк выше тебя, такой ты и есть. Дубль добавит строку, и все ниже съедут. Кто-то отпишется, и тоже все съедут, только в другую сторону.

Теперь картинка, которую я как раз пытаюсь не допустить. Пятница, вечер, запись на субботний кооператив, восемь мест. Стас записался, увидел «вы шестой». Потом в маршрутке открыл ссылку ещё раз, просто проверить себя, а там «вы пятый». Если честно, это хорошая новость: кто-то выше отписался. Но Стас об этом не знает. Он видит, что цифра поменялась сама, и пишет мне в личку: «а меня точно записали? вчера было шесть». Мне на этом месте уже всё равно, был там дубль или нет. Человек нервничает, а я сижу и сверяю таблицу вместо того, чтобы раскладывать карточки.

Поэтому я думаю разделить два числа, которые я по лени склеила:

— номер записи. Выдаётся один раз, при первом успешном приёме токена, и дальше не меняется никогда. Повтор с тем же токеном показывает его же. Это ровно ваше «одна попытка — один номер».
— место за столом. Оно правда может меняться, и врать про него не надо. Но меняться оно должно с объяснением: «вы были шестым, двое отписались, теперь вы четвёртый». Без этой строчки любое движение цифры читается как «что-то сломалось».

И ещё одно. Если человек выше лимита, то есть девятый из восьми, слово «девятый» его расстроит сильнее, чем надо. Лучше «вы первый в листе ожидания». Номер тот же, а ощущение другое: ты не опоздал, ты стоишь в очереди, и тебя позовут.

А вот замечание про оплату, которое выше было, меня пока не касается: у меня запись бесплатная, максимум кто-то не придёт и съест мои печеньки за свои воображаемые деньги. Но правило то же. Не прятать изменение, а показывать, почему оно случилось.

В общем, вывод неприятный, но полезный: «шестой из восьми» у меня был не фичей, а отражением того, как устроена таблица. Пойду заводить отдельную колонку с номером, пока до субботы далеко)
0
R
robert_sound
Junior
Сообщения: 8
Репутация: 3
Дата регистрации:
30.10.2026
01 нояб. 2026 г., 20:00 (GMT+3)

andrei_brest писал:
номер должен оставаться прежним, а место — нет.



Согласен. Только пусть место не меняется молча. «№1042, бронь снята в 19:40: банк не подтвердил оплату». Человек видит причину и время, а не гадает.

В студии то же. Студент открывает проект, а вокал заглушён. Кто, когда, зачем — тишина. Номер дорожки на месте, песни нет.

Изменение без строчки в журнале хуже самого изменения.
0
A
andrei_brest
Junior
Сообщения: 18
Репутация: 11
Дата регистрации:
19.09.2026
01 нояб. 2026 г., 21:08 (GMT+3)

dima_radio писал:
Надёжнейшая схема: ни одного дубля, ни одного показания.



Датчик с севшей батарейкой честнее моей страницы: он хотя бы замолчал. Моя продолжала бы вежливо показывать «ждём подтверждения от банка» до Нового года.

Как это выглядит со стороны покупателя: пятница, девятый час. Женщина звонит в магазин с этой страницей на экране. Кнопки нет, у неё смс о списании, а у нас заказ висит без статуса. Менеджер просит подождать, сам лезет в личный кабинет платёжки и там видит оплату. Двадцать минут нервов на ровном месте.

Поэтому ожидание надо ограничить, как у вас с повторами кадров. Через несколько минут без уведомления сайт сам спрашивает платёжный сервис о статусе по номеру заказа, если его API это позволяет. Плюс фоновая задача раз в какое-то время перебирает зависшие заказы. Это и есть повтор с тем же номером, только инициатива с нашей стороны.

Если и после этого ответа нет, канал объявляется потерянным, и решает человек. Странице не стоит изображать уверенность: «Мы пока не знаем, прошла ли оплата по №1042. Не платите повторно, проверим вручную», дальше телефон и часы работы. А кнопку «оплатить заново» вернуть только после того, как платёжка прямо ответила, что оплаты не было. «Ответа нет» и «не оплачено» — разные состояния, и склеивать их нельзя.
0
ruslan_krd
Junior
Сообщения: 72
Репутация: 71
Дата регистрации:
16.08.2026
02 нояб. 2026 г., 09:03 (GMT+3)

vlad_dv писал:
Я бы проверял его с телефона на плохой связи, а не по своему вайфаю.



Влад, это хорошее правило. Оно очень легко теряется по дороге от разговора к задаче.

Представь понедельник, планёрка. В карточке задачи написано: «Таймаут ожидания ответа — 15 секунд. Проверено». Всё правда. Разработчик проверил со своего стола, по проводу, и уложился в две секунды. Менеджер видит слово «проверено» и закрывает задачу. Автобуса на Русский в этой карточке нет, потому что его никто туда не вписал. Тестировали число, а не то, что увидит человек.

Дальше всё уже описал Андрей. Пятница, вечер, звонок, «у меня смс о списании, а у вас ничего». Только в моей картинке трубку берёт не разработчик, а менеджер, который про таймауты ничего не знает. Он открывает задачу, а там всё ещё «проверено» ;)

Поэтому я бы записывал такие проверки не как параметр, а как ситуацию. Не «таймаут 15 с», а примерно так:

— телефон, мобильная сеть, лучше не в центре города;
— нажать «оплатить», на середине включить режим самолёта, через минуту выключить;
— нажать ещё раз;
— записать, что видит человек на каждом шаге и какой у заказа номер.

Это час работы, если не меньше. Его стоит закладывать в оценку задачи сразу, а не выбивать потом как «доп». Иначе при первом же сжатии сроков эту строчку вычеркнут первой: она выглядит как перестраховка, пока не позвонит женщина со смс.

Второе, что я бы добавил к схеме Андрея и Димы, — у статуса «мы не знаем» должен быть хозяин. Страница честно пишет «не платите повторно, проверим вручную». Хорошо. А кто проверяет? Если ответ «ну, кто-нибудь», то никто. Нужна одна фамилия и понятное время, когда этот человек смотрит зависшие заказы. И чтобы у него был доступ в кабинет платёжки, а не только у разработчика, который в пятницу вечером уже за рулём.

И третье, про номер, который Мира отделила от места. Он нужен не только покупателю. Если дубль всё-таки проскочил, кто-то потом сверяет поступления с заказами. Человеку, который сверяет, всё равно, какой там был токен и сколько было повторов кадра. Ему нужно, чтобы у одной покупки был один номер, и по нему было видно: это лишнее списание, а это настоящее. Как потом оформлять возврат — вопрос к вашей бухгалтерии и правилам платёжного сервиса, тут я не советчик. Но без неизменного номера этот разговор начинается с угадывания.

Так что я бы взял твою фразу почти дословно и вставил её в шаблон задачи по приёмке. Не как совет, а как обязательный пункт: «проверено с телефона на плохой связи, вот что видел пользователь». Звучит скучно. Зато в пятницу вечером можно открыть карточку и сразу понять, что именно проверяли и что видел человек.
0
igor_kzn
Junior
Сообщения: 53
Репутация: 33
Дата регистрации:
28.08.2026
02 нояб. 2026 г., 10:06 (GMT+3)

ruslan_krd писал:
Тестировали число, а не то, что увидит человек.



У слова «проверено» есть ещё одна беда, кроме пропавшего автобуса: оно не стареет, а проверка стареет.

У меня с договорами это выглядит так. Коллега приносит папку, на первой странице карандашом «согласовано». Всё правда, согласовали. Только третью редакцию, а в папке уже пятая, где поменяли срок оплаты. Подпись осталась, условия уехали. Никто не обманывал, просто пометка жила своей жизнью отдельно от текста.

Ваша карточка задачи устроена так же. Через полгода сменят платёжный сервис или перепишут страницу возврата, а в задаче по-прежнему «проверено». Менеджер, который потом возьмёт трубку, увидит это слово и решит, что сценарий с режимом самолёта всё ещё в силе.

Поэтому к вашему списку шагов я бы добавил простую шапку, как у протокола:

— кто проверял и когда;
— какая версия сайта и какой платёжный сервис были на тот момент;
— что именно увидел человек, своими словами, а не «ок».

И правило: если поменялось что-то из второй строки, пометка «проверено» снимается сама, пока сценарий не пройдут заново. Это не бюрократия, а способ не спорить потом, что именно когда-то работало. С юристами, поверьте, такие споры длинные :)
0
D
dasha_voice
Junior
Сообщения: 30
Репутация: 7
Дата регистрации:
08.09.2026
02 нояб. 2026 г., 22:07 (GMT+3)

igor_kzn писал:
Подпись осталась, условия уехали.



В редакции это называется «согласовано с героем», и стареет оно так же быстро.

Вторник. Героиня читает свою цитату и пишет: «да, всё так». В среду выпускающий режет материал в полтора раза, и цитата теряет первую половину. Слова те же, её собственные, а смысл другой: было «пока не готовы закрывать цех», стало «закрывать цех». В четверг у меня в почте очень вежливое и очень холодное письмо. Формально мне нечего ответить: согласовано же. Только не этот текст.

На своём сервере я сделала ту же ошибку, только тише. Расшифровки интервью я вычитываю вручную и помечала готовые файлы в имени: «_ok». Потом прогнала старую папку через whisper заново, другой моделью, потому что хотела проверить, лучше ли она слышит. Файлы перезаписались, а «_ok» в именах остались. Через месяц беру «вычитанную» расшифровку, а там вместо «Минпромторг» — «мин пром торг», и фамилия героя склеена с предлогом.

Теперь пометка живёт внутри файла, первой строкой: кто вычитал, когда и какая модель расшифровывала. Если хоть что-то из этого поменялось, строка стирается вместе со старым текстом. Ровно ваше правило «поменялась вторая строка — проверено снимается», только мне до него пришлось дойти через неловкий звонок герою.
0