Идемпотентность и разделяемые потоки работ
Важно обеспечивать идемпотентность одиночных операций. Это следует учитывать при разработке приложений, допускающих асинхронность, поскольку идемпотентность операций способствует отказоустойчивости и допускает слабую связанность, что, в свою очередь, позволяет снизить задержку, облегчает масштабирование и дает возможность автономной работы.
Каждая операция должна производить одно и только одно воздействие, даже если она выполняется (или просто журнализуется) в нескольких репликах. Бронирование одной комнаты в гостинице должно (с высокой вероятностью) приводить к тому, что постоялец получит в точности одну комнату. Заказ в режиме онлайн одной книги не должен (очень часто) приводить к отправке заказчику двух книг.
Чтобы это гарантировать, приложение обычно связывает с каждой работой уникальный номер, или идентификатор. Это связывание происходит на входе в систему (т.е. независимо от того, в какой реплике эта работа будет выполняться изначально). Когда запрос данной работы двигается по сети, каждая реплика может легко опознать, что уже встречала эту операцию, и не выполнять работу повторно.
Иногда поступающая работа стимулирует выполнение другой работы. Например, обработка заказа на покупку может привести к планированию доставки. Обработкой этого заказа и даже планированием доставки могут заняться, например, две реплики. При наличии уникальной идентификации заказа на покупку на входе в систему этот избыток энтузиазма со стороны реплик системы можно выявлять по мере того как информация распространяется по сети. Ниже, при обсуждении согласованности "рано или поздно" (eventual consistency) мы увидим, как это можно проделать (вероятностным образом).
Уникальный идентификатор работы ("uniquifier") играет две важные роли:
-
используется в качестве ключа при разделении работы в масштабируемой системе;
-
позволяет системе выявлять повторное выполнение одного и того же запроса; в результате работа становится идемпотентной.