Skip to content

feat(compat): Garnet is a full Redis relative, QuestDB is refused a row, and a failed schema read shows the wrong tables (#424) - #506

Merged
cevheri merged 1 commit into
mainfrom
probe/questdb-garnet-wire-compat
Aug 26, 2026
Merged

cevheri merged 1 commit into
mainfrom
probe/questdb-garnet-wire-compat

Conversation

@cevheri

@cevheri cevheri commented Aug 26, 2026

Copy link
Copy Markdown
Member

Continues #424 Phase 0. Stacked on #503 (Apache Doris) — that PR's commit is the parent
here, so review or merge it first and this diff reduces to its own commit.

Two engines probed, one entry

Engine Verdict Probed
Garnet 2.1.5 registered full via redis ghcr.io/microsoft/garnet:2.1.5
QuestDB 10.0.1 measured and refused a row questdb/questdb:10.0.1

Both called surface by surface through the real provider, then read again in real Chrome
against bun run build && bun run start.

QuestDB is the reason this PR exists

Everything short of a browser said "register it": QuestDB speaks the PostgreSQL wire protocol
on 8812, and through the provider SELECT country, count() c FROM probe_orders returns three
rows. So it looks exactly like a third query-editor-only relative beside Materialize and
RisingWave.

In the product the editor cannot run anything. Pressing Run answers
500 unknown function name: pg_backend_pid() — the editor always attaches a queryId, and
the provider then issues SELECT pg_backend_pid() before the statement. Measured both ways at
the provider boundary so the cause is established rather than inferred:

provider.query(sql)                 -> OK, 3 rows
provider.query(sql, [], "editor-1") -> FAIL unknown function name: pg_backend_pid()

A query-editor-only tier claims exactly one thing, and that is the thing that does not work.
This is the Cloud Spanner case already recorded in this repo, found the same way, so it is
handled the same way: no registry entry, and the number is the finding in
docs/providers/README.md under Measured, and refused a row. The unit test and the e2e hint
assertion both pin the absence, because QuestDB is the most re-addable name on that list —
and the questdb compose service stays under the compat profile so the refusal can be
reproduced instead of trusted.

Recorded there too, because it is the trap for whoever revisits this: QuestDB is not the
Materialize/RisingWave case of an absent catalog. information_schema.tables lists the table
and pg_catalog.pg_class answers with 36 columns. The object browser fails on two independent
causes of ours — the schema query's AS MATERIALIZED CTEs are a syntax error there, and
with the keyword removed the same query dies on pg_total_relation_size() — so fixing either
one alone changes nothing.

Garnet

Every Redis surface answers, so full by this registry's own definition, with the wrong numbers
recorded as caveats the way Citus's are. Browser-verified: tree user:* 50, session:* 20,
queue:* 1; a {"command":"GET","args":["user:1"]} run returned the value; Monitoring showed
7.4.3, Connections 0 / no limit published, Tables 71 / 0 indexes.

What makes its row different from the three Redis relatives already here:

  • its own version was available and went unread — INFO carries garnet_version:2.1.5 and
    server_name:garnet, and the overview shows Redis 7.4.3. Valkey and DragonflyDB publish only
    an emulation level and KeyDB nothing at all, so on those three the displayed version is the best
    reading available; here it is not;
  • two readings are absences wearing a value, both checked against Redis 8.10.0 in the same
    pass: no used_memory, so every size reads 0 B; no keyspace_hits / keyspace_misses, so the
    cache hit ratio reads 100% — and the dashboard rates it Excellent, which is D14's fallback
    being graded;
  • fixture note: it is in-memory with no persistence, measured — dbsize 71 before a docker stop,
    0 after.

D31, found while chasing the QuestDB tree

A connection whose POST /api/db/schema/list fails leaves the previous connection's tables on
screen as its own
. Measured with the network captured: probe_customers 3 and probe_orders 2.0k
— Doris's tables, Doris's row counts — under a header reading QuestDB 10.0.1 (probe), while
QuestDB's own questdb_only_marker never appeared and QuestDB has no probe_customers at all.

fetchSchema in src/hooks/use-connection-manager.ts toasts and returns without touching
schema; Studio.tsx clears it only when the active connection becomes null. Storage is not
involved — /api/storage/config answers serverMode: false and libredb_schema_snapshots was
empty. Worse than an empty tree, because it attributes real objects with real row counts to a
database that does not contain them and the tables are clickable. Hits every engine whose object
browser fails: CockroachDB, Materialize, RisingWave, QuestDB.

Filed as D31, not fixed — the fix is a hook change with its own coverage, not a line inside a
labelling PR. One observation is parked in the entry rather than filed separately: a single
connection selection issues the same schema/list read two or three times.

Counts

Relatives 20 → 21, published figure 34 → 35, both re-derived from connectableProductCount()
(14 external drivers + 21 relatives). The shipped half does not move — no new driver, no new
dependency.

Verification

format · lint · typecheck · knip · build · tests/run-core.sh (351 files) ·
tests/run-components.sh (33 groups). Browser pass on the built app: hero reads
Plus 21 wire-compatible engines and names Garnet and Apache Doris but not QuestDB; the
PostgreSQL hint names CockroachDB and not QuestDB; the Redis hint names Garnet with no tier
suffix; the MySQL hint names Apache Doris.

…ow, and a failed schema read shows the wrong tables (#424)

Two engines probed on 2026-08-26 through the real providers and then read again in
Chrome on the built app. Only one of them earns an entry, and the one that does not is
the point of this change.

**Garnet 2.1.5, registered `full` via `redis`.** Every Redis surface answers: the key
browser grouped 71 keys into `user:*` (50), `session:*` (20) and `queue:*` (1), a
`GET user:1` run returned the value, and Monitoring rendered version, uptime, sessions
and table counts. It is the first relative here whose own version was available and went
unread - `INFO` carries `garnet_version:2.1.5` and `server_name:garnet` while the product
shows Redis 7.4.3, where Valkey and DragonflyDB publish only an emulation level and KeyDB
nothing at all. Two of its readings are absences wearing a value, both from `INFO` fields
Garnet does not publish and both checked against Redis 8.10.0 in the same pass: no
`used_memory`, so every size reads 0 B; and no keyspace counters, so the cache hit ratio
reads 100% - and the dashboard rates that `Excellent`, which is D14's fallback being
graded. The row says so.

**QuestDB 10.0.1 is measured and REFUSED a row, and the refusal is the finding.** It
speaks the PostgreSQL wire protocol and through the provider a statement answers -
`SELECT country, count() c FROM probe_orders` returned three rows - so every signal short
of a browser said "query-only relative, register it". In the product the editor cannot run
anything: it always attaches a `queryId`, the provider then issues `SELECT pg_backend_pid()`
first, and QuestDB has no such function, so pressing Run answers
`500 unknown function name: pg_backend_pid()`. Measured both ways at the provider boundary
to establish the cause rather than guess it: the identical call with a `queryId` fails and
without one returns the rows. A query-editor-only tier claims exactly one thing and that is
the thing that does not work, which makes this the Cloud Spanner case - the number is the
finding, and it lives in `docs/providers/README.md` under "Measured, and refused a row".
The unit test and the e2e hint assertion both pin the ABSENCE, because QuestDB is the most
re-addable name on that list.

Recorded there too, for whoever revisits it: QuestDB is not the Materialize/RisingWave case
of an absent catalog. `information_schema.tables` and `pg_catalog.pg_class` both answer, and
the object browser fails on two independent causes of ours - `AS MATERIALIZED`, then
`pg_total_relation_size()` - so fixing either alone changes nothing.

**D31, found while chasing that.** A connection whose `POST /api/db/schema/list` fails
leaves the previous connection's tables on screen as its own: measured with the network
captured, `probe_customers 3` and `probe_orders 2.0k` (Doris's tables and Doris's numbers)
under a header reading QuestDB, while QuestDB's own table never appeared. `fetchSchema`
returns on failure without clearing `schema`, and storage is not involved
(`serverMode: false`). It is worse than an empty tree, because it attributes real objects
with real row counts to a database that does not contain them, and it hits every engine
whose object browser fails - CockroachDB, Materialize, RisingWave, QuestDB. Filed, not
fixed: the fix is a hook change with its own coverage, not a line inside a labelling PR.

Counts: relatives 20 -> 21, published figure 34 -> 35, both re-derived from
`connectableProductCount()`. The `questdb` compose service is kept under the `compat`
profile deliberately, so the refusal can be reproduced instead of taken on trust.

Local gates: format, lint, typecheck, knip, build, run-core.sh (351 files),
run-components.sh (33 groups).
@cevheri

cevheri commented Aug 26, 2026

Copy link
Copy Markdown
Member Author

Rebased onto origin/main after #503 merged (208011fb), and one docs edit was added rather than carried over: main also gained #505, which made docs/providers/redis.md name its three relatives explicitly. Garnet is the fourth, so that section is now Valkey, DragonflyDB, KeyDB and Garnet and three of its paragraphs say what Garnet does differently — the unread garnet_version, the two absent INFO fields behind the 0 B size and the 100% cache ratio, and the fact that CLIENT LIST itself answers correctly so the divergence is in the counters above it, not the session row. No conflict produced this; nothing I touched overlapped, which is exactly why it needed looking for.

@cevheri
cevheri force-pushed the probe/questdb-garnet-wire-compat branch from 25fd43f to 1aa0376 Compare August 26, 2026 21:06
@sonarqubecloud

Copy link
Copy Markdown

@cevheri
cevheri merged commit d8d9e80 into main Aug 26, 2026
19 checks passed
@cevheri
cevheri deleted the probe/questdb-garnet-wire-compat branch August 26, 2026 21:13
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.

1 participant