Documentation
¶
Overview ¶
Package stores holds mcpkit's generic storage seams — backend-agnostic interfaces (plus in-memory defaults) that library features call through so a deployment can swap in Redis, SQL, or a custom backend. See STORAGE_SEAMS.md.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type CountQuotaRequest ¶
CountQuotaRequest reads the current count for (Principal, Key) without mutating state. Used by inspection paths (admin frontends, debugging); not on the Reserve / Release hot path.
type CountQuotaResponse ¶
type CountQuotaResponse struct {
Count int
}
CountQuotaResponse carries the current count. Implementations MAY approximate Count for performance under high contention — callers use it for inspection, never for correctness-critical comparisons.
type QuotaStore ¶
type QuotaStore interface {
ReserveQuota(ctx context.Context, req ReserveQuotaRequest) (ReserveQuotaResponse, error)
ReleaseQuota(ctx context.Context, req ReleaseQuotaRequest) (ReleaseQuotaResponse, error)
CountQuota(ctx context.Context, req CountQuotaRequest) (CountQuotaResponse, error)
}
QuotaStore is the storage seam behind a per-(Principal, Key) reservation counter. The shape is generic: Principal identifies who is reserving, Key names the thing being rate-limited (an event type, a tool name, a method — whatever the caller buckets on). The default in-memory implementation (NewInMemoryQuotaStore) keeps counts in a plain map; alternative implementations (Redis, SQL) plug in behind the same interface.
API shape follows the gRPC-style convention pinned in STORAGE_SEAMS.md:
Method(ctx context.Context, req XRequest) (XResponse, error)
ctx threads cancellation, deadlines, and trace context. Application-level state (Granted, Count) lives on the response — error is reserved for storage-layer failures (connection drops, transaction conflicts).
Concurrency contract: the atomicity unit is "compare current count against Max and conditionally increment" — a one-shot compare-and-set that maps cleanly onto Postgres `ON CONFLICT DO UPDATE … WHERE count < $max` and Redis `EVAL` scripts. The in-memory implementation does NOT take internal locks; it assumes the caller serializes access (the events Quota wrapper calls under its own mutex). Implementations shared across callers must take their own locks or use transaction semantics.
func NewInMemoryQuotaStore ¶
func NewInMemoryQuotaStore() QuotaStore
NewInMemoryQuotaStore returns the default in-memory storage implementation — a plain map with no internal locking. Suitable for single-process deployments and the default when no store is configured. Multi-replica deployments plug in a shared backend.
type ReleaseQuotaRequest ¶
ReleaseQuotaRequest returns one slot for (Principal, Key). The store decrements the count if it is currently > 0, otherwise no-ops (release-at-zero is a benign no-op, not an error). Pair 1:1 with ReserveQuota.
type ReleaseQuotaResponse ¶
type ReleaseQuotaResponse struct{}
ReleaseQuotaResponse is empty today; reserved for future fields.
type ReserveQuotaRequest ¶
type ReserveQuotaRequest struct {
Principal string
// Key names the bucket being rate-limited (event type, tool, method, ...).
Key string
// Max is the cap the caller read from its config. Must be > 0; callers
// short-circuit uncapped keys and never call the store with Max <= 0.
Max int
}
ReserveQuotaRequest asks the store to claim one slot for (Principal, Key) only if the current count is strictly less than Max. Implementations MUST perform the check + increment atomically with respect to concurrent Reserve / Release calls for the same key.
type ReserveQuotaResponse ¶
ReserveQuotaResponse carries the reservation outcome. Granted is true iff the slot was claimed; Count is the current count after reservation (when Granted) or the count that blocked reservation (when not).