feat: add try_scan/try_for_each to the scan cursors - #482
Draft
memark wants to merge 1 commit into
Draft
Conversation
4 tasks
Both `KeysCursor` and `ScanKeyCursor` could only scan to completion: the callback had no way to signal "stop". That gap is most visible on `ScanKeyCursor`, where a listpack-encoded value is walked entirely within a single `RedisModule_ScanKey` call, so the existing `while cursor.scan(..)` loop never gets a chance to break. Add `try_scan` and `try_for_each` to both cursors. The callback returns `ControlFlow<B>`, and `try_for_each` is the analogue of `Iterator::try_for_each` -- it stops issuing scan calls as soon as the callback breaks and propagates the break value. `KeysCursor` also gains the `for_each` that `ScanKeyCursor` already had. `RedisModuleScanCB` and `RedisModuleScanKeyCB` both return `void`, so Redis cannot be aborted mid-batch: it finishes the batch and advances the cursor past all of it. Breaking therefore stops the user callback, not Redis's iteration, and the rest of the current batch is skipped for good. Both `try_scan` doc comments spell this out, including how much gets skipped per encoding. `ScanKeyCursor::scan` now delegates to `try_scan`, which removes its duplicate `extern "C"` callback along with the `&f as *const F as *mut c_void` cast it needed. `KeysCursor::scan` cannot do the same: it only holds a `&F`, and hoisting the `&F -> &mut F` cast out of the callback trips the deny-by-default `invalid_reference_casting` lint, so it keeps its own callback unchanged. Covered by three new example commands and three integration tests, including one that returns exactly the first two pairs of a three-field listpack hash. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rykj2WP9fzGjnseyrnVgBf
memark
force-pushed
the
memark-try-scan
branch
from
August 18, 2026 07:44
2d8d8a6 to
826b6dd
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Both
KeysCursorandScanKeyCursorcould only scan to completion: the callback had no way to signal "stop". That gap is most visible onScanKeyCursor, where a listpack-encoded value is walked entirely within a singleRedisModule_ScanKeycall, so the existingwhile cursor.scan(..)loop never gets a chance to break.Add
try_scanandtry_for_eachto both cursors. The callback returnsControlFlow<B>, andtry_for_eachis the analogue ofIterator::try_for_each-- it stops issuing scan calls as soon as the callback breaks and propagates the break value.KeysCursoralso gains thefor_eachthatScanKeyCursoralready had.RedisModuleScanCBandRedisModuleScanKeyCBboth returnvoid, so Redis cannot be aborted mid-batch: it finishes the batch and advances the cursor past all of it. Breaking therefore stops the user callback, not Redis's iteration, and the rest of the current batch is skipped for good. Bothtry_scandoc comments spell this out, including how much gets skipped per encoding.ScanKeyCursor::scannow delegates totry_scan, which removes its duplicateextern "C"callback along with the&f as *const F as *mut c_voidcast it needed.KeysCursor::scancannot do the same: it only holds a&F, and hoisting the&F -> &mut Fcast out of the callback trips the deny-by-defaultinvalid_reference_castinglint, so it keeps its own callback unchanged.Covered by three new example commands and three integration tests, including one that returns exactly the first two pairs of a three-field listpack hash.
Claude-Session: https://claude.ai/code/session_01Rykj2WP9fzGjnseyrnVgBf