Story P0.6 — Pricing-funnel instrumentation (cap-hits), elasticsql leg
Pre-mortem Killer #2 / launch ship-blocker: the leading-indicator signal (cap-hits by quota type) is the only missing PRD §15.1 funnel class. This leg adds the SEMANTIC cap-hit primitive to the shared TelemetryCollector and wires the maxQueryResults reject site.
Changes
licensing/.../TelemetryCollector.scala: NEW CapHitKind sealed trait {MaterializedViews, QueryResults, Joins, Clusters} (snake-case key); 4 independent lock-free AtomicLong cap-hit DELTA buckets; incrementCapHit(kind); collectAndResetCapHits() (getAndSet, all-4-keys); TelemetryData.capHitsByKind populated in collect (read, no reset) and collectAndReset (getAndSet); Noop overrides. Mirrors the Story-15.3 JOIN-bucket pattern exactly.
core/.../extensions/CoreDqlExtension.scala: capture capHitCollector = licenseRefreshStrategy.telemetryCollector at initialize; incrementCapHit(QueryResults) on BOTH capOrReject branches — the explicit-LIMIT 402 reject AND the P0.5 no-LIMIT truncation/cappedScroll bite (OQ-2). Suppressed JOIN legs do not count (no double-count).
- Tests:
TelemetryCollectorSpec (+10 cap-hit cases), CoreDqlExtensionSpec (+5 cap-hit cases). Both green.
Design decisions
- OQ-1: semantic
incrementCapHit(kind) at each reject site, NOT 'count 402s' (joins/clusters never raise a 402).
- OQ-2: the no-LIMIT truncation IS counted as a QueryResults cap-hit.
- Cap-hits ride the existing
InstancePing daily-ping delta (read via collect, like joinQueryCount).
Build
+ core/compile + + licensing/compile + + softclient4es-core-testkit/compile (cross 2.12+2.13) green
licensing/testOnly *TelemetryCollectorSpec 24 green; core/testOnly *CoreDqlExtensionSpec 14 green; headerCheck clean
- publishLocal'd softclient4es-core + softclient4es-licensing 0.20-SNAPSHOT (unblocks extensions + arrow legs)
Part of the cross-repo P0.6 epic (elasticsql → extensions → arrow → license-server). Phase 0 final story.
Story P0.6 — Pricing-funnel instrumentation (cap-hits), elasticsql leg
Pre-mortem Killer #2 / launch ship-blocker: the leading-indicator signal (cap-hits by quota type) is the only missing PRD §15.1 funnel class. This leg adds the SEMANTIC cap-hit primitive to the shared TelemetryCollector and wires the maxQueryResults reject site.
Changes
licensing/.../TelemetryCollector.scala: NEWCapHitKindsealed trait {MaterializedViews, QueryResults, Joins, Clusters} (snake-casekey); 4 independent lock-freeAtomicLongcap-hit DELTA buckets;incrementCapHit(kind);collectAndResetCapHits()(getAndSet, all-4-keys);TelemetryData.capHitsByKindpopulated incollect(read, no reset) andcollectAndReset(getAndSet);Noopoverrides. Mirrors the Story-15.3 JOIN-bucket pattern exactly.core/.../extensions/CoreDqlExtension.scala: capturecapHitCollector = licenseRefreshStrategy.telemetryCollectoratinitialize;incrementCapHit(QueryResults)on BOTHcapOrRejectbranches — the explicit-LIMIT 402 reject AND the P0.5 no-LIMIT truncation/cappedScroll bite (OQ-2). Suppressed JOIN legs do not count (no double-count).TelemetryCollectorSpec(+10 cap-hit cases),CoreDqlExtensionSpec(+5 cap-hit cases). Both green.Design decisions
incrementCapHit(kind)at each reject site, NOT 'count 402s' (joins/clusters never raise a 402).InstancePingdaily-ping delta (read viacollect, likejoinQueryCount).Build
+ core/compile++ licensing/compile++ softclient4es-core-testkit/compile(cross 2.12+2.13) greenlicensing/testOnly *TelemetryCollectorSpec24 green;core/testOnly *CoreDqlExtensionSpec14 green;headerCheckcleanPart of the cross-repo P0.6 epic (elasticsql → extensions → arrow → license-server). Phase 0 final story.