Задать вопрос
@vgrabkowot

Не будет гонки и повреждения данных в памяти?

Я храню конфигурацию в одном json файле, плюс конфигурация должна быть потокобезопасной без обмазывания мьютексами по всему коду (по моему опыту найти пропущенный мьютекс то еще страдание) и лишнем копировании.
На выходе получил подобное API:
cfg := config.Read()
defer cfg.Close()
ctx, cancel := context.WithTimeout(context.Background(), cfg.HTTP.Timeout.StopServer*time.Second)

Не пропустил ли я чего? Первый раз решаю такую задачу. Мб есть решение оптимальней?
package config

import (
	"encoding/json"
	"os"
	"sync"
)

var (
	configPool sync.Pool
	cfgSync    = &sync.RWMutex{}
	cfgValue   *Cfg
)

func Read() *Cfg {
	var c *Cfg
	if v := configPool.Get(); v != nil { //если в пуле есть "свободные" указатели то возвращаем их
		return v.(*Cfg)
	} else { //пул пуст, создаем новый указатель
		c = newCfg()
		valid(c)
		configPool.Put(c)
		return Read()
	}
}
func (cfg *Cfg) Close() {
	configPool.Put(cfg)
}
func newCfg() *Cfg {
	cfgSync.RLock()
	is := cfgValue == nil
	cfgSync.RUnlock()
	if is { //файл незагружен в память, загружаем
		f, err := os.Open("./config/config.json")
		if err != nil {
			panic("Error open config file: `./config/config.json `" + err.Error())
		}
		defer f.Close()
		dec := json.NewDecoder(f)
		cfg := &Cfg{}
		if err := dec.Decode(cfg); err != nil {
			panic("Error parse config file: " + err.Error())
		}

		cfgSync.Lock()
		cfgValue = cfg
		cfgSync.Unlock()
	}
	//создаем новый указатель
	ret := new(Cfg)

	//копируем из старого в новый указатель
	cfgSync.RLock()
	*ret = *cfgValue
	cfgSync.RUnlock()
	//возвращаем копию
	return ret
}
  • Вопрос задан
  • 100 просмотров
Подписаться 1 Средний 1 комментарий
Решения вопроса 1
@falconandy
По-моему вы переусложняете без необходимости. Если значения из конфига только читаются, то потокобезопасность не нужна. Если же у вас предполагается еще и изменение, то в пул возвращаются указатели на "порченные" объекты, что тоже неправильно.
Если возможно, конфиг лучше прочитать в начале выполнения, а также паниковать на уровне main.
В результате и код проще, и тормозить нечему:
package config

import (
	"encoding/json"
	"fmt"
	"os"
)

type Cfg struct {
	// Fields
}

var (
	cfg *Cfg
)

func Config() *Cfg {
	return cfg
}

func LoadConfig() error {
	f, err := os.Open("./config/config.json")
	if err != nil {
		return fmt.Errorf("can't open config file: %w", err)
	}
	defer f.Close()

	dec := json.NewDecoder(f)
	var c *Cfg
	if err := dec.Decode(c); err != nil {
		return fmt.Errorf("can't decode config file: %w", err)
	}

	if err := validate(c); err != nil {
		return fmt.Errorf("can't validate config file: %w", err)
	}
	
	cfg = c

	return nil
}

func validate(cfg *Cfg) error {
	// Logic
	return nil
}
Ответ написан
Комментировать
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы