Best practices для разработчиков плагинов iikoFront API

Теги: v10

Рекомендации выработаны по итогам разбора реальных инцидентов с высокой нагрузкой на кассу. Главный принцип: нагрузку создают не «количество заказов», а поток событий и число их сериализаций/обработок. Каждая лишняя подписка и каждый лишний запрос умножаются на работу ядра и сборщика мусора.


1. Подписывайтесь на события один раз

Каждая подписка на событие (например, OrderChanged) — это отдельная полная сериализация и доставка данных в плагин. Дублирующиеся подписки умножают нагрузку.

2. Фильтруйте события как можно раньше внутри плагина

Важно понимать границы: фильтр .Where(...) в плагине выполняется после того, как ядро уже сериализовало заказ и передало его по каналу. Поэтому:

Пример из инцидента: плагину нужны были только закрытые заказы, а он реагировал на каждый OrderChanged.

PluginContext.Notifications.OrderChanged
    .Select(e => e.Entity)
    .Where(o => o.Status == OrderStatus.Closed)
    .Subscribe(OnOrderClosed);

Саму сериализацию убирают только две вещи:

  1. меньше подписчиков на событие (см. пункт 1);
  2. чтобы ядро не порождало лишних событий (это уже зона ответственности платформы, а не плагина).

3. Не запрашивайте весь заказ на каждое уведомление

На каждое изменение заказа повторный GetOrder / GetOrderItemProductGroups / TryGetOrderExternalDataByKey сериализует весь граф заказа заново.

4. Не вызывайте одни и те же методы по тысячи раз

GetExternalOperations, GetPrice, GetModifiers и аналогичные — дорогие вызовы с сериализацией ответа.

5. Держите обработчики событий лёгкими

События обрабатываются на общих потоках API. Долгий или блокирующий обработчик задерживает все остальные.

6. Синхронные нотификации: интеракция с юзером — да, тяжёлая работа — нет

Синхронные нотификации (INotification<...>, например NavigatingToPaymentScreen, BeforeProceedOrderPayment, BeforeDoCheque) существуют именно для того, чтобы плагин мог взаимодействовать с пользователем и повлиять на операцию:

Это легитимный сценарий — пользоваться им можно и нужно.

Ограничение: по возможности не делайте сетевых/дисковых/тяжёлых вычислений в обработчике. Показать диалог — быстро. А вот сходить по HTTP за данными, чтобы «подготовить» этот диалог, — уже секунды.

При этом, если бизнес-логика требует дождаться актуальных данных синхронно — это допустимо, но делайте это осознанно: задержка ляжет на UI и сложится с другими плагинами.

Реальный кейс: «вход в заказ ~10 сек» полностью раскладывался на три последовательных обработчика NavigatingToPaymentScreen — 5,5 с + 2,2 с + 2,0 с. Это почти наверняка сетевые вызовы, а не показ диалога.

Как делать правильно:

Итого: взаимодействие с юзером — это назначение синхронных нотификаций, используйте IViewManager. Сеть и тяжёлые вычисления внутри обработчика — по возможности в фон; синхронно — только когда этого действительно требует бизнес.

7. Освобождайте подписки

Не забывайте Dispose() подписок и ресурсов при остановке плагина. «Забытая» подписка продолжает получать события и создавать нагрузку, даже когда плагин уже не использует данные.

8. Помните про главный терминал и синхронизацию

На главном терминале группы данные синхронизируются на ведомые терминалы. Избыточные операции на главном умножаются на число ведомых.

9. Не полагайтесь на детали реализации API


Итог одной фразой

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