Documentation
¶
Overview ¶
Package fleetbucket paces the calls a pod makes to Rivian when it takes vehicles over (zero-impact deploys, G7). A roll moves every group within seconds; the 50 ms stagger spaces one process's first connects, but N pods and N vehicles together still arrive as a burst. Wait gives each call a slot: first from a per-process token bucket, then from a fleet-wide per-second budget in Redis, with active vehicles (driving or charging) kept ahead of idle ones. A wait is bounded, so pacing never becomes an outage: after maxWait the call goes ahead and is counted as starved. A Redis that cannot answer means only the local bucket applies.
The fleet budget is a counter, not pub/sub: the app's Valkey user has no channel rights, and INCR plus EXPIRE is allowed.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Bucket ¶
type Bucket struct {
// contains filtered or unexported fields
}
Bucket is safe for concurrent use.
type Config ¶
type Config struct {
// LocalRPS and FleetRPS are the calls per second one process and the whole fleet may start. Defaults 10 and 20 (no Rivian 429 data yet: raise
// them only from observed 429s and the takeover p95). FleetRPS is the budget for ACTIVE calls: idle calls have their own counter, at most
// FleetRPS/2, and an active call does not read it, so the two together can reach 1.5 x FleetRPS in one second (20 active + 10 idle at the
// default). Do not size FleetRPS from Rivian 429 data as if it were the total.
LocalRPS int
FleetRPS int
// ActiveMaxWait and IdleMaxWait bound one Wait. Active vehicles must not wait long; idle ones may queue.
ActiveMaxWait time.Duration
IdleMaxWait time.Duration
ShareTTL time.Duration
// RedisBackoff: after a Redis error the fleet budget is skipped (local only) for this long, so a Redis that does not answer costs one slow call, not every one.
// Default 10 s.
RedisBackoff time.Duration
// Redis and Prefix: the fleet budget. Nil Redis: local only.
Redis *redis.Client
Prefix string
// Now is the clock and Sleep the wait for the next second of the fleet budget (tests drive both, so nothing depends on how loaded the
// machine is). Sleep returns when d has passed or ctx is done.
Now func() time.Time
Sleep func(ctx context.Context, d time.Duration)
// Observe hears the outcome of every Wait (class and priority as the caller gave them): how long it waited, and whether it was shared,
// starved or local-only. It is called once per Wait, on the caller's goroutine, and must not block.
Observe func(class string, active bool, r Result)
}
Config sizes a Bucket. Zero values fall back to the defaults below.