Convex component for idempotent usage metering: record events with dedup keys, get O(1) per-period rollups, and enforce limits atomically in a single transactio
npm install @vllnt/convex-meteringUsage Metering tracks billable events as idempotent, per-period rollups in a Convex component: define meters with `sum`, `max`, or `last` aggregation, record against opaque subject refs with a host-chosen period string, and read O(1) per-period totals for limit checks or billing at your own rates. Idempotency is dedup-ledger-backed — retries return `{ recorded: false, reason: "duplicate" }` without inflating rollups, and the ledger survives `pruneRecords` so double-counting can't occur after retention cleanup. Data lives in sandboxed component tables, timestamps are server-sourced, and the component runs identically on Convex Cloud and self-hosted convex-backend.
Use @vllnt/convex-metering to define a meter with defineMeter and call record with an orgId as the subjectRef and a period string like '2026-06'. Each (meter, subject, period) triple maintains an O(1) rollup you can read with usage. Pricing stays in your own tables; the component only owns the accurate usage count.
The recordWithLimit method in @vllnt/convex-metering checks a usage cap and records the event in a single Convex transaction, eliminating the read-then-write race that causes overages. It returns a limit_exceeded reason without recording if the cap is already reached, so no compensating logic is needed.
Pass an idempotencyKey such as a request ID to the record call in @vllnt/convex-metering. The dedup ledger is stored separately from the usage records, so retries after pruneRecords still return recorded: false with reason duplicate and never increment the rollup a second time.
Call eraseSubject with a subjectRef to remove all usage records and rollups for that subject across every meter in one batched operation. @vllnt/convex-metering keeps subjectRef opaque, so it never stores PII itself; erasure targets whatever identifier the host application passes in.
@vllnt/convex-metering tracks accurate usage counts and rollups but does not store rates or compute invoice amounts. The component is intentionally rate-agnostic: you store pricing in your own Convex tables and multiply the usage value returned by usage() by your rate at billing time.
@vllnt/convex-metering supports three aggregations per meter: sum (accumulate all quantities), max (track peak value), and last (gauge, keeps the most recent value). The aggregation type is locked once any usage exists for that meter, so it cannot be changed after the first record call.
The period argument in @vllnt/convex-metering is an opaque string chosen by the host application. It can be anything: '2026-06', '2026-W24', 'all', or a custom string. The component does no date parsing, so any calendar system or timezone convention works as long as the host applies it consistently.
The verify method in @vllnt/convex-metering recomputes the rollup from remaining raw records and compares it to the stored value, returning consistent: true or false along with rollupValue, recomputedValue, and recordsRemaining. This is intended for billing reconciliation audits, not real-time reads.
Install with npm install @vllnt/convex-metering, then register the component in convex/convex.config.ts by importing metering from '@vllnt/convex-metering/convex.config' and calling app.use(metering). Instantiate Metering with components.metering in your mutation or query files. The peer dependency is convex@^1.41.0.