Что делать с данными в очереди с идентификатором после получения запроса
Что проверяют
Понимаете ли вы жизненный цикл сообщения: кто и в какой момент считает данные обработанными, что произойдёт при падении воркера и как не оставить в памяти запись, которую уже никто не ждёт. В Go к этому добавляется конкурентный доступ: очередь читают несколько горутин сразу.
Как отвечать
Сначала уточните, что за очередь. Внутренняя очередь процесса (канал или структура под мьютексом) и внешний брокер ведут себя по-разному, и весь ответ строится на этом различии.
Для схемы «запрос пришёл, данные лежат в очереди под идентификатором» разумно сказать так:
- идентификатор служит ключом корреляции: обработчик кладёт результат в канал, зарегистрированный под этим ключом, и снимает регистрацию через
defer; - запись удаляется не в момент чтения, а после успешной обработки, иначе падение между чтением и записью результата теряет данные;
- повторная доставка считается нормой, поэтому обработка обязана быть идемпотентной: тот же id второй раз не должен списать деньги дважды;
- у ожидания всегда есть
context.WithTimeout, и отвалившийся клиент освобождает слот; - карта ожидающих закрывается мьютексом либо заменяется на
sync.Map, а её размер ограничен.
mu.Lock()
ch, ok := pending[id]
delete(pending, id)
mu.Unlock()Где спотыкаются
Пишут в обычную map из нескольких горутин без мьютекса. Рантайм это ловит и валит процесс с concurrent map writes, причём под нагрузкой, а не в тестах.
Второе: путают снятие с очереди и подтверждение обработки. Удалили сразу при чтении, значит потерянное сообщение уже не вернуть; не удаляете никогда, значит растёт и память, и число повторов.
Третье: забывают про таймаут и уборку. Ключ, по которому никто не пришёл, живёт вечно, а горутина ждёт ответа в канал, который никто не закроет. Это не гипотетическая утечка, а обычная причина роста памяти у сервиса с корреляцией по id.