Documentation
¶
Index ¶
- Variables
- func RunAdapter(ctx context.Context, adapter FrameworkAdapter, opts RuntimeOptions) error
- type AccountQueue
- type AdmissionController
- type AdmissionDecision
- type AdmissionLimits
- type AdmissionRejectedError
- type Aquifer
- func (a *Aquifer) AdmissionSnapshot() map[string]any
- func (a *Aquifer) AttemptDirect(ctx context.Context, req JobRequest, timeout time.Duration) ProxyOutcome
- func (a *Aquifer) Close()
- func (a *Aquifer) Dispatch(job *Job, accountQueueHeader string)
- func (a *Aquifer) Enqueue(req JobRequest) (EnqueueResult, error)
- func (a *Aquifer) GetJob(id string) (*Job, error)
- func (a *Aquifer) HandleL8Challenge(req L8ChallengeReq) (*L8ChallengeResp, error)
- func (a *Aquifer) Health() map[string]any
- func (a *Aquifer) L8Metadata(host string) L8Meta
- func (a *Aquifer) MaxBodyBytes() int64
- func (a *Aquifer) PrepareJob(req JobRequest) (job *Job, duplicate *EnqueueResult, err error)
- func (a *Aquifer) RegisterPoolMember(poolID, memberID, address string, declaredRPS float64, ...) error
- func (a *Aquifer) RetryAfterSeconds() int
- func (a *Aquifer) SetClusterRouter(router *ClusterRouter)
- func (a *Aquifer) SetRegionAdapter(adapter RegionAdapter)
- func (a *Aquifer) SetRemoteIdempotency(remote RemoteIdempotency)
- func (a *Aquifer) SubscribeJob(id string) (*Job, <-chan SSEEvent, func(), error)
- type Broker
- type ClusterConfig
- type ClusterMember
- type ClusterRouter
- type Config
- type DrainConfig
- type DrainEvent
- type DrainState
- type EnqueueResult
- type FlyRegionAdapter
- type FrameworkAdapter
- type HTTPAdapter
- type Job
- type JobRequest
- type JobStore
- type L8ChallengeReq
- type L8ChallengeResp
- type L8Meta
- type L8Registry
- func (r *L8Registry) Close()
- func (r *L8Registry) EnsureTrust(webhookURL string)
- func (r *L8Registry) HandleChallenge(req L8ChallengeReq) (*L8ChallengeResp, error)
- func (r *L8Registry) IsTrusted(webhookURL string) bool
- func (r *L8Registry) Meta(host string) L8Meta
- func (r *L8Registry) SignHeaders(body []byte) map[string]string
- type LedgerEntry
- type MCPStdioAdapter
- type MetricsAdapter
- type NoopMetricsAdapter
- func (NoopMetricsAdapter) DrainFlushFailed(instanceKey string, ledgerSize int)
- func (NoopMetricsAdapter) DrainFlushSucceeded(instanceKey string, ledgerSize int)
- func (NoopMetricsAdapter) FlowRate(upstream string, rps float64)
- func (NoopMetricsAdapter) JobCompleted(userID, upstream string, durationMs int64)
- func (NoopMetricsAdapter) JobDispatched(userID, upstream string)
- func (NoopMetricsAdapter) JobFailed(userID, upstream string, reason string)
- func (NoopMetricsAdapter) JobQueued(userID, upstream string)
- func (NoopMetricsAdapter) QueueDepth(upstream string, depth int)
- func (NoopMetricsAdapter) WebhookDelivered(url string, attempt int)
- func (NoopMetricsAdapter) WebhookFailed(url string, attempts int)
- type NoopRegionAdapter
- type PebbleStore
- func (s *PebbleStore) AcknowledgeDrainEventsThrough(sequence int64)
- func (s *PebbleStore) CheckOrInsert(job *Job) (string, bool)
- func (s *PebbleStore) ClearIdempotentKeys()
- func (s *PebbleStore) Close() error
- func (s *PebbleStore) Counts() StoreCounts
- func (s *PebbleStore) DeleteJob(jobID string)
- func (s *PebbleStore) GetJob(jobID string) *Job
- func (s *PebbleStore) GetQueuedJobs() []*Job
- func (s *PebbleStore) ListDrainEvents(limit int) []DrainEvent
- func (s *PebbleStore) ListIdempotentKeys() []LedgerEntry
- func (s *PebbleStore) MarkInFlight(jobID string)
- func (s *PebbleStore) Path() string
- func (s *PebbleStore) RecoverInFlight(queueKey string) []*Job
- func (s *PebbleStore) SetQueueKey(jobID, queueKey string)
- func (s *PebbleStore) UpdateStatus(jobID string, status Status)
- type Pool
- func (p *Pool) Pick() *PoolMember
- func (p *Pool) RecordFailure(id string) bool
- func (p *Pool) RecordSuccess(id string)
- func (p *Pool) Register(id, address string, declaredRPS float64, heartbeatInterval time.Duration)
- func (p *Pool) Size() int
- func (p *Pool) Snapshot() []map[string]any
- func (p *Pool) TotalCapacity() float64
- type PoolMember
- type PoolRegistry
- type ProxyFallbackInfo
- type ProxyOutcome
- type RateConfig
- type RedirectExhaustedError
- type RegionAdapter
- type RegistrationConfig
- type Registry
- func (r *Registry) Close()
- func (r *Registry) ConfigureDrain(cfg DrainConfig)
- func (r *Registry) DrainSnapshot() map[string]any
- func (r *Registry) DrainState() DrainState
- func (r *Registry) Enqueue(job *Job, accountQueueHeader string)
- func (r *Registry) EnqueueWebhook(originalJobID, userID, webhookURL string, payload map[string]any)
- func (r *Registry) JobDispatched()
- func (r *Registry) JobDone()
- func (r *Registry) SetDrainRemote(remote RemoteIdempotency)
- type RemoteIdempotency
- type RemoteIdempotencyConfig
- type RemoteIdempotencyEntry
- type Runtime
- type RuntimeOptions
- type SSEEvent
- type Server
- type Status
- type Store
- func (s *Store) AcknowledgeDrainEventsThrough(sequence int64)
- func (s *Store) CheckOrInsert(job *Job) (string, bool)
- func (s *Store) ClearIdempotentKeys()
- func (s *Store) Close() error
- func (s *Store) Counts() StoreCounts
- func (s *Store) DeleteJob(jobID string)
- func (s *Store) GetJob(jobID string) *Job
- func (s *Store) GetQueuedJobs() []*Job
- func (s *Store) ListDrainEvents(limit int) []DrainEvent
- func (s *Store) ListIdempotentKeys() []LedgerEntry
- func (s *Store) MarkInFlight(jobID string)
- func (s *Store) Path() string
- func (s *Store) RecoverInFlight(queueKey string) []*Job
- func (s *Store) SetQueueKey(jobID, queueKey string)
- func (s *Store) UpdateStatus(jobID string, status Status)
- type StoreCounts
- type URLWorker
- type ValkeyRemoteIdempotency
Constants ¶
This section is empty.
Variables ¶
var ErrJobNotFound = errors.New("job not found")
Functions ¶
func RunAdapter ¶
func RunAdapter(ctx context.Context, adapter FrameworkAdapter, opts RuntimeOptions) error
Types ¶
type AccountQueue ¶
type AccountQueue struct {
// contains filtered or unexported fields
}
func NewAccountQueue ¶
func NewAccountQueue(key, upstream string, rps float64, maxConc int, pool *Pool, store JobStore, broker *Broker, l8 *L8Registry, metrics MetricsAdapter, enqueueWebhook webhookEnqueuer, onIdle func(string), slowStart bool, onSlowStartSignal func(bool)) *AccountQueue
func (*AccountQueue) Active ¶ added in v0.9.0
func (q *AccountQueue) Active() bool
Active reports whether this queue currently has real backlog (queued or in-flight work) — used by proxy mode to decide whether a domain should keep routing through the durable queue even after its circuit breaker's cooldown has elapsed, since a cooldown timer alone doesn't know whether the backlog it caused has actually finished draining yet.
func (*AccountQueue) Enqueue ¶
func (q *AccountQueue) Enqueue(job *Job)
func (*AccountQueue) RPS ¶
func (q *AccountQueue) RPS() float64
func (*AccountQueue) Stop ¶ added in v0.10.0
func (q *AccountQueue) Stop()
func (*AccountQueue) Throttle ¶ added in v0.4.0
func (q *AccountQueue) Throttle(rps float64)
Throttle pushes an external rate adjustment into the queue's dispatch loop, reusing the same jobDoneMsg channel that header-driven pacing already uses — the loop doesn't need to know whether a lower rate came from the upstream's own response header or from the URLWorker capping this queue's share of a shared aggregate budget, it's the same signal either way.
type AdmissionController ¶ added in v0.3.0
type AdmissionController struct {
// contains filtered or unexported fields
}
AdmissionController evaluates memory and DB size limits at request time. Body size is enforced separately at the transport layer via http.MaxBytesReader, since that has to happen before the body is even read.
func NewAdmissionController ¶ added in v0.3.0
func NewAdmissionController(limits AdmissionLimits, dbPath string) *AdmissionController
func (*AdmissionController) AnyLimitConfigured ¶ added in v0.3.0
func (c *AdmissionController) AnyLimitConfigured() bool
AnyLimitConfigured reports whether at least one admission limit is actually active, as opposed to merely whether a controller instance exists (the runtime always constructs one, even with everything at zero).
func (*AdmissionController) Check ¶ added in v0.3.0
func (c *AdmissionController) Check() AdmissionDecision
func (*AdmissionController) MaxBodyBytes ¶ added in v0.3.0
func (c *AdmissionController) MaxBodyBytes() int64
func (*AdmissionController) RetryAfterSeconds ¶ added in v0.3.0
func (c *AdmissionController) RetryAfterSeconds() int
RetryAfterSeconds returns the configured base value on the first rejection, then doubles for each additional consecutive rejection (capped at maxRetryAfterSeconds), resetting the moment a request is allowed again.
func (*AdmissionController) Snapshot ¶ added in v0.3.0
func (c *AdmissionController) Snapshot() map[string]any
Snapshot reports current admission pressure for /health, independent of whether any request is currently being rejected.
type AdmissionDecision ¶ added in v0.3.0
type AdmissionDecision struct {
Allowed bool
Reason string // "memory" or "db_size"
Limit int64
Current int64
}
AdmissionDecision is the result of an admission check on a new (non-duplicate) job.
type AdmissionLimits ¶ added in v0.3.0
type AdmissionLimits struct {
MemoryLimitMB int64 // AQUIFER_MEMORY_LIMIT_MB
MaxBodyBytes int64 // AQUIFER_MAX_BODY_BYTES
DBMaxBytes int64 // AQUIFER_DB_MAX_BYTES
RetryAfterSeconds int // AQUIFER_RETRY_AFTER_SECONDS
}
AdmissionLimits are operator-configured ceilings that protect Aquifer itself from the traffic it's meant to be absorbing. A zero value disables that particular check. Memory is opt-in (LoadAdmissionLimits defaults it to disabled); body size and DB size default on — see LoadAdmissionLimits.
func LoadAdmissionLimits ¶ added in v0.3.0
func LoadAdmissionLimits() AdmissionLimits
LoadAdmissionLimits reads the AQUIFER_* admission env vars. Body size and DB size fall back to sane, non-zero defaults when unset (see the default* constants above) rather than being disabled — Aquifer's whole purpose is protecting the process from the traffic it absorbs, so it protects itself by default rather than requiring that to be opted into. An explicit "0" still disables a given check. Memory has no safe one-size-fits-all default (it depends on the deployment's own memory budget, not Aquifer's benchmarked disk usage), so it stays disabled unless set explicitly — LoadAdmissionLimits logs a warning when it is.
type AdmissionRejectedError ¶ added in v0.3.0
type AdmissionRejectedError struct {
Decision AdmissionDecision
}
AdmissionRejectedError is returned by Aquifer.Enqueue when a genuinely new job is rejected due to memory or DB size pressure. Callers (the HTTP server) type-assert on this to build a 429 with Retry-After.
func (*AdmissionRejectedError) Error ¶ added in v0.3.0
func (e *AdmissionRejectedError) Error() string
type Aquifer ¶
type Aquifer struct {
// contains filtered or unexported fields
}
func NewAquifer ¶
func NewAquifer(store JobStore, registry *Registry, broker *Broker, l8 *L8Registry, admission *AdmissionController, pools *PoolRegistry) *Aquifer
func (*Aquifer) AdmissionSnapshot ¶ added in v0.3.0
AdmissionSnapshot reports current admission pressure for /health. Returns enabled:false if admission control isn't configured.
func (*Aquifer) AttemptDirect ¶ added in v0.9.0
func (a *Aquifer) AttemptDirect(ctx context.Context, req JobRequest, timeout time.Duration) ProxyOutcome
AttemptDirect is proxy mode's entry point: persist the job (same idempotency/admission path Enqueue uses), then — for URL-based jobs only, see the PoolID check below — try dispatching it directly and synchronously before ever touching the durable queue. A direct attempt is skipped entirely if this job's target already has its circuit breaker open (see URLWorker.BreakerOpen), so a known-bad upstream doesn't cost every subsequent request the latency of a doomed attempt.
func (*Aquifer) Dispatch ¶ added in v0.9.0
Dispatch hands an already-persisted, admission-approved job to the durable paced queue — Enqueue's last step, exposed for a caller (proxy mode) that already ran PrepareJob itself.
func (*Aquifer) Enqueue ¶
func (a *Aquifer) Enqueue(req JobRequest) (EnqueueResult, error)
func (*Aquifer) HandleL8Challenge ¶
func (a *Aquifer) HandleL8Challenge(req L8ChallengeReq) (*L8ChallengeResp, error)
func (*Aquifer) L8Metadata ¶
func (*Aquifer) MaxBodyBytes ¶ added in v0.3.0
MaxBodyBytes returns the configured request body ceiling, or 0 if unconfigured (unlimited).
func (*Aquifer) PrepareJob ¶ added in v0.9.0
func (a *Aquifer) PrepareJob(req JobRequest) (job *Job, duplicate *EnqueueResult, err error)
PrepareJob validates, persists (idempotency-checked), and admission-checks a request without dispatching it — Enqueue's first two steps, exposed separately so a caller (proxy mode) can attempt a direct dispatch in between persistence and the durable-queue handoff. Returns a non-nil duplicate result if this idempotent_key already exists; job is nil in that case. Behavior is otherwise identical to Enqueue's first half.
func (*Aquifer) RegisterPoolMember ¶ added in v0.4.0
func (a *Aquifer) RegisterPoolMember(poolID, memberID, address string, declaredRPS float64, heartbeatIntervalSeconds int) error
RegisterPoolMember adds or refreshes (heartbeats) a member of a pool. The same call serves both roles — re-registering resets the member's liveness TTL and updates its declared capacity.
func (*Aquifer) RetryAfterSeconds ¶ added in v0.3.0
RetryAfterSeconds returns the configured Retry-After value for 429 responses, defaulting to 5 seconds if admission control isn't configured.
func (*Aquifer) SetClusterRouter ¶ added in v0.10.0
func (a *Aquifer) SetClusterRouter(router *ClusterRouter)
func (*Aquifer) SetRegionAdapter ¶ added in v0.9.0
func (a *Aquifer) SetRegionAdapter(adapter RegionAdapter)
SetRegionAdapter wires in a RegionAdapter after construction -- kept separate from NewAquifer's constructor so adding this opt-in feature doesn't change NewAquifer's signature for every existing caller. A nil Aquifer.regionAdapter (the default) behaves as NoopRegionAdapter via regionAdapterOrDefault.
func (*Aquifer) SetRemoteIdempotency ¶ added in v0.10.0
func (a *Aquifer) SetRemoteIdempotency(remote RemoteIdempotency)
type Broker ¶
type Broker struct {
// contains filtered or unexported fields
}
Broker is the pub/sub layer for SSE streams. Each job gets its own set of subscriber channels.
type ClusterConfig ¶ added in v0.10.0
type ClusterConfig struct {
Enabled bool
Self ClusterMember
Members []ClusterMember
PartitionCount int
ReplicationFactor int
Load float64
}
func LoadClusterConfig ¶ added in v0.10.0
func LoadClusterConfig() ClusterConfig
type ClusterMember ¶ added in v0.10.0
func (ClusterMember) String ¶ added in v0.10.0
func (m ClusterMember) String() string
type ClusterRouter ¶ added in v0.10.0
type ClusterRouter struct {
// contains filtered or unexported fields
}
func NewClusterRouter ¶ added in v0.10.0
func NewClusterRouter(cfg ClusterConfig) *ClusterRouter
func (*ClusterRouter) IsOwner ¶ added in v0.10.0
func (r *ClusterRouter) IsOwner(key string) bool
func (*ClusterRouter) OwnerFor ¶ added in v0.10.0
func (r *ClusterRouter) OwnerFor(key string) (ClusterMember, bool)
func (*ClusterRouter) Snapshot ¶ added in v0.10.0
func (r *ClusterRouter) Snapshot() map[string]any
type Config ¶
type Config struct {
Defaults RateConfig `yaml:"defaults"`
Upstreams map[string]RateConfig `yaml:"upstreams"`
}
func LoadConfig ¶
func (*Config) ForURL ¶
func (c *Config) ForURL(rawURL string) RateConfig
type DrainConfig ¶ added in v0.6.0
type DrainConfig struct {
Enabled bool // AQUIFER_DRAIN_ENABLED
TimerSeconds int64 // AQUIFER_DRAIN_TIMER_SECONDS
Sink string
WebhookURL string
BatchEnabled bool // AQUIFER_DRAIN_BATCH_ENABLED
BatchIntervalSeconds int64 // AQUIFER_DRAIN_BATCH_INTERVAL_SECONDS
BatchMaxEvents int // AQUIFER_DRAIN_BATCH_MAX_EVENTS
}
func LoadDrainConfig ¶ added in v0.6.0
func LoadDrainConfig() DrainConfig
LoadDrainConfig reads the AQUIFER_DRAIN_* env vars. Enabled defaults to false. If enabled but no webhook URL is configured, that's treated as disabled (with a warning) rather than a flush attempt with nowhere to send it -- fail-safe toward "do nothing," never toward "clear the ledger anyway."
type DrainEvent ¶ added in v0.10.0
type DrainEvent struct {
Sequence int64 `json:"sequence"`
HashKey string `json:"idempotent_key_hash"`
JobID string `json:"job_id"`
Status Status `json:"status"`
RecordedAt int64 `json:"recorded_at"`
}
DrainEvent is the durable, acknowledged unit used by batched drain streaming. It intentionally carries the same hash-only ledger data as LedgerEntry, plus a monotonic local sequence so a receiver can treat batches idempotently and Aquifer can delete only acknowledged events.
type DrainState ¶ added in v0.6.0
type DrainState string
DrainState is the instance's explicit position in drain mode's lifecycle, visible via GET /health (see Registry.DrainSnapshot). Only meaningful when drain mode is enabled.
const ( // DrainStateActive: at least one worker has live work. The normal // state for any instance, drain mode enabled or not. DrainStateActive DrainState = "active" // DrainStateDraining: every worker has gone idle, but either the drain // timer hasn't elapsed yet, or a flush attempt is in flight/being // retried. Not yet safe to hand off. DrainStateDraining DrainState = "draining" // DrainStateUnassigned: the ledger was successfully flushed (or there // was nothing to flush) and local state is clear. Safe to hand off to // a different tenant. Reverts to Active the instant new work arrives. DrainStateUnassigned DrainState = "unassigned" )
type EnqueueResult ¶
type FlyRegionAdapter ¶ added in v0.9.0
type FlyRegionAdapter struct {
// contains filtered or unexported fields
}
FlyRegionAdapter polls sibling regions over Fly's private 6PN network to know which are currently live, backing /proxy's cross-region redirect (proxy.go's AttemptDirect). Region enumeration is explicit config only (AQUIFER_FLY_REGIONS) -- Aquifer never silently polls regions a deployer didn't say to use.
func NewFlyRegionAdapter ¶ added in v0.9.0
func NewFlyRegionAdapter() *FlyRegionAdapter
NewFlyRegionAdapter constructs the adapter and starts its background polling loop. Returns nil if AQUIFER_FLY_REGIONS isn't set -- the feature stays off unless explicitly configured, matching every other opt-in feature in this codebase. Callers should treat a nil return as "use NoopRegionAdapter" (ensureRegionAdapter already does this for a nil RegionAdapter passed to Aquifer.SetRegionAdapter).
func (*FlyRegionAdapter) Close ¶ added in v0.10.0
func (a *FlyRegionAdapter) Close()
func (*FlyRegionAdapter) LiveRegions ¶ added in v0.9.0
func (a *FlyRegionAdapter) LiveRegions() []string
func (*FlyRegionAdapter) SelfRegion ¶ added in v0.9.0
func (a *FlyRegionAdapter) SelfRegion() string
type FrameworkAdapter ¶
type HTTPAdapter ¶
type HTTPAdapter struct {
// contains filtered or unexported fields
}
func NewHTTPAdapter ¶
func NewHTTPAdapter(addr string) *HTTPAdapter
func (*HTTPAdapter) Name ¶
func (a *HTTPAdapter) Name() string
type Job ¶
type Job struct {
ID string `json:"id"`
UserID string `json:"user_id"`
IdempotentKey string `json:"idempotent_key"`
URL string `json:"url,omitempty"`
PoolID string `json:"pool_id,omitempty"`
Method string `json:"method"`
Headers map[string]string `json:"headers,omitempty"`
Body string `json:"body,omitempty"`
WebhookURL string `json:"webhook_url"`
Status Status `json:"status"`
CreatedAt int64 `json:"created_at"`
// Cross-region /proxy redirect fields — see proxy.go's AttemptDirect.
// Absent/zero on a fresh top-level request; that absence IS the signal
// "I'm the origin, nobody redirected this to me." OriginMachineID set
// to someone else's ID means this instance must NOT itself originate a
// further redirect tour — it just runs the normal local direct-attempt-
// then-queue path, exactly as if this feature didn't exist.
OriginMachineID string `json:"origin_machine_id,omitempty"`
OriginRegion string `json:"origin_region,omitempty"`
VisitedRegions []string `json:"visited_regions,omitempty"`
RerouteCount int `json:"reroute_count,omitempty"`
}
func NewJob ¶
func NewJob(r *JobRequest) *Job
type JobRequest ¶
type JobRequest struct {
UserID string `json:"user_id"`
IdempotentKey string `json:"idempotent_key"`
URL string `json:"url,omitempty"`
PoolID string `json:"pool_id,omitempty"`
Method string `json:"method"`
Headers map[string]string `json:"headers,omitempty"`
Body string `json:"body,omitempty"`
WebhookURL string `json:"webhook_url"`
// Cross-region /proxy redirect fields — see Job's own doc comment and
// proxy.go's AttemptDirect. Only ever set on an internal redirect hop
// (one Aquifer instance calling another's /proxy directly); a real
// caller never sets these.
OriginMachineID string `json:"origin_machine_id,omitempty"`
OriginRegion string `json:"origin_region,omitempty"`
VisitedRegions []string `json:"visited_regions,omitempty"`
RerouteCount int `json:"reroute_count,omitempty"`
// DirectOnly is set on every redirect hop except the final one (the
// deterministic-hash-selected target that's allowed to actually queue).
// It tells the receiving instance "try a direct dispatch, but if you
// can't, tell me cleanly — do NOT fall back to your own local queue."
// Without this, a tour that moved on to a second candidate after a
// first candidate quietly queued the job locally would leave the job
// committed in two places at once — a real duplicate-delivery bug,
// entirely within one origin's own tour, independent of the separate
// cross-origin race already documented as an accepted gap. See
// region_redirect.go.
DirectOnly bool `json:"direct_only,omitempty"`
// AccountQueueMode is never read from the request body — it's set by the
// HTTP adapter from the X-Aqueduct-Account-Queue / X-Aquifer-Account-Queue
// request header, the only source of truth for this setting. Empty means
// "no opinion, leave the upstream's current mode unchanged."
AccountQueueMode string `json:"-"`
}
func (*JobRequest) Validate ¶
func (r *JobRequest) Validate() string
type JobStore ¶ added in v0.4.0
type JobStore interface {
Path() string
Close() error
CheckOrInsert(job *Job) (string, bool)
SetQueueKey(jobID, queueKey string)
DeleteJob(jobID string)
MarkInFlight(jobID string)
RecoverInFlight(queueKey string) []*Job
UpdateStatus(jobID string, status Status)
Counts() StoreCounts
GetJob(jobID string) *Job
GetQueuedJobs() []*Job
// ListIdempotentKeys and ClearIdempotentKeys back drain mode (see
// drain.go) -- an opt-in feature, off by default, so calling these on
// a deployment that never enables it is never reached.
ListIdempotentKeys() []LedgerEntry
ClearIdempotentKeys()
ListDrainEvents(limit int) []DrainEvent
AcknowledgeDrainEventsThrough(sequence int64)
}
JobStore is the storage backend behind everything that persists a job: idempotency, status transitions, crash recovery, and admission control's db-size check. *Store (SQLite/WAL) is the default and only implementation until now; *PebbleStore is an opt-in alternative for benchmarking whether a memory-first LSM store changes the throughput ceiling the way it did for the Elixir/Mnesia sibling of this project. Selected via AQUIFER_STORE_BACKEND ("sqlite", the default, or "pebble") — existing deployments that don't set it see no change at all.
func NewJobStore ¶ added in v0.4.0
NewJobStore constructs whichever backend AQUIFER_STORE_BACKEND names. path is the same value that used to go straight to NewStore — for SQLite it's a file path, for Pebble it's a directory.
type L8ChallengeReq ¶
type L8ChallengeResp ¶
type L8Meta ¶
type L8Meta struct {
ProtocolVersion string `json:"protocol_version"`
ServiceName string `json:"service_name"`
PublicKey string `json:"public_key"`
ChallengeEndpoint string `json:"challenge_endpoint"`
SupportedAlgos []string `json:"supported_algorithms"`
Capabilities []string `json:"capabilities"`
SpecURL string `json:"spec_url"`
}
type L8Registry ¶
type L8Registry struct {
PubB64 string // exported so server can embed in responses
// contains filtered or unexported fields
}
func NewL8Registry ¶
func NewL8Registry(keyPath, trustDir string) *L8Registry
func (*L8Registry) Close ¶ added in v0.10.0
func (r *L8Registry) Close()
func (*L8Registry) EnsureTrust ¶
func (r *L8Registry) EnsureTrust(webhookURL string)
EnsureTrust runs the L8 handshake with the webhook domain if not already trusted. Silently does nothing if the receiver doesn't support L8 — delivery still proceeds unsigned.
func (*L8Registry) HandleChallenge ¶
func (r *L8Registry) HandleChallenge(req L8ChallengeReq) (*L8ChallengeResp, error)
HandleChallenge verifies the sender's ownership proof and returns Aquifer's signed response.
func (*L8Registry) IsTrusted ¶
func (r *L8Registry) IsTrusted(webhookURL string) bool
IsTrusted returns true if the L8 handshake has been completed for this webhook domain.
func (*L8Registry) Meta ¶
func (r *L8Registry) Meta(host string) L8Meta
func (*L8Registry) SignHeaders ¶
func (r *L8Registry) SignHeaders(body []byte) map[string]string
SignHeaders returns X-L8-* headers to attach to an outgoing webhook delivery.
type LedgerEntry ¶ added in v0.6.0
type LedgerEntry struct {
HashKey string `json:"idempotent_key_hash"`
JobID string `json:"job_id"`
Status Status `json:"status"`
}
LedgerEntry is one row of the drain-mode idempotency ledger -- hash-only, never the plaintext idempotent key. See drain.go.
type MCPStdioAdapter ¶
type MCPStdioAdapter struct {
// contains filtered or unexported fields
}
func NewMCPStdioAdapter ¶
func NewMCPStdioAdapter(in io.Reader, out io.Writer) *MCPStdioAdapter
func (*MCPStdioAdapter) Name ¶
func (a *MCPStdioAdapter) Name() string
type MetricsAdapter ¶
type MetricsAdapter interface {
JobQueued(userID, upstream string)
JobDispatched(userID, upstream string)
JobCompleted(userID, upstream string, durationMs int64)
JobFailed(userID, upstream string, reason string)
WebhookDelivered(url string, attempt int)
WebhookFailed(url string, attempts int)
QueueDepth(upstream string, depth int)
FlowRate(upstream string, rps float64)
// DrainFlushSucceeded/DrainFlushFailed only ever fire when drain mode
// is enabled (see drain.go) -- unreached on a deployment that never
// turns it on.
DrainFlushSucceeded(instanceKey string, ledgerSize int)
DrainFlushFailed(instanceKey string, ledgerSize int)
}
type NoopMetricsAdapter ¶
type NoopMetricsAdapter struct{}
func (NoopMetricsAdapter) DrainFlushFailed ¶ added in v0.6.0
func (NoopMetricsAdapter) DrainFlushFailed(instanceKey string, ledgerSize int)
func (NoopMetricsAdapter) DrainFlushSucceeded ¶ added in v0.6.0
func (NoopMetricsAdapter) DrainFlushSucceeded(instanceKey string, ledgerSize int)
func (NoopMetricsAdapter) FlowRate ¶
func (NoopMetricsAdapter) FlowRate(upstream string, rps float64)
func (NoopMetricsAdapter) JobCompleted ¶
func (NoopMetricsAdapter) JobCompleted(userID, upstream string, durationMs int64)
func (NoopMetricsAdapter) JobDispatched ¶
func (NoopMetricsAdapter) JobDispatched(userID, upstream string)
func (NoopMetricsAdapter) JobFailed ¶
func (NoopMetricsAdapter) JobFailed(userID, upstream string, reason string)
func (NoopMetricsAdapter) JobQueued ¶
func (NoopMetricsAdapter) JobQueued(userID, upstream string)
func (NoopMetricsAdapter) QueueDepth ¶
func (NoopMetricsAdapter) QueueDepth(upstream string, depth int)
func (NoopMetricsAdapter) WebhookDelivered ¶
func (NoopMetricsAdapter) WebhookDelivered(url string, attempt int)
func (NoopMetricsAdapter) WebhookFailed ¶
func (NoopMetricsAdapter) WebhookFailed(url string, attempts int)
type NoopRegionAdapter ¶ added in v0.9.0
type NoopRegionAdapter struct{}
NoopRegionAdapter is the default: no known regions, cross-region redirect never triggers. This is what "the feature is off" looks like -- AttemptDirect checks LiveRegions() being empty as its gate for even considering redirect, so a deployment that never configures a real RegionAdapter sees zero behavior change from this feature existing in the codebase.
func (NoopRegionAdapter) LiveRegions ¶ added in v0.9.0
func (NoopRegionAdapter) LiveRegions() []string
func (NoopRegionAdapter) SelfRegion ¶ added in v0.9.0
func (NoopRegionAdapter) SelfRegion() string
type PebbleStore ¶ added in v0.4.0
type PebbleStore struct {
// contains filtered or unexported fields
}
func NewPebbleStore ¶ added in v0.4.0
func NewPebbleStore(path string) *PebbleStore
func (*PebbleStore) AcknowledgeDrainEventsThrough ¶ added in v0.10.0
func (s *PebbleStore) AcknowledgeDrainEventsThrough(sequence int64)
func (*PebbleStore) CheckOrInsert ¶ added in v0.4.0
func (s *PebbleStore) CheckOrInsert(job *Job) (string, bool)
CheckOrInsert mirrors Store.CheckOrInsert's contract exactly: :ok for a fresh job, or the existing job ID if the (user_id, idempotent_key) pair was already accepted. The shard lock is what makes this atomic instead of a racy Get-then-Set — see the package doc comment above.
func (*PebbleStore) ClearIdempotentKeys ¶ added in v0.6.0
func (s *PebbleStore) ClearIdempotentKeys()
ClearIdempotentKeys wipes both the job: and idem: prefixes -- only ever called by drain mode's watchdog after a successful ledger-flush webhook delivery, never on a normal (non-drain-mode) deployment. Takes every shard lock before wiping so a concurrent CheckOrInsert (which only holds one shard's lock) can't race a mid-wipe read/write and reintroduce a row.
func (*PebbleStore) Close ¶ added in v0.4.0
func (s *PebbleStore) Close() error
func (*PebbleStore) Counts ¶ added in v0.4.0
func (s *PebbleStore) Counts() StoreCounts
func (*PebbleStore) DeleteJob ¶ added in v0.4.0
func (s *PebbleStore) DeleteJob(jobID string)
DeleteJob removes both the job row and its idempotency index entry — dropping only the job: key would leave a dangling idem: entry pointing at a job that no longer exists, the same ghost-row risk DeleteJob exists to prevent on the SQLite side.
func (*PebbleStore) GetJob ¶ added in v0.4.0
func (s *PebbleStore) GetJob(jobID string) *Job
func (*PebbleStore) GetQueuedJobs ¶ added in v0.4.0
func (s *PebbleStore) GetQueuedJobs() []*Job
func (*PebbleStore) ListDrainEvents ¶ added in v0.10.0
func (s *PebbleStore) ListDrainEvents(limit int) []DrainEvent
func (*PebbleStore) ListIdempotentKeys ¶ added in v0.6.0
func (s *PebbleStore) ListIdempotentKeys() []LedgerEntry
ListIdempotentKeys backs drain mode's ledger export. Unlike SQLite, pebbleRecord.Job retains the plaintext IdempotentKey (for unrelated reasons -- see the package doc comment), but this must never surface it: the hash is recomputed the same way CheckOrInsert derives it, and only the hash/job_id/status ever go into the returned LedgerEntry. Excludes webhook-delivery jobs (WebhookURL == "", see Job.isWebhookDeliveryJob) -- those are internal delivery bookkeeping, not real user-submitted work, and have no business appearing in a ledger meant for tenant-handoff dedup.
func (*PebbleStore) MarkInFlight ¶ added in v0.4.0
func (s *PebbleStore) MarkInFlight(jobID string)
func (*PebbleStore) Path ¶ added in v0.4.0
func (s *PebbleStore) Path() string
func (*PebbleStore) RecoverInFlight ¶ added in v0.4.0
func (s *PebbleStore) RecoverInFlight(queueKey string) []*Job
func (*PebbleStore) SetQueueKey ¶ added in v0.4.0
func (s *PebbleStore) SetQueueKey(jobID, queueKey string)
func (*PebbleStore) UpdateStatus ¶ added in v0.4.0
func (s *PebbleStore) UpdateStatus(jobID string, status Status)
type Pool ¶ added in v0.4.0
type Pool struct {
// contains filtered or unexported fields
}
Pool is one named group of registered members. Selection uses virtual-time weighted round robin: each pick advances the chosen member's virtual position by 1/weight (a higher-weight member advances less per pick, so it comes back to the front of the heap sooner and gets picked proportionally more often), giving O(log n) per dispatch — the naive nginx-style smooth-weighted-round-robin algorithm (recompute every member's running weight on every pick) is O(n) per pick instead; this avoids that by only ever touching the single member actually chosen.
func (*Pool) Pick ¶ added in v0.4.0
func (p *Pool) Pick() *PoolMember
Pick selects a member proportional to declared_rate x reputation and advances its virtual position. Returns nil if the pool has no members.
func (*Pool) RecordFailure ¶ added in v0.4.0
RecordFailure halves a member's reputation — the same shape as the existing Retry-After backoff (doubles per consecutive event), just applied as a share reduction instead of a delay increase. Once reputation has been at or below the floor continuously for defaultFloorEvictionWindow, the member is evicted. Returns whether this call caused an eviction.
func (*Pool) RecordSuccess ¶ added in v0.4.0
RecordSuccess nudges a member's reputation back toward full trust and unconditionally clears any in-progress floor-eviction timer — the eviction criterion is sustained badness with zero successes in between, not a numeric reputation threshold held for a duration, so any success resets the clock even if the reputation number itself hasn't recovered above the floor yet.
func (*Pool) Register ¶ added in v0.4.0
Register adds a new member or refreshes an existing one. The same call serves as both initial registration and heartbeat — re-calling it resets LastHeartbeat and updates declared capacity, so a member that wants to lower or raise its reported rate just re-registers.
func (*Pool) TotalCapacity ¶ added in v0.4.0
TotalCapacity is the live sum of every member's current effective weight — the pool's aggregate ceiling grows and shrinks automatically as members register, degrade, or drop out, rather than being a static number an operator configures once.
type PoolMember ¶ added in v0.4.0
type PoolMember struct {
ID string
Address string
DeclaredRPS float64
HeartbeatInterval time.Duration
LastHeartbeat time.Time
// contains filtered or unexported fields
}
PoolMember is one registered target within a pool — a service instance that pinged in with its own address and declared capacity.
func (*PoolMember) Reputation ¶ added in v0.4.0
func (m *PoolMember) Reputation() float64
type PoolRegistry ¶ added in v0.4.0
type PoolRegistry struct {
// contains filtered or unexported fields
}
PoolRegistry owns every named pool. One registry per Aquifer instance.
func NewPoolRegistry ¶ added in v0.4.0
func NewPoolRegistry() *PoolRegistry
func (*PoolRegistry) Get ¶ added in v0.4.0
func (pr *PoolRegistry) Get(poolID string) *Pool
Get returns the named pool, creating an empty one if nobody has registered to it yet. A job dispatched to a pool that exists but has zero members is a different, correctly-handled state (fails cleanly with "no pool members registered") from a job that isn't pool-backed at all — returning nil here would collapse that distinction and let a pool-backed job silently fall through to a non-pool dispatch path with an empty URL instead.
func (*PoolRegistry) Register ¶ added in v0.4.0
func (pr *PoolRegistry) Register(poolID, memberID, address string, declaredRPS float64, heartbeatInterval time.Duration)
func (*PoolRegistry) Snapshot ¶ added in v0.4.0
func (pr *PoolRegistry) Snapshot() map[string]any
func (*PoolRegistry) Stop ¶ added in v0.4.0
func (pr *PoolRegistry) Stop()
type ProxyFallbackInfo ¶ added in v0.9.0
ProxyFallbackInfo carries the one extra SSE event proxy mode's fallback path emits before the normal queued/dispatching/terminal sequence — see streamEvents. Status is 0 when a real upstream status was never received (a skipped attempt or a timeout), non-zero when the upstream actually responded (e.g. 429/503).
type ProxyOutcome ¶ added in v0.9.0
type ProxyOutcome struct {
Err error
Duplicate bool
ExistingJob *Job
Job *Job
Direct bool
Status int
Header http.Header
Body []byte
// FallbackReason and FallbackStatus are set whenever Job is set but
// Direct is false — a short, stable label for why a direct attempt
// wasn't completed, surfaced to the caller as a proxy_fallback SSE
// event before the normal queued/dispatching/terminal sequence, so a
// client watching the stream (a browser, an agent with no server of
// its own to explain this some other way) knows it's in the queue
// because something specific happened, not just "queued" with no
// context. FallbackStatus is the upstream's real status code when one
// was actually received (0 for a skipped or timed-out attempt).
FallbackReason string
FallbackStatus int
// RelayFrom is set when this request was redirected to another region
// (region_redirect.go) and that region accepted it into its own
// durable queue rather than completing directly — the caller
// (server.go's proxyJob) should relay every SSE event read from this
// response body onto the original caller's connection in real time,
// rather than subscribing to a local job's events the normal way.
// Job/Direct/FallbackReason are meaningless in this case; the real job
// now lives on the target region under its own ID — the caller learns
// it from the relayed stream itself, the same place it always would
// for any fallback. The caller owns closing RelayFrom.Body once the
// stream ends. RerouteRegion is always set alongside it — which region
// actually ended up owning the job — so the caller can tell the client
// via a synthetic "rerouted" event before relaying, same rationale as
// FallbackReason/proxy_fallback: a client with no server of its own to
// explain this (a browser, an agent) shouldn't have to wonder why its
// connection is still open or where the response actually came from.
RelayFrom *http.Response
RerouteRegion string
}
ProxyOutcome is AttemptDirect's result. Exactly one of the following holds: Err is set (validation/admission failure, same contract as Enqueue); Duplicate is true (ExistingJob holds the prior job); or Job is set, with Direct indicating whether it was completed synchronously (Status/Header/Body then valid) or needs the caller to Dispatch it and stream the result the normal way.
type RateConfig ¶
type RedirectExhaustedError ¶ added in v0.9.0
type RedirectExhaustedError struct {
JobID string
}
RedirectExhaustedError is returned by Aquifer.AttemptDirect (via fallbackOutcome) when cross-region redirect is configured and was actually attempted (or is still within its post-exhaustion gate cooldown — see redirectGate) but no known-live region could serve the request directly or accept it into its own queue. Callers (the HTTP server) type-assert on this to build a 429 with a long Retry-After, the same way AdmissionRejectedError already does for admission control.
This is deliberately NOT the same as "queue it locally instead" — queueing on total redirect exhaustion was never actually decided; the default is to fail loudly so a caller (or its own retry/alerting logic) knows the whole fleet is degraded, not just this one instance. A future per-deployment option to queue locally instead (e.g. for an Aquifer instance dedicated to a single customer, where "queue and eventually deliver" might be preferable to erroring) is a real possibility, just not the default and not built here.
func (*RedirectExhaustedError) Error ¶ added in v0.9.0
func (e *RedirectExhaustedError) Error() string
type RegionAdapter ¶ added in v0.9.0
type RegionAdapter interface {
// LiveRegions returns the currently known-live regions, excluding
// SelfRegion. Safe to call frequently; implementations should return a
// cached snapshot, not block on a live check.
LiveRegions() []string
// SelfRegion returns this instance's own region identifier, or "" if
// unknown/not applicable.
SelfRegion() string
}
RegionAdapter reports which regions Aquifer is currently deployed to and reachable in, backing /proxy's cross-region redirect (see proxy.go's AttemptDirect). Same shape as MetricsAdapter/JobStore: a small interface, a no-op default, the deployer plugs in a real implementation for their platform. Off unless explicitly configured -- matches every other opt-in feature in this codebase.
type RegistrationConfig ¶ added in v0.10.0
type RegistrationConfig struct {
URL string // AQUIFER_REGISTRY_URL -- presence enables this feature
Port string // PORT (same env var and "8080" default region_adapter_fly.go already uses)
IntervalSeconds int64 // AQUIFER_REGISTRY_INTERVAL_SECONDS
}
func LoadRegistrationConfig ¶ added in v0.10.0
func LoadRegistrationConfig() RegistrationConfig
LoadRegistrationConfig reads the AQUIFER_REGISTRY_* env vars (plus the already-established PORT var, not a new one of its own).
func (RegistrationConfig) Enabled ¶ added in v0.10.0
func (cfg RegistrationConfig) Enabled() bool
type Registry ¶
type Registry struct {
// contains filtered or unexported fields
}
func NewRegistry ¶
func NewRegistry(store JobStore, cfg *Config, broker *Broker, l8 *L8Registry, metrics MetricsAdapter, pools *PoolRegistry) *Registry
NewRegistry reads drain mode's config from AQUIFER_DRAIN_* env vars (LoadDrainConfig) — disabled unless AQUIFER_DRAIN_ENABLED is explicitly set, matching NewPebbleStore's existing precedent of reading its own opt-in env vars internally. Callers wanting a programmatic override (RuntimeOptions.DrainConfig) call ConfigureDrain after construction.
func (*Registry) ConfigureDrain ¶ added in v0.6.0
func (r *Registry) ConfigureDrain(cfg DrainConfig)
ConfigureDrain overrides drain mode's config after construction (used by RuntimeOptions.DrainConfig) and starts the watchdog if the override enables it and the constructor-time env-based config hadn't already started one. There is no supported path to stop an already-running watchdog at runtime — disabling drain mode requires a restart, same as every other env-var-driven config in this codebase.
func (*Registry) DrainSnapshot ¶ added in v0.6.0
DrainSnapshot reports drain mode's current state for GET /health, or nil when drain mode isn't enabled — an instance that never turned this on shouldn't see a new key appear in its health output.
func (*Registry) DrainState ¶ added in v0.6.0
func (r *Registry) DrainState() DrainState
DrainState is the instance's current position in drain mode's lifecycle (active/draining/unassigned) — meaningful only when drain mode is enabled, but always safe to call (returns DrainStateActive otherwise, since the watchdog that would ever move it elsewhere never runs).
func (*Registry) Enqueue ¶
Enqueue queues a job on the URLWorker for its upstream domain, or for its target pool if the job carries a PoolID instead of a URL. accountQueueHeader is the raw X-Aqueduct-Account-Queue/X-Aquifer-Account-Queue value from the originating HTTP request, or "" if this job has no live request behind it (e.g. recovered from disk at startup). An empty value leaves the worker's current account-queue mode unchanged rather than forcing it off — the mode is shared per upstream domain, so one request that doesn't care about it shouldn't be able to flip it off for every other concurrent tenant relying on it being on.
func (*Registry) EnqueueWebhook ¶ added in v0.7.0
EnqueueWebhook queues a webhook delivery through the same domain-keyed account-queue pacing and backpressure machinery as forward dispatch (RPS/concurrency limits, X-Aqueduct-* response-header throttling) instead of firing immediately with a fixed retry schedule — a slow or rate-limited webhook receiver can now shed load exactly the way an upstream API already can, and delivery is durable across a restart the same way a real job is (the underlying webhook-delivery Job is persisted via CheckOrInsert, not just an in-memory retry loop).
originalJobID scopes the idempotent key (see Job.isWebhookDeliveryJob) so a given job's webhook is enqueued at most once even if this were somehow called twice for it.
func (*Registry) JobDispatched ¶
func (r *Registry) JobDispatched()
func (*Registry) SetDrainRemote ¶ added in v0.10.0
func (r *Registry) SetDrainRemote(remote RemoteIdempotency)
type RemoteIdempotency ¶ added in v0.10.0
type RemoteIdempotency interface {
Lookup(hash string) (RemoteIdempotencyEntry, bool)
Record(events []DrainEvent) bool
}
func NewValkeyRemoteIdempotency ¶ added in v0.10.0
func NewValkeyRemoteIdempotency(cfg RemoteIdempotencyConfig) RemoteIdempotency
type RemoteIdempotencyConfig ¶ added in v0.10.0
type RemoteIdempotencyConfig struct {
Enabled bool
URL string
Timeout time.Duration
Prefix string
TTLSeconds int64
}
func LoadRemoteIdempotencyConfig ¶ added in v0.10.0
func LoadRemoteIdempotencyConfig() RemoteIdempotencyConfig
type RemoteIdempotencyEntry ¶ added in v0.10.0
type Runtime ¶
type Runtime struct {
Aquifer *Aquifer
Store JobStore
Broker *Broker
Registry *Registry
L8 *L8Registry
Config *Config
Admission *AdmissionController
Pools *PoolRegistry
}
func NewRuntime ¶
func NewRuntime(opts RuntimeOptions) *Runtime
func (*Runtime) RecoverQueuedJobs ¶
type RuntimeOptions ¶
type RuntimeOptions struct {
DBPath string
ConfigPath string
Config *Config
L8KeyPath string
L8TrustDir string
Metrics MetricsAdapter
AdmissionLimits *AdmissionLimits
// Store overrides the storage backend entirely. If nil, NewRuntime falls
// back to NewJobStore(DBPath), selecting sqlite/pebble via
// AQUIFER_STORE_BACKEND as before. Set this to plug in a custom JobStore
// implementation (e.g. Postgres, rqlite) without needing to bypass
// NewRuntime and wire the lower-level constructors by hand.
Store JobStore
// DrainConfig overrides drain mode's config (see drain.go). If nil,
// the Registry reads AQUIFER_DRAIN_* env vars itself — disabled unless
// AQUIFER_DRAIN_ENABLED is explicitly set to true.
DrainConfig *DrainConfig
// RegionAdapter backs /proxy's cross-region redirect (proxy.go,
// region_adapter.go). If nil, NewRuntime tries NewFlyRegionAdapter,
// which itself only activates if AQUIFER_FLY_REGIONS is set — same
// zero-code, env-var-only activation pattern as AQUIFER_DRAIN_ENABLED.
// Set this to plug in a RegionAdapter for a different platform.
RegionAdapter RegionAdapter
// ClusterRouter optionally routes HTTP /jobs and /proxy requests to the
// Aquifer node that owns the request's partition. If nil, NewRuntime
// loads static env config from AQUIFER_CLUSTER_*.
ClusterRouter *ClusterRouter
// RemoteIdempotency optionally checks a shared remote ledger after the
// local store accepts a new key but before the job is dispatched. If
// nil, NewRuntime loads AQUIFER_REMOTE_IDEMPOTENCY_* env config.
RemoteIdempotency RemoteIdempotency
}
type Store ¶
type Store struct {
// contains filtered or unexported fields
}
func (*Store) AcknowledgeDrainEventsThrough ¶ added in v0.10.0
func (*Store) CheckOrInsert ¶
CheckOrInsert inserts job unless its (user_id, idempotent_key) pair already exists, in which case it reports the existing job's ID as a duplicate. The duplicate/fresh decision is read directly off the INSERT's own RowsAffected, not a follow-up SELECT — a SELECT-after-INSERT here would race under real concurrency (multiple open connections): if the INSERT is still in flight relative to another goroutine's read, or a transient busy-retry delays it, the SELECT can find nothing and this would misreport a brand-new job as an empty-ID "duplicate," silently losing it. RowsAffected==1 is authoritative and atomic: it's exactly the row this call just wrote, no read-your-own-write race possible.
func (*Store) ClearIdempotentKeys ¶ added in v0.6.0
func (s *Store) ClearIdempotentKeys()
ClearIdempotentKeys wipes the whole table -- only ever called by drain mode's watchdog after a successful ledger-flush webhook delivery, never on a normal (non-drain-mode) deployment.
func (*Store) Close ¶ added in v0.4.0
Close releases the underlying connection pool. In WAL mode, SQLite keeps -wal/-shm files alongside the main database file for as long as any connection is open; callers that manage a Store's lifetime explicitly (tests especially, cleaning up a t.TempDir()) should call this before their directory is removed.
func (*Store) Counts ¶
func (s *Store) Counts() StoreCounts
func (*Store) DeleteJob ¶ added in v0.3.0
DeleteJob removes a job row outright. Used when a freshly-inserted, non-duplicate job is rejected by admission control — CheckOrInsert already wrote the row before duplicate status was known, so a rejected job must be deleted here or it would sit as a ghost "queued" row that never dispatches.
func (*Store) GetQueuedJobs ¶
func (*Store) ListDrainEvents ¶ added in v0.10.0
func (s *Store) ListDrainEvents(limit int) []DrainEvent
func (*Store) ListIdempotentKeys ¶ added in v0.6.0
func (s *Store) ListIdempotentKeys() []LedgerEntry
ListIdempotentKeys backs drain mode's ledger export -- hash-only, matches what this table has always stored (the plaintext idempotent key was never persisted here, only its hash, see hashKey/CheckOrInsert). Excludes webhook-delivery jobs (webhook_url = ”, see Job.isWebhookDeliveryJob) -- those are internal delivery bookkeeping, not real user-submitted work, and have no business appearing in a ledger meant for tenant-handoff dedup.
func (*Store) MarkInFlight ¶
func (*Store) RecoverInFlight ¶
func (*Store) SetQueueKey ¶
func (*Store) UpdateStatus ¶
type StoreCounts ¶
type URLWorker ¶
type URLWorker struct {
// contains filtered or unexported fields
}
func NewURLWorker ¶
func NewURLWorker(domain string, rps float64, maxConc int, pool *Pool, store JobStore, broker *Broker, l8 *L8Registry, metrics MetricsAdapter, enqueueWebhook webhookEnqueuer, onIdle func(string)) *URLWorker
func (*URLWorker) BreakerKind ¶ added in v0.9.0
BreakerKind reports which kind of signal tripped the breaker last — "queue" or "reroute" (see classifyOverload) — only meaningful while BreakerOpen is true. A subsequent request arriving while the breaker is still open has no fresh response of its own to classify, so it reuses whichever kind actually tripped it: a domain breaker-tripped by a 429 stays queue-only on every retry during that cooldown, not reroute-eligible just because SOME overload happened.
func (*URLWorker) BreakerOpen ¶ added in v0.9.0
BreakerOpen reports whether proxy mode should skip a direct dispatch attempt to this worker's domain entirely and fall straight back to the durable queue — set by TripBreaker after an overload signal, cleared automatically once the cooldown elapses.
func (*URLWorker) QueueActive ¶ added in v0.9.0
QueueActive reports whether any of this domain's account queues currently has real backlog (queued or in-flight work). Distinct from BreakerOpen: a breaker cooldown is a fixed clock that can expire while a real backlog is still draining, letting proxy mode resume direct dispatch against an upstream that's still catching up from the very overload that tripped the breaker. QueueActive self-corrects instead — it stays true for exactly as long as there's real work in flight, independent of any timer, and goes false the instant the backlog is actually empty.
func (*URLWorker) TripBreaker ¶ added in v0.9.0
TripBreaker opens the breaker for cooldown, recording which kind of signal caused it (see BreakerKind). No separate half-open state is needed: once breakerUntil passes, BreakerOpen naturally returns false again, so the next request after the cooldown is itself a real probe against the live upstream — success leaves the breaker closed, a repeat overload signal re-trips it via another TripBreaker call.
type ValkeyRemoteIdempotency ¶ added in v0.10.0
type ValkeyRemoteIdempotency struct {
// contains filtered or unexported fields
}
func (*ValkeyRemoteIdempotency) Lookup ¶ added in v0.10.0
func (v *ValkeyRemoteIdempotency) Lookup(hash string) (RemoteIdempotencyEntry, bool)
func (*ValkeyRemoteIdempotency) Record ¶ added in v0.10.0
func (v *ValkeyRemoteIdempotency) Record(events []DrainEvent) bool
Source Files
¶
- account_queue.go
- adapter.go
- admission.go
- aquifer.go
- broker.go
- cluster.go
- config.go
- drain.go
- job.go
- job_store.go
- l8.go
- mcp_adapter.go
- metrics.go
- orca.go
- pebble_store.go
- pool.go
- proxy.go
- region_adapter.go
- region_adapter_fly.go
- region_redirect.go
- registration.go
- registry.go
- remote_idempotency.go
- runtime.go
- server.go
- store.go
- url_worker.go
- webhook.go
Directories
¶
| Path | Synopsis |
|---|---|
|
Package a2aadapter exposes Aquifer as an A2A (Agent2Agent protocol, v1.0) agent over JSON-RPC/HTTPS.
|
Package a2aadapter exposes Aquifer as an A2A (Agent2Agent protocol, v1.0) agent over JSON-RPC/HTTPS. |
|
cmd
|
|
|
aquifer
command
|
|
|
aquifer-scenario
command
|
|
|
examples
|
|
|
custom_adapter
command
|