Comments 8
А как такой вариант: код запускает горутину отправки в канал и блокируется уже горутина, тем самым мы не блокируем основной поток, из минусов можно наплодить слишком много горутин (но если предположить что это временный пик и очередь постоянно вычитывается, то вроде и нет в этом проблемы)
Тут возможны, как мне кажется, всего два варианта. Либо сообщения из канала вам важны и вы блокируетесь на записи, либо сообщения не важны и вы их выкидываете при заполненном буфере.
Варианты с временным "расширением" вместимости канала кажутся странными и как минимум это переусложнение на ровном месте, можно просто сделать канал больше, но это путь в никуда. Можно "выкидывать" данные в другой канал, заполнение которого будет явно свидетельствовать о проблеме и тут уже можно будет "бить в колокол".
Размер канала в 50 кажется странным решением. Зачем вам ещё одна очередь? Что станет с данными в канале, если сервис вдруг "упадёт"? Вероятно стоит поднять количество обработчиков, если очерёдность не важна, ну и канал с нулевым буфером тоже напрашивается сам собой. Ну и конечно же добавить метрику по тому, на какой промежуток времени блокируетесь при записи в канал, либо сколько выкидываете данных и после на данную метрику повесить алерты.
Похоже, вы используете каналы go не по назначению. Если вам нужна очередь, то лучше использовать нормальную очередь, например, RabbitMQ, у которого есть нормальная админка и он управляем и расширяем. А каналы в go - это замена межпотокового взаимодействия и это довольно низкоуровневый механизм. Поэтому (имхо) в каналах go можно только блокироваться.
Не стоит использовать каналы как очереди. Их конечно можно использовать так, но это плохая идея. Каналы - это в первую очередь способ синхронизации горутин с возможностью обмена сообщениями, и размер буфера канала должен определяться не логикой функции(нужно ли вам отправить и забыть или заблокироваться), а нагрузкой на этот канал(вы должны четко понимать, сколько у вас будет чтений из канала и сколько записей и исходя из этого подобрать оптимальный размер буфера). А вы при помощи канала пытаетесь решить задачу, которую решают очереди. В таком случае лучше обернуть слайс в структуру и защитить ее sync.Cond, таким образом получив подобие каналов, которые с одной стороны блокируют поток, если буфер пустой, расширяют буфер если его недостаточно и обеспечивают потокобезопасность. Или использовать службы очередей вроде Kafka или RabbitMQ
Так если через канал пересылаются сообщения о том, что что-то в базе изменилось, то закономерно предположить, что в заполненном канале такие сообщения уже есть. И консюмер всё равно обработает ситуацию "база обновилась" когда прочитает такое сообщение из канала. Значит можно спокойно дропать дублирующие сообщения.
Выше уже озвучили мнения, и я дополню.
Если рассматривать каналы как один из вариантов Inter process communication (IPC), станет более понятно, зачем каналы нужны.
В описании модели памяти Go:
Программы, которые изменяют данные, к которым одновременно обращаются несколько горутин, должны сериализовать такой доступ. Чтобы упорядочить доступ, защитите данные с помощью операций с каналами или других примитивов синхронизации, таких как в пакетах sync и sync/atomic.
P. S. Кажется, что использовать каналы в Go как очередь можно и используют, но надо понимать при этом про ограничения и риски. В вашем случае можно попробовать применить паттерн Fanout с автоскейлингом.
Вариантов вообще то один: допустима длина канала 0 или 1, все остальное всегда забъется с потерями. Канал будет эпизодически блокироваться, но можно увеличить количество горутин на вычитку. Остальное неприемлимо, поставьте это правило на ревью
Забитый канал — как вы с ним поступаете в Go?