Runtime metrics for generated executors (design doc 18) - #22
Merged
Conversation
Neutral Observer/Stats surface in runtime/, codegen wiring with a generate-time ShapeSpace registry, and an OTel adapter in a separate contrib module (OpenMetrics obtained via the Prometheus exporter). Decisions D1-D8 settled with all recommendations adopted. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U8QzqDQ6XJpN3hbD7y8nzi
Observer (compose/exec/reject events), ComposedCache.SetObserver, CacheStats/Stats, TopShapes, and ShapeSpaceInfo. Hits are counted on the entry and folded on eviction so eviction never loses history; counting is gated behind first observability use after the parallel hit-path benchmark showed the ungated increment costing ~2.5x, and observers receive the entry's retained key because threading the caller's key through the interface call heap-allocated every call site's key slices. Unobserved traffic measures at baseline; BenchmarkGeneratedCallParallelObserved pins the enabled cost. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U8QzqDQ6XJpN3hbD7y8nzi
Queries gains SetObserver (shared with the cache; WithTx carries it) and a Cache() accessor for scrape-time Stats/TopShapes. Every method reports ObserveReject on its validation branches, and ObserveExec with a guarded clock and per-annotation row counts; @filter-tree exec events route through observeExecTree so the call site never encodes the tree twice. db.gen.go carries the generate-time ShapeSpace registry from shape.Count, saturating to uint64 with explicit Exact and Unbounded flags. Examples regenerated for all three dialects. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U8QzqDQ6XJpN3hbD7y8nzi
Trace correlation and metric exemplars need the request context, and only generated code has it — ObserveExec/ObserveReject gain a ctx first parameter (ObserveCompose stays context-free: the cache API takes none, and the exec event repeats the key). Examples regenerated. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U8QzqDQ6XJpN3hbD7y8nzi
Package otelsqletch in its own go.mod so the OTel graph never touches runtime/ or consumers who skip it. Metrics/Bind wire a generated Queries into the doc-17 instrument table: per-event counters and histograms labeled by query/instance only, scrape-time callbacks over Stats/TopShapes/ShapeSpace, bounded exact used-shape tracking with a loud saturated flag, and rejectName classification via errors.Is. TraceObserver decorates any observer with span attributes — the home for full-cardinality shape keys, per the §7 rule the tests assert negatively. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U8QzqDQ6XJpN3hbD7y8nzi
The three generated-module E2E fixtures install a recording observer and pin the exec/compose 1:1 relationship, the deliberate-misuse reject counts, cache-stats coherence, and the ShapeSpace flags (@filter-tree unbounded everywhere; @in unbounded on the expanding dialects). Manual gains the 'Runtime metrics' chapter — instrument table, cardinality policy, and the prepared-statement join recipe — and CI tests the contrib/otel module. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U8QzqDQ6XJpN3hbD7y8nzi
Conflict resolution: generate.go combines the Option[T] scan temporaries with the doc-17 observation sites, and the new :maybe-one annotation gains exec observation (no-row branch reports rows 0 with a nil error — a successful empty execution, not a failure). Examples regenerated on the merged generator. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U8QzqDQ6XJpN3hbD7y8nzi
Design number 17 was taken on main by the go-optional adoption doc; all references (runtime, codegen templates, contrib/otel, manual, CI) follow, and the examples are regenerated for the emitted-comment change. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U8QzqDQ6XJpN3hbD7y8nzi
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements design doc 18 — runtime metrics for generated executors (included as the first commit, D1–D8 settled with all recommendations adopted): operators can now answer, from a metrics endpoint, how the composed-SQL cache performs, which shapes traffic actually uses vs. what was verified, how long calls take, and what gets refused before any SQL is sent.
Layers
runtime/— neutral observation surface (no new dependencies)Observerinterface:ObserveCompose(from the cache, hit/miss + the retainedShapeKey),ObserveExec(ctx, canonical key encoding, duration, rows, error),ObserveReject(the sentinel refusals).ComposedCache.Stats()(hits/misses/inserts/evictions/entries/capacity/SQLBytes) andTopShapes(n)— scrape-time snapshots under the mutex, deterministic ordering.ShapeSpaceInfo— the shared struct behind the generated registry.codegen — generated packages wire it up
Queries.SetObserver(travels throughWithTx, shared with the cache) +Cache()accessor.@filter-treeroutes throughobserveExecTreeso the call site never encodes the tree twice).db.gen.gocarries aShapeSpaceregistry computed fromshape.Countat generate time: enumerable count (uint64-saturating,Exactflag) +Unboundedflag (@filter-treeeverywhere;@inarity on expanding dialects). Examples regenerated for all three dialects.contrib/otel— OpenTelemetry adapter, separate go.modotelsqletch: the doc-18 §6 instrument table, per-event series labeled by query/instance only, bounded exact used-shape tracking with a loudsaturatedflip (D5), scrape callbacks overStats/TopShapes/ShapeSpace, and aTraceObserverdecorator that puts full-cardinality shape keys where they belong — on spans, never on metric labels (§7, asserted negatively in tests).Performance findings (measured, pinned in doc 18 §4/D4 and by benchmarks)
SetObserver/Stats/TopShapes. Unobserved traffic measures at baseline (12.0–12.3 ns/op, 1 alloc);BenchmarkGeneratedCallParallelObservedpins the metrics-on worst case (~130 ns/op).ShapeKeythrough the observer interface heap-allocated every call site's key slices (+1 alloc/op, observed or not). Fix: the cache passes its retained key (proven identical bykeysEqual), andObserveExectakes the canonical encoding, built inside the observer guard.Testing
runtime/: scripted CLOCK-eviction stats pins, hits-survive-eviction fold, all three serve paths' hit bits, concurrent same-shape coherence, allocation-free hit path with and without an observer (AllocsPerRun), stats-under-churn cross-checks, race-detector scrape-during-churn.ShapeSpacegolden assertions incl. uint64 saturation via a 17-key@order-by;TestComposeConformancepasses unmodified.contrib/otel: SDK manual-reader tests for every instrument, the no-shape-key-label rule, saturation flip,TraceObserverspan attributes/events/forwarding.ShapeSpaceflags.golangci-lint --build-tags devdb, standalone staticcheck (main + contrib), goimports: clean. Fuzz smoke on both targets: pass.🤖 Generated with Claude Code
https://claude.ai/code/session_01U8QzqDQ6XJpN3hbD7y8nzi