Skip to content
Tracon

IQuotaStore

Namespace Tracon · Assembly Tracon.Abstractions.dll

The store for quota rules and consumption counters.

public interface IQuotaStore

Two different kinds of data live here: rules (defined by an administrator, change rarely) and counters (increment at the end of every run). Both sit in the same contract because a check always reads them together.

Important: IQuotaStore.AddUsageAsync must be atomic — the PostgreSQL implementation uses INSERT ... ON CONFLICT DO UPDATE. Concurrent runs increment the same row and no increment is lost.

AddUsageAsync(QuotaConsumption, IReadOnlyDictionary<QuotaPeriod, DateOnly>, CancellationToken)

Section titled “ AddUsageAsync(QuotaConsumption, IReadOnlyDictionary<QuotaPeriod, DateOnly>, CancellationToken)”

Adds a consumption to both the agent counter and the tenant-wide counter.

ValueTask AddUsageAsync(QuotaConsumption consumption, IReadOnlyDictionary<QuotaPeriod, DateOnly> periodStarts, CancellationToken cancellationToken = default)

consumption QuotaConsumption

The consumption.

periodStarts IReadOnlyDictionary<QuotaPeriod, DateOnly>

For each period, the first day of the period the consumption falls into. The caller computes this from the configured time zone; the store does not know time zones.

cancellationToken CancellationToken

The cancellation token.

ValueTask

The completion task.

The store carries no time zone: the caller computes the period boundary and passes it ready-made. This lets the same store behave correctly for consumers running in different time zones.

DeleteAsync(string, Guid, CancellationToken)

Section titled “ DeleteAsync(string, Guid, CancellationToken)”

Deletes a rule.

ValueTask<bool> DeleteAsync(string tenantId, Guid id, CancellationToken cancellationToken = default)

tenantId string

The tenant identifier.

id Guid

The rule identifier.

cancellationToken CancellationToken

The cancellation token.

ValueTask<bool>

true if the delete happened.

GetAsync(string, Guid, CancellationToken)

Section titled “ GetAsync(string, Guid, CancellationToken)”

Fetches a single rule.

ValueTask<QuotaDefinition?> GetAsync(string tenantId, Guid id, CancellationToken cancellationToken = default)

tenantId string

The tenant identifier.

id Guid

The rule identifier.

cancellationToken CancellationToken

The cancellation token.

ValueTask<QuotaDefinition?>

The rule; null if it does not exist or belongs to another tenant.

GetUsageAsync(QuotaUsageQuery, CancellationToken)

Section titled “ GetUsageAsync(QuotaUsageQuery, CancellationToken)”

Fetches the current period’s counters.

ValueTask<IReadOnlyList<QuotaUsageRecord>> GetUsageAsync(QuotaUsageQuery query, CancellationToken cancellationToken = default)

query QuotaUsageQuery

The filter.

cancellationToken CancellationToken

The cancellation token.

ValueTask<IReadOnlyList<QuotaUsageRecord>>

The counters. An empty list when there is no consumption at all.

Lists a tenant’s quota rules.

ValueTask<IReadOnlyList<QuotaDefinition>> ListAsync(string tenantId, CancellationToken cancellationToken = default)

tenantId string

The tenant identifier.

cancellationToken CancellationToken

The cancellation token.

ValueTask<IReadOnlyList<QuotaDefinition>>

The rules.

SaveAsync(QuotaDefinition, CancellationToken)

Section titled “ SaveAsync(QuotaDefinition, CancellationToken)”

Saves a rule. Overwrites an existing rule for the same scope (tenant + agent + period), if one already exists.

ValueTask<QuotaDefinition> SaveAsync(QuotaDefinition definition, CancellationToken cancellationToken = default)

definition QuotaDefinition

The rule.

cancellationToken CancellationToken

The cancellation token.

ValueTask<QuotaDefinition>

The saved rule.

Scope uniqueness is built over COALESCE(agent_name, ''); a plain UNIQUE constraint treats NULLs as distinct from each other and would let the same rule be added an unlimited number of times.

TryClaimThresholdNotificationAsync(string, string, QuotaPeriod, DateOnly, QuotaMetric, int, CancellationToken)

Section titled “ TryClaimThresholdNotificationAsync(string, string, QuotaPeriod, DateOnly, QuotaMetric, int, CancellationToken)”

Atomically claims a quota threshold notification for one scope/period, so that only one caller among concurrent workers — and only the first run to reach it across a process restart — proceeds to publish it.

ValueTask<bool> TryClaimThresholdNotificationAsync(string tenantId, string agentName, QuotaPeriod period, DateOnly periodStart, QuotaMetric metric, int thresholdPercent, CancellationToken cancellationToken = default)

tenantId string

The tenant identifier.

agentName string

The scope: an agent name, or an empty string ("") for the tenant-wide counter — the same convention QuotaUsageRecord.AgentName uses.

period QuotaPeriod

The counter’s interval.

periodStart DateOnly

The first day of the period (local time).

metric QuotaMetric

The metric whose threshold was crossed.

thresholdPercent int

The crossed threshold percentage.

cancellationToken CancellationToken

The cancellation token.

ValueTask<bool>

true if this call is the one that claims the threshold — no earlier call claimed it in this period, and the caller should publish the notice. false if it was already claimed (by an earlier run, or a concurrent worker), or the usage row for this scope/period does not exist yet.

The claim survives a process restart: it lives in the same durable row IQuotaStore.AddUsageAsync writes to, not in memory. When the period rolls over, a new row forms and the claim starts clean — there is nothing to reset by hand.