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  }