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 }