github.com/mundipagg/tracer-splunk-writer@v1.0.6/buffer/buffer.go (about) 1 package buffer 2 3 import ( 4 "fmt" 5 "sync" 6 "time" 7 ) 8 9 const ( 10 DefaultCapacity = 100 11 DefaultOnWait = 100 12 DefaultExpiration = 60000 13 DefaultBackoff = 10000 14 ) 15 16 type Buffer interface { 17 Write(item interface{}) 18 } 19 20 type buffer struct { 21 sync.Locker 22 cap int 23 size int 24 expiration time.Duration 25 chunks chan entry 26 items []interface{} 27 backoff time.Duration 28 } 29 30 func (b *buffer) Write(item interface{}) { 31 b.Lock() 32 defer b.Unlock() 33 b.items[b.size] = item 34 b.size++ 35 if b.size >= b.cap { 36 b.clear() 37 } 38 } 39 40 func (b *buffer) clear() { 41 if b.size > 0 { 42 events := b.items[:b.size] 43 b.size = 0 44 b.items = make([]interface{}, b.cap) 45 go func() { 46 b.chunks <- entry{ 47 items: events, 48 retries: cap(b.chunks), 49 } 50 }() 51 } 52 } 53 54 func (b *buffer) watcher() { 55 defer func() { 56 err := recover() 57 if err != nil { 58 fmt.Printf("%v\n", err) 59 } 60 }() 61 for { 62 time.Sleep(b.expiration) 63 b.Lock() 64 b.clear() 65 b.Unlock() 66 } 67 } 68 69 type Config struct { 70 Cap int 71 OnWait int 72 Expiration time.Duration 73 BackOff time.Duration 74 OnOverflow func([]interface{}) error 75 } 76 77 type entry struct { 78 items []interface{} 79 retries int 80 } 81 82 func New(c Config) Buffer { 83 if c.Cap == 0 { 84 c.Cap = DefaultCapacity 85 } 86 if c.Expiration == 0 { 87 c.Expiration = DefaultExpiration 88 } 89 if c.BackOff == 0 { 90 c.BackOff = DefaultBackoff 91 } 92 if c.OnWait == 0 { 93 c.OnWait = DefaultOnWait 94 } 95 96 b := &buffer{ 97 Locker: &sync.Mutex{}, 98 size: 0, 99 cap: c.Cap, 100 expiration: c.Expiration, 101 chunks: make(chan entry, c.OnWait), 102 items: make([]interface{}, c.Cap), 103 backoff: c.BackOff, 104 } 105 go b.watcher() 106 go b.consumer(c) 107 return b 108 } 109 110 func (b *buffer) consumer(c Config) { 111 defer func() { 112 err := recover() 113 if err != nil { 114 fmt.Printf("%v\n", err) 115 } 116 }() 117 for events := range b.chunks { 118 go func(events entry) { 119 err := c.OnOverflow(events.items) 120 if err != nil { 121 go func(events entry) { 122 events.retries-- 123 if events.retries >= 0 { 124 time.Sleep(b.backoff) 125 b.chunks <- events 126 } 127 }(events) 128 } 129 }(events) 130 } 131 }