Skip to content

feat(commands): add FLASH.MGET and FLASH.MSET batched flash-string ops#23

Open
mbocevski wants to merge 2 commits into
feat/v1.1.1-flash-expirefrom
feat/v1.1.1-flash-mget-mset
Open

feat(commands): add FLASH.MGET and FLASH.MSET batched flash-string ops#23
mbocevski wants to merge 2 commits into
feat/v1.1.1-flash-expirefrom
feat/v1.1.1-flash-mget-mset

Conversation

@mbocevski
Copy link
Copy Markdown
Owner

Summary

Adds FLASH.MGET and FLASH.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 raises WRONGTYPE.

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_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.

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.

Stacked on top of #22 (flash-expire). Merge in order.

Test plan

  • 22 integration tests in 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
  • 4 unit tests cover argument-layout invariants
  • All gates clean
  • CHANGELOG [Unreleased] Added

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.
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
Copy link
Copy Markdown

codecov Bot commented Apr 25, 2026

Codecov Report

❌ Patch coverage is 53.26087% with 86 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/commands/mget.rs 35.33% 86 Missing ⚠️

📢 Thoughts on this report? Let us know!

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