Реализуйте паттерн singleflight: подавление дублирующихся вызовов.
Если несколько горутин одновременно запрашивают один и тот же ключ, дорогая
функция fn должна выполниться ровно один раз, а её результат (значение и
ошибка) — раздаться всем ожидающим.
Это классический способ защититься от «cache stampede»: когда популярный ключ
протух, и сотни запросов одновременно ломятся в БД за одним и тем же значением.
type Group struct {
// ...
}
// Do выполняет fn для key. Параллельные вызовы с тем же key
// дожидаются результата единственного выполнения fn.
func (g *Group) Do(key string, fn func() (interface{}, error)) (interface{}, error)
В тесте 20 горутин одновременно вызывают Do с одним ключом. Функция fn
атомарно увеличивает счётчик, немного «работает» и возвращает значение.
После завершения счётчик обязан быть равен 1, а все 20 горутин — получить
одинаковые значение и ошибку. Для разных ключей fn выполняется отдельно.
Проверка идёт под детектором гонок (-race).
На что смотрит интервьюер:
- Классическая схема:
map[string]*call под sync.Mutex, где call
содержит sync.WaitGroup, результат и ошибку.
- Первый вызов для ключа создаёт
call, делает wg.Add(1), запускает fn;
остальные находят существующий call и ждут wg.Wait().
- После завершения запись по ключу удаляется из map, чтобы следующая волна
снова могла выполнить
fn.
- Отсутствие гонок и дедлоков под
-race; корректная работа с разными ключами.