Files
insights-go/backoff.go
T
Eli ReismanandGitHub 8a6e9ae51e feat: refactor internal event submission pipeline and test suites (#151)
* intitial refactor:

- Add Context-based graceful shutdown and drain phase for enqueued requests
- Use buffered channels vs. mutex for concurrency mgmt.
- Use atomic.Pointer vs. locks for FF mgmt
- Use Callback ifc vs. lossy internal tracking for unit test result validation
- Add new APIs mirroring current that accept caller's Context to control
  operation timeouts

* expose batch submission timeout with default equiv to old hardcoded timeout

* restructure event submission and batching pipeline

* overhaul tests; implement new stress tests and address coverage gaps

* parallelize tests where safe to do so (3x CI time savings!)

* introduce faster pure Go JSON seralization library

* more refactors: unit tests & benchmarks

* DRY up shutdown req drain and prepareForSend serializer

* use go-json in CLI utils too; remove redundant helpers

* move error type to proper pkg

* DRY up loop() and fix deadlock in old test case

* normalize CI and Makefile test and benchmark target behavviors

* more realistic feature flags benchmark

* replace leaky time.After pattern with idiomatic safer Timers

* internal queue size minimums decoupled from batch and worker configs

* remove dead comments

* fix test bug

* update default http.Transport cfg to avoid conn pool as bottleneck; update batch enqueue limiting strategy and tests/benches

* adjust stress test defaults to match prod

* update flaky tests and rm stale file

* make graceful shutdown timeout opt-in for now

* fix tests that reverted to hold helper utils during rebase

* fix flaky test due to overhead from -race detector now default in test flags

* add more graceful shutdown test cases
2026-01-13 12:22:44 -08:00

87 lines
1.7 KiB
Go

package posthog
import (
"math"
"math/rand"
"time"
)
type Backoff struct {
base time.Duration
factor uint8
jitter float64
cap time.Duration
}
// Creates a backo instance with the given parameters
func NewBackoff(base time.Duration, factor uint8, jitter float64, cap time.Duration) *Backoff {
return &Backoff{base, factor, jitter, cap}
}
// Creates a backoff instance with the following defaults:
//
// base: 100 milliseconds
// factor: 2
// jitter: 0
// cap: 10 seconds
func DefaultBackoff() *Backoff {
return NewBackoff(time.Millisecond*100, 2, 0, time.Second*10)
}
// Duration returns the backoff interval for the given attempt.
func (b *Backoff) Duration(attempt int) time.Duration {
duration := float64(b.base) * math.Pow(float64(b.factor), float64(attempt))
if b.jitter != 0 {
random := rand.Float64()
deviation := math.Floor(random * b.jitter * duration)
if (int(math.Floor(random*10)) & 1) == 0 {
duration = duration - deviation
} else {
duration = duration + deviation
}
}
duration = math.Min(float64(duration), float64(b.cap))
return time.Duration(duration)
}
// Sleep pauses the current goroutine for the backoff interval for the given attempt.
func (b *Backoff) Sleep(attempt int) {
duration := b.Duration(attempt)
time.Sleep(duration)
}
type Ticker struct {
done chan struct{}
C <-chan time.Time
}
func (b *Backoff) NewTicker() *Ticker {
c := make(chan time.Time, 1)
ticker := &Ticker{
done: make(chan struct{}, 1),
C: c,
}
go func() {
for i := 0; ; i++ {
timer := time.NewTimer(b.Duration(i))
select {
case t := <-timer.C:
c <- t
case <-ticker.done:
timer.Stop()
close(c)
return
}
}
}()
return ticker
}
func (t *Ticker) Stop() {
t.done <- struct{}{}
}