Почему в sync.Map в Go отсутствует функция для получения длины
Что проверяют
Понимаете ли вы, что у конкурентной структуры длина это не свойство, а снимок, устаревающий в момент возврата. И знаете ли, когда sync.Map вообще уместна.
Как отвечать
Внутри sync.Map две карты: read, которую читают без блокировки через атомарную загрузку, и dirty под мьютексом. Удаление часто не убирает запись, а помечает её, живые данные переезжают из dirty в read по счётчику промахов. Единого счётчика живых ключей там просто нет, и честная длина потребовала бы взять мьютекс и пройти обе карты.
Дальше главный аргумент: даже посчитав, вы получите число, которое уже неверно. Пока значение возвращается наверх, соседние горутины успевают писать и удалять. Годится такое число как метрика, но для решения «влезет ли ещё один элемент» оно негодное, а API, которое провоцирует именно такое использование, в стандартную библиотеку не пустили.
Что делать вместо этого:
- для метрики держать рядом
atomic.Int64и менять его только по результату операций, которые сообщают, изменился ли состав:LoadOrStoreвозвращает признак загрузки,LoadAndDeleteпризнак наличия; - для логики, где длина обязана быть согласована с содержимым, брать обычную карту под
sync.RWMutexили карту с шардированием: там счётчик меняется под тем же замком, что и данные; - помнить, где
sync.Mapвыигрывает: ключ записан один раз и много читается, либо горутины работают с непересекающимися наборами ключей. В остальных случаях карта под мьютексом обычно быстрее.
Где спотыкаются
Считают длину через Range и называют это решением. Это обход всей структуры, и снимка он не даёт: элементы, добавленные во время обхода, могут быть посещены, а могут и нет.
Второе: заводят счётчик и увеличивают его на каждом Store. Повторная запись по существующему ключу элемент не добавляет, счётчик уезжает вверх и больше никогда не сходится с содержимым.
Третье: тащат sync.Map всюду как быстрый потокобезопасный аналог карты, не замерив. На нагрузке с активной записью она проигрывает обычной карте под RWMutex, и это видно на простом бенчмарке за несколько минут.