Skip to content

DOC-6968 Add the four SCAN steps to seven cmds_generic client examples - #3812

Open
andy-stark-redis wants to merge 1 commit into
mainfrom
DOC-6968-cmds-generic-scan-examples
Open

DOC-6968 Add the four SCAN steps to seven cmds_generic client examples#3812
andy-stark-redis wants to merge 1 commit into
mainfrom
DOC-6968-cmds-generic-scan-examples

Conversation

@andy-stark-redis

@andy-stark-redis andy-stark-redis commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

DOC-6968 — the four SCAN steps for seven clients

Adds scan1scan4 to hiredis, go-redis, jedis, lettuce-async, lettuce-reactive, ioredis and predis in the cmds_generic set. 27 implementations, 822 lines.

Omission warnings: 63 → 36. content/commands/scan.md is back to 12 client tabs on scan1, scan2 and scan4 (from 5), and 12 on scan3 where PHP is legitimately absent — see below.

@dwdougherty this is the "next PR" from #3811: the warnings that triggered your OCD are now down by 43%, and scan.md was the page paying the visible price.

18 is not the number of matching keys

*11* matches 19 of key:1key:1000 — I counted them. The reference gets 18 because its COUNT 1000 call continues from the cursor left by four preceding default-COUNT iterations, one of which has already yielded a match. My first ioredis attempt restarted at cursor 0, got 19, and failed its own assertion, which is how this surfaced.

Every tab now mirrors the reference's continuation semantics. One nuance for the record: on Redis 8.8 the observed split is 0, 0, 0, 1, 18, whereas the page's CLI transcript shows the match landing on the first iteration. Same total, different distribution — the split depends on iteration order, which matters for the next point.

Why Go's scan2 prints less than its siblings

A Go Example function only runs under go test if it has an // Output: block, and that block must match stdout exactly. Encoding the per-iteration counts (0,0,0,1) would make the Go test fail whenever SCAN's iteration order shifted — a red build for a reason unrelated to the docs.

So Go runs the four iterations but prints only the final 18. Same headline number, less visible narrative, no new fragility. The alternative (match the siblings exactly, accept Go as the canary that breaks first) was considered and rejected.

predis cannot express scan3, so PHP is absent from it

SCAN.php::prepareOptions in predis 3.5.1 emits only MATCH and COUNT. TYPE is silently dropped — probed against a live server:

with TYPE=zset -> geokey, stringkey, zkey
without TYPE   -> geokey, stringkey, zkey

stringkey is a plain string. An idiomatic-looking $r->scan(0, ['TYPE' => 'zset']) returns everything, which is worse than an absent tab, and using executeRaw in a docs example teaches the wrong thing. PHP therefore has scan1, scan2 and scan4 only.

This is the first real use of the behaviour added in #3811: PHP's absence renders as a cleanly omitted tab rather than a 46-line whole-file dump, and the one remaining warning for cmds_generic/scan3 is a truthful record rather than noise.

Verification

All seven PASS against Redis 8.8.0 via build/example-test-harness/run.sh cmds_generic, including hiredis through the portable C runner added earlier in this ticket. Observed output was checked against every >>> comment.

Two pre-existing failures in that sweep are not from this change — verified by re-running both with these changes stashed:

Client Failure Cause
nredisstack CS0246: SkipIfRedisFactAttribute could not be found the portable C# stub lacks that attribute
rust-async unresolved import futures_util, AsyncIter is not an iterator async iteration API drift in the pinned redis-rs

Neither file is touched here. Both deserve their own tickets.

What's left

36 warnings: cmds_generic 18 (del/expire/ttl 5 each, exists 2, scan3 1 for PHP), cmds_hash 12 (C and ioredis), query_vector 4 (deliberately out of scope), cmds_stream 2.

🤖 Generated with Claude Code


Note

Low Risk
Docs and example-test harness only; no production runtime or API changes.

Overview
Adds scan1scan4 doc examples to seven cmds_generic clients (hiredis, go-redis, jedis, lettuce-async/reactive, ioredis, predis) so scan.md can show more client tabs and cut omission warnings.

scan1: set + SSCAN with MATCH. scan2: SCAN with MATCH *11*, four default-COUNT iterations then COUNT 1000 continuing from the prior cursor (final batch 18 keys). scan3: SCAN … TYPE zset over geo/zset keys. scan4: HSCAN vs NOVALUES.

predis gets scan1, scan2, and scan4 only—TYPE on SCAN is not supported idiomatically. go-redis scan2 still runs the loop but // Output: asserts only 18, not per-iteration counts.

Reviewed by Cursor Bugbot for commit 06bb0e5. Bugbot is set up for automated code reviews on this repo. Configure here.

Adds scan1, scan2, scan3 and scan4 to the hiredis, go-redis, jedis,
lettuce-async, lettuce-reactive, ioredis and predis files, clearing 27 of the
63 omission warnings the shortcode fix surfaced. content/commands/scan.md goes
back to twelve client tabs on scan1, scan2 and scan4.

The 18 in scan2 is not the number of matching keys. `*11*` matches 19 of
key:1..key:1000; the reference gets 18 because its COUNT 1000 call continues
from the cursor left by four preceding default-COUNT iterations, one of which
has already yielded a match. My first attempt restarted at cursor 0, got 19 and
failed its own assertion, which is how this surfaced. Every tab now mirrors the
reference's continuation semantics. Worth knowing that the observed split on
Redis 8.8 is 0,0,0,1,18 whereas the CLI transcript on the page shows the match
landing on the first iteration instead — same total, different distribution,
because the split depends on iteration order.

That order-dependence is why Go's scan2 differs from its siblings. A Go Example
function only runs under `go test` if it has an `// Output:` block, and that
block must match stdout exactly, so printing per-iteration counts would make
the test fail whenever SCAN's iteration order shifts — a red build for a reason
unrelated to the docs. Go therefore runs the four iterations but prints only
the final 18. Same headline number, less visible narrative, no new fragility.

predis cannot express scan3 at all, so PHP is deliberately absent from that
step. `SCAN.php::prepareOptions` in predis 3.5.1 emits only MATCH and COUNT, so
`$r->scan(0, ['TYPE' => 'zset'])` silently drops the filter: probed against a
live server it returned a plain string key alongside the two sorted sets, byte
for byte identical to the unfiltered call. An idiomatic-looking call that
quietly returns everything is worse than an absent tab, and the omission now
renders as a missing tab rather than a whole-file dump — the first real use of
the behaviour added earlier in this ticket.

All seven pass against Redis 8.8.0. Two clients fail cmds_generic for
pre-existing, unrelated reasons, verified by re-running with these changes
stashed: nredisstack cannot compile because the portable C# stub lacks
SkipIfRedisFactAttribute, and rust-async fails on `unresolved import
futures_util` plus AsyncIter no longer being an iterator in the pinned crate.
Neither file is touched here.

Learned: scan2's expected 18 is a cursor-continuation artifact, not a match count — a new tab must continue from the cursor the earlier iterations returned, never restart at 0, or it correctly gets 19 and disagrees with every other tab
Constraint: Go example funcs only execute with an exact `// Output:` block, so scan2 in Go must not print per-iteration counts — 0,0,0,1 is iteration-order dependent and would break on an unrelated Redis change
Constraint: predis 3.5.1 SCAN accepts only MATCH and COUNT; TYPE is silently dropped and every key comes back, which is why PHP has no scan3 example
Directive: do not "complete" PHP's scan3 with $r->scan(0, ['TYPE' => 'zset']) — it looks idiomatic, filters nothing, and was verified wrong against a live server
Gaps: nredisstack and rust-async already fail cmds_generic for unrelated reasons (missing SkipIfRedisFact stub; futures_util/AsyncIter drift in the pinned redis-rs) — do not read those as regressions from this change
Ticket: DOC-6968
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

DOC-6968

@github-actions

Copy link
Copy Markdown
Contributor

🧠 Redis Memory

Found 5 related items from repository history (5 new this commit):

Memory updated at 06bb0e5

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