Пост

Длительные операции в API (long-running operations)

Это пост с кратким пересказом очередной главы книги “API Design Patterns” Дж. Дж. Гивакса, посвящённой длительным операциям.

Для чего нужны длительные операции? Синхронный API отлично справляется с быстрыми задачами. Но для ресурсоемких операций, таких как обработка больших данных, простое ожидание ответа может оказаться проблемой. Появляется масса вопросов: сколько ждать? как понять, что запрос обрабатывается? что делать, если результат больше не нужен? Клиент остаётся в подвешенном состоянии и для таких случаев напрашивается другое решение. Вот тут и появляется паттерн длительные операции.

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

Для реализации LRO создается отдельный ресурс Operation, содержащий ID задачи, ее статус (например, выполнена или приостановлена) и метаданные с информацией о ходе выполнения. Интерфейс API должен поддерживать методы для работы с длительными операциями.

Способы получения конечного результата

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

Обработка ошибок. HTTP-коды ошибок недостаточны для точной обработки на стороне клиента, так как они не различают ошибки на этапе извлечения ресурса и ошибки, вызванные самой операцией. Лучше возвращать код, описание ошибки и дополнительные детали в структуре OperationError.

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

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

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

Инспектирование операций необходимо, чтобы отслеживать состояние списка LRO. Для этого в API можно реализовать метод GET /operations, который вернет список операций с возможностью фильтрации по статусу и метаданным. Это позволит, например, увидеть какие операции еще выполняются или приостановлены.

Хранить или чистить? Длительные операции (имеется ввиду ресурсы в БД) сразу после завершения становятся практически бесполезными, поскольку весь смысл в их результате. Простое решение, конечно, хранить ресурсы постоянно, но это может быть ресурсозатратно. Ресурсы можно очищать через скользящее окно по полю expireTime, удаляя их, скажем, через 30 дней после завершения. Более сложные политики хранения могут привести к усложнению системы, поэтому лучше придерживаться простого, единого срока хранения для всех LRO.

Авторский пост защищен лицензией CC BY 4.0 .

© Ainur. Некоторые права защищены.

Использует тему Chirpy для Jekyll