Перед вами сервис, который собирает метрики и пишет их в хранилище пачками. Код компилируется, но работает неправильно. Нужно найти ошибки и исправить их в starter.go.
AddMetric принимает метрики от многих горутин. Фоновая горутина копит их в буфере и отдаёт в ClickHouseClient.Save пачкой: когда набралось flushSize (50) штук или когда сработал таймер. Что именно с этим кодом не так, решайте сами. Известно три симптома: -race ругается, горутина после Stop продолжает жить, часть метрик не доходит до хранилища.
type Metric struct {
Name string
Value float64
}
// NewMetricsAggregator запускает фоновую горутину.
func NewMetricsAggregator(db *ClickHouseClient) *MetricsAggregator
// AddMetric добавляет метрику; вызывается из многих горутин.
func (ma *MetricsAggregator) AddMetric(m Metric)
// Stop останавливает агрегатор, сохраняет всё накопленное и возвращается.
func (ma *MetricsAggregator) Stop()
Хранилище ClickHouseClient уже написано (файл support.go, менять его не нужно). Save можно звать из любых горутин. Итоги читаются через Total() (сколько метрик сохранено всего) и Count(name) (сколько раз сохранена метрика с таким именем).
Что должно получиться:
- каждая добавленная метрика сохранена ровно один раз: без потерь и без дублей, независимо от того, что сработало, таймер или размер пачки;
- после
Stop() всё накопленное уже лежит в хранилище, а фоновая горутина завершилась;
- под
go test -race нет ни одной гонки.
Пример. Добавляем 250 метрик с именами m-0 ... m-249, затем вызываем Stop(). После этого db.Total() == 250 и db.Count("m-7") == 1 для любого имени из списка.
Условия использования: сначала NewMetricsAggregator, потом AddMetric. Все вызовы AddMetric заканчиваются до Stop, после Stop метрики не добавляют.
Тесты проверяют: 250 метрик, Stop, затем сразу (без ожидания) Total() и Count для каждого имени; сброс по таймеру, когда метрик меньше flushSize и Stop не вызывали (ждём до 3 секунд); 16 горутин по 100 метрик с AddMetric одновременно, после Stop всё сохранено ровно по разу и лишних горутин нет. Всё под -race.