feat: meter Pinecone query read units by family and caller

Merged
GI
1d ago
+447-15

Record Pinecone provider query attempts and returned read units by the shared namespace family, actual route mode (legacy or partitioned), and a closed set of explicit callers. Unknown labels map to other; no UID or partition namespace is exported. Missing usage contributes no inferred units and has a separate counter. Metering errors are swallowed and counted.

All 11 query sites in backend/database/vector_db.py pass their function name to OwnerIndex.query. Serving account-purge freshness verification is the one direct _query bypass and is separately metered as account_purge_verify, preserving headers, retries and routing. Operator migration queries, fetch/list and mutations are outside this query meter. No route/copy/mutation behavior changes; ships on without a flag.

Live response-shape proof: pinned pinecone==7.3.0, memories-backend-dev, synthetic-owner-only query, no matches or writes, metadata/values disabled. Response type QueryResponse, usage type pinecone.core.openapi.db_data.model.usage.Usage, usage.read_units == 1 (integer). The meter handles SDK attributes and mapping shapes. No production account data read.

Integrated local validation on 648ab1813aa5afd12652f2b3083654394ee1d87a: 424 tests passed via nice -n 19 bash backend/test.sh, selected with BACKEND_UNIT_TEST_FILE_LIST and run through omi-heavy (26 metering, 53 owner-routing, 345 storefront tests). Coverage includes real SDK models, integer/zero/missing/invalid units, fraction/float/boolean rejection, bounded labels, failed provider calls, exporter failures, preserved arguments/results, both account-purge copies, sync/async caller wiring and sync scrape admission. The independent storefront baseline fix is included from main (#24389); the PR diff remains seven meter files.

make preflight and scripts/pr-preflight --metadata-only --pr-body-file <draft> passed with the committed candidate and tracked files unchanged throughout. A local no-network smoke drove the actual OwnerIndex.query and utils.metrics.metrics_response with a pinned SDK response: the default-registry exporter contained one query and one read unit, and no owner label. Usage validation follows the pinned client's integer-only data-plane schema. GCP read tier: ro-prod; only infrastructure metric aggregates were read. Luna review and GitHub CI are separate merge gates.

Read-only ro-prod GMP aggregates on 2026-11-02 confirm existing Cloud Run application scraping for backend, desktop-backend, sync, sync-backfill, integration and notifications. The sync sidecar allowlist now admits omi_pinecone_.*. GKE listen/pusher use authenticated local Prometheus /metrics jobs but were absent from GMP; the GPU batch worker uses a separate registry and performs no Pinecone queries. The local Prometheus sync bridge does not import these counters. Details and acceptance commands: backend/docs/runbooks/pinecone-query-metering.md.

Dashboard-free query: which caller spends the most reported read units over 24h, for the GMP Cloud Run scope:

topk(10, sum by (caller) (
  increase(omi_pinecone_read_units_total{project_id="based-hardware",cluster="__run__"}[24h])
))

Inspect omi_pinecone_query_missing_usage_total and omi_pinecone_query_metering_errors_total alongside it. GMP totals do not include listen/pusher until that collection gap is resolved or local series are analyzed separately. This meter does not establish a root cause or savings.

Deployment: rollout lead to include backend image plus revision-local sync scrape profile in the next backend train; listen/pusher need their own image rolls to emit this instrumentation. Verify running release identity and newly emitted samples after deploy. No deployment performed by this lane.

Product invariants affected

none