Вопрос в продолжение Redis streams кто-нибудь юзал?. Пришла в голову идея как сделать очередь на редисе на стримах вместо пачки zset так чтоб было красиво и органично. Вернее не очередь, а скорее распределенное выполнение задач.
Дальше идёт стена текста, но если не лень - попинайте если видите явный косяк
Что нужно:
- потребитель закидывает задачу через grpc в сервис с воркерами
- задача идёт в очередь, один из воркеров берет задачу, делает
- потребитель получает результат.
- если задача не выполнена за N (отличается в зависимости от задачи) секунд - скорее всего воркер сдох или затупил и её надо выдать другому воркеру
- нагрузка небольшая, от силы 10рпс. Но таски бывают очень разные по стоимости выполнения
- таски в целом не критичные, пролюбить пару-тройку при смене мастера в редисе можно
По факту вызов синхронный с точки зрения потребителя, он почти всегда ждёт результат.
Что есть: редис с двумя zset, прям как по манам про reliable queue. Описание задачи в виде строки атомарно через луа переходит из одного сета в другой когда её берет воркер. Есть монитор, который смотрит второй сет и пинает просроченные таски. Результат задачи обычно файл (исторически), идёт на s3, потребитель поллит его ожидая результат.
Что не нравится:
- сериализация в строку это треш, лучше hash, но тогда нужно отдельно писать в хеш задачу и отдельно пихать её ключ в очередь
- в задаче появляется служебная информация типа номера попытки
- если результат не файл - надо что-то где-то извращать, например давать какой-то ключ и поллить его потребителем либо юзать пабсаб
- луа, без него такое не сделать
- кастомный код, много
- монитор должен магическим образом знать через какое время задача считается зафейленной либо это информация опять же пихается в описание задачи (например скором в zset)
- сложно корректно вернуть ошибку
- поллить s3 это не очень весело
Что думаю сделать: Очередь на стримах по типу кассы в маке (который самозапретился в рф).
- Потребитель кидает таск в интерфейс/распределитель/оркестратор. Для простоты - в кассу. Касса кидает его в стрим в виде хеша, и в ответ получает талон с номером в стриме, отдает консьюмеру. Консьюмер открывает пабсаб и и ждёт «возле экрана с номерами над кассой».
- воркер берет таск и начинает утюжить. Когда он его завершает, он отдает его в кассу.
- Касса кидает в пабсаб клич с номером на талоне N раз с небольшим интервалом проверяя что таск не закоммичен. Если на выдачу никто не пришёл - пожимает плечами и убирает заказ (коммитит в стрим)
- Консьюмер идёт к кассе, берет заказ, смотрит что там бургер вместо эксепшена, коммитит в стрим и идёт с подносом обратно в вызывающий сервис. Ну либо берет экспепшн, как повезёт.
- Монитора нет, но раз в N секунд потребитель тихо спрашивает на кассе где его картоха, и на этом запросе таск перераспределяется другому воркеру (тут видимо +1 очередь и клейм, не нашёл метода как сказать стриму что таск свободен). Это в целом исключение, таймаут заведомо больше типового времени работы таска
Плюсы:
- нативно хеш вместо велосипеда для сериализации и двойных ключей
- нет служебной информации в таске, номер попытки и уник идут автоматом из стрима
- таск коммитит тот кто его заказал, а не так что воркер сделал и дальше хоть потоп
- нет кастомного монитора, поддержка разных таймаутов идёт весьма органично
- почти нет кастомного кода
- тип результата может быть любым без геморроя, можно нормально вернуть исключение (ну, почти нормально, таки с сериализацией)
- если сдохла касса - можно тем же потребителем с тем же талоном пойти на соседнюю. Результат в пабсаб идёт несколько раз с интервалом, заведомо превышающим время запроса внутри цода. Тут правда не все так просто ибо номер нужно будет сначала вернуть в вызывающий сервис, либо делать второй запрос либо извращаться с partial response. Проще имхо второй запрос.
Пните кто видит какие проблемы у этого подхода.
П.с. почему не кролик/кафка/зеро/актив: редис уже есть и его поддерживает провайдер, с бэкапами, sla и вот этим всем. Брать ради одной-двух очередей ещё три сервака и пачку софта на поддержу я не буду. Да и оно немного не для того, тут скорее распределение при некой синхронности для консьюмера