965 lines
28 KiB
Go
965 lines
28 KiB
Go
/*
|
|
* Copyright 2026 Safronov Grigorii
|
|
*
|
|
* Licensed under the CDDL, Version 1.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
*
|
|
* You may obtain a copy of the License at
|
|
* https://opensource.org/licenses/CDDL-1.0
|
|
*/
|
|
|
|
// Файл: internal/config/config.go
|
|
// Назначение: Загрузка и парсинг TOML-конфигурации, валидация параметров,
|
|
// предоставление доступа к настройкам кластера, хранилища и REPL.
|
|
|
|
package config
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/BurntSushi/toml"
|
|
)
|
|
|
|
type Config struct {
|
|
Cluster ClusterConfig `toml:"cluster"`
|
|
Storage StorageConfig `toml:"storage"`
|
|
Repl ReplConfig `toml:"repl"`
|
|
Log LogConfig `toml:"log"`
|
|
API APIConfig `toml:"api"`
|
|
Replication ReplicationConfig `toml:"replication"`
|
|
Plugins PluginsConfig `toml:"plugins"`
|
|
Compression CompressionConfig `toml:"compression"`
|
|
WebUI WebUIConfig `toml:"webui"`
|
|
Performance PerformanceConfig `toml:"performance"`
|
|
Security SecurityConfig `toml:"security"`
|
|
Monitoring MonitoringConfig `toml:"monitoring"`
|
|
Recovery RecoveryConfig `toml:"recovery"`
|
|
WAL WALConfig `toml:"wal"`
|
|
MVCC MVCCConfig `toml:"mvcc"`
|
|
Transactions TransactionsConfig `toml:"transactions"`
|
|
ACL ACLConfig `toml:"acl"`
|
|
}
|
|
|
|
type ClusterConfig struct {
|
|
Name string `toml:"name"`
|
|
NodeIP string `toml:"node_ip"`
|
|
NodePort int `toml:"node_port"`
|
|
RaftPort int `toml:"raft_port"`
|
|
RaftDataDir string `toml:"raft_data_dir"`
|
|
Bootstrap bool `toml:"bootstrap"`
|
|
Nodes []string `toml:"nodes"`
|
|
|
|
HeartbeatTimeoutMs int `toml:"heartbeat_timeout_ms"`
|
|
ElectionTimeoutMs int `toml:"election_timeout_ms"`
|
|
CommitTimeoutMs int `toml:"commit_timeout_ms"`
|
|
|
|
SnapshotIntervalMin int `toml:"snapshot_interval_min"`
|
|
SnapshotThreshold int `toml:"snapshot_threshold"`
|
|
|
|
SplitBrainPrevention bool `toml:"split_brain_prevention"`
|
|
RecoveryTimeoutSec int `toml:"recovery_timeout_sec"`
|
|
|
|
Region string `toml:"region"`
|
|
PriorityZone int `toml:"priority_zone"`
|
|
}
|
|
|
|
type StorageConfig struct {
|
|
PageSizeMB int `toml:"page_size_mb"`
|
|
MaxCollections int `toml:"max_collections"`
|
|
MaxDocumentsPerCollection int `toml:"max_documents_per_collection"`
|
|
}
|
|
|
|
type ReplConfig struct {
|
|
PromptColor string `toml:"prompt_color"`
|
|
HistorySize int `toml:"history_size"`
|
|
}
|
|
|
|
type LogConfig struct {
|
|
LogFile string `toml:"log_file"`
|
|
LogLevel string `toml:"log_level"`
|
|
}
|
|
|
|
type APIConfig struct {
|
|
Port int `toml:"port"`
|
|
}
|
|
|
|
type ReplicationConfig struct {
|
|
Enabled bool `toml:"enabled"`
|
|
MasterMaster bool `toml:"master_master"`
|
|
SyncReplication bool `toml:"sync_replication"`
|
|
ReplicationTimeoutMs int `toml:"replication_timeout_ms"`
|
|
}
|
|
|
|
type PluginsConfig struct {
|
|
Enabled bool `toml:"enabled"`
|
|
ScriptDir string `toml:"script_dir"`
|
|
AllowList []string `toml:"allow_list"`
|
|
MaxCPUTimeMs int `toml:"max_cpu_time_ms"`
|
|
MaxMemoryMB int `toml:"max_memory_mb"`
|
|
MaxExecutionTimeSec int `toml:"max_execution_time_sec"`
|
|
MaxInstructions int64 `toml:"max_instructions"`
|
|
HotReloadIntervalSec int `toml:"hot_reload_interval_sec"`
|
|
MaxEventLogSize int `toml:"max_event_log_size"`
|
|
LoadTimeoutSec int `toml:"load_timeout_sec"`
|
|
}
|
|
|
|
type CompressionConfig struct {
|
|
Enabled bool `toml:"enabled"`
|
|
Algorithm string `toml:"algorithm"`
|
|
Level int `toml:"level"`
|
|
MinSize int `toml:"min_size"`
|
|
}
|
|
|
|
type WebUIConfig struct {
|
|
Enabled bool `toml:"enabled"`
|
|
Port int `toml:"port"`
|
|
Theme string `toml:"theme"`
|
|
}
|
|
|
|
type PerformanceConfig struct {
|
|
EnablePipeline bool `toml:"enable_pipeline"`
|
|
BatchSize int `toml:"batch_size"`
|
|
ReadFromFollower bool `toml:"read_from_follower"`
|
|
MaxConnections int `toml:"max_connections"`
|
|
}
|
|
|
|
type SecurityConfig struct {
|
|
EnableTLS bool `toml:"enable_tls"`
|
|
CertFile string `toml:"cert_file"`
|
|
KeyFile string `toml:"key_file"`
|
|
CAFile string `toml:"ca_file"`
|
|
MinVersion string `toml:"min_version"`
|
|
}
|
|
|
|
type MonitoringConfig struct {
|
|
EnableMetrics bool `toml:"enable_metrics"`
|
|
MetricsPort int `toml:"metrics_port"`
|
|
EnableTracing bool `toml:"enable_tracing"`
|
|
TraceSampleRate float64 `toml:"trace_sample_rate"`
|
|
}
|
|
|
|
type RecoveryConfig struct {
|
|
AutoRejoin bool `toml:"auto_rejoin"`
|
|
MaxRetrySec int `toml:"max_retry_sec"`
|
|
DataReplicationTimeoutSec int `toml:"data_replication_timeout_sec"`
|
|
StaleReadTimeoutSec int `toml:"stale_read_timeout_sec"`
|
|
}
|
|
|
|
// ========== НОВЫЕ КОНФИГУРАЦИОННЫЕ СТРУКТУРЫ ==========
|
|
|
|
// WALConfig настройки Write-Ahead Log
|
|
type WALConfig struct {
|
|
SegmentSizeMB int `toml:"segment_size_mb"`
|
|
SyncIntervalSec int `toml:"sync_interval_sec"`
|
|
BatchSize int `toml:"batch_size"`
|
|
RecoveryWorkers int `toml:"recovery_workers"`
|
|
Enabled bool `toml:"enabled"`
|
|
}
|
|
|
|
// MVCCConfig настройки Multi-Version Concurrency Control
|
|
type MVCCConfig struct {
|
|
MaxVersionsPerDoc int `toml:"max_versions_per_doc"`
|
|
VisibilityMapSize int `toml:"visibility_map_size"`
|
|
PruneIntervalMin int `toml:"prune_interval_min"`
|
|
RetentionDays int `toml:"retention_days"`
|
|
ReadCacheSize int `toml:"read_cache_size"`
|
|
ReadCacheTTLSec int `toml:"read_cache_ttl_sec"`
|
|
}
|
|
|
|
// TransactionsConfig настройки транзакций
|
|
type TransactionsConfig struct {
|
|
DefaultTimeoutSec int `toml:"default_timeout_sec"`
|
|
DeadlockCheckIntervalSec int `toml:"deadlock_check_interval_sec"`
|
|
MaxSavepointsPerTx int `toml:"max_savepoints_per_tx"`
|
|
TwoPhaseCommitTimeoutSec int `toml:"two_phase_commit_timeout_sec"`
|
|
Enabled bool `toml:"enabled"`
|
|
CheckpointIntervalSec int `toml:"checkpoint_interval_sec"`
|
|
}
|
|
|
|
// ACLConfig настройки Access Control List
|
|
type ACLConfig struct {
|
|
MaxDeniedLogSize int `toml:"max_denied_log_size"`
|
|
TemporaryGrantTTLHours int `toml:"temporary_grant_ttl_hours"`
|
|
EnableRoleHierarchy bool `toml:"enable_role_hierarchy"`
|
|
CacheTTLSec int `toml:"cache_ttl_sec"`
|
|
}
|
|
|
|
// ========== Методы для новых конфигураций ==========
|
|
|
|
// GetSegmentSize возвращает размер сегмента WAL в байтах
|
|
func (w *WALConfig) GetSegmentSize() int64 {
|
|
if w.SegmentSizeMB <= 0 {
|
|
return 64 * 1024 * 1024
|
|
}
|
|
return int64(w.SegmentSizeMB) * 1024 * 1024
|
|
}
|
|
|
|
// GetSyncInterval возвращает интервал синхронизации WAL
|
|
func (w *WALConfig) GetSyncInterval() time.Duration {
|
|
if w.SyncIntervalSec <= 0 {
|
|
return 5 * time.Second
|
|
}
|
|
return time.Duration(w.SyncIntervalSec) * time.Second
|
|
}
|
|
|
|
// GetBatchSize возвращает размер пакета WAL
|
|
func (w *WALConfig) GetBatchSize() int {
|
|
if w.BatchSize <= 0 {
|
|
return 100
|
|
}
|
|
return w.BatchSize
|
|
}
|
|
|
|
// GetRecoveryWorkers возвращает количество воркеров для parallel recovery
|
|
func (w *WALConfig) GetRecoveryWorkers() int {
|
|
if w.RecoveryWorkers <= 0 {
|
|
return 4
|
|
}
|
|
return w.RecoveryWorkers
|
|
}
|
|
|
|
// IsWALEnabled возвращает статус WAL
|
|
func (w *WALConfig) IsWALEnabled() bool {
|
|
return w.Enabled
|
|
}
|
|
|
|
// GetMaxVersionsPerDoc возвращает максимальное количество версий на документ
|
|
func (m *MVCCConfig) GetMaxVersionsPerDoc() int {
|
|
if m.MaxVersionsPerDoc <= 0 {
|
|
return 10
|
|
}
|
|
return m.MaxVersionsPerDoc
|
|
}
|
|
|
|
// GetVisibilityMapSize возвращает размер карты видимости
|
|
func (m *MVCCConfig) GetVisibilityMapSize() int {
|
|
if m.VisibilityMapSize <= 0 {
|
|
return 1024 * 1024
|
|
}
|
|
return m.VisibilityMapSize
|
|
}
|
|
|
|
// GetPruneInterval возвращает интервал очистки версий
|
|
func (m *MVCCConfig) GetPruneInterval() time.Duration {
|
|
if m.PruneIntervalMin <= 0 {
|
|
return 5 * time.Minute
|
|
}
|
|
return time.Duration(m.PruneIntervalMin) * time.Minute
|
|
}
|
|
|
|
// GetRetentionDays возвращает дни хранения версий
|
|
func (m *MVCCConfig) GetRetentionDays() int {
|
|
if m.RetentionDays <= 0 {
|
|
return 7
|
|
}
|
|
return m.RetentionDays
|
|
}
|
|
|
|
// GetReadCacheSize возвращает размер кэша чтения
|
|
func (m *MVCCConfig) GetReadCacheSize() int {
|
|
if m.ReadCacheSize <= 0 {
|
|
return 10000
|
|
}
|
|
return m.ReadCacheSize
|
|
}
|
|
|
|
// GetReadCacheTTL возвращает TTL кэша чтения
|
|
func (m *MVCCConfig) GetReadCacheTTL() time.Duration {
|
|
if m.ReadCacheTTLSec <= 0 {
|
|
return 300 * time.Second
|
|
}
|
|
return time.Duration(m.ReadCacheTTLSec) * time.Second
|
|
}
|
|
|
|
// GetDefaultTimeout возвращает таймаут транзакции по умолчанию
|
|
func (t *TransactionsConfig) GetDefaultTimeout() time.Duration {
|
|
if t.DefaultTimeoutSec <= 0 {
|
|
return 30 * time.Second
|
|
}
|
|
return time.Duration(t.DefaultTimeoutSec) * time.Second
|
|
}
|
|
|
|
// GetDeadlockCheckInterval возвращает интервал проверки дедлоков
|
|
func (t *TransactionsConfig) GetDeadlockCheckInterval() time.Duration {
|
|
if t.DeadlockCheckIntervalSec <= 0 {
|
|
return 1 * time.Second
|
|
}
|
|
return time.Duration(t.DeadlockCheckIntervalSec) * time.Second
|
|
}
|
|
|
|
// GetMaxSavepointsPerTx возвращает максимальное количество точек сохранения
|
|
func (t *TransactionsConfig) GetMaxSavepointsPerTx() int {
|
|
if t.MaxSavepointsPerTx <= 0 {
|
|
return 100
|
|
}
|
|
return t.MaxSavepointsPerTx
|
|
}
|
|
|
|
// GetTwoPhaseCommitTimeout возвращает таймаут двухфазного коммита
|
|
func (t *TransactionsConfig) GetTwoPhaseCommitTimeout() time.Duration {
|
|
if t.TwoPhaseCommitTimeoutSec <= 0 {
|
|
return 10 * time.Second
|
|
}
|
|
return time.Duration(t.TwoPhaseCommitTimeoutSec) * time.Second
|
|
}
|
|
|
|
// IsTransactionsEnabled возвращает статус поддержки транзакций
|
|
func (t *TransactionsConfig) IsTransactionsEnabled() bool {
|
|
return t.Enabled
|
|
}
|
|
|
|
// GetCheckpointInterval возвращает интервал чекпоинтов
|
|
func (t *TransactionsConfig) GetCheckpointInterval() int64 {
|
|
if t.CheckpointIntervalSec <= 0 {
|
|
return 300
|
|
}
|
|
return int64(t.CheckpointIntervalSec)
|
|
}
|
|
|
|
// GetMaxDeniedLogSize возвращает максимальный размер лога отказов
|
|
func (a *ACLConfig) GetMaxDeniedLogSize() int {
|
|
if a.MaxDeniedLogSize <= 0 {
|
|
return 10000
|
|
}
|
|
return a.MaxDeniedLogSize
|
|
}
|
|
|
|
// GetTemporaryGrantTTL возвращает TTL временных разрешений
|
|
func (a *ACLConfig) GetTemporaryGrantTTL() time.Duration {
|
|
if a.TemporaryGrantTTLHours <= 0 {
|
|
return 24 * time.Hour
|
|
}
|
|
return time.Duration(a.TemporaryGrantTTLHours) * time.Hour
|
|
}
|
|
|
|
// IsRoleHierarchyEnabled возвращает статус иерархии ролей
|
|
func (a *ACLConfig) IsRoleHierarchyEnabled() bool {
|
|
return a.EnableRoleHierarchy
|
|
}
|
|
|
|
// GetCacheTTL возвращает TTL кэша ACL
|
|
func (a *ACLConfig) GetCacheTTL() time.Duration {
|
|
if a.CacheTTLSec <= 0 {
|
|
return 60 * time.Second
|
|
}
|
|
return time.Duration(a.CacheTTLSec) * time.Second
|
|
}
|
|
|
|
// ========== Методы PluginsConfig (расширенные) ==========
|
|
|
|
// GetMaxCPUTime возвращает лимит CPU для плагина
|
|
func (p *PluginsConfig) GetMaxCPUTime() time.Duration {
|
|
if p.MaxCPUTimeMs <= 0 {
|
|
return 100 * time.Millisecond
|
|
}
|
|
return time.Duration(p.MaxCPUTimeMs) * time.Millisecond
|
|
}
|
|
|
|
// GetMaxMemory возвращает лимит памяти для плагина в байтах
|
|
func (p *PluginsConfig) GetMaxMemory() int64 {
|
|
if p.MaxMemoryMB <= 0 {
|
|
return 50 * 1024 * 1024
|
|
}
|
|
return int64(p.MaxMemoryMB) * 1024 * 1024
|
|
}
|
|
|
|
// GetMaxExecutionTime возвращает лимит времени выполнения плагина
|
|
func (p *PluginsConfig) GetMaxExecutionTime() time.Duration {
|
|
if p.MaxExecutionTimeSec <= 0 {
|
|
return 5 * time.Second
|
|
}
|
|
return time.Duration(p.MaxExecutionTimeSec) * time.Second
|
|
}
|
|
|
|
// GetMaxInstructions возвращает лимит инструкций Lua
|
|
func (p *PluginsConfig) GetMaxInstructions() int64 {
|
|
if p.MaxInstructions <= 0 {
|
|
return 1000000
|
|
}
|
|
return p.MaxInstructions
|
|
}
|
|
|
|
// GetHotReloadInterval возвращает интервал проверки изменений плагинов
|
|
func (p *PluginsConfig) GetHotReloadInterval() time.Duration {
|
|
if p.HotReloadIntervalSec <= 0 {
|
|
return 30 * time.Second
|
|
}
|
|
return time.Duration(p.HotReloadIntervalSec) * time.Second
|
|
}
|
|
|
|
// GetMaxEventLogSize возвращает максимальный размер лога событий плагина
|
|
func (p *PluginsConfig) GetMaxEventLogSize() int {
|
|
if p.MaxEventLogSize <= 0 {
|
|
return 1000
|
|
}
|
|
return p.MaxEventLogSize
|
|
}
|
|
|
|
// GetLoadTimeout возвращает таймаут загрузки плагина
|
|
func (p *PluginsConfig) GetLoadTimeout() time.Duration {
|
|
if p.LoadTimeoutSec <= 0 {
|
|
return 10 * time.Second
|
|
}
|
|
return time.Duration(p.LoadTimeoutSec) * time.Second
|
|
}
|
|
|
|
// ========== Остальные методы без изменений ==========
|
|
|
|
func (c *ClusterConfig) GetHeartbeatTimeout() time.Duration {
|
|
if c.HeartbeatTimeoutMs <= 0 {
|
|
return 1000 * time.Millisecond
|
|
}
|
|
return time.Duration(c.HeartbeatTimeoutMs) * time.Millisecond
|
|
}
|
|
|
|
func (c *ClusterConfig) GetElectionTimeout() time.Duration {
|
|
if c.ElectionTimeoutMs <= 0 {
|
|
return 1000 * time.Millisecond
|
|
}
|
|
return time.Duration(c.ElectionTimeoutMs) * time.Millisecond
|
|
}
|
|
|
|
func (c *ClusterConfig) GetCommitTimeout() time.Duration {
|
|
if c.CommitTimeoutMs <= 0 {
|
|
return 500 * time.Millisecond
|
|
}
|
|
return time.Duration(c.CommitTimeoutMs) * time.Millisecond
|
|
}
|
|
|
|
func (c *ClusterConfig) GetSnapshotInterval() time.Duration {
|
|
if c.SnapshotIntervalMin <= 0 {
|
|
return 30 * time.Minute
|
|
}
|
|
return time.Duration(c.SnapshotIntervalMin) * time.Minute
|
|
}
|
|
|
|
func (c *ClusterConfig) GetSnapshotThreshold() uint64 {
|
|
if c.SnapshotThreshold <= 0 {
|
|
return 1000
|
|
}
|
|
return uint64(c.SnapshotThreshold)
|
|
}
|
|
|
|
func (c *ClusterConfig) IsSplitBrainPreventionEnabled() bool {
|
|
return c.SplitBrainPrevention
|
|
}
|
|
|
|
func (c *ClusterConfig) GetRecoveryTimeout() time.Duration {
|
|
if c.RecoveryTimeoutSec <= 0 {
|
|
return 30 * time.Second
|
|
}
|
|
return time.Duration(c.RecoveryTimeoutSec) * time.Second
|
|
}
|
|
|
|
func (c *ClusterConfig) GetRegion() string {
|
|
if c.Region == "" {
|
|
return "default"
|
|
}
|
|
return c.Region
|
|
}
|
|
|
|
func (c *ClusterConfig) GetPriorityZone() int {
|
|
if c.PriorityZone < 0 {
|
|
return 0
|
|
}
|
|
if c.PriorityZone > 9 {
|
|
return 9
|
|
}
|
|
return c.PriorityZone
|
|
}
|
|
|
|
func (r *ReplicationConfig) GetReplicationTimeout() time.Duration {
|
|
if r.ReplicationTimeoutMs <= 0 {
|
|
return 5 * time.Second
|
|
}
|
|
return time.Duration(r.ReplicationTimeoutMs) * time.Millisecond
|
|
}
|
|
|
|
func (r *ReplicationConfig) IsReplicationEnabled() bool {
|
|
return r.Enabled
|
|
}
|
|
|
|
func (r *ReplicationConfig) IsMasterMasterEnabled() bool {
|
|
return r.MasterMaster
|
|
}
|
|
|
|
func (r *ReplicationConfig) IsSyncReplicationEnabled() bool {
|
|
return r.SyncReplication
|
|
}
|
|
|
|
func (r *RecoveryConfig) GetMaxRetryDuration() time.Duration {
|
|
if r.MaxRetrySec <= 0 {
|
|
return 300 * time.Second
|
|
}
|
|
return time.Duration(r.MaxRetrySec) * time.Second
|
|
}
|
|
|
|
func (r *RecoveryConfig) GetDataReplicationTimeout() time.Duration {
|
|
if r.DataReplicationTimeoutSec <= 0 {
|
|
return 60 * time.Second
|
|
}
|
|
return time.Duration(r.DataReplicationTimeoutSec) * time.Second
|
|
}
|
|
|
|
func (r *RecoveryConfig) GetStaleReadTimeout() time.Duration {
|
|
if r.StaleReadTimeoutSec <= 0 {
|
|
return 30 * time.Second
|
|
}
|
|
return time.Duration(r.StaleReadTimeoutSec) * time.Second
|
|
}
|
|
|
|
func (r *RecoveryConfig) IsAutoRejoinEnabled() bool {
|
|
return r.AutoRejoin
|
|
}
|
|
|
|
func (p *PerformanceConfig) GetBatchSize() int {
|
|
if p.BatchSize <= 0 {
|
|
return 100
|
|
}
|
|
return p.BatchSize
|
|
}
|
|
|
|
func (p *PerformanceConfig) GetMaxConnections() int {
|
|
if p.MaxConnections <= 0 {
|
|
return 1000
|
|
}
|
|
return p.MaxConnections
|
|
}
|
|
|
|
func (p *PerformanceConfig) IsReadFromFollowerEnabled() bool {
|
|
return p.ReadFromFollower
|
|
}
|
|
|
|
func (p *PerformanceConfig) IsPipelineEnabled() bool {
|
|
return p.EnablePipeline
|
|
}
|
|
|
|
func (m *MonitoringConfig) GetMetricsPort() int {
|
|
if m.MetricsPort <= 0 {
|
|
return 9090
|
|
}
|
|
return m.MetricsPort
|
|
}
|
|
|
|
func (m *MonitoringConfig) GetTraceSampleRate() float64 {
|
|
if m.TraceSampleRate <= 0 || m.TraceSampleRate > 1 {
|
|
return 0.01
|
|
}
|
|
return m.TraceSampleRate
|
|
}
|
|
|
|
func (m *MonitoringConfig) IsMetricsEnabled() bool {
|
|
return m.EnableMetrics
|
|
}
|
|
|
|
func (m *MonitoringConfig) IsTracingEnabled() bool {
|
|
return m.EnableTracing
|
|
}
|
|
|
|
func (s *SecurityConfig) GetTLSMinVersion() uint16 {
|
|
switch s.MinVersion {
|
|
case "1.0":
|
|
return 0x0301
|
|
case "1.1":
|
|
return 0x0302
|
|
case "1.2":
|
|
return 0x0303
|
|
case "1.3":
|
|
return 0x0304
|
|
default:
|
|
return 0x0303
|
|
}
|
|
}
|
|
|
|
func (s *SecurityConfig) IsTLSEnabled() bool {
|
|
return s.EnableTLS && s.CertFile != "" && s.KeyFile != ""
|
|
}
|
|
|
|
func (s *SecurityConfig) GetCertFile() string {
|
|
return s.CertFile
|
|
}
|
|
|
|
func (s *SecurityConfig) GetKeyFile() string {
|
|
return s.KeyFile
|
|
}
|
|
|
|
func (s *SecurityConfig) GetCAFile() string {
|
|
return s.CAFile
|
|
}
|
|
|
|
func (c *CompressionConfig) IsCompressionEnabled() bool {
|
|
return c.Enabled
|
|
}
|
|
|
|
func (c *CompressionConfig) GetAlgorithm() string {
|
|
if c.Algorithm == "" {
|
|
return "snappy"
|
|
}
|
|
return c.Algorithm
|
|
}
|
|
|
|
func (c *CompressionConfig) GetLevel() int {
|
|
if c.Level < 1 {
|
|
return 3
|
|
}
|
|
if c.Level > 9 {
|
|
return 9
|
|
}
|
|
return c.Level
|
|
}
|
|
|
|
func (c *CompressionConfig) GetMinSize() int {
|
|
if c.MinSize <= 0 {
|
|
return 1024
|
|
}
|
|
return c.MinSize
|
|
}
|
|
|
|
func (w *WebUIConfig) IsWebUIEnabled() bool {
|
|
return w.Enabled
|
|
}
|
|
|
|
func (w *WebUIConfig) GetWebUIPort() int {
|
|
if w.Port <= 0 {
|
|
return 8080
|
|
}
|
|
return w.Port
|
|
}
|
|
|
|
func (w *WebUIConfig) GetTheme() string {
|
|
if w.Theme == "" {
|
|
return "dark"
|
|
}
|
|
return w.Theme
|
|
}
|
|
|
|
// Validate выполняет базовую валидацию конфигурации
|
|
func (c *Config) Validate() error {
|
|
if c.Cluster.NodePort <= 0 || c.Cluster.NodePort > 65535 {
|
|
return fmt.Errorf("invalid node_port: %d", c.Cluster.NodePort)
|
|
}
|
|
if c.Cluster.RaftPort <= 0 || c.Cluster.RaftPort > 65535 {
|
|
return fmt.Errorf("invalid raft_port: %d", c.Cluster.RaftPort)
|
|
}
|
|
if c.Cluster.PriorityZone < 0 || c.Cluster.PriorityZone > 9 {
|
|
return fmt.Errorf("priority_zone must be between 0 and 9, got %d", c.Cluster.PriorityZone)
|
|
}
|
|
if c.Performance.BatchSize < 1 {
|
|
return fmt.Errorf("batch_size must be at least 1, got %d", c.Performance.BatchSize)
|
|
}
|
|
if c.Performance.MaxConnections < 1 {
|
|
return fmt.Errorf("max_connections must be at least 1, got %d", c.Performance.MaxConnections)
|
|
}
|
|
if c.Monitoring.TraceSampleRate < 0 || c.Monitoring.TraceSampleRate > 1 {
|
|
return fmt.Errorf("trace_sample_rate must be between 0 and 1, got %f", c.Monitoring.TraceSampleRate)
|
|
}
|
|
if c.API.Port == c.Cluster.NodePort {
|
|
return fmt.Errorf("API port %d conflicts with cluster node port", c.API.Port)
|
|
}
|
|
if c.WebUI.Port == c.Cluster.NodePort {
|
|
return fmt.Errorf("WebUI port %d conflicts with cluster node port", c.WebUI.Port)
|
|
}
|
|
if c.Monitoring.MetricsPort == c.Cluster.NodePort {
|
|
return fmt.Errorf("metrics port %d conflicts with cluster node port", c.Monitoring.MetricsPort)
|
|
}
|
|
if c.Security.IsTLSEnabled() {
|
|
if c.Security.CertFile == "" {
|
|
return fmt.Errorf("cert_file is required when TLS is enabled")
|
|
}
|
|
if c.Security.KeyFile == "" {
|
|
return fmt.Errorf("key_file is required when TLS is enabled")
|
|
}
|
|
}
|
|
if c.Compression.Enabled {
|
|
switch c.Compression.Algorithm {
|
|
case "snappy", "lz4", "zstd":
|
|
default:
|
|
return fmt.Errorf("unsupported compression algorithm: %s, supported: snappy, lz4, zstd", c.Compression.Algorithm)
|
|
}
|
|
}
|
|
|
|
// Валидация новых настроек
|
|
if c.WAL.RecoveryWorkers < 1 {
|
|
return fmt.Errorf("wal.recovery_workers must be at least 1, got %d", c.WAL.RecoveryWorkers)
|
|
}
|
|
if c.MVCC.MaxVersionsPerDoc < 0 {
|
|
return fmt.Errorf("mvcc.max_versions_per_doc cannot be negative, got %d", c.MVCC.MaxVersionsPerDoc)
|
|
}
|
|
if c.Plugins.MaxCPUTimeMs < 0 {
|
|
return fmt.Errorf("plugins.max_cpu_time_ms cannot be negative, got %d", c.Plugins.MaxCPUTimeMs)
|
|
}
|
|
if c.Plugins.MaxMemoryMB < 0 {
|
|
return fmt.Errorf("plugins.max_memory_mb cannot be negative, got %d", c.Plugins.MaxMemoryMB)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// LoadConfig загружает и валидирует конфигурацию из TOML файла
|
|
func LoadConfig(path string) (*Config, error) {
|
|
var cfg Config
|
|
if _, err := toml.DecodeFile(path, &cfg); err != nil {
|
|
return nil, fmt.Errorf("failed to decode config file: %v", err)
|
|
}
|
|
|
|
// Значения по умолчанию для существующих настроек
|
|
if cfg.Cluster.RaftPort == 0 {
|
|
cfg.Cluster.RaftPort = 9878
|
|
}
|
|
if cfg.Cluster.RaftDataDir == "" {
|
|
cfg.Cluster.RaftDataDir = "raft_data"
|
|
}
|
|
if cfg.Replication.ReplicationTimeoutMs == 0 {
|
|
cfg.Replication.ReplicationTimeoutMs = 5000
|
|
}
|
|
if cfg.Plugins.ScriptDir == "" {
|
|
cfg.Plugins.ScriptDir = "plugins"
|
|
}
|
|
if cfg.API.Port == 0 {
|
|
cfg.API.Port = 8080
|
|
}
|
|
if cfg.Compression.Algorithm == "" {
|
|
cfg.Compression.Algorithm = "snappy"
|
|
}
|
|
if cfg.Compression.MinSize == 0 {
|
|
cfg.Compression.MinSize = 1024
|
|
}
|
|
if cfg.Compression.Level == 0 {
|
|
cfg.Compression.Level = 3
|
|
}
|
|
if cfg.WebUI.Port == 0 {
|
|
cfg.WebUI.Port = 9080
|
|
}
|
|
if cfg.WebUI.Theme == "" {
|
|
cfg.WebUI.Theme = "dark"
|
|
}
|
|
if cfg.Cluster.HeartbeatTimeoutMs == 0 {
|
|
cfg.Cluster.HeartbeatTimeoutMs = 1000
|
|
}
|
|
if cfg.Cluster.ElectionTimeoutMs == 0 {
|
|
cfg.Cluster.ElectionTimeoutMs = 1000
|
|
}
|
|
if cfg.Cluster.CommitTimeoutMs == 0 {
|
|
cfg.Cluster.CommitTimeoutMs = 500
|
|
}
|
|
if cfg.Cluster.SnapshotIntervalMin == 0 {
|
|
cfg.Cluster.SnapshotIntervalMin = 30
|
|
}
|
|
if cfg.Cluster.SnapshotThreshold == 0 {
|
|
cfg.Cluster.SnapshotThreshold = 1000
|
|
}
|
|
if cfg.Cluster.RecoveryTimeoutSec == 0 {
|
|
cfg.Cluster.RecoveryTimeoutSec = 30
|
|
}
|
|
if cfg.Cluster.Region == "" {
|
|
cfg.Cluster.Region = "default"
|
|
}
|
|
if cfg.Performance.BatchSize == 0 {
|
|
cfg.Performance.BatchSize = 100
|
|
}
|
|
if cfg.Performance.MaxConnections == 0 {
|
|
cfg.Performance.MaxConnections = 1000
|
|
}
|
|
if cfg.Monitoring.MetricsPort == 0 {
|
|
cfg.Monitoring.MetricsPort = 9090
|
|
}
|
|
if cfg.Monitoring.TraceSampleRate == 0 {
|
|
cfg.Monitoring.TraceSampleRate = 0.01
|
|
}
|
|
if cfg.Recovery.MaxRetrySec == 0 {
|
|
cfg.Recovery.MaxRetrySec = 300
|
|
}
|
|
if cfg.Recovery.DataReplicationTimeoutSec == 0 {
|
|
cfg.Recovery.DataReplicationTimeoutSec = 60
|
|
}
|
|
if cfg.Recovery.StaleReadTimeoutSec == 0 {
|
|
cfg.Recovery.StaleReadTimeoutSec = 30
|
|
}
|
|
if cfg.Security.MinVersion == "" {
|
|
cfg.Security.MinVersion = "1.2"
|
|
}
|
|
|
|
// Значения по умолчанию для WAL
|
|
if cfg.WAL.SegmentSizeMB == 0 {
|
|
cfg.WAL.SegmentSizeMB = 64
|
|
}
|
|
if cfg.WAL.SyncIntervalSec == 0 {
|
|
cfg.WAL.SyncIntervalSec = 5
|
|
}
|
|
if cfg.WAL.BatchSize == 0 {
|
|
cfg.WAL.BatchSize = 100
|
|
}
|
|
if cfg.WAL.RecoveryWorkers == 0 {
|
|
cfg.WAL.RecoveryWorkers = 4
|
|
}
|
|
|
|
// Значения по умолчанию для MVCC
|
|
if cfg.MVCC.MaxVersionsPerDoc == 0 {
|
|
cfg.MVCC.MaxVersionsPerDoc = 10
|
|
}
|
|
if cfg.MVCC.VisibilityMapSize == 0 {
|
|
cfg.MVCC.VisibilityMapSize = 1024 * 1024
|
|
}
|
|
if cfg.MVCC.PruneIntervalMin == 0 {
|
|
cfg.MVCC.PruneIntervalMin = 5
|
|
}
|
|
if cfg.MVCC.RetentionDays == 0 {
|
|
cfg.MVCC.RetentionDays = 7
|
|
}
|
|
if cfg.MVCC.ReadCacheSize == 0 {
|
|
cfg.MVCC.ReadCacheSize = 10000
|
|
}
|
|
if cfg.MVCC.ReadCacheTTLSec == 0 {
|
|
cfg.MVCC.ReadCacheTTLSec = 300
|
|
}
|
|
|
|
// Значения по умолчанию для транзакций
|
|
if cfg.Transactions.DefaultTimeoutSec == 0 {
|
|
cfg.Transactions.DefaultTimeoutSec = 30
|
|
}
|
|
if cfg.Transactions.DeadlockCheckIntervalSec == 0 {
|
|
cfg.Transactions.DeadlockCheckIntervalSec = 1
|
|
}
|
|
if cfg.Transactions.MaxSavepointsPerTx == 0 {
|
|
cfg.Transactions.MaxSavepointsPerTx = 100
|
|
}
|
|
if cfg.Transactions.TwoPhaseCommitTimeoutSec == 0 {
|
|
cfg.Transactions.TwoPhaseCommitTimeoutSec = 10
|
|
}
|
|
if cfg.Transactions.CheckpointIntervalSec == 0 {
|
|
cfg.Transactions.CheckpointIntervalSec = 300
|
|
}
|
|
|
|
// Значения по умолчанию для ACL
|
|
if cfg.ACL.MaxDeniedLogSize == 0 {
|
|
cfg.ACL.MaxDeniedLogSize = 10000
|
|
}
|
|
if cfg.ACL.TemporaryGrantTTLHours == 0 {
|
|
cfg.ACL.TemporaryGrantTTLHours = 24
|
|
}
|
|
if cfg.ACL.CacheTTLSec == 0 {
|
|
cfg.ACL.CacheTTLSec = 60
|
|
}
|
|
|
|
// Значения по умолчанию для расширенных настроек плагинов
|
|
if cfg.Plugins.MaxCPUTimeMs == 0 {
|
|
cfg.Plugins.MaxCPUTimeMs = 100
|
|
}
|
|
if cfg.Plugins.MaxMemoryMB == 0 {
|
|
cfg.Plugins.MaxMemoryMB = 50
|
|
}
|
|
if cfg.Plugins.MaxExecutionTimeSec == 0 {
|
|
cfg.Plugins.MaxExecutionTimeSec = 5
|
|
}
|
|
if cfg.Plugins.MaxInstructions == 0 {
|
|
cfg.Plugins.MaxInstructions = 1000000
|
|
}
|
|
if cfg.Plugins.HotReloadIntervalSec == 0 {
|
|
cfg.Plugins.HotReloadIntervalSec = 30
|
|
}
|
|
if cfg.Plugins.MaxEventLogSize == 0 {
|
|
cfg.Plugins.MaxEventLogSize = 1000
|
|
}
|
|
if cfg.Plugins.LoadTimeoutSec == 0 {
|
|
cfg.Plugins.LoadTimeoutSec = 10
|
|
}
|
|
|
|
// Базовая валидация
|
|
if err := cfg.Validate(); err != nil {
|
|
return nil, fmt.Errorf("config validation failed: %v", err)
|
|
}
|
|
|
|
// ========== НОВАЯ ПОЛНАЯ ВАЛИДАЦИЯ ==========
|
|
validationResult := ValidateConfig(&cfg)
|
|
if !validationResult.Valid {
|
|
var errMsg strings.Builder
|
|
errMsg.WriteString("config validation failed:\n")
|
|
for _, err := range validationResult.Errors {
|
|
errMsg.WriteString(fmt.Sprintf(" - %s\n", err.Error()))
|
|
}
|
|
if len(validationResult.Warnings) > 0 {
|
|
errMsg.WriteString("Warnings:\n")
|
|
for _, warn := range validationResult.Warnings {
|
|
errMsg.WriteString(fmt.Sprintf(" - %s\n", warn.Error()))
|
|
}
|
|
}
|
|
return nil, fmt.Errorf("%s", errMsg.String())
|
|
}
|
|
|
|
// Выводим предупреждения, если есть (логгер ещё не инициализирован, используем fmt)
|
|
if len(validationResult.Warnings) > 0 {
|
|
fmt.Println("Configuration warnings:")
|
|
for _, warn := range validationResult.Warnings {
|
|
fmt.Printf(" - %s\n", warn.Error())
|
|
}
|
|
}
|
|
|
|
return &cfg, nil
|
|
}
|
|
|
|
// GetConfigSummary возвращает сводку по конфигурации
|
|
func (c *Config) GetConfigSummary() map[string]interface{} {
|
|
return map[string]interface{}{
|
|
"cluster": map[string]interface{}{
|
|
"name": c.Cluster.Name,
|
|
"node_ip": c.Cluster.NodeIP,
|
|
"node_port": c.Cluster.NodePort,
|
|
"raft_port": c.Cluster.RaftPort,
|
|
"bootstrap": c.Cluster.Bootstrap,
|
|
"nodes_count": len(c.Cluster.Nodes),
|
|
"region": c.Cluster.GetRegion(),
|
|
"priority_zone": c.Cluster.GetPriorityZone(),
|
|
"split_brain_prevention": c.Cluster.IsSplitBrainPreventionEnabled(),
|
|
},
|
|
"replication": map[string]interface{}{
|
|
"enabled": c.Replication.IsReplicationEnabled(),
|
|
"master_master": c.Replication.IsMasterMasterEnabled(),
|
|
"sync": c.Replication.IsSyncReplicationEnabled(),
|
|
"timeout_ms": c.Replication.ReplicationTimeoutMs,
|
|
},
|
|
"performance": map[string]interface{}{
|
|
"pipeline": c.Performance.IsPipelineEnabled(),
|
|
"batch_size": c.Performance.GetBatchSize(),
|
|
"read_from_follower": c.Performance.IsReadFromFollowerEnabled(),
|
|
"max_connections": c.Performance.GetMaxConnections(),
|
|
},
|
|
"security": map[string]interface{}{
|
|
"tls_enabled": c.Security.IsTLSEnabled(),
|
|
"tls_version": c.Security.MinVersion,
|
|
},
|
|
"monitoring": map[string]interface{}{
|
|
"metrics_enabled": c.Monitoring.IsMetricsEnabled(),
|
|
"metrics_port": c.Monitoring.GetMetricsPort(),
|
|
"tracing_enabled": c.Monitoring.IsTracingEnabled(),
|
|
"trace_sample_rate": c.Monitoring.GetTraceSampleRate(),
|
|
},
|
|
"compression": map[string]interface{}{
|
|
"enabled": c.Compression.IsCompressionEnabled(),
|
|
"algorithm": c.Compression.GetAlgorithm(),
|
|
"level": c.Compression.GetLevel(),
|
|
"min_size": c.Compression.GetMinSize(),
|
|
},
|
|
"wal": map[string]interface{}{
|
|
"segment_size_mb": c.WAL.SegmentSizeMB,
|
|
"sync_interval_sec": c.WAL.SyncIntervalSec,
|
|
"batch_size": c.WAL.BatchSize,
|
|
"recovery_workers": c.WAL.RecoveryWorkers,
|
|
"enabled": c.WAL.Enabled,
|
|
},
|
|
"mvcc": map[string]interface{}{
|
|
"max_versions_per_doc": c.MVCC.MaxVersionsPerDoc,
|
|
"prune_interval_min": c.MVCC.PruneIntervalMin,
|
|
"retention_days": c.MVCC.RetentionDays,
|
|
},
|
|
"transactions": map[string]interface{}{
|
|
"default_timeout_sec": c.Transactions.DefaultTimeoutSec,
|
|
"enabled": c.Transactions.Enabled,
|
|
},
|
|
"acl": map[string]interface{}{
|
|
"enable_role_hierarchy": c.ACL.EnableRoleHierarchy,
|
|
},
|
|
}
|
|
}
|