Four other servers behind one Redis connection
There is no separate connection type and no per-server branch, so the version panel shows the compatibility level a server publishes, not its product release.
To reach Valkey, DragonflyDB, KeyDB or Garnet you pick Redis in the connection
dialog, because there is no other entry to pick. That is the documented route,
not a workaround: ioredis speaks the protocol the whole family shares, and the
provider behind the redis type id is what a Valkey connection uses. The
overview panel then reports a Valkey 9.1.1 server’s version as 7.2.4.
Neither of those is a bug. Both are the same decision, and it decides which numbers on that screen you can trust.
One connection type, four more servers
src/lib/db/compatibility.ts lists four Redis-protocol relatives at the full
tier, each against the version it was probed on: Valkey 9.1.1, DragonflyDB
df-v1.40.1, KeyDB 6.3.4 and Garnet 2.1.5. full there means every introspection
surface answered - and the tier definition in that same file says caveats may
still record data that is present but inaccurate, which is why a full engine
can carry them. There is no valkey type id, no dragonfly one, and no branch
anywhere in the provider that asks which of the five it is talking to.
So a DragonflyDB session and a Redis one run identical code. Schema discovery is
the same bounded SCAN with COUNT 100, capped at 1000 keys, that groups
user:123 and user:456 into a user:* row. The editor dispatches the same
generic client.call(). Monitoring reads the same INFO, SLOWLOG GET 10 and
CLIENT LIST. Nothing renames the server and nothing reads a vendor-specific
field, because the alternative is asserting a product the server’s own INFO
reply never claimed.
Why the version shown is a compatibility number
getOverview() reads one field, parsed.redis_version, and passes it through as
the server gave it. That field is the only version line the whole family
publishes, which is exactly why it is the one that gets read - and exactly why
what it reports is a compatibility level rather than a release.
| Server (probed version) | redis_version in INFO |
What the panel shows |
|---|---|---|
| Valkey 9.1.1 | 7.2.4 |
7.2.4 |
| DragonflyDB df-v1.40.1 | 7.4.0 |
7.4.0 |
| KeyDB 6.3.4 | absent | indistinguishable from a Redis 6 server |
| Garnet 2.1.5 | 7.4.3 |
7.4.3 |
Valkey is the clean case: the server declares the Redis level it implements, the
panel repeats it, and the caveat is recorded in compatibility.ts beside the
entry rather than left for you to discover. KeyDB is the thin case - it
publishes no version field of its own, so there is nothing to distinguish its
overview from a Redis 6 server.
Garnet is the case where the choice is visible. Its INFO carries
garnet_version:2.1.5 and server_name:garnet sitting right beside
redis_version:7.4.3, so the real product and release genuinely were available
and go unread. The branch that would read them stays unwritten for the reason
the provider gives everywhere else: the moment one field is special-cased, the
panel starts making a claim about the server’s identity instead of reporting the
reply the server sent. The reading it gives now is narrow and consistent: this
is the compatibility level the server published.
What each server publishes, and what it omits
The same read-it-as-given rule governs the session list, and there the
divergences are wider than one field. Every CLIENT LIST field is optional, and
getActiveSessions() substitutes a default for anything absent: user falls back
to default, pid to 0, state to N, command to idle. A server that omits a
field therefore produces a plausible-looking row rather than an error.
- DragonflyDB fills
namewith the connection id, so the user column shows a number rather than reaching thedefaultfallback at all. Its line carries nocmd=and noflags=, so every session readsidleandNwhatever that session is doing. - KeyDB reports a command without its subcommand -
cmd=clientwhere Redis and Valkey both sendcmd=client|list- and the query column shows it verbatim. - Garnet answers
CLIENT LISTin full. Its session rows are complete and correct.
One field on that list is not a relative’s quirk at all: the user column reads
name, not the user field Redis also publishes, and name= is empty until a
client calls CLIENT SETNAME. Every engine here shows default, Redis
included.
Dashboard numbers that are absences wearing a value
maxclients is not universal. Redis, Valkey and KeyDB each publish
maxclients:10000. Neither DragonflyDB’s nor Garnet’s INFO carries the line,
so max connections reads 0 - an absent limit shown as a zero, not a measured
one.
Garnet is where that goes furthest, and this is the limit to carry away from the
post. On one of these servers three dashboard numbers are absences wearing a
value: no memory figure, so size reads zero bytes on a populated server, no hit
counters, so the cache ratio falls back to a full score, and a client count that
stays zero. In fields: Garnet publishes no used_memory, so database size and
the memory storage panel read 0 B on a populated server; it publishes neither
keyspace_hits nor keyspace_misses, so the cache hit ratio takes the : 100
fallback the provider uses when there has been no traffic, and the dashboard
rates that a full score, Excellent; and connected_clients stays 0 with a
client attached, even though CLIENT LIST answers correctly, so the session
list below the counter is right while the counter above it is not.
None of those three is a failure the panel can see. A zero is a legal reading of a metric that exists, and 100 percent is a legal hit ratio. The dashboard is faithfully reporting a reply that does not contain the number it is asking for.
When to trust a gauge on a compatible server
The rule that falls out of this is narrow and usable: trust the surfaces that read a list, and check the surfaces that read a scalar.
The list-shaped reads carry their own evidence. CLIENT LIST produces one row
per client and you can count the rows. SLOWLOG GET 10 produces entries or it
produces none. The key browser scans real key names and shows you what it found.
When those disagree with a counter, the list is the one that was measured.
The scalar reads - version, memory, hit ratio, client count, max connections -
come from single INFO fields, and a missing field is indistinguishable from a
zero once it reaches a gauge. The check takes one line in the editor: run INFO
and read the section yourself. parseInfoResult() renders the bulk reply as one
row per metric, each tagged with its # Section header, so an absent
used_memory is visibly absent rather than rounded to 0 B. That is also what
the single maintenance operation does, for the admins who have the Operations
page - analyze runs INFO and reports a line count, and there is nothing else
in the maintenance list here.
Two more boundaries travel with the family, not with any one relative. Agent
AUTO mode does not run on any of these servers: the read-only profile is
database-native and only PostgreSQL, SQLite and DuckDB implement it, so an auto
run ends engine-unsupported. Agent PLAN mode does open, toolless, executing
nothing, and it is told in one sentence that a user:* row is a grouping this
server derived from a bounded scan rather than a key any command can be given.
That same fact is why the schema explorer offers no per-row Profile Table,
Generate Test Data or maintenance action here.
The engine list publishes what each engine deliberately cannot do next to its transport and port, and the capability pages publish the limit beside each feature.