Код-ревью Senior sync и атомики обновлено

Код-ревью Go: ленивый синглтон и горячая перезагрузка конфига

Вопрос

Глобальный конфиг загружается лениво и перезагружается по сигналу. Найдите проблемы.

var (
    instance *Config
    mu       sync.Mutex
)

func GetConfig() *Config {
    if instance == nil {
        mu.Lock()
        if instance == nil {
            instance = loadConfig()
        }
        mu.Unlock()
    }
    return instance
}

func (c *Config) Reload() {
    newCfg := loadConfig()
    *c = *newCfg
}

Коротко

Ленивая инициализация и горячая замена без гонок: Конфиг после публикации не меняется, поэтому читать его поля можно без блокировок.

Что должен найти ревьюер

  1. Ошибкастрока 7
    Double-checked locking без атомиков: чтение instance вне мьютекса это гонка данных. По модели памяти Go другая горутина может увидеть ненулевой указатель на ещё не полностью записанную структуру. Используйте sync.Once или sync.OnceValue.
  2. Ошибкастрока 19
    Перезапись структуры на месте, пока другие горутины её читают: гонка, читатель может увидеть половину старых и половину новых полей. Нужна атомарная замена указателя через atomic.Pointer[Config].
  3. Стильстрока 10
    Ошибка загрузки конфига никак не обрабатывается: loadConfig должна возвращать error, а старт приложения падать с понятным сообщением.

Разбор

Ленивая инициализация и горячая замена без гонок:

var current atomic.Pointer[Config]

var initOnce = sync.OnceValue(func() error {
    cfg, err := loadConfig()
    if err != nil {
        return err
    }
    current.Store(cfg)
    return nil
})

func GetConfig() (*Config, error) {
    if err := initOnce(); err != nil {
        return nil, err
    }
    return current.Load(), nil
}

func Reload() error {
    cfg, err := loadConfig()
    if err != nil {
        return err
    }
    current.Store(cfg) // читатели видят либо старый, либо новый конфиг целиком
    return nil
}

Конфиг после публикации не меняется, поэтому читать его поля можно без блокировок.

Ответьте сами, а не прочитайте

В тренажёре вопрос попадёт в повторения и вернётся, когда начнёт забываться. Плюс билеты с зачётом: 20 вопросов, не больше 2 ошибок.