All streams
Search
Write a publication
Pull to refresh
197
0
Денис Исаев @jirfag

User

Send message
Именно: версия/фингерпринт по сути делает то же самое только проще/удобнее/экономнее.

Полная блокировка UI может решать часть проблем, в статье это и описано:


В итоге вдвоем они придумали следующее решение: приложение не дает изменить параметры заказа и бесконечно пытается создать заказ, пока получает коды ответа 5xx или же сетевые ошибки. Вася добавил серверную валидацию, предложенную Федей.

Но есть и другие точки входа в изменение заказов:


  1. водитель/диспетчер меняет/отменяет заказ
  2. кнопки действий в пушах
  3. второе устройство
Использовать key-value хранилище, умеющее в шардинг, их много на выбор.

Чем шардинг тут поможет? Как вы атомарно запишите в две разные субд? Либо вероятностный 2PC использовать, либо через очередь добиваться eventual consistency, либо подобные варианты, это сильно сложнее описанного в статье.

Перевод статьи про миграцию Uber с Postgres на MySQL

На масштабах Яндекс.Такси таймауты происходят много раз каждую секунду даже при взаимодействии между микросервисами: где-то у сетевиков роутер подлагнул, где-то на сервере базы данных обращение к диску тормозит из-за битого сектора, где-то CPU перегружен у машины и тд.
Если рассмотреть пользователя такси, то это обычно мобильное приложение. Мобильная сеть обычно ненадежна и имеет большой latency. Легко попасть в зону плохого покрытия, зайти в переход. Особенно проблема актуальна в городах/странах где плохо с 3G.


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

точно, спасибо, оно вносило путаницу, переименовал почти везде в статье клиент в приложение.

Пользователь может не заметить — он уже мог свернуть приложение, телефон мог сесть.

Пользователь вызвал такси (у него номер 1), заказ завершился, после этого пользователь еще раз вызвал такси (у него тоже номер 1). Как теперь поменять для самого первого заказа оценку, например? PUT /taxi/1 будет менять уже другой заказ.

Если вы про продакшн Яндекс.Такси, то не видел чтобы подобные проблемы проходили дальше дизайн-ревью. История все таки выдуманная и около-реальная.

Опыт около-реальный и не только по работе в Яндекс.Такси. Но в Яндекс.Такси до продакшена при мне ничего из подобного не доходило, все отлавливалось на этапе дизайн-ревью.

Опыт около-реальный и не только по работе в Яндекс.Такси. Но в Яндекс.Такси до продакшена при мне ничего из подобного не доходило, все отлавливалось на этапе дизайн-ревью.
Мне кажется стоит в саппорт написать детали.

Здравствуйте.
Во-первых, стоит эмулировать как медленные запросы, приводящие к таймаутам, так и просто быстрый connection refused.
Во-вторых, для ручного эмулирования плохой сети на серверной машине я обычно использовал iptables, есть еще netem.
В-третьих, в девелоперской консоли хрома можно сэмулировать медленное мобильное соединение.
В-четвертых, можно протестировать один раз потерю пакетов, но код дописывается постоянно, баги возникают постоянно, и оно уже не тестируется. Есть chaos engineering: эмулировать сетевые ошибки (и не только: падения сервисов и тп) в продакшене регулярно. Например, есть chaos monkey и chaoskube.

Спасибо, схема с &additional_taxi={n} выглядит интересно. Подозреваю, что дубли и гонки могут быть в сценариях подобных такому:


  1. пользовател создал заказ №1
  2. пользователь отправляет запрос на создание заказа №2 с параметром &additional_taxi={2}
  3. заказ создается, ответ от сервер не доходит до клиента, клиент начинает ретраить запрос
  4. параллельно диспетчером/водителем отменяется заказ №1, что уменьшает n в таблице active_orders c 2 до 1
  5. запрос на создание заказа №2 отретраился и успешно создал дубль

Вы придумали использовать уникальный ключ идемпотентности, который будет одинаковый для всех заказов этого клиента в течение 24 часов. То есть придумываете ID, который должен быть каждый раз уникальным, но он у вас не уникален. А затем вы боретесь с тем, что этот ваш неуникальный ключ всегда повторяется, и используете кучу костылей для исправления этой неуникальности.

Не совсем: ключ идемпотентности надо генерировать новый под каждый уникальный (в интерфейсе) заказ, а не использовать один для всех заказов.

Уникальный уид у каждой команды — аналог ключа идемпотентности из статьи.


— На сервере проверяем есть ли уже команда с таким уид, если нет, то выполняем

Важный нюанс: для микросервисной архитектуры может не получиться просто так взять и сделать для всех API в одном месте в коде такую проверку идемпотентности. Потому что часто в микросервисных архитектурах у каждого сервиса своя база данных, а для работы этой логики нужно атомарное сохранение уида и изменения/создания заказа. Для этого нужна распределенная транзакция, что далеко нетривиально.


— Клиентское приложение спрашивает о состоянии заказа с уид: его просит подтвердить заказ (приложение должно само отправить true например). Заказ помечается как созданный и по нему дальше производятся действия.

А зачем этот шаг? Если приложение само отправляет подтверждение всегда, то я выше писал комментарий про задачу о двух генералах, что проблемы остаются те же. Если пользователь должен интерфейсно нажать какое-то подтверждение у заказа, тогда это защитит от ряда дублей, но ценой лишнего клика.

сценарий 2 сюда скопирую для удобства:


  1. клиент отправляет запрос на создание заказа, заказ создается, но мобильная сеть лагает, и клиент не получает ответ об успешном создании заказа;
  2. диспетчер, либо сам клиент через пуш по какой-то причине отменяет заказ: отмена заказа сделана как удаление строки из таблицы базы данных;
  3. клиент шлет повторный запрос на создание заказа: запрос успешно выполняется и создается еще один заказ, так как ключ идемпотентности, хранившийся в прошлом заказе, больше не существует в таблице.

Почему приехало отменённое такси если записи в бд нету

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

Понял, этот подход работает, пока не нужно позволять заказывать несколько такси одновременно. В статье есть раздел про мультизаказ.
Интересная аналогия.
Если сравнивать операцию создания заказа с открытием TCP соединения, то в случае ее неидемпотентности либо приезжает лишнее такси, либо создается лишнее соединение. Первое намного критичнее второго.
Если сравнивать операцию изменения заказа с посылкой данных по TCP соединению, то у TCP есть sequence numbers (в какой-то степени аналогия с версионированием списка заказов и ключа идемпотентности) для защиты от дублирования, переупорядочивания и тп.
На шаге 3 клиент делает повторный запрос заказа такси, так как на шаге 1 клиент не получил ответа от сервера. И повторный запрос успешно выполняется, что приводит к приезду такси.
Спасибо! Я дописал в этот абзац следующее предложение:
Также стоит отметить, что версия может быть как числом (номером последнего изменения), так и хэшом от списка заказов: так, например, работает параметр `fingerprint` в Google Cloud API для изменения тегов инстансов.
на любое количество одинаковых заявок создается один заказ

звучит слишком абстрактно, я привел кейс выше, как в нем избежать дублирования без использования хотя бы ключа идемпотентности?

Information

Rating
Does not participate
Location
Москва, Москва и Московская обл., Россия
Works in
Date of birth
Registered
Activity