feat(commands): add FLASH.MGET and FLASH.MSET batched flash-string ops#23
Open
mbocevski wants to merge 2 commits into
Open
feat(commands): add FLASH.MGET and FLASH.MSET batched flash-string ops#23mbocevski wants to merge 2 commits into
mbocevski wants to merge 2 commits into
Conversation
Closes the v1.1.x performance gap that forced wrappers to do per-key dispatch (N round-trips for an N-key bulk-fetch / bulk-write). Both commands take exactly one round-trip from the client. FLASH.MGET key [key ...] Returns array of N entries. Each entry is the value bytes if the key exists as a FlashString, else nil. Non-string flash types (FlashHash/List/ZSet), native keys, and missing keys all map to nil — never raises WRONGTYPE. Type-checks via open_key BEFORE consulting the hot cache; the cache is unified across types and would otherwise reply with cached hash/list/zset bytes for a key whose type changed. Cold-tier reads are bundled into one async task on the I/O pool that performs them sequentially before unblocking the client. The io_uring batch optimisation (one SQE-batch instead of N sequential reads) is a deferred follow-up; correctness ships now. Successful cold reads are promoted into the hot cache. FLASH.MSET key value [key value ...] Hot-tier-only writes that land in RAM atomically within one event-loop tick. Phase 1 type-checks every key (any non-flash-string aborts the whole batch with WRONGTYPE — no partial writes); phase 2 writes all keys. TTL cleared per key, matching native MSET. Auto- demotion handles eventual NVMe propagation via the standard pipeline. Batch size capped at 1024 keys per call to bound transient RAM inflation before demotion catches up. Larger batches return a clear error suggesting client-side chunking. Replication: MSET replicates verbatim (one MSET command to replicas and AOF). MGET is read-only, no replication. Keyspace notifications: per-key STRING/flash.set events from MSET, matching native MSET behaviour. Tests: 4 unit tests cover argument-layout invariants (odd/even total args, pair count math). 22 integration tests against a live module cover MSET basics, type-check atomicity (WRONGTYPE aborts whole batch, no partial writes), arity errors including the 1024-key cap, MGET nil mapping for every non-string-flash type and native keys, duplicate-argument behaviour, ordered preservation, and round-trip MSET → MGET correctness.
4 tasks
CI lint job runs ruff format --check on the whole repo. Reformats test_flash_exists.py (inherited) and test_flash_mget_mset.py (introduced in this branch's parent commit).
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
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.
Summary
Adds
FLASH.MGETandFLASH.MSET— batched read/write across flash strings. Closes the v1.1.x performance gap that forced wrappers into per-key dispatch (N round-trips per N-key batch). Both commands take exactly one round-trip from the client.FLASH.MGET key [key ...]Returns array of N entries — value bytes if the key exists as a
FlashString, else nil. Non-string flash types, native keys, and missing keys all map to nil. Never raisesWRONGTYPE.Cold-tier reads are bundled into one async task on the I/O pool that performs them sequentially before unblocking the client. The io_uring batch optimisation (one SQE-batch instead of N sequential reads) is a deferred follow-up; correctness ships now, the bandwidth win comes later. Successful cold reads are promoted into the hot cache.
Type-checks via
open_keyBEFORE consulting the hot cache — the cache is unified across types and would otherwise reply with cached hash/list/zset bytes for a key whose type changed.FLASH.MSET key value [key value ...]Hot-tier-only writes that land in RAM atomically within one event-loop tick. Phase 1 type-checks every key (any non-flash-string aborts the whole batch with
WRONGTYPE— no partial writes); phase 2 writes all keys. TTL cleared per key, matching native MSET. Auto-demotion handles eventual NVMe propagation.Batch size capped at 1024 keys per call to bound transient RAM inflation before demotion catches up. Larger batches return a clear error suggesting client-side chunking.
Test plan
tests/test_flash_mget_mset.py— MSET basics, type-check atomicity (WRONGTYPE aborts whole batch, no partial writes), arity errors including the 1024-key cap, MGET nil mapping for every non-string-flash type and native keys, duplicate-argument behaviour, ordered preservation, round-trip MSET → MGET correctness[Unreleased]Added