github.com/grafana/synthetic-monitoring-agent@v0.64.2/internal/pusher/v2/options.go (about)

     1  package v2
     2  
     3  import (
     4  	"context"
     5  	"sync"
     6  	"time"
     7  
     8  	"github.com/rs/zerolog"
     9  
    10  	"github.com/grafana/synthetic-monitoring-agent/internal/model"
    11  	"github.com/grafana/synthetic-monitoring-agent/internal/pusher"
    12  )
    13  
    14  const (
    15  	// This one is tricky. Most of the time, buffers are 1-2KiB in size. Are we allocating too much by default?
    16  	defaultBufferCapacity = 4096
    17  )
    18  
    19  var (
    20  	defaultPusherOptions = pusherOptions{
    21  		// Max bytes to send on a single push request
    22  		// Both loki and mimir can take at least 5MiB.
    23  		maxPushBytes: 64 * 1024,
    24  
    25  		// Max bytes to hold queued
    26  		// 0: Disabled
    27  		maxQueuedBytes: 1024 * 1024,
    28  
    29  		// Max time to keep an item in the queue before it's discarded
    30  		// Note that loki/mimir will probably reject data older than 1h anyway.
    31  		// 0: Disabled
    32  		maxQueuedTime: time.Hour,
    33  
    34  		// Max number of retries in case of network(retriable) error.
    35  		// Ideally we make this big and have data expired with the above limits.
    36  		maxRetries: 50,
    37  
    38  		// Backoff between retries. Doubling at each attempt.
    39  		minBackoff: time.Millisecond * 50,
    40  		maxBackoff: time.Second * 30,
    41  
    42  		// Max time a tenant pusher is active. This is useful to cause the tenant info
    43  		// to be refreshed.
    44  		maxLifetime: 2 * time.Hour,
    45  
    46  		// Apply a jitter of 25% of maxLifetime.
    47  		maxLifetimeJitter: 0.25,
    48  
    49  		// How long without receiving check results until a tenant pusher is stopped.
    50  		// This is to cleanup tenants that don't have active checks anymore.
    51  		// Set it to a value higher than the max interval between a single check run.
    52  		maxIdleTime: 65 * time.Minute,
    53  
    54  		// How long to wait before refreshing tenant due to an error.
    55  		tenantDelay: 10 * time.Second,
    56  
    57  		// How long to stop pushing new metrics when a 429 (Too Many Requests) is received.
    58  		waitPeriod: time.Minute,
    59  
    60  		// How long to discard metrics when a fatal error is encountered.
    61  		discardPeriod: 15 * time.Minute,
    62  
    63  		pool: bufferPool{
    64  			inner: &sync.Pool{
    65  				New: func() any {
    66  					buf := make([]byte, 0, defaultBufferCapacity)
    67  					return &buf
    68  				},
    69  			},
    70  		},
    71  	}
    72  )
    73  
    74  type pusherOptions struct {
    75  	maxPushBytes      uint64        // Max bytes to send on a single push request
    76  	maxQueuedBytes    uint64        // Max bytes to hold queued
    77  	maxQueuedTime     time.Duration // Max time an item can be queued until it expires
    78  	maxRetries        int           // Max retries for a push
    79  	minBackoff        time.Duration
    80  	maxBackoff        time.Duration
    81  	maxLifetime       time.Duration // How long to run a tenant pusher before re-fetching the tenant
    82  	maxLifetimeJitter float64       // Jitter to apply to maxLifetime, expressed as % of maxLifetime
    83  	maxIdleTime       time.Duration // How long without receiving pushes until a tenant pusher is cleaned up
    84  	tenantDelay       time.Duration // How long to wait between GetTenant calls.
    85  	waitPeriod        time.Duration // How long to wait in case of errors
    86  	discardPeriod     time.Duration // How long to discard metrics when a fatal error is encountered.
    87  	logger            zerolog.Logger
    88  	metrics           pusher.Metrics
    89  	pool              bufferPool
    90  }
    91  
    92  func (o pusherOptions) withTenant(id model.GlobalID) pusherOptions {
    93  	localID, regionID := model.GetLocalAndRegionIDs(id)
    94  	o.logger = o.logger.With().Int("region", regionID).Int64("tenant", localID).Logger()
    95  	o.metrics = o.metrics.WithTenant(localID, regionID)
    96  
    97  	return o
    98  }
    99  
   100  func (o pusherOptions) withType(t string) pusherOptions {
   101  	o.logger = o.logger.With().Str("type", t).Logger()
   102  	o.metrics = o.metrics.WithType(t)
   103  
   104  	return o
   105  }
   106  
   107  func (o pusherOptions) retriesCounter() retriesCounter {
   108  	return retriesCounter{
   109  		left: o.maxRetries,
   110  		max:  o.maxRetries,
   111  	}
   112  }
   113  
   114  func (o pusherOptions) backOffer() backoffer {
   115  	return backoffer{
   116  		min: o.minBackoff,
   117  		max: o.maxBackoff,
   118  	}
   119  }
   120  
   121  type bufferPool struct {
   122  	inner *sync.Pool
   123  }
   124  
   125  func (p *bufferPool) get() *[]byte {
   126  	if p == nil || p.inner == nil {
   127  		return nil
   128  	}
   129  
   130  	return p.inner.Get().(*[]byte)
   131  }
   132  
   133  func (p *bufferPool) put(buf *[]byte) {
   134  	if p == nil || p.inner == nil {
   135  		return
   136  	}
   137  
   138  	p.inner.Put(buf)
   139  }
   140  
   141  func (p *bufferPool) returnAll(records []queueEntry) (size uint64) {
   142  	for _, rec := range records {
   143  		size += uint64(len(*rec.data))
   144  		p.put(rec.data)
   145  		rec.data = nil // Ensure it can't be re-used after return.
   146  	}
   147  
   148  	return size
   149  }
   150  
   151  type retriesCounter struct {
   152  	left, max int
   153  }
   154  
   155  func (c *retriesCounter) retry() bool {
   156  	if c.left <= 0 {
   157  		return false
   158  	}
   159  
   160  	c.left--
   161  
   162  	return true
   163  }
   164  
   165  func (c *retriesCounter) reset() {
   166  	c.left = c.max
   167  }
   168  
   169  type backoffer struct {
   170  	last, min, max time.Duration
   171  }
   172  
   173  func (b *backoffer) wait(ctx context.Context) error {
   174  	if b.last < b.min {
   175  		b.last = b.min
   176  	} else {
   177  		b.last = min(2*b.last, b.max)
   178  	}
   179  
   180  	return sleepCtx(ctx, b.last)
   181  }
   182  
   183  func (b *backoffer) reset() {
   184  	b.last = 0
   185  }
   186  
   187  func sleepCtx(ctx context.Context, d time.Duration) error {
   188  	t := time.NewTimer(d)
   189  	select {
   190  	case <-ctx.Done():
   191  		t.Stop()
   192  		return ctx.Err()
   193  	case <-t.C:
   194  		return nil
   195  	}
   196  }