package config import ( "bytes" "errors" "io" "io/ioutil" "sync" "fmt" "github.com/pelletier/go-toml" "github.com/siddontang/go/ioutil2" ) var ( ErrNoConfigFile = errors.New("Running without a config file") ) const ( DefaultAddr string = "127.0.0.1:6380" DefaultDBName string = "goleveldb" DefaultDataDir string = "./var" KB int = 1024 MB int = KB * 1024 GB int = MB * 1024 ) type LevelDBConfig struct { Compression bool `toml:"compression"` BlockSize int `toml:"block_size"` WriteBufferSize int `toml:"write_buffer_size"` CacheSize int `toml:"cache_size"` MaxOpenFiles int `toml:"max_open_files"` MaxFileSize int `toml:"max_file_size"` } type RocksDBConfig struct { Compression int `toml:"compression"` BlockSize int `toml:"block_size"` WriteBufferSize int `toml:"write_buffer_size"` CacheSize int `toml:"cache_size"` MaxOpenFiles int `toml:"max_open_files"` MaxWriteBufferNum int `toml:"max_write_buffer_num"` MinWriteBufferNumberToMerge int `toml:"min_write_buffer_number_to_merge"` NumLevels int `toml:"num_levels"` Level0FileNumCompactionTrigger int `toml:"level0_file_num_compaction_trigger"` Level0SlowdownWritesTrigger int `toml:"level0_slowdown_writes_trigger"` Level0StopWritesTrigger int `toml:"level0_stop_writes_trigger"` TargetFileSizeBase int `toml:"target_file_size_base"` TargetFileSizeMultiplier int `toml:"target_file_size_multiplier"` MaxBytesForLevelBase int `toml:"max_bytes_for_level_base"` MaxBytesForLevelMultiplier int `toml:"max_bytes_for_level_multiplier"` DisableAutoCompactions bool `toml:"disable_auto_compactions"` UseFsync bool `toml:"use_fsync"` MaxBackgroundCompactions int `toml:"max_background_compactions"` MaxBackgroundFlushes int `toml:"max_background_flushes"` EnableStatistics bool `toml:"enable_statistics"` StatsDumpPeriodSec int `toml:"stats_dump_period_sec"` BackgroundThreads int `toml:"background_theads"` HighPriorityBackgroundThreads int `toml:"high_priority_background_threads"` DisableWAL bool `toml:"disable_wal"` MaxManifestFileSize int `toml:"max_manifest_file_size"` } type LMDBConfig struct { MapSize int `toml:"map_size"` NoSync bool `toml:"nosync"` } type ReplicationConfig struct { Path string `toml:"path"` Sync bool `toml:"sync"` WaitSyncTime int `toml:"wait_sync_time"` WaitMaxSlaveAcks int `toml:"wait_max_slave_acks"` ExpiredLogDays int `toml:"expired_log_days"` StoreName string `toml:"store_name"` MaxLogFileSize int64 `toml:"max_log_file_size"` MaxLogFileNum int `toml:"max_log_file_num"` SyncLog int `toml:"sync_log"` Compression bool `toml:"compression"` UseMmap bool `toml:"use_mmap"` } type SnapshotConfig struct { Path string `toml:"path"` MaxNum int `toml:"max_num"` } type TLS struct { Enabled bool `toml:"enabled"` Certificate string `toml:"certificate"` Key string `toml:"key"` } type AuthMethod func(c *Config, password string) bool type Config struct { m sync.RWMutex `toml:"-"` AuthPassword string `toml:"auth_password"` //AuthMethod custom authentication method AuthMethod AuthMethod `toml:"-"` FileName string `toml:"-"` // Addr can be empty to assign a local address dynamically Addr string `toml:"addr"` AddrUnixSocketPerm string `toml:"addr_unixsocketperm"` HttpAddr string `toml:"http_addr"` SlaveOf string `toml:"slaveof"` Readonly bool `toml:readonly` DataDir string `toml:"data_dir"` Databases int `toml:"databases"` DBName string `toml:"db_name"` DBPath string `toml:"db_path"` DBSyncCommit int `toml:"db_sync_commit"` LevelDB LevelDBConfig `toml:"leveldb"` RocksDB RocksDBConfig `toml:"rocksdb"` LMDB LMDBConfig `toml:"lmdb"` AccessLog string `toml:"access_log"` UseReplication bool `toml:"use_replication"` Replication ReplicationConfig `toml:"replication"` Snapshot SnapshotConfig `toml:"snapshot"` ConnReadBufferSize int `toml:"conn_read_buffer_size"` ConnWriteBufferSize int `toml:"conn_write_buffer_size"` ConnKeepaliveInterval int `toml:"conn_keepalive_interval"` TTLCheckInterval int `toml:"ttl_check_interval"` //tls config TLS TLS `toml:"tls"` } func NewConfigWithFile(fileName string) (*Config, error) { data, err := ioutil.ReadFile(fileName) if err != nil { return nil, err } cfg, err := NewConfigWithData(data) if err != nil { return nil, err } cfg.FileName = fileName return cfg, nil } func NewConfigWithData(data []byte) (*Config, error) { cfg := NewConfigDefault() if err := toml.Unmarshal(data, cfg); err != nil { return nil, fmt.Errorf("newConfigwithData: unmarashal: %s", err) } cfg.adjust() return cfg, nil } func NewConfigDefault() *Config { cfg := new(Config) cfg.Addr = DefaultAddr cfg.HttpAddr = "" cfg.DataDir = DefaultDataDir cfg.DBName = DefaultDBName cfg.SlaveOf = "" cfg.Readonly = false // Disable Auth by default, by setting password to blank cfg.AuthPassword = "" // default databases number cfg.Databases = 16 // disable access log cfg.AccessLog = "" cfg.LMDB.MapSize = 20 * MB cfg.LMDB.NoSync = true cfg.UseReplication = false cfg.Replication.WaitSyncTime = 500 cfg.Replication.Compression = true cfg.Replication.WaitMaxSlaveAcks = 2 cfg.Replication.SyncLog = 0 cfg.Replication.UseMmap = true cfg.Snapshot.MaxNum = 1 cfg.RocksDB.EnableStatistics = false cfg.RocksDB.UseFsync = false cfg.RocksDB.DisableAutoCompactions = false cfg.RocksDB.DisableWAL = false cfg.adjust() return cfg } func getDefault(d int, s int) int { if s <= 0 { return d } return s } func (cfg *Config) adjust() { cfg.LevelDB.adjust() cfg.RocksDB.adjust() cfg.Replication.ExpiredLogDays = getDefault(7, cfg.Replication.ExpiredLogDays) cfg.Replication.MaxLogFileNum = getDefault(50, cfg.Replication.MaxLogFileNum) cfg.ConnReadBufferSize = getDefault(4*KB, cfg.ConnReadBufferSize) cfg.ConnWriteBufferSize = getDefault(4*KB, cfg.ConnWriteBufferSize) cfg.TTLCheckInterval = getDefault(1, cfg.TTLCheckInterval) cfg.Databases = getDefault(16, cfg.Databases) } func (cfg *LevelDBConfig) adjust() { cfg.CacheSize = getDefault(4*MB, cfg.CacheSize) cfg.BlockSize = getDefault(4*KB, cfg.BlockSize) cfg.WriteBufferSize = getDefault(4*MB, cfg.WriteBufferSize) cfg.MaxOpenFiles = getDefault(1024, cfg.MaxOpenFiles) cfg.MaxFileSize = getDefault(32*MB, cfg.MaxFileSize) } func (cfg *RocksDBConfig) adjust() { cfg.CacheSize = getDefault(4*MB, cfg.CacheSize) cfg.BlockSize = getDefault(4*KB, cfg.BlockSize) cfg.WriteBufferSize = getDefault(4*MB, cfg.WriteBufferSize) cfg.MaxOpenFiles = getDefault(1024, cfg.MaxOpenFiles) cfg.MaxWriteBufferNum = getDefault(2, cfg.MaxWriteBufferNum) cfg.MinWriteBufferNumberToMerge = getDefault(1, cfg.MinWriteBufferNumberToMerge) cfg.NumLevels = getDefault(7, cfg.NumLevels) cfg.Level0FileNumCompactionTrigger = getDefault(4, cfg.Level0FileNumCompactionTrigger) cfg.Level0SlowdownWritesTrigger = getDefault(16, cfg.Level0SlowdownWritesTrigger) cfg.Level0StopWritesTrigger = getDefault(64, cfg.Level0StopWritesTrigger) cfg.TargetFileSizeBase = getDefault(32*MB, cfg.TargetFileSizeBase) cfg.TargetFileSizeMultiplier = getDefault(1, cfg.TargetFileSizeMultiplier) cfg.MaxBytesForLevelBase = getDefault(32*MB, cfg.MaxBytesForLevelBase) cfg.MaxBytesForLevelMultiplier = getDefault(1, cfg.MaxBytesForLevelMultiplier) cfg.MaxBackgroundCompactions = getDefault(1, cfg.MaxBackgroundCompactions) cfg.MaxBackgroundFlushes = getDefault(1, cfg.MaxBackgroundFlushes) cfg.StatsDumpPeriodSec = getDefault(3600, cfg.StatsDumpPeriodSec) cfg.BackgroundThreads = getDefault(2, cfg.BackgroundThreads) cfg.HighPriorityBackgroundThreads = getDefault(1, cfg.HighPriorityBackgroundThreads) cfg.MaxManifestFileSize = getDefault(20*MB, cfg.MaxManifestFileSize) } func (cfg *Config) Dump(w io.Writer) error { data, err := toml.Marshal(*cfg) if err != nil { return err } if _, err := w.Write(data); err != nil { return err } return nil } func (cfg *Config) DumpFile(fileName string) error { var b bytes.Buffer if err := cfg.Dump(&b); err != nil { return err } return ioutil2.WriteFileAtomic(fileName, b.Bytes(), 0644) } func (cfg *Config) Rewrite() error { if len(cfg.FileName) == 0 { return ErrNoConfigFile } return cfg.DumpFile(cfg.FileName) } func (cfg *Config) GetReadonly() bool { cfg.m.RLock() b := cfg.Readonly cfg.m.RUnlock() return b } func (cfg *Config) SetReadonly(b bool) { cfg.m.Lock() cfg.Readonly = b cfg.m.Unlock() }