Skip to content

fix(plugin-redis): list databases on a server that refuses CONFIG - #3037

Closed
junghyuneun wants to merge 1 commit into
TableProApp:mainfrom
junghyuneun:fix/redis-config-databases-fallback
Closed

junghyuneun wants to merge 1 commit into
TableProApp:mainfrom
junghyuneun:fix/redis-config-databases-fallback

Conversation

@junghyuneun

Copy link
Copy Markdown

Fixes #3036

An AWS ElastiCache node lists no databases at all. The window shows the server's own refusal where the keyspace should be:

CONFIG: ERR unknown command 'CONFIG', with args beginning with: 'GET' 'databases'

Root cause

docs/databases/redis.mdx already states the intended behaviour:

The sidebar then lists one entry per database, db0 upward, counted from the server's own CONFIG GET databases (16 if it does not answer).

The fallback did not cover a server that answers with an error. In RedisPluginDriver.databaseCount(on:):

let reply = try await conn.run(["CONFIG", "GET", "databases"])
guard let array = reply.arrayValue, array.count >= 2, let count = array[1].intValue, count > 0 else {
    return 16
}

That guard only catches a successful reply of an unexpected shape. An error reply never reaches it, because run is the choke point that turns one into a throw: throwIfError(name) in RedisCommandChannel.run, which is also where the CONFIG: prefix on the message comes from. The throw left databaseCount and took fetchTables and fetchDatabases with it.

ElastiCache restricts config alongside bgsave, debug, migrate, replicaof, save, shutdown and sync. It is removed rather than denied, so the answer is unknown command and not NOPERM. Both now fall back; an ACL that denies config|get was failing in exactly the same way.

CONFIG is also the only command in this driver's vocabulary that ElastiCache blocks. INFO, SCAN, SELECT, DBSIZE, TYPE, TTL and the rest are all permitted, so the browser works once the count is known.

What changed

The rule moved into RedisDatabaseCount, a pure file with no connection behind it, for the reason RedisReply.swift gives in its own header: the plugin imports CRedis and the test target cannot, so logic that needs covering lives apart from the connection. RedisPluginDriver.swift keeps the two call sites and loses the private helper, and the new file joins the list of plugin sources the test bundle compiles, beside RedisDatabaseIndex.swift and RedisQueuedCommandPolicy.swift.

A cluster node is still answered from the topology rather than probed, which is what the old first line did.

No docs change: the documented behaviour is what this restores.

Tests

RedisDatabaseCountTests, 12 cases over a stub channel modelled on the one in RedisQueuedReplyTests.swift:

  • a removed CONFIG (the ElastiCache shape) lists 16
  • a denied CONFIG (NOPERM) lists 16
  • a server that answers is taken at its word, and is asked exactly CONFIG GET databases
  • seven unusable answers, including a count of 0 and -1, fall back
  • a cluster node reports one keyspace and is never probed

The error strings are measured, not invented. Redis 8.10.1 started with --rename-command CONFIG '' reproduces the reported message byte for byte:

$ redis-cli -p 6399 CONFIG GET databases
ERR unknown command 'CONFIG', with args beginning with: 'GET' 'databases'

Verification, and its limits

I do not have Xcode on this machine, only the Command Line Tools, so I have not built the project or run the suite. CI is the real gate here and I will fix whatever it reports.

What I could check, with the CLT Swift 6.4 compiler against faithful stand-ins for RedisReply, RedisCommandChannel and run copied from this repo:

  • swiftc -typecheck -swift-version 6 -strict-concurrency=complete over the new file and the test file: clean
  • the same logic compiled and run, which is where the numbers above come from:
ElastiCache (CONFIG removed) -> 16
ACL denied (NOPERM)          -> 16
server answers 4             -> 4 [["CONFIG", "GET", "databases"]]
cluster node                 -> 1 probed: []
zero count                   -> 16

Separately, the fix was confirmed against the real thing before it was written: a loopback proxy answering CONFIG GET databases with *2\r\n$9\r\ndatabases\r\n$2\r\n16\r\n, byte-identical to a real Redis reply, in front of an untouched ElastiCache endpoint makes the sidebar list db0-db15 with correct counts from INFO keyspace. The count probe is the only thing standing between the driver and that keyspace.

SwiftLint and SwiftFormat were not run for the same reason; the new files follow the spacing, access control and doc-comment style of the files beside them.

Checklist

  • Tests added
  • CHANGELOG.md updated under [Unreleased], credit left off
  • Docs unchanged: the change makes the code match what docs/databases/redis.mdx already documents
  • No user-facing strings added
  • SwiftLint/SwiftFormat not run locally (no Xcode); relying on CI

@datlechin

Copy link
Copy Markdown
Member

Hi @junghyuneun, thanks for your contribution! Unfortunately, this issue has already been fixed in PR #3038.

@datlechin datlechin closed this Sep 21, 2026
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.

Redis: CONFIG GET databases error aborts the sidebar instead of falling back to 16

2 participants