feat(usage): record per-tenant usage events into the app DB
Deck #67 data-plane slice: tenant Pods record billable operations (embedding queries, pages/chunks embedded) into an app-DB usage_events table that the control plane later pulls read-only into the billing ledger and syncs to Stripe Meter Events. - migration 007: usage_events table (Postgres TIMESTAMPTZ/JSONB/UUID with portable SQLite fallbacks), indexed (occurred_at, metric) for the CP rollup's per-day range scan + GROUP BY metric. - UsageEventStore: best-effort, flag-gated writer reusing the shared RefreshTokenStorage engine; ON CONFLICT (event_id) DO NOTHING for idempotent retries; dialect-branched occurred_at bind. All work (incl. metadata JSON encode) is swallowed so a metering failure never surfaces to the user op. - USAGE_METERING_ENABLED flag (default off) wired through Settings + env map; off-path touches no storage, so OSS self-hosters get an empty table and zero write overhead. - two recording hooks: embeddings_queries (per nc_semantic_search, which nc_semantic_search_answer reuses) and pages_chunks (after dense embedding succeeds, covering both in-process and procrastinate paths). - storage.acquire()/.dialect public seams so the sibling store doesn't reach into the underscored internal. - tests parametrized over SQLite + Postgres: flag-off no-op, roundtrip, ON CONFLICT dedup, JSON/NULL metadata, and the best-effort swallow of both DB errors and unserializable metadata. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
42505f8f87
commit
1c6b1a84ea
@@ -157,6 +157,18 @@ class TestGetSettings:
|
||||
assert settings.vector_sync_processor_workers == 5
|
||||
assert settings.vector_sync_queue_max_size == 5000
|
||||
|
||||
@patch.dict(os.environ, {}, clear=True)
|
||||
def test_usage_metering_disabled_by_default(self):
|
||||
"""USAGE_METERING_ENABLED defaults to False (OSS doesn't self-monitor)."""
|
||||
_reload_config()
|
||||
assert get_settings().usage_metering_enabled is False
|
||||
|
||||
@patch.dict(os.environ, {"USAGE_METERING_ENABLED": "true"}, clear=True)
|
||||
def test_usage_metering_enabled_via_env(self):
|
||||
"""USAGE_METERING_ENABLED=true maps to settings.usage_metering_enabled."""
|
||||
_reload_config()
|
||||
assert get_settings().usage_metering_enabled is True
|
||||
|
||||
|
||||
class TestChunkConfigValidation:
|
||||
"""Test document chunking configuration validation."""
|
||||
|
||||
Reference in New Issue
Block a user