Долгие операции на backend глазами Android-разработчика

Недавно на работе появилась задача: Android отправляет запрос на backend, а результат может готовиться от нескольких секунд до 5 минут.

Пока результат не готов, backend возвращает специальный статус, после которого клиент должен повторить запрос позже.

Самое простое решение - polling. Но возник вопрос, как часто опрашивать backend.

Если делать запрос каждые 500 мс, то за 5 минут один клиент в худшем случае сделает около 600 запросов. Кажется избыточным, особенно если таких клиентов много.

Первое, что приходит в голову - увеличивать интервал между запросами. Например: 1 сек -> 2 сек -> 3 сек -> 5 сек, а дальше оставить какой-то максимальный интервал. Плюс общий timeout и ограничение количества попыток.

Как можно избавиться от постоянных запросов:

WebSocket для такой задачи кажется избыточным. Постоянного двустороннего обмена нет: Android по сути ждёт от backend одно событие - операция завершена.

Тогда есть SSE (Server-Sent Events).

Клиент открывает HTTP-соединение, а backend сам отправляет событие, когда результат готов. Для операции на несколько минут звучит неплохо: вместо постоянного polling держим одно соединение и ждём результат.

Но когда начал смотреть на это именно со стороны Android, оказалось, что не всё так гладко.

Во-первых, соединение может оборваться. Сменился Wi-Fi на мобильную сеть, пропала сеть, сработал timeout на клиенте или где-нибудь на proxy/load balancer - SSE больше нет.

Во-вторых, мобильное приложение может уйти в background. Пользователь свернул приложение, заблокировал экран, Android спустя какое-то время убил процесс. Рассчитывать на то, что открытое соединение всё это время будет жить, нельзя.

Получается, даже с SSE всё равно нужно продумывать восстановление.

Я бы строил это примерно так:

Android запускает операцию и получает `operationId`.

Пока пользователь находится в приложении - слушаем SSE. Если соединение оборвалось, можем переподключиться.

А если пользователь свернул приложение и вернулся через несколько минут - не пытаемся восстановить какое-то старое соединение, а запрашиваем состояние операции по `operationId`.

Если результат уже готов - забираем его. Если операция ещё выполняется - снова начинаем ждать.

То есть backend в любом случае должен хранить состояние операции, а SSE становится скорее способом быстро получить изменение этого состояния, а не единственным источником информации о результате.

И после всего этого polling уже не выглядит таким плохим вариантом :)

Для операции максимум на 5 минут polling с нормальным интервалом/backoff может оказаться проще в реализации и достаточно эффективным. SSE уменьшает количество запросов и позволяет быстрее получить результат, но добавляет работу с reconnect, lifecycle приложения и восстановлением состояния.

Поэтому я пока смотрю на выбор скорее как на trade-off:

Polling- проще и устойчивее, но создаёт лишние запросы и задержку между готовностью результата и следующим опросом.

SSE - backend сам сообщает о готовности, но соединение ненадёжно и нужно проектировать восстановление.

WebSocket - скорее всего избыточен для сценария, где от сервера нужно дождаться одного результата.

Интересно, что бы вы выбрали для операции, которая выполняется от 5 секунд до 5 минут? Polling с backoff или SSE?

Долгие операции на backend глазами Android-разработчика | Сетка — социальная сеть от hh.ru Долгие операции на backend глазами Android-разработчика | Сетка — социальная сеть от hh.ru