Реализуйте ограничитель по алгоритму leaky bucket («дырявое ведро»),
который сглаживает всплески нагрузки: запросы «вытекают» строго по одному
раз в interval.
В отличие от token bucket, метод Acquire() здесь блокирующий: горутина
ждёт своей очереди, и каждый interval ровно один ожидающий получает право
продолжить. Это превращает любой всплеск в равномерный поток.
type LeakyBucket struct {
// ...
}
// NewLeakyBucket создаёт ведро, пропускающее 1 запрос раз в interval.
func NewLeakyBucket(interval time.Duration) *LeakyBucket
// Acquire блокирует вызывающую горутину до её очереди.
func (lb *LeakyBucket) Acquire()
// Stop останавливает внутренний таймер.
func (lb *LeakyBucket) Stop()
В тесте при interval = 10ms запускается 10 конкурентных вызовов Acquire().
Поскольку проходить можно лишь по одному раз в 10мс, все 10 завершатся не
мгновенно, а примерно за 9 * 10ms = 90ms. Все вызовы обязаны рано или поздно
вернуться. Проверка идёт под детектором гонок (-race).
На что смотрит интервьюер:
- Понимание алгоритма leaky bucket и отличия от token bucket
(здесь поток сглаживается, вызывающий ждёт).
- Реализация «ворот», открывающихся раз в
interval: например,
time.Ticker + общий канал, на который сериализуются Acquire.
- Корректная остановка таймера в
Stop() без утечки горутин.
- Потокобезопасность при конкурентных
Acquire() под -race.