github.com/mailru/activerecord@v1.12.2/pkg/activerecord/activerecord.go (about) 1 package activerecord 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "runtime" 8 "sync" 9 "time" 10 ) 11 12 var ErrNoData = errors.New("no data") 13 14 type SelectorLimiter interface { 15 Limit() uint32 16 Offset() uint32 17 FullfillWarn() bool 18 fmt.Stringer 19 } 20 21 type Limiter struct { 22 limit, offset uint32 23 fullfillWarn bool 24 } 25 26 func EmptyLimiter() Limiter { 27 return Limiter{} 28 } 29 30 func NewLimiter(limit uint32) Limiter { 31 return Limiter{limit: limit} 32 } 33 34 func NewLimitOffset(limit uint32, offset uint32) Limiter { 35 return Limiter{limit: limit, offset: offset} 36 } 37 38 func NewThreshold(limit uint32) Limiter { 39 return Limiter{limit: limit, fullfillWarn: true} 40 } 41 42 func (l Limiter) Offset() uint32 { 43 return l.offset 44 } 45 46 func (l Limiter) Limit() uint32 { 47 return l.limit 48 } 49 50 func (l Limiter) FullfillWarn() bool { 51 return l.fullfillWarn 52 } 53 54 func (l Limiter) String() string { 55 return fmt.Sprintf("Limit: %d, Offset: %d, Is Threshold: %t", l.limit, l.offset, l.fullfillWarn) 56 } 57 58 //go:generate mockery --name ConfigInterface --filename mock_config.go --structname MockConfig --with-expecter=true --inpackage 59 type ConfigInterface interface { 60 GetBool(ctx context.Context, confPath string, dfl ...bool) bool 61 GetBoolIfExists(ctx context.Context, confPath string) (value bool, ok bool) 62 GetInt(ctx context.Context, confPath string, dfl ...int) int 63 GetIntIfExists(ctx context.Context, confPath string) (int, bool) 64 GetDuration(ctx context.Context, confPath string, dfl ...time.Duration) time.Duration 65 GetDurationIfExists(ctx context.Context, confPath string) (time.Duration, bool) 66 GetString(ctx context.Context, confPath string, dfl ...string) string 67 GetStringIfExists(ctx context.Context, confPath string) (string, bool) 68 GetStrings(ctx context.Context, confPath string, dfl []string) []string 69 GetStruct(ctx context.Context, confPath string, valuePtr interface{}) (bool, error) 70 GetLastUpdateTime() time.Time 71 } 72 73 type LoggerInterface interface { 74 SetLoggerValueToContext(ctx context.Context, addVal ValueLogPrefix) context.Context 75 76 SetLogLevel(level uint32) 77 Fatal(ctx context.Context, args ...interface{}) 78 Error(ctx context.Context, args ...interface{}) 79 Warn(ctx context.Context, args ...interface{}) 80 Info(ctx context.Context, args ...interface{}) 81 Debug(ctx context.Context, args ...interface{}) 82 Trace(ctx context.Context, args ...interface{}) 83 84 CollectQueries(ctx context.Context, f func() (MockerLogger, error)) 85 } 86 87 type ConnectionCacherInterface interface { 88 Add(shard ShardInstance, connector func(interface{}) (ConnectionInterface, error)) (ConnectionInterface, error) 89 GetOrAdd(shard ShardInstance, connector func(interface{}) (ConnectionInterface, error)) (ConnectionInterface, error) 90 Get(shard ShardInstance) ConnectionInterface 91 CloseConnection(context.Context) 92 } 93 94 type ClusterCheckerInterface interface { 95 AddClusterChecker(ctx context.Context, path string, params ClusterConfigParameters) (*Cluster, error) 96 } 97 98 type ConfigCacherInterface interface { 99 Get(ctx context.Context, path string, glob MapGlobParam, optionCreator func(ShardInstanceConfig) (OptionInterface, error)) (*Cluster, error) 100 } 101 102 type SerializerInterface interface { 103 Unmarshal(val interface{}) (interface{}, error) 104 Marshal(data interface{}) (interface{}, error) 105 } 106 107 type MetricTimerInterface interface { 108 Timing(ctx context.Context, name string) 109 Finish(ctx context.Context, name string) 110 } 111 112 type MetricStatCountInterface interface { 113 Inc(ctx context.Context, name string, val float64) 114 } 115 116 type MetricErrorCountInterface interface { 117 Inc(ctx context.Context, name string, val float64) 118 } 119 120 type MetricInterface interface { 121 StatCount(storage, entity string) MetricStatCountInterface 122 ErrorCount(storage, entity string) MetricStatCountInterface 123 Timer(storage, entity string) MetricTimerInterface 124 } 125 126 type ActiveRecord struct { 127 instanceCreator string 128 config ConfigInterface 129 logger LoggerInterface 130 metric MetricInterface 131 connectionCacher ConnectionCacherInterface 132 configCacher ConfigCacherInterface 133 pinger ClusterCheckerInterface 134 } 135 136 var instance *ActiveRecord 137 var createMutex sync.Mutex 138 139 func ReinitActiveRecord(opts ...Option) { 140 instance = nil 141 142 InitActiveRecord(opts...) 143 } 144 145 func InitActiveRecord(opts ...Option) { 146 createMutex.Lock() 147 defer createMutex.Unlock() 148 149 if instance != nil { 150 panic(fmt.Sprintf("can't initialise twice, first from `%s`", instance.instanceCreator)) 151 } 152 153 caller := "unknown_caller" 154 155 _, file, no, ok := runtime.Caller(1) 156 if ok { 157 caller = fmt.Sprintf("%s:%d ", file, no) 158 } 159 160 instance = &ActiveRecord{ 161 instanceCreator: caller, 162 logger: NewLogger(), 163 config: NewDefaultConfig(), 164 metric: NewDefaultNoopMetric(), 165 connectionCacher: newConnectionPool(), 166 configCacher: NewConfigCacher(), 167 } 168 169 for _, opt := range opts { 170 opt.apply(instance) 171 } 172 } 173 174 func GetInstance() *ActiveRecord { 175 if instance == nil { 176 panic("get instance before initialization") 177 } 178 179 return instance 180 } 181 182 func Logger() LoggerInterface { 183 return GetInstance().logger 184 } 185 186 func Metric() MetricInterface { 187 return GetInstance().metric 188 } 189 190 func Config() ConfigInterface { 191 return GetInstance().config 192 } 193 194 func ConnectionCacher() ConnectionCacherInterface { 195 return GetInstance().connectionCacher 196 } 197 198 func ConfigCacher() ConfigCacherInterface { 199 return GetInstance().configCacher 200 } 201 202 // AddClusterChecker регистрирует конфигурацию кластера в локальном пингере 203 func AddClusterChecker(ctx context.Context, configPath string, params ClusterConfigParameters) (*Cluster, error) { 204 if GetInstance().pinger == nil { 205 return nil, fmt.Errorf("connection pinger is not configured. Configure it with function InitActiveRecord and WithConnectionPinger option ") 206 } 207 208 return GetInstance().pinger.AddClusterChecker(ctx, configPath, params) 209 }