Реализуйте Batcher. Он принимает события по одному и отдаёт их в flush пачками. Пачка сбрасывается, когда выполнилось любое из условий:
- накопилось 100 событий;
- прошло 50 мс с предыдущего сброса.
type Event struct {
ID int
}
func NewBatcher(flush func(batch []Event)) *Batcher
func (b *Batcher) Add(event Event)
func (b *Batcher) Stop() // сбросить остаток и остановить фоновую горутину
Пример:
b := NewBatcher(func(batch []Event) { fmt.Println(len(batch)) })
for i := 0; i < 250; i++ {
b.Add(Event{ID: i})
}
b.Stop()
// 100, 100, 50 — две пачки по размеру и остаток при остановке
Если события идут редко, пачка уходит по таймеру: три события и пауза 150 мс дадут один вызов flush с тремя событиями.
Требования:
- Каждая пачка содержит от 1 до 100 событий. Пустые пачки в
flush не передаются.
- Все события доставляются по одному разу, порядок внутри пачки совпадает с порядком
Add.
Add можно вызывать из нескольких горутин. Если flush работает медленно, Add будет ждать: очередь не бесконечная.
Stop сбрасывает остаток, дожидается завершения фоновой горутины и вызывается один раз, после того как все Add завершились. Add после Stop запрещён.
flush должен возвращаться, иначе Stop не завершится.
- Нет гонок под
-race, нет утечек горутин.
Тесты: 250 событий подряд, порядок сохранён, пустых пачек нет, ни одна не больше 100, а полная пачка из 100 встречается; три события сбрасываются по таймеру одним вызовом flush без Stop; остаток из двух событий сбрасывается при Stop; восемь параллельных продьюсеров, каждое событие доставлено ровно один раз и лишних горутин после Stop нет; при заблокированном flush двадцать тысяч Add не проходят все (очередь ограничена).
Сначала попробуй сам — подсказки не дают готовый ответ.