Как клиент узнает результат обработки файла
Что проверяют
Умеете ли вы спроектировать асинхронную обработку: клиент не должен держать HTTP-соединение минутами, пока сервер разбирает файл. Следом обычно спрашивают, что будет с задачей, если сервис перезапустится посреди обработки.
Как отвечать
Базовая схема: клиент загружает файл, сервер сохраняет его (например, в S3-совместимое хранилище), создаёт запись задачи и сразу отвечает 202 Accepted с идентификатором задачи и заголовком Location на ресурс статуса. Дальше способы узнать результат:
- polling:
GET /jobs/{id}возвращаетpending,processing,doneилиfailed, при готовности ссылку на результат. Сервер может подсказать интервал черезRetry-After; - webhook: клиент при загрузке передаёт URL, сервер вызывает его по завершении. Нужны подпись тела (HMAC), повторы с экспоненциальной задержкой и идемпотентность на стороне получателя;
- SSE или WebSocket: если результат ждёт открытый браузер и важна скорость реакции;
- server streaming в gRPC: для внутренних клиентов на gRPC.
Со стороны Go важно, где живёт обработка. Задача ставится в очередь (Kafka, RabbitMQ, NATS) или в таблицу, которую воркеры забирают через SELECT ... FOR UPDATE SKIP LOCKED. Воркеры это пул горутин с ограничением параллельности, каждая получает свой context с таймаутом. Статус и ошибка пишутся в базу, завершение сервиса идёт через http.Server.Shutdown и дожидается воркеров.
Где спотыкаются
Запускают go process(file) прямо в хендлере. При рестарте задача пропадает без следа, количество горутин ничем не ограничено, а если передать им r.Context(), контекст отменится, как только хендлер вернёт ответ.
Держат синхронный запрос до конца обработки и упираются в таймауты балансировщика и клиента, после чего клиент повторяет загрузку и файл обрабатывается дважды.
Делают webhook без подписи и без повторов: одна неудачная доставка, и клиент так и не узнаёт результат. Статус задачи всё равно нужен как запасной путь.