Update split SDK to 6.0.2 to fix sync bug (#17060)
* Update split SDK to 6.0.2 to fix sync bug * Vendor and tidy
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
fa2ecad0a9
Коммит
aba00a3cfd
13
vendor/github.com/splitio/go-toolkit/v4/LICENSE
сгенерированный
поставляемый
Обычный файл
13
vendor/github.com/splitio/go-toolkit/v4/LICENSE
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,13 @@
|
||||
Copyright © 2020 Split Software, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
150
vendor/github.com/splitio/go-toolkit/v4/asynctask/asynctasks.go
сгенерированный
поставляемый
Обычный файл
150
vendor/github.com/splitio/go-toolkit/v4/asynctask/asynctasks.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,150 @@
|
||||
package asynctask
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/splitio/go-toolkit/v4/logging"
|
||||
"github.com/splitio/go-toolkit/v4/struct/traits/lifecycle"
|
||||
)
|
||||
|
||||
// AsyncTask is a struct that wraps tasks that should run periodically and can be remotely stopped & started,
|
||||
// as well as making it's status (running/stopped) available.
|
||||
type AsyncTask struct {
|
||||
lifecycle lifecycle.Manager
|
||||
task func(l logging.LoggerInterface) error
|
||||
name string
|
||||
incoming chan int
|
||||
period int
|
||||
onInit func(l logging.LoggerInterface) error
|
||||
onStop func(l logging.LoggerInterface)
|
||||
logger logging.LoggerInterface
|
||||
}
|
||||
|
||||
const (
|
||||
taskMessageWakeup = iota
|
||||
)
|
||||
|
||||
// Start initiates the task. It wraps the execution in a closure guarded by a call to recover() in order
|
||||
// to prevent the main application from crashin if something goes wrong while the sdk interacts with the backend.
|
||||
func (t *AsyncTask) Start() {
|
||||
|
||||
if !t.lifecycle.BeginInitialization() {
|
||||
if t.logger != nil {
|
||||
t.logger.Warning(fmt.Sprintf("Task %s is not idle. Aborting new execution.", t.name))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
go func() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
if t.logger != nil {
|
||||
t.logger.Error(fmt.Sprintf(
|
||||
"AsyncTask %s is panicking! shutting down. Consider restarting this instance and raising an issue",
|
||||
t.name,
|
||||
))
|
||||
t.logger.Error(r)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
defer t.lifecycle.ShutdownComplete()
|
||||
if !t.lifecycle.InitializationComplete() {
|
||||
return
|
||||
}
|
||||
|
||||
// If there's an initialization function, execute it
|
||||
if t.onInit != nil {
|
||||
err := t.onInit(t.logger)
|
||||
if err != nil { // If something goes wrong during initialization, abort.
|
||||
if t.logger != nil {
|
||||
t.logger.Error(err.Error())
|
||||
}
|
||||
t.lifecycle.AbnormalShutdown()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Create timeout timer
|
||||
idleDuration := time.Second * time.Duration(t.period)
|
||||
taskTimer := time.NewTimer(idleDuration)
|
||||
defer taskTimer.Stop()
|
||||
|
||||
if t.onStop != nil {
|
||||
defer t.onStop(t.logger)
|
||||
}
|
||||
|
||||
// Task execution
|
||||
for {
|
||||
select {
|
||||
case <-t.lifecycle.ShutdownRequested():
|
||||
return
|
||||
case <-t.incoming: // wake up signal
|
||||
case <-taskTimer.C: // Timedout
|
||||
}
|
||||
|
||||
// Run the wrapped task and handle the returned error if any.
|
||||
err := t.task(t.logger)
|
||||
if err != nil && t.logger != nil {
|
||||
t.logger.Error(err.Error())
|
||||
}
|
||||
|
||||
// Resetting timer
|
||||
taskTimer.Reset(idleDuration)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (t *AsyncTask) sendSignal(signal int) error {
|
||||
select {
|
||||
case t.incoming <- signal:
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf("Couldn't send message to task %s", t.name)
|
||||
}
|
||||
}
|
||||
|
||||
// Stop executes onStop hook if any, blocks until its done (if blocking = true) and prevents future executions of the task.
|
||||
func (t *AsyncTask) Stop(blocking bool) error {
|
||||
if !t.lifecycle.BeginShutdown() {
|
||||
return fmt.Errorf("task '%s' not running", t.name)
|
||||
}
|
||||
|
||||
if blocking {
|
||||
t.lifecycle.AwaitShutdownComplete()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// WakeUp interrupts the task's sleep period and resumes execution
|
||||
func (t *AsyncTask) WakeUp() error {
|
||||
return t.sendSignal(taskMessageWakeup)
|
||||
}
|
||||
|
||||
// IsRunning returns true if the task is currently running
|
||||
func (t *AsyncTask) IsRunning() bool {
|
||||
return t.lifecycle.IsRunning()
|
||||
}
|
||||
|
||||
// NewAsyncTask creates a new task and returns a pointer to it
|
||||
func NewAsyncTask(
|
||||
name string,
|
||||
task func(l logging.LoggerInterface) error,
|
||||
period int,
|
||||
onInit func(l logging.LoggerInterface) error,
|
||||
onStop func(l logging.LoggerInterface),
|
||||
logger logging.LoggerInterface,
|
||||
) *AsyncTask {
|
||||
t := AsyncTask{
|
||||
name: name,
|
||||
task: task,
|
||||
period: period,
|
||||
onInit: onInit,
|
||||
onStop: onStop,
|
||||
logger: logger,
|
||||
incoming: make(chan int, 10),
|
||||
}
|
||||
t.lifecycle.Setup()
|
||||
return &t
|
||||
}
|
||||
35
vendor/github.com/splitio/go-toolkit/v4/backoff/backoff.go
сгенерированный
поставляемый
Обычный файл
35
vendor/github.com/splitio/go-toolkit/v4/backoff/backoff.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,35 @@
|
||||
package backoff
|
||||
|
||||
import (
|
||||
"math"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Interface is the backoff interface
|
||||
type Interface interface {
|
||||
Next() time.Duration
|
||||
Reset()
|
||||
}
|
||||
|
||||
// Impl implements the Backoff interface
|
||||
type Impl struct {
|
||||
base int64
|
||||
current int64
|
||||
}
|
||||
|
||||
// Next returns how long to wait and updates the current count
|
||||
func (b *Impl) Next() time.Duration {
|
||||
current := atomic.AddInt64(&b.current, 1)
|
||||
return time.Duration(math.Pow(float64(b.base), float64(current))) * time.Second
|
||||
}
|
||||
|
||||
// Reset sets the current count to 0
|
||||
func (b *Impl) Reset() {
|
||||
atomic.StoreInt64(&b.current, 0)
|
||||
}
|
||||
|
||||
// New creates a new Backoffer
|
||||
func New() *Impl {
|
||||
return &Impl{base: 2}
|
||||
}
|
||||
53
vendor/github.com/splitio/go-toolkit/v4/common/interface.go
сгенерированный
поставляемый
Обычный файл
53
vendor/github.com/splitio/go-toolkit/v4/common/interface.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,53 @@
|
||||
package common
|
||||
|
||||
// AsIntOrNil returns ref
|
||||
func AsIntOrNil(data interface{}) *int {
|
||||
if data == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
number, ok := data.(int)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return IntRef(number)
|
||||
}
|
||||
|
||||
// AsInt64OrNil returns ref
|
||||
func AsInt64OrNil(data interface{}) *int64 {
|
||||
if data == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
number, ok := data.(int64)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return Int64Ref(number)
|
||||
}
|
||||
|
||||
// AsFloat64OrNil return ref
|
||||
func AsFloat64OrNil(data interface{}) *float64 {
|
||||
if data == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
number, ok := data.(float64)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return Float64Ref(number)
|
||||
}
|
||||
|
||||
// AsStringOrNil returns ref
|
||||
func AsStringOrNil(data interface{}) *string {
|
||||
if data == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
str, ok := data.(string)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return StringRef(str)
|
||||
}
|
||||
67
vendor/github.com/splitio/go-toolkit/v4/common/iterutil.go
сгенерированный
поставляемый
Обычный файл
67
vendor/github.com/splitio/go-toolkit/v4/common/iterutil.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,67 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math"
|
||||
"time"
|
||||
)
|
||||
|
||||
// WithAttempts executes a function N times or until no error is returned
|
||||
func WithAttempts(attempts int, main func() error) error {
|
||||
err := errors.New("")
|
||||
remaining := attempts
|
||||
for err != nil && remaining > 0 {
|
||||
remaining--
|
||||
err = main()
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// WithBackoff wraps the function to add Exponential backoff
|
||||
func WithBackoff(duration time.Duration, main func() error) func() error {
|
||||
var count time.Duration = 1
|
||||
return func() error {
|
||||
err := main()
|
||||
if err != nil {
|
||||
time.Sleep(count * duration)
|
||||
count++
|
||||
} else {
|
||||
count = 0
|
||||
}
|
||||
return main()
|
||||
}
|
||||
}
|
||||
|
||||
// WithBackoffCancelling wraps the function to add Exponential backoff
|
||||
func WithBackoffCancelling(unit time.Duration, max time.Duration, main func() bool) func() {
|
||||
cancel := make(chan struct{})
|
||||
go func() {
|
||||
attempts := 0
|
||||
isDone := main()
|
||||
|
||||
// Create timeout timer for backoff
|
||||
backoffTimer := time.NewTimer(MinDuration(time.Duration(math.Pow(2, float64(attempts)))*unit, max))
|
||||
defer backoffTimer.Stop()
|
||||
|
||||
for !isDone {
|
||||
attempts++
|
||||
|
||||
// Setting timer considerint attempts
|
||||
backoffTimer.Reset(MinDuration(time.Duration(math.Pow(2, float64(attempts)))*unit, max))
|
||||
|
||||
select {
|
||||
case <-cancel:
|
||||
return
|
||||
case <-backoffTimer.C: // Timedout
|
||||
isDone = main()
|
||||
}
|
||||
}
|
||||
}()
|
||||
return func() {
|
||||
select {
|
||||
case cancel <- struct{}{}:
|
||||
return
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
74
vendor/github.com/splitio/go-toolkit/v4/common/refutil.go
сгенерированный
поставляемый
Обычный файл
74
vendor/github.com/splitio/go-toolkit/v4/common/refutil.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,74 @@
|
||||
package common
|
||||
|
||||
// StringRef returns ref
|
||||
func StringRef(str string) *string {
|
||||
return &str
|
||||
}
|
||||
|
||||
// StringFromRef returns original value if not empty. Default otherwise.
|
||||
func StringFromRef(str *string) string {
|
||||
if str == nil {
|
||||
return ""
|
||||
}
|
||||
return *str
|
||||
}
|
||||
|
||||
// IntRef returns ref
|
||||
func IntRef(number int) *int {
|
||||
return &number
|
||||
}
|
||||
|
||||
// IntFromRef returns 0 if nil, dereferenced value otherwhise.
|
||||
func IntFromRef(ref *int) int {
|
||||
if ref == nil {
|
||||
return 0
|
||||
}
|
||||
return *ref
|
||||
}
|
||||
|
||||
// Int64Ref returns ref
|
||||
func Int64Ref(number int64) *int64 {
|
||||
return &number
|
||||
}
|
||||
|
||||
// Int64FromRef returns value
|
||||
func Int64FromRef(number *int64) int64 {
|
||||
if number == nil {
|
||||
return 0
|
||||
}
|
||||
return *number
|
||||
}
|
||||
|
||||
// Int64Value kept to prevent breaking change. TODO: Deprecate in v4
|
||||
func Int64Value(number *int64) int64 {
|
||||
return Int64FromRef(number)
|
||||
}
|
||||
|
||||
// Float64Ref returns ref
|
||||
func Float64Ref(number float64) *float64 {
|
||||
return &number
|
||||
}
|
||||
|
||||
// IntRefOrNil returns ref
|
||||
func IntRefOrNil(number int) *int {
|
||||
if number == 0 {
|
||||
return nil
|
||||
}
|
||||
return IntRef(number)
|
||||
}
|
||||
|
||||
// Int64RefOrNil returns ref
|
||||
func Int64RefOrNil(number int64) *int64 {
|
||||
if number == 0 {
|
||||
return nil
|
||||
}
|
||||
return Int64Ref(number)
|
||||
}
|
||||
|
||||
// StringRefOrNil returns ref
|
||||
func StringRefOrNil(str string) *string {
|
||||
if str == "" {
|
||||
return nil
|
||||
}
|
||||
return StringRef(str)
|
||||
}
|
||||
18
vendor/github.com/splitio/go-toolkit/v4/common/sliceutil.go
сгенерированный
поставляемый
Обычный файл
18
vendor/github.com/splitio/go-toolkit/v4/common/sliceutil.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,18 @@
|
||||
package common
|
||||
|
||||
// Partition create partitions considering the passed amount
|
||||
func Partition(items []string, maxItems int) [][]string {
|
||||
var splitted [][]string
|
||||
|
||||
for i := 0; i < len(items); i += maxItems {
|
||||
end := i + maxItems
|
||||
|
||||
if end > len(items) {
|
||||
end = len(items)
|
||||
}
|
||||
|
||||
splitted = append(splitted, items[i:end])
|
||||
}
|
||||
|
||||
return splitted
|
||||
}
|
||||
9
vendor/github.com/splitio/go-toolkit/v4/common/strutil.go
сгенерированный
поставляемый
Обычный файл
9
vendor/github.com/splitio/go-toolkit/v4/common/strutil.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,9 @@
|
||||
package common
|
||||
|
||||
// StringValueOrDefault returns original value if not empty. Default otherwise.
|
||||
func StringValueOrDefault(str string, def string) string {
|
||||
if str != "" {
|
||||
return str
|
||||
}
|
||||
return def
|
||||
}
|
||||
25
vendor/github.com/splitio/go-toolkit/v4/common/timeutil.go
сгенерированный
поставляемый
Обычный файл
25
vendor/github.com/splitio/go-toolkit/v4/common/timeutil.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,25 @@
|
||||
package common
|
||||
|
||||
import "time"
|
||||
|
||||
// MinDuration returns the min duration among them
|
||||
func MinDuration(d1, d2 time.Duration, ds ...time.Duration) time.Duration {
|
||||
min := d1
|
||||
for _, d := range append(ds, d2) {
|
||||
if d < min {
|
||||
min = d
|
||||
}
|
||||
}
|
||||
return min
|
||||
}
|
||||
|
||||
// MaxDuration returns the max duration among them
|
||||
func MaxDuration(d1, d2 time.Duration, ds ...time.Duration) time.Duration {
|
||||
max := d1
|
||||
for _, d := range append(ds, d2) {
|
||||
if d > max {
|
||||
max = d
|
||||
}
|
||||
}
|
||||
return max
|
||||
}
|
||||
56
vendor/github.com/splitio/go-toolkit/v4/datastructures/set/functions.go
сгенерированный
поставляемый
Обычный файл
56
vendor/github.com/splitio/go-toolkit/v4/datastructures/set/functions.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,56 @@
|
||||
package set
|
||||
|
||||
// Union calculates the union of two or more sets
|
||||
func Union(set1, set2 Set, sets ...Set) Set {
|
||||
u := set1.Copy()
|
||||
set2.Each(func(item interface{}) bool {
|
||||
u.Add(item)
|
||||
return true
|
||||
})
|
||||
|
||||
for _, set := range sets {
|
||||
set.Each(func(item interface{}) bool {
|
||||
u.Add(item)
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
return u
|
||||
}
|
||||
|
||||
// Intersection calculates the intersection of two or more sets
|
||||
func Intersection(set1, set2 Set, sets ...Set) Set {
|
||||
all := Union(set1, set2, sets...)
|
||||
result := Union(set1, set2, sets...)
|
||||
|
||||
all.Each(func(item interface{}) bool {
|
||||
if !set1.Has(item) || !set2.Has(item) {
|
||||
result.Remove(item)
|
||||
}
|
||||
|
||||
for _, set := range sets {
|
||||
if !set.Has(item) {
|
||||
result.Remove(item)
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
// Difference calculates the difference of two or more sets
|
||||
func Difference(set1, set2 Set, sets ...Set) Set {
|
||||
s := set1.Copy()
|
||||
s.Separate(set2)
|
||||
for _, set := range sets {
|
||||
s.Separate(set) // seperate is thread safe
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// SymmetricDifference calculates the symmetric difference of two or more sets
|
||||
func SymmetricDifference(s Set, t Set) Set {
|
||||
u := Difference(s, t)
|
||||
v := Difference(t, s)
|
||||
return Union(u, v)
|
||||
}
|
||||
355
vendor/github.com/splitio/go-toolkit/v4/datastructures/set/implementations.go
сгенерированный
поставляемый
Обычный файл
355
vendor/github.com/splitio/go-toolkit/v4/datastructures/set/implementations.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,355 @@
|
||||
package set
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
var keyExists = struct{}{} // Value that indicates existance in the set for the key element
|
||||
|
||||
// ThreadUnsafeSet structure. Container of unique items with O(1) access time. NOT THREAD SAFE
|
||||
type ThreadUnsafeSet struct {
|
||||
set
|
||||
}
|
||||
|
||||
// NewSet Constructs a new set from an optinal slice of items
|
||||
func NewSet(items ...interface{}) *ThreadUnsafeSet {
|
||||
s := &ThreadUnsafeSet{}
|
||||
s.m = make(map[interface{}]struct{})
|
||||
s.Add(items...)
|
||||
return s
|
||||
}
|
||||
|
||||
// Add adds new items to the set
|
||||
func (s *ThreadUnsafeSet) Add(items ...interface{}) {
|
||||
if len(items) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
for _, item := range items {
|
||||
s.m[item] = keyExists
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Remove removes items from the set
|
||||
func (s *ThreadUnsafeSet) Remove(items ...interface{}) {
|
||||
if len(items) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
for _, item := range items {
|
||||
delete(s.m, item)
|
||||
}
|
||||
}
|
||||
|
||||
// Pop removes an item from the set and returns it.
|
||||
func (s *set) Pop() interface{} {
|
||||
for item := range s.m {
|
||||
delete(s.m, item)
|
||||
return item
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Has returns true if the items passed are present in the set
|
||||
func (s *ThreadUnsafeSet) Has(items ...interface{}) bool {
|
||||
// assume checked for empty item, which not exist
|
||||
if len(items) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
has := true
|
||||
for _, item := range items {
|
||||
if _, has = s.m[item]; !has {
|
||||
break
|
||||
}
|
||||
}
|
||||
return has
|
||||
}
|
||||
|
||||
// Size returns the size of the set
|
||||
func (s *ThreadUnsafeSet) Size() int {
|
||||
return len(s.m)
|
||||
}
|
||||
|
||||
// Clear removes all elements from the set
|
||||
func (s *ThreadUnsafeSet) Clear() {
|
||||
s.m = make(map[interface{}]struct{})
|
||||
}
|
||||
|
||||
// IsEqual returns true if the received set is equal to this one
|
||||
func (s *ThreadUnsafeSet) IsEqual(t Set) bool {
|
||||
// Force locking only if given set is threadsafe.
|
||||
if conv, ok := t.(*ThreadSafeSet); ok {
|
||||
conv.l.RLock()
|
||||
defer conv.l.RUnlock()
|
||||
}
|
||||
|
||||
// return false if they are no the same size
|
||||
if sameSize := len(s.m) == t.Size(); !sameSize {
|
||||
return false
|
||||
}
|
||||
|
||||
equal := true
|
||||
t.Each(func(item interface{}) bool {
|
||||
_, equal = s.m[item]
|
||||
return equal // if false, Each() will end
|
||||
})
|
||||
|
||||
return equal
|
||||
}
|
||||
|
||||
// IsSubset returns true if the passed set is a subset of this one
|
||||
func (s *ThreadUnsafeSet) IsSubset(t Set) (subset bool) {
|
||||
subset = true
|
||||
|
||||
t.Each(func(item interface{}) bool {
|
||||
_, subset = s.m[item]
|
||||
return subset
|
||||
})
|
||||
|
||||
return subset
|
||||
}
|
||||
|
||||
// Each executes a passed function on each of the items passed.
|
||||
func (s *ThreadUnsafeSet) Each(f func(item interface{}) bool) {
|
||||
for item := range s.m {
|
||||
if !f(item) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// List returns a slice of the items in th set
|
||||
func (s *ThreadUnsafeSet) List() []interface{} {
|
||||
list := make([]interface{}, 0, len(s.m))
|
||||
|
||||
for item := range s.m {
|
||||
list = append(list, item)
|
||||
}
|
||||
|
||||
return list
|
||||
}
|
||||
|
||||
// Copy returns a new set with a copy of the elements
|
||||
func (s *ThreadUnsafeSet) Copy() Set {
|
||||
return NewSet(s.List()...)
|
||||
}
|
||||
|
||||
// Merge adds all the elefements in the passed set to this one.
|
||||
func (s *ThreadUnsafeSet) Merge(t Set) {
|
||||
t.Each(func(item interface{}) bool {
|
||||
s.m[item] = keyExists
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
// Separate removes all the items that are present in the passed set from this set
|
||||
func (s *ThreadUnsafeSet) Separate(t Set) {
|
||||
s.Remove(t.List()...)
|
||||
}
|
||||
|
||||
// IsEmpty returns true if the set has no elements
|
||||
func (s *ThreadUnsafeSet) IsEmpty() bool {
|
||||
return s.Size() == 0
|
||||
}
|
||||
|
||||
// IsSuperset returns true if the passed set is a supertset of this one
|
||||
func (s *ThreadUnsafeSet) IsSuperset(t Set) bool {
|
||||
return t.IsSubset(s)
|
||||
}
|
||||
|
||||
// ** Thread safe implementation
|
||||
|
||||
// ThreadSafeSet is a thread safe implementation of the set data structure
|
||||
type ThreadSafeSet struct {
|
||||
set
|
||||
l sync.RWMutex
|
||||
}
|
||||
|
||||
// NewThreadSafeSet instantiates a new ThreadSafeSet
|
||||
func NewThreadSafeSet(items ...interface{}) *ThreadSafeSet {
|
||||
s := &ThreadSafeSet{}
|
||||
s.m = make(map[interface{}]struct{})
|
||||
|
||||
// Ensure interface compliance
|
||||
var _ Set = s
|
||||
|
||||
s.Add(items...)
|
||||
return s
|
||||
}
|
||||
|
||||
// Add adds a new element to the set
|
||||
func (s *ThreadSafeSet) Add(items ...interface{}) {
|
||||
if len(items) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
s.l.Lock()
|
||||
defer s.l.Unlock()
|
||||
|
||||
for _, item := range items {
|
||||
s.m[item] = keyExists
|
||||
}
|
||||
}
|
||||
|
||||
// Remove deletes an elemenet from the set.
|
||||
func (s *ThreadSafeSet) Remove(items ...interface{}) {
|
||||
if len(items) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
s.l.Lock()
|
||||
defer s.l.Unlock()
|
||||
|
||||
for _, item := range items {
|
||||
delete(s.m, item)
|
||||
}
|
||||
}
|
||||
|
||||
// Pop removes an element from the set and returns it
|
||||
func (s *ThreadSafeSet) Pop() interface{} {
|
||||
s.l.RLock()
|
||||
for item := range s.m {
|
||||
s.l.RUnlock()
|
||||
s.l.Lock()
|
||||
delete(s.m, item)
|
||||
s.l.Unlock()
|
||||
return item
|
||||
}
|
||||
s.l.RUnlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Has returns true if the element passed is in the set
|
||||
func (s *ThreadSafeSet) Has(items ...interface{}) bool {
|
||||
// assume checked for empty item, which not exist
|
||||
if len(items) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
s.l.RLock()
|
||||
defer s.l.RUnlock()
|
||||
|
||||
has := true
|
||||
for _, item := range items {
|
||||
if _, has = s.m[item]; !has {
|
||||
break
|
||||
}
|
||||
}
|
||||
return has
|
||||
}
|
||||
|
||||
// Size returns the number of elements in the set
|
||||
func (s *ThreadSafeSet) Size() int {
|
||||
s.l.RLock()
|
||||
defer s.l.RUnlock()
|
||||
|
||||
l := len(s.m)
|
||||
return l
|
||||
}
|
||||
|
||||
// Clear removes all the elements in the set
|
||||
func (s *ThreadSafeSet) Clear() {
|
||||
s.l.Lock()
|
||||
defer s.l.Unlock()
|
||||
|
||||
s.m = make(map[interface{}]struct{})
|
||||
}
|
||||
|
||||
// IsEqual returns true if the set contains the same elements as the passed one
|
||||
func (s *ThreadSafeSet) IsEqual(t Set) bool {
|
||||
s.l.RLock()
|
||||
defer s.l.RUnlock()
|
||||
|
||||
// Force locking only if given set is threadsafe.
|
||||
if conv, ok := t.(*ThreadSafeSet); ok {
|
||||
conv.l.RLock()
|
||||
defer conv.l.RUnlock()
|
||||
}
|
||||
|
||||
// return false if they are no the same size
|
||||
if sameSize := len(s.m) == t.Size(); !sameSize {
|
||||
return false
|
||||
}
|
||||
|
||||
equal := true
|
||||
t.Each(func(item interface{}) bool {
|
||||
_, equal = s.m[item]
|
||||
return equal // if false, Each() will end
|
||||
})
|
||||
|
||||
return equal
|
||||
}
|
||||
|
||||
// IsSubset returns true if the passed set is a subset of this one
|
||||
func (s *ThreadSafeSet) IsSubset(t Set) (subset bool) {
|
||||
s.l.RLock()
|
||||
defer s.l.RUnlock()
|
||||
|
||||
subset = true
|
||||
|
||||
t.Each(func(item interface{}) bool {
|
||||
_, subset = s.m[item]
|
||||
return subset
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Each executes the passed function on each item from the set
|
||||
func (s *ThreadSafeSet) Each(f func(item interface{}) bool) {
|
||||
s.l.RLock()
|
||||
defer s.l.RUnlock()
|
||||
|
||||
for item := range s.m {
|
||||
if !f(item) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// List returns a list with all the elements of the set
|
||||
func (s *ThreadSafeSet) List() []interface{} {
|
||||
s.l.RLock()
|
||||
defer s.l.RUnlock()
|
||||
|
||||
list := make([]interface{}, 0, len(s.m))
|
||||
|
||||
for item := range s.m {
|
||||
list = append(list, item)
|
||||
}
|
||||
|
||||
return list
|
||||
}
|
||||
|
||||
// Merge adds all the elements of the passed set into this one
|
||||
func (s *ThreadSafeSet) Merge(t Set) {
|
||||
s.l.Lock()
|
||||
defer s.l.Unlock()
|
||||
|
||||
t.Each(func(item interface{}) bool {
|
||||
s.m[item] = keyExists
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
// Copy returns a copy of the this thread
|
||||
func (s *ThreadSafeSet) Copy() Set {
|
||||
return NewThreadSafeSet(s.List()...)
|
||||
}
|
||||
|
||||
// Separate removes all the items that are present in the passed set from this set
|
||||
func (s *ThreadSafeSet) Separate(t Set) {
|
||||
s.Remove(t.List()...)
|
||||
}
|
||||
|
||||
// IsEmpty returns true if the set has no elements
|
||||
func (s *ThreadSafeSet) IsEmpty() bool {
|
||||
return s.Size() == 0
|
||||
}
|
||||
|
||||
// IsSuperset returns true if the passed set is a supertset of this one
|
||||
func (s *ThreadSafeSet) IsSuperset(t Set) bool {
|
||||
return t.IsSubset(s)
|
||||
}
|
||||
24
vendor/github.com/splitio/go-toolkit/v4/datastructures/set/set.go
сгенерированный
поставляемый
Обычный файл
24
vendor/github.com/splitio/go-toolkit/v4/datastructures/set/set.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,24 @@
|
||||
package set
|
||||
|
||||
// Set interface shared between Thread-Safe and Thread-Unsafe implementations
|
||||
type Set interface {
|
||||
Add(items ...interface{})
|
||||
Remove(items ...interface{})
|
||||
Pop() interface{}
|
||||
Has(items ...interface{}) bool
|
||||
Size() int
|
||||
Clear()
|
||||
IsEmpty() bool
|
||||
IsEqual(s Set) bool
|
||||
IsSubset(s Set) bool
|
||||
IsSuperset(s Set) bool
|
||||
Each(func(interface{}) bool)
|
||||
List() []interface{}
|
||||
Copy() Set
|
||||
Merge(s Set)
|
||||
Separate(t Set)
|
||||
}
|
||||
|
||||
type set struct {
|
||||
m map[interface{}]struct{}
|
||||
}
|
||||
47
vendor/github.com/splitio/go-toolkit/v4/injection/container.go
сгенерированный
поставляемый
Обычный файл
47
vendor/github.com/splitio/go-toolkit/v4/injection/container.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,47 @@
|
||||
package injection
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// NewContext return an instance of Context
|
||||
func NewContext() *Context {
|
||||
return &Context{container: make(map[string]interface{})}
|
||||
}
|
||||
|
||||
// Context of dependencies to be injected as context
|
||||
type Context struct {
|
||||
container map[string]interface{}
|
||||
mx sync.Mutex
|
||||
}
|
||||
|
||||
// AddDependency adds a dependency to the container
|
||||
func (c *Context) AddDependency(key string, d interface{}) {
|
||||
c.mx.Lock()
|
||||
c.container[key] = d
|
||||
c.mx.Unlock()
|
||||
|
||||
}
|
||||
|
||||
// Dependency returns an object given a key
|
||||
func (c *Context) Dependency(key string) interface{} {
|
||||
c.mx.Lock()
|
||||
defer c.mx.Unlock()
|
||||
|
||||
return c.container[key]
|
||||
}
|
||||
|
||||
// Inject add the Context instance as dependency
|
||||
func (c *Context) Inject(o interface{}) {
|
||||
rv := reflect.ValueOf(c)
|
||||
reflectedContext := reflect.ValueOf(o).Elem()
|
||||
typeOfT := rv.Type()
|
||||
|
||||
for i := 0; i < reflectedContext.NumField(); i++ {
|
||||
f := reflectedContext.Field(i)
|
||||
if f.Type().String() == typeOfT.String() {
|
||||
reflectedContext.Field(i).Set(rv)
|
||||
}
|
||||
}
|
||||
}
|
||||
33
vendor/github.com/splitio/go-toolkit/v4/logging/functions.go
сгенерированный
поставляемый
Обычный файл
33
vendor/github.com/splitio/go-toolkit/v4/logging/functions.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,33 @@
|
||||
package logging
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ObfuscateAPIKey obfucate part of api key
|
||||
func ObfuscateAPIKey(apikey string) string {
|
||||
obfuscationIndex := 80
|
||||
|
||||
total := len(apikey)
|
||||
charsToObfuscate := obfuscationIndex * total / 100
|
||||
toShow := (total - charsToObfuscate) / 2
|
||||
|
||||
return strings.Join([]string{apikey[:toShow], apikey[len(apikey)-toShow:]}, "...")
|
||||
}
|
||||
|
||||
// ObfuscateHTTPHeader obfuscates sensitive data into headers
|
||||
func ObfuscateHTTPHeader(headers http.Header) string {
|
||||
var re = regexp.MustCompile(`Authorization:\[Bearer ([0-9|a-z|A-Z|\s]*)\]`)
|
||||
var str = fmt.Sprint(headers)
|
||||
match := re.FindStringSubmatch(str)
|
||||
|
||||
if len(match) == 2 {
|
||||
str = strings.Replace(str, match[1], ObfuscateAPIKey(match[1]), 1)
|
||||
return fmt.Sprint("[REQUEST_HEADERS]", str, "[END_REQUEST_HEADERS]")
|
||||
}
|
||||
|
||||
return str
|
||||
}
|
||||
12
vendor/github.com/splitio/go-toolkit/v4/logging/interface.go
сгенерированный
поставляемый
Обычный файл
12
vendor/github.com/splitio/go-toolkit/v4/logging/interface.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,12 @@
|
||||
package logging
|
||||
|
||||
// LoggerInterface ...
|
||||
// If a custom logger object is to be used, it should comply with the following
|
||||
// interface. (Standard go-lang library log.Logger.Println method signature)
|
||||
type LoggerInterface interface {
|
||||
Error(msg ...interface{})
|
||||
Warning(msg ...interface{})
|
||||
Info(msg ...interface{})
|
||||
Debug(msg ...interface{})
|
||||
Verbose(msg ...interface{})
|
||||
}
|
||||
93
vendor/github.com/splitio/go-toolkit/v4/logging/levels.go
сгенерированный
поставляемый
Обычный файл
93
vendor/github.com/splitio/go-toolkit/v4/logging/levels.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,93 @@
|
||||
package logging
|
||||
|
||||
import (
|
||||
"math"
|
||||
)
|
||||
|
||||
// Standard values
|
||||
const (
|
||||
// Discard 0 value, so when can use it as "the lack of a logging level"
|
||||
_ = iota
|
||||
|
||||
// LevelError log level
|
||||
LevelError
|
||||
|
||||
// LevelWarning log level
|
||||
LevelWarning
|
||||
|
||||
// LevelInfo log level
|
||||
LevelInfo
|
||||
|
||||
// LevelDebug log level
|
||||
LevelDebug
|
||||
|
||||
// LevelVerbose log level
|
||||
LevelVerbose
|
||||
)
|
||||
|
||||
// Special values
|
||||
const (
|
||||
// LevelNone implies that NOTHING will be logged, not even errors
|
||||
LevelNone = math.MinInt32
|
||||
|
||||
// LevelAll implies that All logging levels will be recorded
|
||||
LevelAll = math.MaxInt32
|
||||
)
|
||||
|
||||
// LevelFilteredLoggerWrapper forwards log message to delegate if level is set higher than incoming message
|
||||
type LevelFilteredLoggerWrapper struct {
|
||||
level int
|
||||
delegate LoggerInterface
|
||||
}
|
||||
|
||||
// Error forwards error logging messages
|
||||
func (l *LevelFilteredLoggerWrapper) Error(is ...interface{}) {
|
||||
if l.level >= LevelError {
|
||||
l.delegate.Error(is...)
|
||||
}
|
||||
}
|
||||
|
||||
// Warning forwards warning logging messages
|
||||
func (l *LevelFilteredLoggerWrapper) Warning(is ...interface{}) {
|
||||
if l.level >= LevelWarning {
|
||||
l.delegate.Warning(is...)
|
||||
}
|
||||
}
|
||||
|
||||
// Info forwards info logging messages
|
||||
func (l *LevelFilteredLoggerWrapper) Info(is ...interface{}) {
|
||||
if l.level >= LevelInfo {
|
||||
l.delegate.Info(is...)
|
||||
}
|
||||
}
|
||||
|
||||
// Debug forwards debug logging messages
|
||||
func (l *LevelFilteredLoggerWrapper) Debug(is ...interface{}) {
|
||||
if l.level >= LevelDebug {
|
||||
l.delegate.Debug(is...)
|
||||
}
|
||||
}
|
||||
|
||||
// Verbose forwards verbose logging messages
|
||||
func (l *LevelFilteredLoggerWrapper) Verbose(is ...interface{}) {
|
||||
if l.level >= LevelVerbose {
|
||||
l.delegate.Verbose(is...)
|
||||
}
|
||||
}
|
||||
|
||||
var levels map[string]int = map[string]int{
|
||||
"ERROR": LevelError,
|
||||
"WARNING": LevelWarning,
|
||||
"INFO": LevelInfo,
|
||||
"DEBUG": LevelDebug,
|
||||
"VERBOSE": LevelVerbose,
|
||||
}
|
||||
|
||||
// Level gets current level
|
||||
func Level(level string) int {
|
||||
l, ok := levels[level]
|
||||
if !ok {
|
||||
panic("Invalid log level " + level)
|
||||
}
|
||||
return l
|
||||
}
|
||||
129
vendor/github.com/splitio/go-toolkit/v4/logging/logging.go
сгенерированный
поставляемый
Обычный файл
129
vendor/github.com/splitio/go-toolkit/v4/logging/logging.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,129 @@
|
||||
// Package logging ...
|
||||
// Handles logging within the SDK
|
||||
package logging
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
)
|
||||
|
||||
const (
|
||||
skipStackFrameBase = 3 // How many stack frames to skip when logging filename
|
||||
)
|
||||
|
||||
// LoggerOptions ...
|
||||
// Struct that must be passed to the NewLogger constructor to setup a logger
|
||||
// CommonWriter and ErrorWriter can be <nil>. In that case they'll default to os.Stdout
|
||||
type LoggerOptions struct {
|
||||
LogLevel int
|
||||
ErrorWriter io.Writer
|
||||
WarningWriter io.Writer
|
||||
InfoWriter io.Writer
|
||||
DebugWriter io.Writer
|
||||
VerboseWriter io.Writer
|
||||
StandardLoggerFlags int
|
||||
Prefix string
|
||||
ExtraFramesToSkip int
|
||||
}
|
||||
|
||||
// Logger struct. Encapsulates four different loggers, each for a different "level",
|
||||
// and provides Error, Debug, Warning and Info functions, that will forward a message
|
||||
// to the appropriate logger.
|
||||
type Logger struct {
|
||||
debugLogger log.Logger
|
||||
infoLogger log.Logger
|
||||
warningLogger log.Logger
|
||||
errorLogger log.Logger
|
||||
verboseLogger log.Logger
|
||||
framesToSkip int
|
||||
}
|
||||
|
||||
// Verbose logs a message with Debug level
|
||||
func (l *Logger) Verbose(msg ...interface{}) {
|
||||
l.verboseLogger.Output(l.framesToSkip, fmt.Sprintln(msg...))
|
||||
}
|
||||
|
||||
// Debug logs a message with Debug level
|
||||
func (l *Logger) Debug(msg ...interface{}) {
|
||||
l.debugLogger.Output(l.framesToSkip, fmt.Sprintln(msg...))
|
||||
}
|
||||
|
||||
// Info logs a message with Info level
|
||||
func (l *Logger) Info(msg ...interface{}) {
|
||||
l.infoLogger.Output(l.framesToSkip, fmt.Sprintln(msg...))
|
||||
}
|
||||
|
||||
// Warning logs a message with Warning level
|
||||
func (l *Logger) Warning(msg ...interface{}) {
|
||||
l.warningLogger.Output(l.framesToSkip, fmt.Sprintln(msg...))
|
||||
}
|
||||
|
||||
// Error logs a message with Error level
|
||||
func (l *Logger) Error(msg ...interface{}) {
|
||||
l.errorLogger.Output(l.framesToSkip, fmt.Sprintln(msg...))
|
||||
}
|
||||
|
||||
func normalizeOptions(options *LoggerOptions) *LoggerOptions {
|
||||
var toRet *LoggerOptions
|
||||
if options == nil {
|
||||
toRet = &LoggerOptions{}
|
||||
} else {
|
||||
toRet = options
|
||||
}
|
||||
|
||||
if toRet.DebugWriter == nil {
|
||||
toRet.DebugWriter = os.Stdout
|
||||
}
|
||||
|
||||
if toRet.ErrorWriter == nil {
|
||||
toRet.ErrorWriter = os.Stdout
|
||||
}
|
||||
|
||||
if toRet.InfoWriter == nil {
|
||||
toRet.InfoWriter = os.Stdout
|
||||
}
|
||||
|
||||
if toRet.VerboseWriter == nil {
|
||||
toRet.VerboseWriter = os.Stdout
|
||||
}
|
||||
|
||||
if toRet.WarningWriter == nil {
|
||||
toRet.WarningWriter = os.Stdout
|
||||
}
|
||||
|
||||
if toRet.StandardLoggerFlags == 0 {
|
||||
toRet.StandardLoggerFlags = log.Ldate | log.Ltime
|
||||
}
|
||||
|
||||
switch toRet.LogLevel {
|
||||
case LevelAll, LevelDebug, LevelError, LevelInfo, LevelNone, LevelVerbose, LevelWarning:
|
||||
default:
|
||||
toRet.LogLevel = LevelError
|
||||
}
|
||||
return toRet
|
||||
}
|
||||
|
||||
// NewLogger instantiates a new Logger instance. Requires a pointer to a LoggerOptions struct to be passed.
|
||||
func NewLogger(options *LoggerOptions) LoggerInterface {
|
||||
|
||||
options = normalizeOptions(options)
|
||||
prefix := ""
|
||||
if options.Prefix != "" {
|
||||
prefix = fmt.Sprintf("%s - ", options.Prefix)
|
||||
}
|
||||
logger := &Logger{
|
||||
debugLogger: *log.New(options.DebugWriter, fmt.Sprintf("%sDEBUG - ", prefix), options.StandardLoggerFlags),
|
||||
infoLogger: *log.New(options.InfoWriter, fmt.Sprintf("%sINFO - ", prefix), options.StandardLoggerFlags),
|
||||
warningLogger: *log.New(options.WarningWriter, fmt.Sprintf("%sWARNING - ", prefix), options.StandardLoggerFlags),
|
||||
errorLogger: *log.New(options.ErrorWriter, fmt.Sprintf("%sERROR - ", prefix), options.StandardLoggerFlags),
|
||||
verboseLogger: *log.New(options.VerboseWriter, fmt.Sprintf("%sVERBOSE - ", prefix), options.StandardLoggerFlags),
|
||||
framesToSkip: 3 + options.ExtraFramesToSkip,
|
||||
}
|
||||
|
||||
return &LevelFilteredLoggerWrapper{
|
||||
delegate: logger,
|
||||
level: options.LogLevel,
|
||||
}
|
||||
}
|
||||
106
vendor/github.com/splitio/go-toolkit/v4/logging/rotate.go
сгенерированный
поставляемый
Обычный файл
106
vendor/github.com/splitio/go-toolkit/v4/logging/rotate.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,106 @@
|
||||
package logging
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// FileRotateOptions struct to configure FileRotate
|
||||
type FileRotateOptions struct {
|
||||
MaxBytes int64
|
||||
BackupCount int
|
||||
Path string
|
||||
}
|
||||
|
||||
// FileRotate rotates a log file at MaxBytes
|
||||
type FileRotate struct {
|
||||
fl *os.File
|
||||
fm *sync.Mutex
|
||||
options *FileRotateOptions
|
||||
}
|
||||
|
||||
// NewFileRotate returns a pointer to a FileRotate instance
|
||||
func NewFileRotate(opt *FileRotateOptions) (*FileRotate, error) {
|
||||
|
||||
fileWriter, err := os.OpenFile(opt.Path, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fl := &FileRotate{fl: fileWriter, fm: &sync.Mutex{}, options: opt}
|
||||
return fl, nil
|
||||
}
|
||||
|
||||
func (f *FileRotate) shouldRotate(bytesToAdd int64) bool {
|
||||
fi, err := f.fl.Stat()
|
||||
if err != nil {
|
||||
fmt.Println("Error getting stats of file")
|
||||
return false
|
||||
}
|
||||
|
||||
if fi.Size()+bytesToAdd >= f.options.MaxBytes {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (f *FileRotate) rotate() error {
|
||||
|
||||
f.fl.Close()
|
||||
|
||||
for i := f.options.BackupCount - 1; i >= 0; i-- {
|
||||
var currentLog string
|
||||
if i == 0 {
|
||||
currentLog = f.options.Path
|
||||
} else {
|
||||
currentLog = f.options.Path + "." + strconv.Itoa(i)
|
||||
}
|
||||
|
||||
if _, err := os.Stat(currentLog); err == nil {
|
||||
rotateLog := f.options.Path + "." + strconv.Itoa(i+1)
|
||||
err := os.Rename(currentLog, rotateLog)
|
||||
if err != nil {
|
||||
fmt.Printf("Error rotating log file: %s \n", err.Error())
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
var err error
|
||||
f.fl, err = os.OpenFile(f.options.Path, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
|
||||
if err != nil {
|
||||
fmt.Printf("Error reopening log file: %s \n", err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *FileRotate) write(p []byte) (n int, err error) {
|
||||
f.fm.Lock()
|
||||
if f.shouldRotate(int64(len(p))) {
|
||||
f.rotate()
|
||||
}
|
||||
|
||||
n, err = f.fl.Write(p)
|
||||
f.fm.Unlock()
|
||||
|
||||
if err != nil {
|
||||
fmt.Println("Error writing in rotated log file", f.options.Path)
|
||||
return n, err
|
||||
}
|
||||
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// Write writes async the log message
|
||||
func (f *FileRotate) Write(p []byte) (n int, err error) {
|
||||
dst := make([]byte, len(p))
|
||||
copy(dst, p)
|
||||
go f.write(dst)
|
||||
return len(p), nil
|
||||
}
|
||||
44
vendor/github.com/splitio/go-toolkit/v4/nethelpers/ip.go
сгенерированный
поставляемый
Обычный файл
44
vendor/github.com/splitio/go-toolkit/v4/nethelpers/ip.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,44 @@
|
||||
package nethelpers
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
)
|
||||
|
||||
// ExternalIP returns the IP address of the host
|
||||
func ExternalIP() (string, error) {
|
||||
ifaces, err := net.Interfaces()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
for _, iface := range ifaces {
|
||||
if iface.Flags&net.FlagUp == 0 {
|
||||
continue // interface down
|
||||
}
|
||||
if iface.Flags&net.FlagLoopback != 0 {
|
||||
continue // loopback interface
|
||||
}
|
||||
addrs, err := iface.Addrs()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
for _, addr := range addrs {
|
||||
var ip net.IP
|
||||
switch v := addr.(type) {
|
||||
case *net.IPNet:
|
||||
ip = v.IP
|
||||
case *net.IPAddr:
|
||||
ip = v.IP
|
||||
}
|
||||
if ip == nil || ip.IsLoopback() {
|
||||
continue
|
||||
}
|
||||
ip = ip.To4()
|
||||
if ip == nil {
|
||||
continue // not an ipv4 address
|
||||
}
|
||||
return ip.String(), nil
|
||||
}
|
||||
}
|
||||
return "", errors.New("Could not determine IP Address")
|
||||
}
|
||||
231
vendor/github.com/splitio/go-toolkit/v4/provisional/hashing/murmur128.go
сгенерированный
поставляемый
Обычный файл
231
vendor/github.com/splitio/go-toolkit/v4/provisional/hashing/murmur128.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,231 @@
|
||||
package hashing
|
||||
|
||||
// Implementation borrowed from https://github.com/spaolacci/murmur3,
|
||||
// distributed under BSD-3 license.
|
||||
|
||||
import (
|
||||
//"encoding/binary"
|
||||
"hash"
|
||||
"math/bits"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
c1_128 = 0x87c37b91114253d5
|
||||
c2_128 = 0x4cf5ad432745937f
|
||||
)
|
||||
|
||||
// Hash128 represents a 128-bit hasher
|
||||
// Hack: the standard api doesn't define any Hash128 interface.
|
||||
type Hash128 interface {
|
||||
hash.Hash
|
||||
Sum128() (uint64, uint64)
|
||||
}
|
||||
|
||||
// digest128 represents a partial evaluation of a 128 bites hash.
|
||||
type digest128 struct {
|
||||
clen int // Digested input cumulative length.
|
||||
tail []byte // 0 to Size()-1 bytes view of `buf'.
|
||||
buf [16]byte // Expected (but not required) to be Size() large.
|
||||
seed uint32 // Seed for initializing the hash.
|
||||
h1 uint64 // Unfinalized running hash part 1.
|
||||
h2 uint64 // Unfinalized running hash part 2.
|
||||
}
|
||||
|
||||
// New128 returns a 128-bit hasher
|
||||
func New128() Hash128 { return New128WithSeed(0) }
|
||||
|
||||
// New128WithSeed returns a 128-bit hasher set with explicit seed value
|
||||
func New128WithSeed(seed uint32) Hash128 {
|
||||
d := new(digest128)
|
||||
d.seed = seed
|
||||
d.Reset()
|
||||
return d
|
||||
}
|
||||
|
||||
func (d *digest128) BlockSize() int { return 1 }
|
||||
|
||||
func (d *digest128) Write(p []byte) (n int, err error) {
|
||||
n = len(p)
|
||||
d.clen += n
|
||||
|
||||
if len(d.tail) > 0 {
|
||||
// Stick back pending bytes.
|
||||
nfree := d.Size() - len(d.tail) // nfree ∈ [1, d.Size()-1].
|
||||
if nfree < len(p) {
|
||||
// One full block can be formed.
|
||||
block := append(d.tail, p[:nfree]...)
|
||||
p = p[nfree:]
|
||||
_ = d.bmix(block) // No tail.
|
||||
} else {
|
||||
// Tail's buf is large enough to prevent reallocs.
|
||||
p = append(d.tail, p...)
|
||||
}
|
||||
}
|
||||
|
||||
d.tail = d.bmix(p)
|
||||
|
||||
// Keep own copy of the 0 to Size()-1 pending bytes.
|
||||
nn := copy(d.buf[:], d.tail)
|
||||
d.tail = d.buf[:nn]
|
||||
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (d *digest128) Reset() {
|
||||
d.clen = 0
|
||||
d.tail = nil
|
||||
d.reset()
|
||||
}
|
||||
|
||||
func (d *digest128) Size() int { return 16 }
|
||||
|
||||
func (d *digest128) reset() { d.h1, d.h2 = uint64(d.seed), uint64(d.seed) }
|
||||
|
||||
func (d *digest128) Sum(b []byte) []byte {
|
||||
h1, h2 := d.Sum128()
|
||||
return append(b,
|
||||
byte(h1>>56), byte(h1>>48), byte(h1>>40), byte(h1>>32),
|
||||
byte(h1>>24), byte(h1>>16), byte(h1>>8), byte(h1),
|
||||
|
||||
byte(h2>>56), byte(h2>>48), byte(h2>>40), byte(h2>>32),
|
||||
byte(h2>>24), byte(h2>>16), byte(h2>>8), byte(h2),
|
||||
)
|
||||
}
|
||||
|
||||
func (d *digest128) bmix(p []byte) (tail []byte) {
|
||||
h1, h2 := d.h1, d.h2
|
||||
|
||||
nblocks := len(p) / 16
|
||||
for i := 0; i < nblocks; i++ {
|
||||
t := (*[2]uint64)(unsafe.Pointer(&p[i*16]))
|
||||
k1, k2 := t[0], t[1]
|
||||
|
||||
k1 *= c1_128
|
||||
k1 = bits.RotateLeft64(k1, 31)
|
||||
k1 *= c2_128
|
||||
h1 ^= k1
|
||||
|
||||
h1 = bits.RotateLeft64(h1, 27)
|
||||
h1 += h2
|
||||
h1 = h1*5 + 0x52dce729
|
||||
|
||||
k2 *= c2_128
|
||||
k2 = bits.RotateLeft64(k2, 33)
|
||||
k2 *= c1_128
|
||||
h2 ^= k2
|
||||
|
||||
h2 = bits.RotateLeft64(h2, 31)
|
||||
h2 += h1
|
||||
h2 = h2*5 + 0x38495ab5
|
||||
}
|
||||
d.h1, d.h2 = h1, h2
|
||||
return p[nblocks*d.Size():]
|
||||
}
|
||||
|
||||
func (d *digest128) Sum128() (h1, h2 uint64) {
|
||||
|
||||
h1, h2 = d.h1, d.h2
|
||||
|
||||
var k1, k2 uint64
|
||||
switch len(d.tail) & 15 {
|
||||
case 15:
|
||||
k2 ^= uint64(d.tail[14]) << 48
|
||||
fallthrough
|
||||
case 14:
|
||||
k2 ^= uint64(d.tail[13]) << 40
|
||||
fallthrough
|
||||
case 13:
|
||||
k2 ^= uint64(d.tail[12]) << 32
|
||||
fallthrough
|
||||
case 12:
|
||||
k2 ^= uint64(d.tail[11]) << 24
|
||||
fallthrough
|
||||
case 11:
|
||||
k2 ^= uint64(d.tail[10]) << 16
|
||||
fallthrough
|
||||
case 10:
|
||||
k2 ^= uint64(d.tail[9]) << 8
|
||||
fallthrough
|
||||
case 9:
|
||||
k2 ^= uint64(d.tail[8]) << 0
|
||||
|
||||
k2 *= c2_128
|
||||
k2 = bits.RotateLeft64(k2, 33)
|
||||
k2 *= c1_128
|
||||
h2 ^= k2
|
||||
|
||||
fallthrough
|
||||
|
||||
case 8:
|
||||
k1 ^= uint64(d.tail[7]) << 56
|
||||
fallthrough
|
||||
case 7:
|
||||
k1 ^= uint64(d.tail[6]) << 48
|
||||
fallthrough
|
||||
case 6:
|
||||
k1 ^= uint64(d.tail[5]) << 40
|
||||
fallthrough
|
||||
case 5:
|
||||
k1 ^= uint64(d.tail[4]) << 32
|
||||
fallthrough
|
||||
case 4:
|
||||
k1 ^= uint64(d.tail[3]) << 24
|
||||
fallthrough
|
||||
case 3:
|
||||
k1 ^= uint64(d.tail[2]) << 16
|
||||
fallthrough
|
||||
case 2:
|
||||
k1 ^= uint64(d.tail[1]) << 8
|
||||
fallthrough
|
||||
case 1:
|
||||
k1 ^= uint64(d.tail[0]) << 0
|
||||
k1 *= c1_128
|
||||
k1 = bits.RotateLeft64(k1, 31)
|
||||
k1 *= c2_128
|
||||
h1 ^= k1
|
||||
}
|
||||
|
||||
h1 ^= uint64(d.clen)
|
||||
h2 ^= uint64(d.clen)
|
||||
|
||||
h1 += h2
|
||||
h2 += h1
|
||||
|
||||
h1 = fmix64(h1)
|
||||
h2 = fmix64(h2)
|
||||
|
||||
h1 += h2
|
||||
h2 += h1
|
||||
|
||||
return h1, h2
|
||||
}
|
||||
|
||||
func fmix64(k uint64) uint64 {
|
||||
k ^= k >> 33
|
||||
k *= 0xff51afd7ed558ccd
|
||||
k ^= k >> 33
|
||||
k *= 0xc4ceb9fe1a85ec53
|
||||
k ^= k >> 33
|
||||
return k
|
||||
}
|
||||
|
||||
// Sum128 returns the MurmurHash3 sum of data. It is equivalent to the
|
||||
// following sequence (without the extra burden and the extra allocation):
|
||||
// hasher := New128()
|
||||
// hasher.Write(data)
|
||||
// return hasher.Sum128()
|
||||
func Sum128(data []byte) (h1 uint64, h2 uint64) { return Sum128WithSeed(data, 0) }
|
||||
|
||||
// Sum128WithSeed returns the MurmurHash3 sum of data. It is equivalent to the
|
||||
// following sequence (without the extra burden and the extra allocation):
|
||||
// hasher := New128WithSeed(seed)
|
||||
// hasher.Write(data)
|
||||
// return hasher.Sum128()
|
||||
func Sum128WithSeed(data []byte, seed uint32) (h1 uint64, h2 uint64) {
|
||||
d := digest128{h1: uint64(seed), h2: uint64(seed)}
|
||||
d.seed = seed
|
||||
d.tail = d.bmix(data)
|
||||
d.clen = len(data)
|
||||
return d.Sum128()
|
||||
}
|
||||
91
vendor/github.com/splitio/go-toolkit/v4/provisional/int64cache/cache.go
сгенерированный
поставляемый
Обычный файл
91
vendor/github.com/splitio/go-toolkit/v4/provisional/int64cache/cache.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,91 @@
|
||||
package int64cache
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
"fmt"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Int64Cache is an in-memory TTL & LRU cache
|
||||
type Int64Cache interface {
|
||||
Get(key int64) (int64, error)
|
||||
Set(key int64, value int64) error
|
||||
}
|
||||
|
||||
// Impl implements the LocalCache interface
|
||||
type Impl struct {
|
||||
maxLen int
|
||||
items map[int64]*list.Element
|
||||
lru *list.List
|
||||
mutex sync.Mutex
|
||||
}
|
||||
|
||||
type entry struct {
|
||||
key int64
|
||||
value int64
|
||||
}
|
||||
|
||||
// Get retrieves an item if exist, nil + an error otherwise
|
||||
func (c *Impl) Get(key int64) (int64, error) {
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
node, ok := c.items[key]
|
||||
if !ok {
|
||||
return 0, &Miss{}
|
||||
}
|
||||
|
||||
entry, ok := node.Value.(entry)
|
||||
if !ok {
|
||||
return 0, fmt.Errorf("Invalid data in cache for key %d", key)
|
||||
}
|
||||
|
||||
c.lru.MoveToFront(node)
|
||||
return entry.value, nil
|
||||
}
|
||||
|
||||
// Set adds a new item. Since the cache being full results in removing the LRU element, this method never fails.
|
||||
func (c *Impl) Set(key int64, value int64) error {
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
if node, ok := c.items[key]; ok {
|
||||
c.lru.MoveToFront(node)
|
||||
node.Value = entry{key: key, value: value}
|
||||
} else {
|
||||
// Drop the LRU item on the list before adding a new one.
|
||||
if c.lru.Len() == c.maxLen {
|
||||
entry, ok := c.lru.Back().Value.(entry)
|
||||
if !ok {
|
||||
return fmt.Errorf("Invalid data in list for key %d", key)
|
||||
}
|
||||
key := entry.key
|
||||
delete(c.items, key)
|
||||
c.lru.Remove(c.lru.Back())
|
||||
}
|
||||
|
||||
ptr := c.lru.PushFront(entry{key: key, value: value})
|
||||
c.items[key] = ptr
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewInt64Cache returns a new LocalCache instance of the specified size and TTL
|
||||
func NewInt64Cache(maxSize int) (*Impl, error) {
|
||||
if maxSize <= 0 {
|
||||
return nil, fmt.Errorf("Cache size should be > 0. Is: %d", maxSize)
|
||||
}
|
||||
|
||||
return &Impl{
|
||||
maxLen: maxSize,
|
||||
lru: new(list.List),
|
||||
items: make(map[int64]*list.Element, maxSize),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Miss is a special error indicating the key was not found in the cache
|
||||
type Miss struct {
|
||||
Key int64
|
||||
}
|
||||
|
||||
func (m *Miss) Error() string {
|
||||
return fmt.Sprintf("key %d not found in cache", m.Key)
|
||||
}
|
||||
127
vendor/github.com/splitio/go-toolkit/v4/queuecache/cache.go
сгенерированный
поставляемый
Обычный файл
127
vendor/github.com/splitio/go-toolkit/v4/queuecache/cache.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,127 @@
|
||||
package queuecache
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// RefillError struct to be returned when the refill function panics
|
||||
type RefillError struct {
|
||||
OriginalPanic interface{}
|
||||
}
|
||||
|
||||
func (e *RefillError) Error() string {
|
||||
return "Supplied refilling function panicked. See `.OriginalPanic` property to get panicked content"
|
||||
}
|
||||
|
||||
// MessagesDroppedError is the Error to be returned when messages fail to be added to the queue.
|
||||
type MessagesDroppedError struct {
|
||||
MessagesDropped int
|
||||
}
|
||||
|
||||
func (e *MessagesDroppedError) Error() string {
|
||||
return fmt.Sprintf(
|
||||
"%d messages were dropped. Please report this error as it's most likely a bug in the library",
|
||||
e.MessagesDropped,
|
||||
)
|
||||
}
|
||||
|
||||
// InMemoryQueueCacheOverlay offers an in-memory queue that gets re-populated whenever it runs out of items
|
||||
type InMemoryQueueCacheOverlay struct {
|
||||
maxSize int
|
||||
writeCursor int
|
||||
readCursor int
|
||||
queue []interface{}
|
||||
lock sync.Mutex
|
||||
refillCustom func(count int) ([]interface{}, error)
|
||||
}
|
||||
|
||||
// New creates a new InMemoryQueueCacheOverlay
|
||||
func New(maxSize int, refillFunc func(count int) ([]interface{}, error)) *InMemoryQueueCacheOverlay {
|
||||
return &InMemoryQueueCacheOverlay{
|
||||
queue: make([]interface{}, maxSize),
|
||||
maxSize: maxSize,
|
||||
writeCursor: 0,
|
||||
readCursor: 0,
|
||||
refillCustom: refillFunc,
|
||||
}
|
||||
}
|
||||
|
||||
// Count returns the number of cached items
|
||||
func (i *InMemoryQueueCacheOverlay) Count() int {
|
||||
if i.writeCursor == i.readCursor {
|
||||
return 0
|
||||
} else if i.writeCursor > i.readCursor {
|
||||
return i.writeCursor - i.readCursor
|
||||
}
|
||||
return i.maxSize - (i.readCursor - i.writeCursor)
|
||||
}
|
||||
|
||||
func (i *InMemoryQueueCacheOverlay) write(elem interface{}) error {
|
||||
if ((i.writeCursor + 1) % i.maxSize) == i.readCursor {
|
||||
return errors.New("QUEUE_FULL")
|
||||
}
|
||||
|
||||
i.queue[i.writeCursor] = elem
|
||||
i.writeCursor = (i.writeCursor + 1) % i.maxSize
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *InMemoryQueueCacheOverlay) read() (interface{}, error) {
|
||||
if i.readCursor == i.writeCursor {
|
||||
return nil, errors.New("QUEUE_EMPTY")
|
||||
}
|
||||
|
||||
toReturn := i.queue[i.readCursor]
|
||||
i.readCursor = (i.readCursor + 1) % i.maxSize
|
||||
return toReturn, nil
|
||||
}
|
||||
|
||||
func (i *InMemoryQueueCacheOverlay) refillWrapper(count int) (result []interface{}, err error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
result = nil
|
||||
err = &RefillError{OriginalPanic: r}
|
||||
}
|
||||
}()
|
||||
|
||||
return i.refillCustom(count)
|
||||
|
||||
}
|
||||
|
||||
// Fetch items (will re-populate if necessary)
|
||||
func (i *InMemoryQueueCacheOverlay) Fetch(requestedCount int) ([]interface{}, error) {
|
||||
defer i.lock.Unlock()
|
||||
i.lock.Lock()
|
||||
|
||||
dropped := 0
|
||||
if i.Count() < requestedCount {
|
||||
toAdd, err := i.refillWrapper(i.maxSize - i.Count() - 1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, item := range toAdd {
|
||||
err = i.write(item)
|
||||
if err != nil {
|
||||
dropped++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
toReturn := make([]interface{}, int(math.Min(float64(requestedCount), float64(i.Count()))))
|
||||
for index := 0; index < len(toReturn); index++ {
|
||||
elem, err := i.read()
|
||||
if err != nil {
|
||||
return toReturn[0:index], nil
|
||||
}
|
||||
toReturn[index] = elem
|
||||
}
|
||||
|
||||
if dropped > 0 {
|
||||
return toReturn, &MessagesDroppedError{MessagesDropped: dropped}
|
||||
}
|
||||
return toReturn, nil
|
||||
|
||||
}
|
||||
23
vendor/github.com/splitio/go-toolkit/v4/redis/helpers/helpers.go
сгенерированный
поставляемый
Обычный файл
23
vendor/github.com/splitio/go-toolkit/v4/redis/helpers/helpers.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,23 @@
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/splitio/go-toolkit/v4/redis"
|
||||
)
|
||||
|
||||
const (
|
||||
pong = "PONG"
|
||||
)
|
||||
|
||||
// EnsureConnected pings redis
|
||||
func EnsureConnected(client redis.Client) {
|
||||
res := client.Ping()
|
||||
if res.Err() != nil {
|
||||
panic(fmt.Sprintf("Couldn't connect to redis: %s", res.Err()))
|
||||
}
|
||||
|
||||
if res.String() != pong {
|
||||
panic(fmt.Sprintf("Invalid redis ping response when connecting: %s", res.String()))
|
||||
}
|
||||
}
|
||||
160
vendor/github.com/splitio/go-toolkit/v4/redis/prefixedclient.go
сгенерированный
поставляемый
Обычный файл
160
vendor/github.com/splitio/go-toolkit/v4/redis/prefixedclient.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,160 @@
|
||||
package redis
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// PrefixedRedisClient struct
|
||||
type PrefixedRedisClient struct {
|
||||
Prefix string
|
||||
Client Client
|
||||
}
|
||||
|
||||
// withPrefix adds a prefix to the key if the prefix supplied has a length greater than 0
|
||||
func (p *PrefixedRedisClient) withPrefix(key string) string {
|
||||
if len(p.Prefix) > 0 {
|
||||
return fmt.Sprintf("%s.%s", p.Prefix, key)
|
||||
}
|
||||
return key
|
||||
}
|
||||
|
||||
// withoutPrefix removes the prefix from a key if the prefix has a length greater than 0
|
||||
func (p *PrefixedRedisClient) withoutPrefix(key string) string {
|
||||
if len(p.Prefix) > 0 {
|
||||
return strings.Replace(key, fmt.Sprintf("%s.", p.Prefix), "", 1)
|
||||
}
|
||||
return key
|
||||
}
|
||||
|
||||
// Get wraps around redis get method by adding prefix and returning string and error directly
|
||||
func (p *PrefixedRedisClient) Get(key string) (string, error) {
|
||||
return p.Client.Get(p.withPrefix(key)).ResultString()
|
||||
}
|
||||
|
||||
// Set wraps around redis get method by adding prefix and returning error directly
|
||||
func (p *PrefixedRedisClient) Set(key string, value interface{}, expiration time.Duration) error {
|
||||
return p.Client.Set(p.withPrefix(key), value, expiration).Err()
|
||||
}
|
||||
|
||||
// Keys wraps around redis keys method by adding prefix and returning []string and error directly
|
||||
func (p *PrefixedRedisClient) Keys(pattern string) ([]string, error) {
|
||||
keys, err := p.Client.Keys(p.withPrefix(pattern)).Multi()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
woPrefix := make([]string, len(keys))
|
||||
for index, key := range keys {
|
||||
woPrefix[index] = p.withoutPrefix(key)
|
||||
}
|
||||
return woPrefix, nil
|
||||
|
||||
}
|
||||
|
||||
// Del wraps around redis del method by adding prefix and returning int64 and error directly
|
||||
func (p *PrefixedRedisClient) Del(keys ...string) (int64, error) {
|
||||
prefixedKeys := make([]string, len(keys))
|
||||
for i, k := range keys {
|
||||
prefixedKeys[i] = p.withPrefix(k)
|
||||
}
|
||||
return p.Client.Del(prefixedKeys...).Result()
|
||||
}
|
||||
|
||||
// SMembers returns a slice with all the members of a set
|
||||
func (p *PrefixedRedisClient) SMembers(key string) ([]string, error) {
|
||||
return p.Client.SMembers(p.withPrefix(key)).Multi()
|
||||
}
|
||||
|
||||
// SIsMember returns true if members is in the set
|
||||
func (p *PrefixedRedisClient) SIsMember(key string, member interface{}) bool {
|
||||
return p.Client.SIsMember(p.withPrefix(key), member).Bool()
|
||||
}
|
||||
|
||||
// SAdd adds new members to a set
|
||||
func (p *PrefixedRedisClient) SAdd(key string, members ...interface{}) (int64, error) {
|
||||
return p.Client.SAdd(p.withPrefix(key), members...).Result()
|
||||
}
|
||||
|
||||
// SRem removes members from a set
|
||||
func (p *PrefixedRedisClient) SRem(key string, members ...interface{}) (int64, error) {
|
||||
return p.Client.SRem(p.withPrefix(key), members...).Result()
|
||||
}
|
||||
|
||||
// Exists returns true if a key exists in redis
|
||||
func (p *PrefixedRedisClient) Exists(keys ...string) (int64, error) {
|
||||
prefixedKeys := make([]string, len(keys))
|
||||
for i, k := range keys {
|
||||
prefixedKeys[i] = p.withPrefix(k)
|
||||
}
|
||||
val, err := p.Client.Exists(prefixedKeys...).Result()
|
||||
return val, err
|
||||
}
|
||||
|
||||
// Incr increments a key. Sets it in one if it doesn't exist
|
||||
func (p *PrefixedRedisClient) Incr(key string) (int64, error) {
|
||||
return p.Client.Incr(p.withPrefix(key)).Result()
|
||||
}
|
||||
|
||||
// Decr increments a key. Sets it in one if it doesn't exist
|
||||
func (p *PrefixedRedisClient) Decr(key string) (int64, error) {
|
||||
return p.Client.Decr(p.withPrefix(key)).Result()
|
||||
}
|
||||
|
||||
// RPush insert all the specified values at the tail of the list stored at key
|
||||
func (p *PrefixedRedisClient) RPush(key string, values ...interface{}) (int64, error) {
|
||||
return p.Client.RPush(p.withPrefix(key), values...).Result()
|
||||
}
|
||||
|
||||
// LRange Returns the specified elements of the list stored at key
|
||||
func (p *PrefixedRedisClient) LRange(key string, start, stop int64) ([]string, error) {
|
||||
return p.Client.LRange(p.withPrefix(key), start, stop).Multi()
|
||||
}
|
||||
|
||||
// LTrim Trim an existing list so that it will contain only the specified range of elements specified
|
||||
func (p *PrefixedRedisClient) LTrim(key string, start, stop int64) error {
|
||||
return p.Client.LTrim(p.withPrefix(key), start, stop).Err()
|
||||
}
|
||||
|
||||
// LLen Returns the length of the list stored at key
|
||||
func (p *PrefixedRedisClient) LLen(key string) (int64, error) {
|
||||
return p.Client.LLen(p.withPrefix(key)).Result()
|
||||
}
|
||||
|
||||
// Expire set expiration time for particular key
|
||||
func (p *PrefixedRedisClient) Expire(key string, value time.Duration) bool {
|
||||
return p.Client.Expire(p.withPrefix(key), value).Bool()
|
||||
}
|
||||
|
||||
// TTL for particular key
|
||||
func (p *PrefixedRedisClient) TTL(key string) time.Duration {
|
||||
return p.Client.TTL(p.withPrefix(key)).Duration()
|
||||
}
|
||||
|
||||
// MGet fetchs multiple results
|
||||
func (p *PrefixedRedisClient) MGet(keys []string) ([]interface{}, error) {
|
||||
keysWithPrefix := make([]string, 0)
|
||||
for _, key := range keys {
|
||||
keysWithPrefix = append(keysWithPrefix, p.withPrefix(key))
|
||||
}
|
||||
return p.Client.MGet(keysWithPrefix).MultiInterface()
|
||||
}
|
||||
|
||||
// SCard implements SCard wrapper for redis
|
||||
func (p *PrefixedRedisClient) SCard(key string) (int64, error) {
|
||||
return p.Client.SCard(p.withPrefix(key)).Result()
|
||||
}
|
||||
|
||||
// Eval implements Eval wrapper for redis
|
||||
func (p *PrefixedRedisClient) Eval(script string, keys []string, args ...interface{}) error {
|
||||
return p.Client.Eval(script, keys, args...).Err()
|
||||
}
|
||||
|
||||
// NewPrefixedRedisClient returns a new Prefixed Redis Client
|
||||
func NewPrefixedRedisClient(redisClient Client, prefix string) (*PrefixedRedisClient, error) {
|
||||
return &PrefixedRedisClient{
|
||||
Client: redisClient,
|
||||
Prefix: prefix,
|
||||
}, nil
|
||||
}
|
||||
8
vendor/github.com/splitio/go-toolkit/v4/redis/types.go
сгенерированный
поставляемый
Обычный файл
8
vendor/github.com/splitio/go-toolkit/v4/redis/types.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,8 @@
|
||||
package redis
|
||||
|
||||
import (
|
||||
wredis "github.com/go-redis/redis/v8"
|
||||
)
|
||||
|
||||
// UniversalOptions type used for redis package
|
||||
type UniversalOptions = wredis.UniversalOptions
|
||||
293
vendor/github.com/splitio/go-toolkit/v4/redis/wrapper.go
сгенерированный
поставляемый
Обычный файл
293
vendor/github.com/splitio/go-toolkit/v4/redis/wrapper.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,293 @@
|
||||
package redis
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/go-redis/redis/v8"
|
||||
)
|
||||
|
||||
// ===== Command output / return value types
|
||||
|
||||
// Result generic interface
|
||||
type Result interface {
|
||||
Int() int64
|
||||
String() string
|
||||
Bool() bool
|
||||
Duration() time.Duration
|
||||
Result() (int64, error)
|
||||
ResultString() (string, error)
|
||||
Multi() ([]string, error)
|
||||
MultiInterface() ([]interface{}, error)
|
||||
Err() error
|
||||
}
|
||||
|
||||
// ResultImpl generic interface
|
||||
type ResultImpl struct {
|
||||
value int64
|
||||
valueString string
|
||||
valueBool bool
|
||||
valueDuration time.Duration
|
||||
err error
|
||||
multi []string
|
||||
multiInterface []interface{}
|
||||
}
|
||||
|
||||
// Int implementation
|
||||
func (r *ResultImpl) Int() int64 {
|
||||
return r.value
|
||||
}
|
||||
|
||||
// String implementation
|
||||
func (r *ResultImpl) String() string {
|
||||
return r.valueString
|
||||
}
|
||||
|
||||
// Bool implementation
|
||||
func (r *ResultImpl) Bool() bool {
|
||||
return r.valueBool
|
||||
}
|
||||
|
||||
// Duration implementation
|
||||
func (r *ResultImpl) Duration() time.Duration {
|
||||
return r.valueDuration
|
||||
}
|
||||
|
||||
// Err implementation
|
||||
func (r *ResultImpl) Err() error {
|
||||
return r.err
|
||||
}
|
||||
|
||||
// Result implementation
|
||||
func (r *ResultImpl) Result() (int64, error) {
|
||||
return r.value, r.err
|
||||
}
|
||||
|
||||
// ResultString implementation
|
||||
func (r *ResultImpl) ResultString() (string, error) {
|
||||
return r.valueString, r.err
|
||||
}
|
||||
|
||||
// Multi implementation
|
||||
func (r *ResultImpl) Multi() ([]string, error) {
|
||||
return r.multi, r.err
|
||||
}
|
||||
|
||||
// MultiInterface implementation
|
||||
func (r *ResultImpl) MultiInterface() ([]interface{}, error) {
|
||||
return r.multiInterface, r.err
|
||||
}
|
||||
|
||||
// ====== Client
|
||||
|
||||
// Client interface which specifies the currently used subset of redis operations
|
||||
type Client interface {
|
||||
Del(keys ...string) Result
|
||||
Exists(keys ...string) Result
|
||||
Get(key string) Result
|
||||
Set(key string, value interface{}, expiration time.Duration) Result
|
||||
Ping() Result
|
||||
Keys(pattern string) Result
|
||||
SMembers(key string) Result
|
||||
SIsMember(key string, member interface{}) Result
|
||||
SAdd(key string, members ...interface{}) Result
|
||||
SRem(key string, members ...interface{}) Result
|
||||
Incr(key string) Result
|
||||
Decr(key string) Result
|
||||
RPush(key string, values ...interface{}) Result
|
||||
LRange(key string, start, stop int64) Result
|
||||
LTrim(key string, start, stop int64) Result
|
||||
LLen(key string) Result
|
||||
Expire(key string, value time.Duration) Result
|
||||
TTL(key string) Result
|
||||
MGet(keys []string) Result
|
||||
SCard(key string) Result
|
||||
Eval(script string, keys []string, args ...interface{}) Result
|
||||
}
|
||||
|
||||
// ClientImpl wrapps redis client
|
||||
type ClientImpl struct {
|
||||
wrapped redis.UniversalClient
|
||||
}
|
||||
|
||||
func (c *ClientImpl) wrapResult(result interface{}) Result {
|
||||
if result == nil {
|
||||
return nil
|
||||
}
|
||||
switch v := result.(type) {
|
||||
case *redis.StatusCmd:
|
||||
return &ResultImpl{
|
||||
valueString: v.Val(),
|
||||
err: v.Err(),
|
||||
}
|
||||
case *redis.IntCmd:
|
||||
return &ResultImpl{
|
||||
value: v.Val(),
|
||||
err: v.Err(),
|
||||
}
|
||||
case *redis.StringCmd:
|
||||
return &ResultImpl{
|
||||
valueString: v.Val(),
|
||||
err: v.Err(),
|
||||
}
|
||||
case *redis.StringSliceCmd:
|
||||
return &ResultImpl{
|
||||
err: v.Err(),
|
||||
multi: v.Val(),
|
||||
}
|
||||
case *redis.BoolCmd:
|
||||
return &ResultImpl{
|
||||
valueBool: v.Val(),
|
||||
err: v.Err(),
|
||||
}
|
||||
case *redis.DurationCmd:
|
||||
return &ResultImpl{
|
||||
valueDuration: v.Val(),
|
||||
err: v.Err(),
|
||||
}
|
||||
case *redis.SliceCmd:
|
||||
return &ResultImpl{
|
||||
err: v.Err(),
|
||||
multiInterface: v.Val(),
|
||||
}
|
||||
case *redis.Cmd:
|
||||
return &ResultImpl{
|
||||
err: v.Err(),
|
||||
}
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Del implements Del wrapper for redis
|
||||
func (c *ClientImpl) Del(keys ...string) Result {
|
||||
res := c.wrapped.Del(context.TODO(), keys...)
|
||||
return c.wrapResult(res)
|
||||
}
|
||||
|
||||
// Exists implements Exists wrapper for redis
|
||||
func (c *ClientImpl) Exists(keys ...string) Result {
|
||||
res := c.wrapped.Exists(context.TODO(), keys...)
|
||||
return c.wrapResult(res)
|
||||
}
|
||||
|
||||
// Get implements Get wrapper for redis
|
||||
func (c *ClientImpl) Get(key string) Result {
|
||||
res := c.wrapped.Get(context.TODO(), key)
|
||||
return c.wrapResult(res)
|
||||
}
|
||||
|
||||
// Set implements Set wrapper for redis
|
||||
func (c *ClientImpl) Set(key string, value interface{}, expiration time.Duration) Result {
|
||||
res := c.wrapped.Set(context.TODO(), key, value, expiration)
|
||||
return c.wrapResult(res)
|
||||
}
|
||||
|
||||
// Ping implements Ping wrapper for redis
|
||||
func (c *ClientImpl) Ping() Result {
|
||||
res := c.wrapped.Ping(context.TODO())
|
||||
return c.wrapResult(res)
|
||||
}
|
||||
|
||||
// Keys implements Keys wrapper for redis
|
||||
func (c *ClientImpl) Keys(pattern string) Result {
|
||||
res := c.wrapped.Keys(context.TODO(), pattern)
|
||||
return c.wrapResult(res)
|
||||
}
|
||||
|
||||
// SMembers implements SMembers wrapper for redis
|
||||
func (c *ClientImpl) SMembers(key string) Result {
|
||||
res := c.wrapped.SMembers(context.TODO(), key)
|
||||
return c.wrapResult(res)
|
||||
}
|
||||
|
||||
// SIsMember implements SIsMember wrapper for redis
|
||||
func (c *ClientImpl) SIsMember(key string, member interface{}) Result {
|
||||
res := c.wrapped.SIsMember(context.TODO(), key, member)
|
||||
return c.wrapResult(res)
|
||||
}
|
||||
|
||||
// SAdd implements SAdd wrapper for redis
|
||||
func (c *ClientImpl) SAdd(key string, members ...interface{}) Result {
|
||||
res := c.wrapped.SAdd(context.TODO(), key, members...)
|
||||
return c.wrapResult(res)
|
||||
}
|
||||
|
||||
// SRem implements SRem wrapper for redis
|
||||
func (c *ClientImpl) SRem(key string, members ...interface{}) Result {
|
||||
res := c.wrapped.SRem(context.TODO(), key, members...)
|
||||
return c.wrapResult(res)
|
||||
}
|
||||
|
||||
// Incr implements Incr wrapper for redis
|
||||
func (c *ClientImpl) Incr(key string) Result {
|
||||
res := c.wrapped.Incr(context.TODO(), key)
|
||||
return c.wrapResult(res)
|
||||
}
|
||||
|
||||
// Decr implements Decr wrapper for redis
|
||||
func (c *ClientImpl) Decr(key string) Result {
|
||||
res := c.wrapped.Decr(context.TODO(), key)
|
||||
return c.wrapResult(res)
|
||||
}
|
||||
|
||||
// RPush implements RPush wrapper for redis
|
||||
func (c *ClientImpl) RPush(key string, values ...interface{}) Result {
|
||||
res := c.wrapped.RPush(context.TODO(), key, values...)
|
||||
return c.wrapResult(res)
|
||||
}
|
||||
|
||||
// LRange implements LRange wrapper for redis
|
||||
func (c *ClientImpl) LRange(key string, start, stop int64) Result {
|
||||
res := c.wrapped.LRange(context.TODO(), key, start, stop)
|
||||
return c.wrapResult(res)
|
||||
}
|
||||
|
||||
// LTrim implements LTrim wrapper for redis
|
||||
func (c *ClientImpl) LTrim(key string, start, stop int64) Result {
|
||||
res := c.wrapped.LTrim(context.TODO(), key, start, stop)
|
||||
return c.wrapResult(res)
|
||||
}
|
||||
|
||||
// LLen implements LLen wrapper for redis
|
||||
func (c *ClientImpl) LLen(key string) Result {
|
||||
res := c.wrapped.LLen(context.TODO(), key)
|
||||
return c.wrapResult(res)
|
||||
}
|
||||
|
||||
// Expire implements Expire wrapper for redis
|
||||
func (c *ClientImpl) Expire(key string, value time.Duration) Result {
|
||||
res := c.wrapped.Expire(context.TODO(), key, value)
|
||||
return c.wrapResult(res)
|
||||
}
|
||||
|
||||
// TTL implements TTL wrapper for redis
|
||||
func (c *ClientImpl) TTL(key string) Result {
|
||||
res := c.wrapped.TTL(context.TODO(), key)
|
||||
return c.wrapResult(res)
|
||||
}
|
||||
|
||||
// MGet implements MGet wrapper for redis
|
||||
func (c *ClientImpl) MGet(keys []string) Result {
|
||||
res := c.wrapped.MGet(context.TODO(), keys...)
|
||||
return c.wrapResult(res)
|
||||
}
|
||||
|
||||
// SCard implements SCard wrapper for redis
|
||||
func (c *ClientImpl) SCard(key string) Result {
|
||||
res := c.wrapped.SCard(context.TODO(), key)
|
||||
return c.wrapResult(res)
|
||||
}
|
||||
|
||||
// Eval implements Eval wrapper for redis
|
||||
func (c *ClientImpl) Eval(script string, keys []string, args ...interface{}) Result {
|
||||
res := c.wrapped.Eval(context.TODO(), script, keys, args...)
|
||||
return c.wrapResult(res)
|
||||
}
|
||||
|
||||
// NewClient returns new client implementation
|
||||
func NewClient(options *UniversalOptions) (Client, error) {
|
||||
return &ClientImpl{
|
||||
wrapped: redis.NewUniversalClient(options),
|
||||
}, nil
|
||||
}
|
||||
31
vendor/github.com/splitio/go-toolkit/v4/sse/errors.go
сгенерированный
поставляемый
Обычный файл
31
vendor/github.com/splitio/go-toolkit/v4/sse/errors.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,31 @@
|
||||
package sse
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
// ErrNotIdle is the error tor eturn when Do() gets called on an already running client.
|
||||
var ErrNotIdle = errors.New("sse client already running")
|
||||
|
||||
// ErrReadingStream is the error to return when channel event channel is closed because of an error reading the stream
|
||||
var ErrReadingStream = errors.New("sse channel closed")
|
||||
|
||||
// ErrTimeout is the error to return when keepalive timeout is exceeded
|
||||
var ErrTimeout = errors.New("timeout exceeeded")
|
||||
|
||||
// ErrConnectionFailed contains a nested error
|
||||
type ErrConnectionFailed struct {
|
||||
wrapped error
|
||||
}
|
||||
|
||||
// Error returns the error as a string
|
||||
func (e *ErrConnectionFailed) Error() string {
|
||||
return "error connecting: " + e.wrapped.Error()
|
||||
}
|
||||
|
||||
// Unwrap returns the wrapped error
|
||||
func (e *ErrConnectionFailed) Unwrap() error {
|
||||
return e.wrapped
|
||||
}
|
||||
|
||||
var _ error = &ErrConnectionFailed{}
|
||||
120
vendor/github.com/splitio/go-toolkit/v4/sse/event.go
сгенерированный
поставляемый
Обычный файл
120
vendor/github.com/splitio/go-toolkit/v4/sse/event.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,120 @@
|
||||
package sse
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
const (
|
||||
sseDelimiter = ":"
|
||||
sseData = "data"
|
||||
sseEvent = "event"
|
||||
sseID = "id"
|
||||
sseRetry = "retry"
|
||||
)
|
||||
|
||||
// RawEvent interface contains the methods that expose the incoming SSE properties
|
||||
type RawEvent interface {
|
||||
ID() string
|
||||
Event() string
|
||||
Data() string
|
||||
Retry() int64
|
||||
IsError() bool
|
||||
IsEmpty() bool
|
||||
}
|
||||
|
||||
// RawEventImpl represents an incoming SSE event
|
||||
type RawEventImpl struct {
|
||||
id string
|
||||
event string
|
||||
data string
|
||||
retry int64
|
||||
}
|
||||
|
||||
// ID returns the event id
|
||||
func (r *RawEventImpl) ID() string { return r.id }
|
||||
|
||||
// Event returns the event type
|
||||
func (r *RawEventImpl) Event() string { return r.event }
|
||||
|
||||
// Data returns the event associated data
|
||||
func (r *RawEventImpl) Data() string { return r.data }
|
||||
|
||||
// Retry returns the expected retry time
|
||||
func (r *RawEventImpl) Retry() int64 { return r.retry }
|
||||
|
||||
// IsError returns true if the message is an error
|
||||
func (r *RawEventImpl) IsError() bool { return r.event == "error" }
|
||||
|
||||
// IsEmpty returns true if the event contains no id, event type and data
|
||||
func (r *RawEventImpl) IsEmpty() bool { return r.event == "" && r.id == "" && r.data == "" }
|
||||
|
||||
// EventBuilder interface
|
||||
type EventBuilder interface {
|
||||
AddLine(string)
|
||||
Build() *RawEventImpl
|
||||
}
|
||||
|
||||
// EventBuilderImpl implenets the EventBuilder interface. Used to parse incoming event lines
|
||||
type EventBuilderImpl struct {
|
||||
includesComment bool
|
||||
mutex sync.Mutex
|
||||
lines []string
|
||||
}
|
||||
|
||||
// AddLine adds a new line belonging to the currently being processed event
|
||||
func (b *EventBuilderImpl) AddLine(line string) {
|
||||
if strings.HasPrefix(line, sseDelimiter) {
|
||||
// Ignore comments
|
||||
return
|
||||
}
|
||||
|
||||
b.mutex.Lock()
|
||||
defer b.mutex.Unlock()
|
||||
b.lines = append(b.lines, line)
|
||||
}
|
||||
|
||||
// Build processes all the added lines and builds the event
|
||||
func (b *EventBuilderImpl) Build() *RawEventImpl {
|
||||
b.mutex.Lock()
|
||||
defer b.mutex.Unlock()
|
||||
|
||||
if len(b.lines) == 0 { // Empty event
|
||||
return &RawEventImpl{}
|
||||
}
|
||||
|
||||
e := &RawEventImpl{}
|
||||
for _, line := range b.lines {
|
||||
splitted := strings.SplitN(line, sseDelimiter, 2)
|
||||
if len(splitted) != 2 {
|
||||
// TODO: log invalid line.
|
||||
continue
|
||||
}
|
||||
|
||||
switch splitted[0] {
|
||||
case sseID:
|
||||
e.id = strings.TrimSpace(splitted[1])
|
||||
case sseData:
|
||||
e.data = strings.TrimSpace(splitted[1])
|
||||
case sseEvent:
|
||||
e.event = strings.TrimSpace(splitted[1])
|
||||
case sseRetry:
|
||||
e.retry, _ = strconv.ParseInt(strings.TrimSpace(splitted[1]), 10, 64)
|
||||
}
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// Reset clears the lines accepted
|
||||
func (b *EventBuilderImpl) Reset() {
|
||||
b.mutex.Lock()
|
||||
defer b.mutex.Unlock()
|
||||
b.lines = []string{}
|
||||
}
|
||||
|
||||
// NewEventBuilder constructs a new event builder
|
||||
func NewEventBuilder() *EventBuilderImpl {
|
||||
return &EventBuilderImpl{lines: []string{}}
|
||||
}
|
||||
174
vendor/github.com/splitio/go-toolkit/v4/sse/sse.go
сгенерированный
поставляемый
Обычный файл
174
vendor/github.com/splitio/go-toolkit/v4/sse/sse.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,174 @@
|
||||
package sse
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/splitio/go-toolkit/v4/logging"
|
||||
"github.com/splitio/go-toolkit/v4/struct/traits/lifecycle"
|
||||
)
|
||||
|
||||
const (
|
||||
statusIdle = iota
|
||||
statusRunning
|
||||
statusShuttingDown
|
||||
|
||||
endOfLineChar = '\n'
|
||||
endOfLineStr = "\n"
|
||||
)
|
||||
|
||||
// Client struct
|
||||
type Client struct {
|
||||
lifecycle lifecycle.Manager
|
||||
url string
|
||||
client http.Client
|
||||
timeout time.Duration
|
||||
logger logging.LoggerInterface
|
||||
}
|
||||
|
||||
// NewClient creates new SSEClient
|
||||
func NewClient(url string, timeout int, logger logging.LoggerInterface) (*Client, error) {
|
||||
if timeout < 1 {
|
||||
return nil, errors.New("Timeout should be higher than 0")
|
||||
}
|
||||
|
||||
client := &Client{
|
||||
url: url,
|
||||
client: http.Client{},
|
||||
timeout: time.Duration(timeout) * time.Second,
|
||||
logger: logger,
|
||||
}
|
||||
client.lifecycle.Setup()
|
||||
return client, nil
|
||||
}
|
||||
|
||||
func (l *Client) readEvents(in *bufio.Reader, out chan<- RawEvent) {
|
||||
eventBuilder := NewEventBuilder()
|
||||
for {
|
||||
line, err := in.ReadString(endOfLineChar)
|
||||
l.logger.Debug("Incoming SSE line: ", line)
|
||||
if err != nil {
|
||||
if l.lifecycle.IsRunning() { // If it's supposed to be running, log an error
|
||||
l.logger.Error(err)
|
||||
}
|
||||
close(out)
|
||||
return
|
||||
}
|
||||
if line != endOfLineStr {
|
||||
eventBuilder.AddLine(line)
|
||||
continue
|
||||
|
||||
}
|
||||
l.logger.Debug("Building SSE event")
|
||||
if event := eventBuilder.Build(); event != nil {
|
||||
out <- event
|
||||
}
|
||||
eventBuilder.Reset()
|
||||
}
|
||||
}
|
||||
|
||||
// Do starts streaming
|
||||
func (l *Client) Do(params map[string]string, callback func(e RawEvent)) error {
|
||||
|
||||
if !l.lifecycle.BeginInitialization() {
|
||||
return ErrNotIdle
|
||||
}
|
||||
|
||||
activeGoroutines := sync.WaitGroup{}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer func() {
|
||||
l.logger.Info("SSE streaming exiting")
|
||||
cancel()
|
||||
activeGoroutines.Wait()
|
||||
l.lifecycle.ShutdownComplete()
|
||||
}()
|
||||
|
||||
req, err := l.buildCancellableRequest(ctx, params)
|
||||
if err != nil {
|
||||
return &ErrConnectionFailed{wrapped: fmt.Errorf("error building request: %w", err)}
|
||||
}
|
||||
|
||||
resp, err := l.client.Do(req)
|
||||
if err != nil {
|
||||
return &ErrConnectionFailed{wrapped: fmt.Errorf("error issuing request: %w", err)}
|
||||
}
|
||||
if resp.StatusCode != 200 {
|
||||
return &ErrConnectionFailed{wrapped: fmt.Errorf("sse request status code: %d", resp.StatusCode)}
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if !l.lifecycle.InitializationComplete() {
|
||||
return nil
|
||||
}
|
||||
|
||||
reader := bufio.NewReader(resp.Body)
|
||||
eventChannel := make(chan RawEvent, 1000)
|
||||
go l.readEvents(reader, eventChannel)
|
||||
|
||||
// Create timeout timer in case SSE dont receive notifications or keepalive messages
|
||||
keepAliveTimer := time.NewTimer(l.timeout)
|
||||
defer keepAliveTimer.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-l.lifecycle.ShutdownRequested():
|
||||
l.logger.Info("Shutting down listener")
|
||||
return nil
|
||||
case event, ok := <-eventChannel:
|
||||
keepAliveTimer.Reset(l.timeout)
|
||||
if !ok {
|
||||
if l.lifecycle.IsRunning() {
|
||||
return ErrReadingStream
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if event.IsEmpty() {
|
||||
continue // don't forward empty/comment events
|
||||
}
|
||||
activeGoroutines.Add(1)
|
||||
go func() {
|
||||
defer activeGoroutines.Done()
|
||||
callback(event)
|
||||
}()
|
||||
case <-keepAliveTimer.C: // Timeout
|
||||
l.logger.Warning("SSE idle timeout.")
|
||||
l.lifecycle.AbnormalShutdown()
|
||||
return ErrTimeout
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Shutdown stops SSE
|
||||
func (l *Client) Shutdown(blocking bool) {
|
||||
if !l.lifecycle.BeginShutdown() {
|
||||
l.logger.Info("SSE client stopped or shutdown in progress. Ignoring.")
|
||||
return
|
||||
}
|
||||
|
||||
if blocking {
|
||||
l.lifecycle.AwaitShutdownComplete()
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Client) buildCancellableRequest(ctx context.Context, params map[string]string) (*http.Request, error) {
|
||||
req, err := http.NewRequest("GET", l.url, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error instantiating request: %w", err)
|
||||
}
|
||||
req = req.WithContext(ctx)
|
||||
query := req.URL.Query()
|
||||
|
||||
for key, value := range params {
|
||||
query.Add(key, value)
|
||||
}
|
||||
req.URL.RawQuery = query.Encode()
|
||||
req.Header.Set("Accept", "text/event-stream")
|
||||
return req, nil
|
||||
}
|
||||
108
vendor/github.com/splitio/go-toolkit/v4/struct/traits/lifecycle/lifecycle.go
сгенерированный
поставляемый
Обычный файл
108
vendor/github.com/splitio/go-toolkit/v4/struct/traits/lifecycle/lifecycle.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,108 @@
|
||||
package lifecycle
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
// Status constants
|
||||
const (
|
||||
StatusIdle = iota
|
||||
StatusStarting
|
||||
StatusInitializationCancelled
|
||||
StatusRunning
|
||||
StatusStopping
|
||||
)
|
||||
|
||||
// Status type alias
|
||||
type Status = int32
|
||||
|
||||
// Manager is a trait to be embedded in structs that manage the lifecycle of goroutines.
|
||||
// The trait enables the struct to easily switch between states and await proper shutdown
|
||||
type Manager struct {
|
||||
status int32
|
||||
c *sync.Cond
|
||||
shutdown chan struct{}
|
||||
}
|
||||
|
||||
// Setup must be called in the struct constructor
|
||||
func (l *Manager) Setup() {
|
||||
l.c = sync.NewCond(&sync.Mutex{})
|
||||
l.shutdown = make(chan struct{}, 1)
|
||||
}
|
||||
|
||||
// BeginInitialization should be called in the .Start() method (or whichever begins the async work)
|
||||
func (l *Manager) BeginInitialization() bool {
|
||||
return atomic.CompareAndSwapInt32(&l.status, StatusIdle, StatusStarting)
|
||||
}
|
||||
|
||||
// InitializationComplete should be called just prior to the `go ...` directive starting the async work
|
||||
func (l *Manager) InitializationComplete() bool {
|
||||
if !atomic.CompareAndSwapInt32(&l.status, StatusStarting, StatusRunning) {
|
||||
atomic.StoreInt32(&l.status, StatusStopping)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// BeginShutdown should be called on the .Stop() method or whichever makes a request for the async work to stop
|
||||
func (l *Manager) BeginShutdown() bool {
|
||||
// If we're currently initializing but not yet running, just change the status.
|
||||
if atomic.CompareAndSwapInt32(&l.status, StatusStarting, StatusInitializationCancelled) {
|
||||
return true
|
||||
}
|
||||
|
||||
if !atomic.CompareAndSwapInt32(&l.status, StatusRunning, StatusStopping) {
|
||||
return false
|
||||
}
|
||||
|
||||
l.shutdown <- struct{}{}
|
||||
return true
|
||||
}
|
||||
|
||||
// ShutdownComplete should be called just before the goroutine exits. (ie: it should be the FIRST deferred func)
|
||||
func (l *Manager) ShutdownComplete() {
|
||||
// clean up status channel in case a Stop occurred while the task was exiting on its own
|
||||
select {
|
||||
case <-l.shutdown:
|
||||
default:
|
||||
}
|
||||
|
||||
l.c.L.Lock()
|
||||
atomic.StoreInt32(&l.status, StatusIdle)
|
||||
l.c.Broadcast()
|
||||
l.c.L.Unlock()
|
||||
}
|
||||
|
||||
// AwaitShutdownComplete can be called in case you need to join against the goroutine's end
|
||||
func (l *Manager) AwaitShutdownComplete() {
|
||||
for {
|
||||
l.c.L.Lock()
|
||||
if atomic.LoadInt32(&l.status) == StatusIdle {
|
||||
l.c.L.Unlock()
|
||||
return
|
||||
}
|
||||
l.c.Wait()
|
||||
l.c.L.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
// ShutdownRequested should be queried in a select statement, which should react by terminating the goroutine
|
||||
func (l *Manager) ShutdownRequested() <-chan struct{} {
|
||||
return l.shutdown
|
||||
}
|
||||
|
||||
// AbnormalShutdown should be called when the goroutine exits without Stop being called.
|
||||
func (l *Manager) AbnormalShutdown() {
|
||||
atomic.CompareAndSwapInt32(&l.status, StatusRunning, StatusStopping)
|
||||
}
|
||||
|
||||
// Status Returns the current status as an int32 constant
|
||||
func (l *Manager) Status() int32 {
|
||||
return atomic.LoadInt32(&l.status)
|
||||
}
|
||||
|
||||
// IsRunning returns true if the BG work is still going on
|
||||
func (l *Manager) IsRunning() bool {
|
||||
return atomic.LoadInt32(&l.status) == StatusRunning
|
||||
}
|
||||
41
vendor/github.com/splitio/go-toolkit/v4/sync/atomicbool.go
сгенерированный
поставляемый
Обычный файл
41
vendor/github.com/splitio/go-toolkit/v4/sync/atomicbool.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,41 @@
|
||||
package sync
|
||||
|
||||
import (
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
const (
|
||||
falseValue = 0
|
||||
trueValue = 1
|
||||
)
|
||||
|
||||
type AtomicBool struct {
|
||||
value uint32
|
||||
}
|
||||
|
||||
func (b *AtomicBool) Set() {
|
||||
atomic.StoreUint32(&b.value, trueValue)
|
||||
}
|
||||
|
||||
func (b *AtomicBool) Unset() {
|
||||
atomic.StoreUint32(&b.value, falseValue)
|
||||
}
|
||||
|
||||
func (b *AtomicBool) IsSet() bool {
|
||||
return atomic.LoadUint32(&b.value) == trueValue
|
||||
}
|
||||
|
||||
func (b *AtomicBool) TestAndSet() bool {
|
||||
return atomic.CompareAndSwapUint32(&b.value, falseValue, trueValue)
|
||||
}
|
||||
|
||||
func (b *AtomicBool) TestAndClear() bool {
|
||||
return atomic.CompareAndSwapUint32(&b.value, trueValue, falseValue)
|
||||
}
|
||||
|
||||
func NewAtomicBool(initialValue bool) *AtomicBool {
|
||||
if initialValue {
|
||||
return &AtomicBool{value: trueValue}
|
||||
}
|
||||
return &AtomicBool{}
|
||||
}
|
||||
181
vendor/github.com/splitio/go-toolkit/v4/workerpool/workerpool.go
сгенерированный
поставляемый
Обычный файл
181
vendor/github.com/splitio/go-toolkit/v4/workerpool/workerpool.go
сгенерированный
поставляемый
Обычный файл
@@ -0,0 +1,181 @@
|
||||
package workerpool
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/splitio/go-toolkit/v4/logging"
|
||||
"github.com/splitio/go-toolkit/v4/struct/traits/lifecycle"
|
||||
)
|
||||
|
||||
const (
|
||||
workerSignalStop = iota
|
||||
)
|
||||
|
||||
// WorkerAdmin struct handles multiple worker execution, popping jobs from a single queue
|
||||
type WorkerAdmin struct {
|
||||
queue chan interface{}
|
||||
mutex sync.RWMutex
|
||||
//signals map[string]chan int
|
||||
workers map[string]*workerWrapper
|
||||
logger logging.LoggerInterface
|
||||
}
|
||||
|
||||
// Worker interface should be implemented by concrete workers that will perform the actual job
|
||||
type Worker interface {
|
||||
// Name should return a unique identifier for a particular worker
|
||||
Name() string
|
||||
// DoWork should receive a message, and perform the actual work, only an error should be returned
|
||||
DoWork(message interface{}) error
|
||||
// OnError will be called if DoWork returns an error != nil
|
||||
OnError(e error)
|
||||
// Cleanup will be called when the worker is shutting down
|
||||
Cleanup() error
|
||||
// FailureTime should return the amount of time the worker should wait after resuming work if an error occurs
|
||||
FailureTime() int64
|
||||
}
|
||||
|
||||
type workerWrapper struct {
|
||||
w Worker
|
||||
lifecycle lifecycle.Manager
|
||||
queue <-chan interface{}
|
||||
logger logging.LoggerInterface
|
||||
}
|
||||
|
||||
func (w *workerWrapper) Start() {
|
||||
if !w.lifecycle.BeginInitialization() {
|
||||
w.logger.Error(fmt.Sprintf("initialization of worker '%s' aborted. Worker not idle.", w.w.Name()))
|
||||
return
|
||||
}
|
||||
go w.do()
|
||||
}
|
||||
|
||||
func (w *workerWrapper) Stop(blocking bool) {
|
||||
if !w.lifecycle.BeginShutdown() {
|
||||
w.logger.Error(fmt.Sprintf("shutodwn of worker '%s' aborted. Worker not running.", w.w.Name()))
|
||||
return
|
||||
}
|
||||
|
||||
if blocking {
|
||||
w.lifecycle.AwaitShutdownComplete()
|
||||
}
|
||||
}
|
||||
|
||||
func (w *workerWrapper) do() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
w.logger.Error(fmt.Sprintf(
|
||||
"Worker %s is panicking with the following error \"%s\" and will be shutted down.",
|
||||
w.w.Name(),
|
||||
r,
|
||||
))
|
||||
w.lifecycle.AbnormalShutdown()
|
||||
}
|
||||
}()
|
||||
defer w.lifecycle.ShutdownComplete()
|
||||
defer w.w.Cleanup()
|
||||
if !w.lifecycle.InitializationComplete() {
|
||||
return
|
||||
}
|
||||
for {
|
||||
select {
|
||||
case <-w.lifecycle.ShutdownRequested():
|
||||
return
|
||||
case msg := <-w.queue:
|
||||
if err := w.w.DoWork(msg); err != nil {
|
||||
w.w.OnError(err)
|
||||
time.Sleep(time.Duration(w.w.FailureTime()) * time.Millisecond)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func newWorkerWraper(w Worker, logger logging.LoggerInterface, queue <-chan interface{}) *workerWrapper {
|
||||
worker := &workerWrapper{w: w, queue: queue, logger: logger}
|
||||
worker.lifecycle.Setup()
|
||||
worker.Start()
|
||||
return worker
|
||||
}
|
||||
|
||||
// AddWorker registers a new worker in the admin
|
||||
func (a *WorkerAdmin) AddWorker(w Worker) {
|
||||
if w == nil {
|
||||
a.logger.Error("AddWorker called with nil")
|
||||
return
|
||||
}
|
||||
a.mutex.Lock()
|
||||
defer a.mutex.Unlock()
|
||||
a.workers[w.Name()] = newWorkerWraper(w, a.logger, a.queue)
|
||||
}
|
||||
|
||||
// QueueMessage adds a new message that will be popped by a worker and processed
|
||||
func (a *WorkerAdmin) QueueMessage(m interface{}) bool {
|
||||
if m == nil {
|
||||
a.logger.Warning("Nil message not added to queue")
|
||||
return false
|
||||
}
|
||||
select {
|
||||
case a.queue <- m:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// StopWorker ends the worker's event loop, preventing it from picking further jobs
|
||||
func (a *WorkerAdmin) StopWorker(name string, blocking bool) error {
|
||||
a.mutex.Lock()
|
||||
defer a.mutex.Unlock()
|
||||
w, ok := a.workers[name]
|
||||
if !ok {
|
||||
return fmt.Errorf("Worker %s doesn't exist, hence it cannot be stopped", name)
|
||||
}
|
||||
|
||||
w.Stop(blocking)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// StopAll ends all worker's event loops
|
||||
func (a *WorkerAdmin) StopAll(blocking bool) error {
|
||||
wg := sync.WaitGroup{}
|
||||
a.mutex.Lock()
|
||||
defer a.mutex.Unlock()
|
||||
for _, w := range a.workers {
|
||||
if w != nil {
|
||||
wg.Add(1)
|
||||
go func(current *workerWrapper) {
|
||||
current.Stop(true)
|
||||
wg.Done()
|
||||
}(w)
|
||||
}
|
||||
}
|
||||
|
||||
if blocking {
|
||||
wg.Wait()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// QueueSize returns the current queue size
|
||||
func (a *WorkerAdmin) QueueSize() int {
|
||||
return len(a.queue)
|
||||
}
|
||||
|
||||
// IsWorkerRunning returns true if the worker exists and is currently running
|
||||
func (a *WorkerAdmin) IsWorkerRunning(name string) bool {
|
||||
a.mutex.RLock()
|
||||
defer a.mutex.RUnlock()
|
||||
x, ok := a.workers[name]
|
||||
return ok && x.lifecycle.IsRunning()
|
||||
}
|
||||
|
||||
// NewWorkerAdmin instantiates a new WorkerAdmin and returns a pointer to it.
|
||||
func NewWorkerAdmin(queueSize int, logger logging.LoggerInterface) *WorkerAdmin {
|
||||
return &WorkerAdmin{
|
||||
workers: make(map[string]*workerWrapper, 0),
|
||||
logger: logger,
|
||||
queue: make(chan interface{}, queueSize),
|
||||
}
|
||||
}
|
||||
Ссылка в новой задаче
Block a user