Pull to refresh

Comments 26

Я, наверное, безбожно торможу. В чем смысл отзыва отложенного сообщения, уже попавшего в очередь? Разве смысл отложенного сообщения не означает, что, получив его, адресат меняет свое состояние т.к. наступил какой-то существенный момент времени? Я имею в виду «логическое» время, которое «порядок следования событий»?
А в целом отмена событий кажется мне очень сомнительной практикой, которая может скорее все запутать, чем помочь что-то решить.
С отложенными сообщениями время от времени люди наступают вот на какие грабли. Допустим, есть агент для выполнения какой-то сложной операции. В процессе ее выполнения агент должен провзаимодействовать с другими агентами. И у него, плюс к тому, есть ограничение на время выполнения всей операции. Если время истекло, а ответ от стороннего агента не получен, то всю операцию нужно отменить и возвратить отрицательный результат. Обычно это выглядит как-то так:
class op_performer : public so_5::agent_t {
  // Отложенное сообщение для ограничения времени операции.
  struct timeout final : public so_5::signal_t {};

  // Состояние ожидания следующей операции.
  state_t st_free{this};
  // Состояние выполнения операции.
  state_t st_working{this};
  ... // возможно, еще и вложенные подсостояния для st_working.

  void so_define_agent() override {
    // Подписываемся на отложенное сообщение в st_working
    // дабы отменить операцию.
    st_working.event(&op_performer::on_timeout);
    ...
  }

  void on_perform_operation(mhood_t<start_operation> cmd) {
     // Получили команду на начало новой операции.
     this >>= st_working;
     // Ограничиваем время выполнения операции.
     so_5::send_delayed<timeout>(this, 25s);
     ... // Начинаем операцию.
  }
  void on_completion(mhood_t<completion_message> cmd) {
    ... // Завершение операции и отсылка ответа кому нужно.
    this >>= st_free; // Возвращаемся в исходное состояние.
  }
  void on_timeout(mhood_t<timeout>) {
    ... // Отменяем выполнение операции.
    ... // Отсылаем отрицательный ответ кому нужно.
    this >>= st_free; // Возвращаемся в исходное состояние.
  }
...
};

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

Очевидная попытка исправить эту проблему: отмена отложенного сообщения при возврате в st_free:
class op_performer : public so_5::agent_t {
  ...
  so_5::timer_id_t timeout_timer_;
  ...
  void so_define_agent() override {
    st_free.on_enter([this]{ timeout_timer_.reset(); });
    st_working.on_enter([this] {
      timeout_timer_ = so_5::send_periodic<timeout>(this, 25s, 0s);
    });
    ...
  }
};

Но, к сожалению, сейчас в SObjectizer возможна ситуация, когда отмена отложенного сообщения фактически не сработает, т.к. сообщение уже будет стоять в очереди агента-получателя. Т.е. представьте себе, что в очереди op_performer-а уже стоят: completion_message (для текущей операции), start_operation (для новой операции) и timeout (для текущей операции). Агент обработает completion_message и start_operation, вновь войдет в st_working и получит timeout, который относился к предыдущей операции.

Вероятность такого стечения событий невелика. Но, к сожалению, когда в приложении работает несколько десятков тысяч агентов и летают десятки миллионов сообщений, иногда эта вероятность воплощается в жизнь. И, что особенно плохо, происходит это редко и без какой-либо системы. Поэтому разбираться с такими проблемами особенно сложно.
Т.е. речь идет не об акторах с состоянием, а о worker'ах, работающих в реальном времени с гарантиями best effort? Они отстают от реального времени (в очереди накапливаются задачи), а таймауты не маркируются идентификатором задачи, к которой относятся?
Мне представляются два способа решить проблему.
Маркировать таймаут идентификатором задачи, к которой он относится.
Обеспечить для события истечения таймаута гарантии доставки: очередь с приоритетами или выделенная очередь и т.д.
Мне кажется, что если речь идет о реальном времени, то надо быть жестким, причем тем жестчее, чем жестчее это самое реальное время)))). Все-таки гарантии реального времени как-то ортогональны к остальной логике выполнения и смешивать, как мне кажется, не стоит. Лучше сделать для этих гарантий явные и отдельные механизмы.
Маркировать таймаут идентификатором задачи, к которой он относится.
Как раз такой способ первым в статье и описан :) И это, кстати говоря, один из возможных подходов к решению проблемы отмены отложенных сообщений. Если пусть с отзывными сообщениями зайдет в тупик, то можно будет сделать просто какой-то штатный механизм в SObjectizer-е, который будет отсылать отложенные сообщения с автоматической генерацией для них ID-шников.
Обеспечить для события истечения таймаута гарантии доставки: очередь с приоритетами или выделенная очередь и т.д.
ИМХО, здесь даже не в реальном времени дело, а в самом факте существования очереди. Т.е. как только у нас может выстроится комбинация сообщений (completion_message, start_operation, timeout) так сразу же мы натыкаемся на эту проблему. Причем, у нас реально может быть правильный порядок появления сообщений: completion_message в момент времени t, start_operation в момент времени (t+1us) и timeout в момент времени (t+2us). Не говоря уже о том, что если мы работаем не в рамках ОС реального времени, а в обычной многозадачной системе, то мы запросто можем наткнуться на ситуацию, когда в момент времени t возникает timeout, но это сообщение не успевает встать в очередь, поскольку нить таймера прерывают (или же в попытке захватить замок очереди нить таймера оказывается не первой). А в очередь встает completion_message в момент времени (t+1us), а затем start_message в момент времени (t+2us). И лишь затем туда попадет timeout.
Как раз такой способ первым в статье и описан :)

Я так и знал, что по пэрвому вопросу сущэственных расхождэний у нас нэ будэт! (С) Сосо Джугашвили)))
Видимо, статью читал недостаточно внимательно, каюсь. Честно говоря, для решения конкретной узкой задачи, когда выполнение текущей задачи прерывается «отставшим» таймаутом от уже заверенной предыдущей — другого разумного решения я не вижу. А вот как его красиво оформить… Сделать специальный производный класс для агентов с неявным состоянием типа id-текущей задачи и в нем утилити-функцию для обработки таймаутов?.. Хм, что не подумаю, не шибко элегантно получается.
Что касается нарушения последовательности сообщений, то полностью согласен с Вами: в реальных условиях, да еще на ОС общего назначения… Я последнее время все на C да на C, да на микроконтроллерах в реальном режиме с аппаратными таймерами — так даже там…
А вот как его красиво оформить…
Возможно, это будет что-то похожее на so_5::extra::async_op::time_limited.
Ну и, в принципе, этот самый async_op::time_limited уже в каких-то сценариях будет тем самым механизмом с гарантированной отменой таймеров. Собственно, под это он и создавался с подачи ув.тов. PavelVainerman
Речь не обязательно о работе в реальном времени. Речь о любой работе (задаче) связанной с ожиданием (с таймаутами). Не знаю как везде, но как минимум в АСУ, «надёжное программирование» подразумевает, что любая операция должна быть конечной во времени. Поэтому чтобы вы не делали, что-либо включали/запускали
или что-то запрашивали, у Вас обязательно должен быть «защитный таймер» который гарантирует, что вы не застрянете на ожидании ответа «навечно». Т.е. у Вас по сути всегда, для какого-либо действия возникает необходимость произвести действие и засечь таймер (таймаут). И дальше либо «ответ»(обратная связь) придёт о том, что действие выполнено (успешно или нет), либо первее сработает таймер (таймаут) и вы будете выполнять какую-то обработку этой ситуации (например повторите попытку ещё несколько раз)… Тут-то и возникает описанная проблема, что если к Вам пришло вперёд сообщение о, допустим, успешном выполнении операции, то Вам сообщение от таймера уже не нужно. Точнее нужно, чтобы оно уже не приходило. Нужен механизм позволяющий отказаться от ранее заказанного таймера.

А eao197 как раз описал проблему. Что если механизм не гарантирует отказ от таймера (типа он уже в очереди, его оттуда не убрать), то почти гарантировано, что возникнет ситуация, когда Вы решите повторить попытку выполнить команду, засекаете вновь таймер, а сообщение приходит в следующее мгновенье, от старого таймера, потому-что оно уже было в очереди в этот момент…
Т.е. мне кажется, что при активной работе с таймерами (а они нужна практически всегда в реальном асинхронном взаимодействии, т.к. всегда должен быть защитный таймер на любую длительную операцию), отмена таймера (или игнорирование «старых» сообщений от таймеров) очень нужны и важны. И конечно, хочется чтобы это поддерживалось на уровне фреймворка.
Будет ли это механизм с маркированием каждого таймера или же с возможностью вытащить его из очереди на обработку, для меня как пользователя фреймворка не важно. Главное чтобы это было «незаметно» и в идеале не требовало от меня добавлять в свои сообщения доп. поля, по крайней мере явным образом.
Не вижу повода для дискуссии, но отмечу два момента:
  1. Создали таймаут — и вы уже в «дивном новом мире» программирования в реальном времени т.к. нужно выбрать величину таймаута и решить, что должно происходить, если он истечет до окончания операции. Если это не программирование в РТ — то что же это?!
    РТ — это, так сказать, аспект или, если угодно, cross-cutting concern, и необязательно «пронизывает весь код». В любом случае, если РТ проблематика где-то возникает — мне кажется, лучше это артикулировать явно. В личном опыте я много раз сталкивался с ситуациями, когда нежелание это делать вело к проблемам. И не разу не видел, чтобы явное рассмотрение аспектов реального времени приводило к «overengenering'у» или к другим проблемам.
  2. Если уж пошла такая пьянка, то то, что таймаут от одной операции прерывает другую операцию — это баг. А возможность такого развития событий — архитектурный просчет.

Вот как-то так, надеюсь, без обид.
РТ — это, так сказать, аспект или, если угодно, cross-cutting concern, и необязательно «пронизывает весь код».


Скорее всего мне не хватает опыта и знаний, посмотреть на проблему таймаутов более масштабно и соответственно решать её на другом уровне.

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


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

Вот как-то так, надеюсь, без обид.

Какие тут обиды. Вы расширяете мне кругозор. Спасибо )
Как заметил недавно небезызвестный bobuk: «Чем старше я становлюсь, тем больше мне хочется высказаться по поводам, по которым меня не спрашивают»))). А тут прямо спрашивают)))
Что почитать: я бы советовал посмотреть на классические работы Лесли Лампорта (Time, clocks, and the ordering of events in a distributed system) о времени в распределенных системах, они великолепны. Дополнительно поищите в интернете Time is illusion и недавнюю публикацию Synchrony is a Myth. Это для начала, а дальше само пойдет, если интерес не иссякнет.
Теперь по сути вопросов.
«Чистая» вычислительная программа выполняется в своеобразном «безвременье», т.е. если ее прерывает ОС, то течение времени для нее останавливается и возобновляется после, извините за тавтологию, возобновления. Время в такой программе чисто «Лампортовское», это последовательность наступления событий, например, завершения вызовов внешних процедур. В этом случае какие-либо соображения и ограничения реального времени отсутствуют.
Реальное время — это то, которое показывают часы на стене, а не то, которое пытается измерить сама программа. Она появляется, по моему опыту, в двух отличающихся случаях.
Во-первых, когда нам нужны гарантии прогресса при взаимодействии со внешними агентами. Посылаем блокирующий запрос ко внешнему http-серверу и не хотим блокироваться на нем вечно. Что делать? Правильно, выставить таймаут. И сразу появляется проблематика реального времени: какой таймаут? что делать, если таймаут истечет? и т.д. Даже при использовании «синхронных» блокирующих вызовов ответы могут не лежать на поверхности, а при использовании асинхронного API эти вопросы могут быть довольно непростыми, о чем, в частности, свидетельствует обсуждаемая публикация.
Во-вторых. Собственно системы реального времени. В этом случае временны ограничения явно появляются в постановке задачи. Например "… информация о совершении платежа на терминале должна быть передана провайдеру услуг не позднее 15 минут после успешного завершения платежа...". У многих разработчиков систем реального времени такое ограничение вызовет разве что улыбку: если величина ограничений сопоставима со скоростью выполнения операций, то это влечет множество проблем реализации, которые и составляют «легендарную сложность» программирования систем реального времени.
В любом случае, в программном коде ограничения реального времени выглядят как явные или «замаскированные» таймауты: мы должны предусмотреть какие-то действия если что-то произойдет (или не произойдет) по истечении какого-то, независимо от программы текущего, времени.
Как-то так. Простите за капитанство, сами напросились)))
Хорошо. Я осознал «высоту полёта и направление»… )
Спасибо.
отмена таймера (или игнорирование «старых» сообщений от таймеров) очень нужны и важны
Мы тут сталкиваемся с асинхронной природой взаимодействия между агентами (ну или вообще акторами в Модели Акторов). Актор A может попросить актора B выполнить какую-то операцию. Актор A может затем попросить актора B отказаться от выполнения этой операции. Но все эти просьбы выполняются асинхронно. Т.е. первая просьба от A к B идет в момент времени t0, но доходит до B только в какой-то последующий момент (t0+d0). Так же и со второй просьбой: А отсылает ее в t1, но дойдет она до B в (t1+d1).

Сейчас мы даже не будем разбирать случай, когда окажется, что t1 < (t0+d0). Т.е. когда A отсылает свою второю просьбу, а B еще даже не получил первую.

Достаточно просто вспомнить, что за время d1 актор B уже может удовлетворить просьбу A и успеть отослать A подтверждение (результат). Более того, B может отослать подтверждение еще до t1, но A мог еще не получить его.

Т.е. при асинхронной работе через очереди сообщений возможна ситуация, когда какой-то результат, который мы в момент t объявляем ненужным, уже стоит в очереди.

Соответственно, если мы хотим, чтобы a) наша работа строилась на взаимодействии через очереди, но при этом, b) мы могли изымать из очередей то, что нам уже «не нужно», то нам требуется:
  • либо возможность точно знать, где стоит ставшее ненужным сообщение + возможность изъять его из любой очереди;
  • либо возможность проигнорировать сообщение непосредственно перед обработкой;

Первый механизм противоречит идее разных диспетчеров и не очень хорошо дружит с механизмом доставки сообщений в режиме 1:N. Поэтому будет считать его реализуемым.

Второй механизм реализуем. Но тут вопрос в том, какова будет его цена. Например, тривиальная реализация проверки актуальности сообщения перед запуском его обработчика будет требовать дополнительного if-а для обработки каждого сообщения. Если делать нетривиальные реализации, в которых if-ы будут применяться только для тех сообщений, которые можно отозвать, то это приведет к дополнительному расходу памяти в каких-то случаях + усложнение механизма доставки сообщений и интерфейсов mbox/mchain. А это усложнение будет означать более высокую стоимость расширений SO-5 самим пользователем. Т.е. чем сложнее механизм mbox-ов, тем сложнее делать собственные mbox-ы под собственные задачи.

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

Ну и у нас, как у авторов библиотеки, есть выбор между следующими вариантами:

1. Реализация проверки актуальности каждого сообщения перед его обработкой. Позволяет получить отзывные сообщения, но ценой увеличения стоимости обработки любого сообщения. На синтетических бенчмарках это может означать потерю 7-8% производительности. Меня лично такой вариант вполне устроит, но с точки зрения маркетинга такая потеря не есть гуд.

2. Усложнение механизма отсылки сообщений через mbox/mchain так, чтобы дополнительные проверки выполнялись только для отзывных сообщений. А все остальные сообщения обрабатывались с такими же накладными расходами, как сейчас (если это вообще возможно). Лично мне этот вариант нравится меньше всего, т.к. любая чрезмерная сложность в реализации фреймворка неизбежно увеличивает затраты на его дальнейшее развитие и сопровождение.

3. Предоставление каких-то средств, упрощающих идентификацию отзывных сообщений. Что-то вроде описанного в статье механизма revocable_t или же что-то похожее на so_5::extra::async_op::time_limited. Это так же симпатичный лично для меня вариант, но тут пользователь будет вынужден с отзывными сообщениями работать не так, как с обычными. Либо, если будет что-то вроде async_op::time_limited, то работа с отзывными таймерами будет выглядеть совсем по-новому и по-другому, нежели текущая работа с таймерами в SO-5.

Вот на данный момент основные варианты, между которыми приходится выбирать. Для меня однозначно хорошего варианта нет. Тут с выбором может помочь мнение [потенциальных] пользователей SO-5.
1. Реализация проверки актуальности каждого сообщения перед его обработкой. Позволяет получить отзывные сообщения, но ценой увеличения стоимости обработки любого сообщения. На синтетических бенчмарках это может означать потерю 7-8% производительности. Меня лично такой вариант вполне устроит, но с точки зрения маркетинга такая потеря не есть гуд.


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

struct my_timer_message: 
  public so_5::lifetime_message_t
{
  ...
}


А подписка:
  st.lifetime_event( &MyAgent::on_timer );

внутри которой «прозрачным образом» пользовательский обработчик будет «обёрнут»
ещё одним предварительным обработчиком с провекой «устаревания»…
Имхо, это одна из вариаций на тему revocable_t. Пользователю все так же необходимо явно указывать, что сообщение является отзывным. Только делается это посредством наследования, а не объявления шаблона в параметре обработчика. Что, в чем-то имеет и свои преимущества.

Использовать отдельный подписчик, наверное, даже и не нужно. В обычном методе event можно определить, что инцидент события наследуется от типа lifetime_message_t. И сгенерировать для этого случая специальную дополнительную обертку.
Имхо, это одна из вариаций на тему revocable_t

Да похоже что так.

Тогда в продолжение темы, мысль о проблеме revocable_t сообщений с «защитой от переполнения»…

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

С другой стороны, как вариант, можно было бы добавить ещё одну (настраиваемую) политику обработки переполнения, при которой в случае невозможности поместить новое сообщение в очередь (или канал), происходит её чистка от «устаревших» сообщений. Т.е. прошлись по очереди, если встретили такой вид сообщений, проверили и удалили если устарело. И после этого, если место не освободилось, то реакция «как обычно». Конечно такая чистка может быть «дорогой операцией», но на то это и «отдельная настраиваемая политика обработки».
Т.е. прошлись по очереди, если встретили такой вид сообщений, проверили и удалили если устарело.
Не во всех диспетчерах такое можно обеспечить. Даже среди тех, что уже есть. А если рассматривать диспетчеры, которые интегрируются с какими-то другими компонентами (например, очередью сообщений Windows, очередью событий в Qt, очередями в QNX и т.д.), то от диспетчеров вообще сложно требовать возможности «пробежаться по очереди».

Для себя я пока пришел к похожей мысли: пока сообщение стоит в очереди агент-получатель не знает, что оно отозвано. Т.е. для получателя одно полноценное, занимает место, требует какого-то времени для обработки. Поэтому в лимитах оно должно учитываться.
Потенциально, есть еще один вариант, который заточен именно под работу с таймерными сообщениями. Его суть в том, что таймерная нить при наступлении времени T отсылает агенту-получателю не само сообщение, а некий прокси для сообщения. Т.е. в очередь агента становится прокси-объект, который затем извлекается из очереди и специальным образом обрабатывается. Специальная обработка состоит в том, чтобы сделать синхронный запрос к таймеру с идентификатором таймерной заявки. Таймер по идентификатору либо отдает экземпляр сообщения, либо же говорит, что идентификатор больше не актуален.

Получается, что при отмене таймерного сообщения идет синхронное обращение к таймеру и при попытке обработать сообщение так же идет запрос к таймеру. Что и позволяет избавится от доставки отмененного таймерного сообщения получателю.

Проблемы в этом подходе две:

1. Опять же нужно как-то разбираться, стоит ли у нас в очереди обычное сообщение или прокси-объект от таймера. Простое решение снизит производительность на синтетических бенчмарках, непростое — чрезмерно усложнит реализацию фреймворка и далее по тексту.

2. К таймеру будет выполняться множество синхронных запросов, что потенциально будет делать таймер узким местом, которое потребуется расшивать. Например, использовать распределение ID таймеров по разным «корзинам» с назначением каждой «корзине» своего объекта синхронизации. Либо вообще запуск нескольких таймеров с распределением отложенных/периодических сообщений между ними.

Либо же вторую проблему нужно будет решать каким-то другим способом, скажем, выделением специального дескриптора для таймерного сообщения, внутри этого дескриптора будет атомарный флаг актуальности сообщения, а умная ссылка на этот дескриптор будет передан в тот самый прокси-объект. Тогда при извлечении прокси-объекта нужно будет проконтролировать атомарный флаг актуальности сообщения…
Потенциально, есть еще один вариант, который заточен именно под работу с таймерными сообщениями. ..


Мне кажется этот вариант «тяжелее» проверки флага перед обработкой. Я в целом видел реализацию как ваш старый проверенный способ — «монотонно растущий ID» у сообщений c проверкой перед доставкой.
Мне кажется проверка флага — не настолько дорогая операция, среди
общей массы «проверок» происходящих в среднестатистическом алгоритме.
Не могу удержаться и дополняю предыдущий пост)))
Я бы оформил предоставление агенту возможность реагировать на таймеры реального времени явно, например, как дополнительный параметр шаблона. В смысле по дефолту этот параметр работает как сейчас, но если его явно задать на специальное значение, то, к примеру, для таймеров создается своя высокоприоритетная очередь или еще как-то.
Это к вопросу об ортогональности аспектов: по умолчанию сообщение от таймера может застревать в общей очереди, отставая от реального времени, а если надо, то агент получает более строгие гарантии доставки связанных с реальным временем сообщений. В некотором смысле, все сообщения, связанные с реальным временем — таймаут. Ну, или каждое такое сообщение — немножечко таймаут)))
Интересная мысль. Но, наверное, в рамках SObjectizer-а это можно обеспечить за счет специализированных диспетчеров, которые знают типы сообщений и их приоритеты. По аналогии с тем, что описывалось в этой статье.
Да, я написал про агенты, но в контексте SO речь шла, конечно, про диспетчеры. Добавление РТ (гарантированные таймауты, время жизни сообщений, обработка просроченных сообщений) — это все про планировщики.
А в целом отмена событий кажется мне очень сомнительной практикой, которая может скорее все запутать, чем помочь что-то решить.
В общем, да, это не такая штука, которая должна использоваться повсеместно. Но бывают ситуации, когда агент A попросил агента B выполнить какую-то операцию, которая может занимать довольно много времени. Например, агент B проверяет рисковость платежа и этот агент единственный, тогда как агентов A несколько и запросы от A к B могут скапливаться в очереди. Пока B обрабатывает предыдущие запросы агент A может понять, что операцию проводить уже не нужно. Скажем, клиент сам отказался от платежа.

По-хорошему, уже отосланный B запрос нужно отозвать. Но как это сделать?

Можно использовать схему collector-performer. Тогда B будет представлять из себя пару из двух агентов: B_collector с очередью запросов и B_performer. Пока B_performer выполняет длительную операцию, B_collector только разбирается с очередью ждущих запросов. В этом случае агент A может послать сообщение на отмену запроса, это сообщение получит B_collector и выбросит запрос, если этот запрос еще ждет своей очереди.

Но создание такой пары B_collector/B_performer — это работа, которую нужно сделать. По хорошему, ее нужно сделать. Однако, если бы делаем какой-то proof-of-concept на коленке, то нам может быть удобно, если бы SObjectizer предоставлял функциональность по отзыву ранее отосланных сообщений.

Так что, да, фича не однозначная. Но т.к. многие насущные потребности разработчиков уже реализованы, то есть возможность позаниматься и вот такими вопросами.
Будь на то моя воля, я бы не заморачивался этой фичей, нужной, как Вы говорите, исключительно в контексте быстрого прототипирования. Нехай collector/performer пилят! Умные и так поймут, почему, а глупцам давать в руки способ выстрелить себе в ногу и поднять крик «SObjectizer г&^%но..!» я бы не стал.
Ну вот поэтому эта фича пока не сделана, а только рассматривается. Ну и статья появилась в том числе и для того, чтобы фидбэк какой-то получить. Так что большое спасибо за то, что нашли время написать свое мнение.
Sign up to leave a comment.

Articles

Change theme settings