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  }