Skip to content

feat(zero-swift): inspector, query metrics, and slowMaterializeThreshold - #210

Merged
tmm merged 2 commits into
mainfrom
feat/zero-swift-inspector
Nov 14, 2026
Merged

tmm merged 2 commits into
mainfrom
feat/zero-swift-inspector

Conversation

@tmm

@tmm tmm commented Nov 14, 2026

Copy link
Copy Markdown
Member

Decision 3, Swift half: JS's inspector, query metrics, and slowMaterializeThreshold in zero-swift, lined up with the Rust port (next PR). Stacked on #206; zero-swift patch changeset, as earlier Swift feature changesets are.

var options = ZeroOptions(...); options.slowMaterializeThreshold = 5_000   // JS default
let insp = zero.inspector
try await insp.metrics()                       // client digests merged with the server's
try await insp.client.queries()                // ZQL, row counts, hydrate/update timings
try await insp.clientsWithQueries()
try await insp.serverVersion()
try await insp.analyzeQuery(request, options: .init(joinPlans: true))
try await insp.authenticate(password: "...")

Same shape as Rust's zero.inspector(): metrics, client/clientGroup (queries, map, rows, clients, clientsWithQueries), serverVersion, analyzeQuery / analyzeServerAST / analyzeNamedQuery, authenticate, formatPlannerEvents; Metrics of five TDigests; typed InspectorQuery and AnalyzeQueryResult.

How it differs from JS, as Rust does:

  • No password dialog: a refused request throws InspectorError.authenticationRequired; authenticate(password:) remembers an accepted password and presents it again after a reconnect, in JS's wire order.
  • Pending requests fail on a dropped connection or close() instead of waiting forever.
  • The slow-materialization log is one line, <message> <queryID> <AST JSON> <ms>.
  • TDigest matches JS bit for bit: V8's fdlibm asin/sin are ported, since glibc's differ in the last bit and move centroid splits (checked on 846,739 inputs and 400 seeded JS digests).

Metrics are recorded where JS calls addMetric; updates are sampled 1 in 32 and weighted 32 (UPDATE_SAMPLE_RATE).

Tests: TDigestTests (7) and InspectorTests (13), with expected values from running the repo's JS sources. Locally: swift format lint --strict, ZeroTests 160 passed, ConformanceTests 63 passed, docs build, and scripts/api-breaks.sh: no public API breaks since 0.7.12. The Rust PR adds the shared JS vectors (client-inspector.json, tdigest.json, ast-to-zql.json, and logs in two existing ones), and Swift's conformance runner replays them there.

Base automatically changed from feat/zero-rust-traceparent to main November 14, 2026 12:35
…reshold

zero.inspector ports @rocicorp/zero's Inspector over the sync connection's inspect messages. Where JS prompts for the admin password, a request throws InspectorError.authenticationRequired until authenticate(password:) succeeds; the accepted password is presented again on an unauthenticated later connection. The client records JS's query metrics in a port of shared/src/tdigest.ts, with fdlibm's asin and sin so its quantiles match Node's bit for bit, and ZeroOptions.slowMaterializeThreshold logs slow end-to-end materializations.
…rs check

TDigest.quantile returns NaN, as JS does, where rounding leaves the last
cumulative weight below the total, instead of trapping. The connect URL
asks for debugPerf at the debug log level, as JS and Rust do. An
inspector query's inactivatedAt is new Date(ms)'s time value, without
its fraction.
@tmm
tmm force-pushed the feat/zero-swift-inspector branch from 581f934 to 1541981 Compare November 14, 2026 12:36
@tmm
tmm merged commit 4629981 into main Nov 14, 2026
22 checks passed
@tmm
tmm deleted the feat/zero-swift-inspector branch November 14, 2026 14:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants