init push

This commit is contained in:
2026-07-16 22:16:45 +07:00
commit 8b068bdb10
1021 changed files with 332816 additions and 0 deletions

106
internal/queue/retry.go Normal file
View File

@@ -0,0 +1,106 @@
package queue
import (
"hash/fnv"
"math"
"time"
)
type RetryAction string
const (
RetryActionRetry RetryAction = "retry"
RetryActionDeadLetter RetryAction = "dead_letter"
RetryActionAck RetryAction = "ack"
)
type RetryDecision struct {
Action RetryAction
Delay time.Duration
Reason string
}
type RetryPolicy interface {
Decide(delivery *Delivery, envelope *Envelope, err error) RetryDecision
}
type ExponentialBackoffPolicy struct {
minDelay time.Duration
maxDelay time.Duration
permanentFailureDelay time.Duration
}
func NewExponentialBackoffPolicy(minDelay, maxDelay, permanentFailureDelay time.Duration) *ExponentialBackoffPolicy {
if minDelay <= 0 {
minDelay = time.Second
}
if maxDelay < minDelay {
maxDelay = minDelay
}
if permanentFailureDelay <= 0 {
permanentFailureDelay = 30 * time.Second
}
return &ExponentialBackoffPolicy{
minDelay: minDelay,
maxDelay: maxDelay,
permanentFailureDelay: permanentFailureDelay,
}
}
func (p *ExponentialBackoffPolicy) Decide(delivery *Delivery, _ *Envelope, err error) RetryDecision {
if err == nil {
return RetryDecision{Action: RetryActionAck}
}
if IsPermanent(err) {
return RetryDecision{
Action: RetryActionDeadLetter,
Delay: p.permanentFailureDelay,
Reason: err.Error(),
}
}
attempt := 1
if delivery != nil && delivery.ReceiveCount > 0 {
attempt = delivery.ReceiveCount
}
delay := p.backoffWithJitter(deliveryID(delivery), attempt)
return RetryDecision{
Action: RetryActionRetry,
Delay: delay,
Reason: err.Error(),
}
}
func (p *ExponentialBackoffPolicy) backoffWithJitter(seed string, attempt int) time.Duration {
if attempt < 1 {
attempt = 1
}
power := math.Pow(2, float64(attempt-1))
delay := time.Duration(float64(p.minDelay) * power)
if delay > p.maxDelay {
delay = p.maxDelay
}
if delay <= 0 {
return p.minDelay
}
jitter := deterministicJitter(seed, delay/2)
return delay/2 + jitter
}
func deterministicJitter(seed string, max time.Duration) time.Duration {
if max <= 0 {
return 0
}
h := fnv.New64a()
_, _ = h.Write([]byte(seed))
return time.Duration(h.Sum64() % uint64(max))
}
func deliveryID(delivery *Delivery) string {
if delivery == nil {
return "unknown"
}
if delivery.ID != "" {
return delivery.ID
}
return "unknown"
}