пятница, 13 ноября 2009 г.

[comp.prog.thoughts] Фокусы распределенности: ответ приходит раньше, чем уходит запрос ;)

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

Представим себе, что есть покупатель (система P), магазин (система M) и склад (система S). Покупатель делает заказ в магазине, магазин выдает заказ на склад, а склад выдает покупателю информацию о сроках поставки заказа. Грубая схема такова:

  • от P в M идет синхронный запрос pay;
  • M выполняет у себя какие-то действия и возвращает P ответ pay_ack;
  • от M в S идет запрос deliver, в котором лежат координаты P;
  • S отсылает P запрос deliver_schedule.

Так вот, если M связывается с S до того, как отошлет pay_ack в P, то P может получить deliver_schedule еще до того, как М отправит в P pay_ack. Более того, даже если M отсылает в S запрос deliver после отсылки pay_ack в P, все равно deliver_schedule может придти к P раньше, чем pay_ack!

Что в этом фокусе страшного? Страшное начинается при попытке понять, к какому именно заказу относится deliver_schedule :)

В самом плохом случае в pay_ack может лежать некий уникальный ID заказа от M. Этот же ID заказа будет и в deliver_schedule. Но для P он окажется неизвестным, т.к. pay_ack до него еще не дошел.

Получается картинка: P инициировал pay и ждет ID заказа, тут к нему приходит deliver_schedule с неизвестным ID заказа. Что же с ним делать? Логировать сей печальный факт и выбрасывать. Потом получать pay_ack, фиксировать ID заказа в БД и ждать deliver_schedule. Который не больше не придет :)

Причем здесь распределенность? При том, что P в современных условиях может быть реализован в виде нескольких (десятков, сотен) автономных процессов, работающих на разных компьютерах. Поэтому простые схемы, когда P не обрабатывает deliver_schedule из-за того, что он заблокирован на ожидании pay_ack, уже не пройдут. Се ля ви.

PS. Простое решение здесь состоит в том, чтобы P мог отказываться принимать deliver_schedule с неизвестным ID. И чтобы в этом случае S перепосылал deliver_schedule через какое-то время. Но, если S не может этого делать, то P придется что-то предпринимать самому (скажем, фиксировать неизвестные ему deliver_schedule и при получении pay_ack проверять их).

PPS. А еще в распределенных системах pay_ack может просто теряться из-за сбоев оборудования или рестартов компонентов. Так что простые двухфазные обмены вроде pay/pay_ack, deliver_schedule/deliver_schedule_ack не есть хорошо. А многофазные схемы снижают пропускную способность и увеличивают время обработки, что так же не есть хорошо.

PPPS. Схема с покупателем, магазином и складом приведена просто для иллюстрации.

Отправить комментарий