Войти Регистрация
Конкурентность

27. Рейтинг продавца на Авито: свой singleflight

Сложная

Go · 49 строк

Рейтинг продавца показывается на каждой карточке объявления. У крупного продавца автомобилей 3 тысячи объявлений, и после рассылки на его карточки за секунду приходит до 300 запросов. Чтобы не класть базу, одновременные запросы по одному продавцу схлопываются: в базу идёт первый, остальные ждут его результат.

Запрос к базе обычно 15 мс, в час пик бывают таймауты. Отметьте строки с ошибками.

В работе: Сервис карточки объявления, блок «Рейтинг продавца».

Как проверяем Нажмите на строку с ошибкой и коротко напишите, что не так. Баг засчитан, если отмечена хотя бы одна его строка. Зачёт — найдено не меньше 60 % багов с учётом серьёзности (критичный весит 3, обычный 2, мелкий 1), а лишних отметок не больше, чем багов в коде. Пустые строки, скобки и комментарии лишними не считаем. Плюс 2 вопроса про поведение программы — на зачёт не влияют. Ответ один на подход: после него показываем разбор.
singleflightcheck-then-actзакрытие каналаcontextотмена

Источник: наша задача на данных Авито: рейтинг продавца на карточке объявления. Условие пересказано своими словами.

  1. package rating
  2. import (
  3. "context"
  4. "sync"
  5. )
  6. type call struct {
  7. done chan struct{}
  8. val float64
  9. err error
  10. }
  11. type Loader struct {
  12. mu sync.RWMutex
  13. calls map[int64]*call
  14. fetch func(ctx context.Context, sellerID int64) (float64, error)
  15. }
  16. // Rating схлопывает одновременные запросы по продавцу в один запрос к базе.
  17. func (l *Loader) Rating(ctx context.Context, sellerID int64) (float64, error) {
  18. l.mu.RLock()
  19. c, ok := l.calls[sellerID]
  20. l.mu.RUnlock()
  21. if ok {
  22. <-c.done
  23. return c.val, c.err
  24. }
  25. c = &call{done: make(chan struct{})}
  26. l.mu.Lock()
  27. l.calls[sellerID] = c
  28. l.mu.Unlock()
  29. defer func() {
  30. l.mu.Lock()
  31. delete(l.calls, sellerID)
  32. l.mu.Unlock()
  33. close(c.done)
  34. }()
  35. v, err := l.fetch(ctx, sellerID)
  36. if err != nil {
  37. c.err = err
  38. close(c.done)
  39. return 0, err
  40. }
  41. c.val = v
  42. return v, nil
  43. }