Skip to content

Add getFirstMulti and setAllMulti - #1

Open
kozak wants to merge 1 commit into
restaumaticfrom
multi-get-set
Open

kozak wants to merge 1 commit into
restaumaticfrom
multi-get-set

Conversation

@kozak

@kozak kozak commented Oct 9, 2026

Copy link
Copy Markdown

Summary

Adds getFirstMulti and setAllMulti, the many-key versions of getFirst and setAll. Each sends its keys as a pipeline of quiet requests (GETKQ / SETQ) closed by a NOOP, so reading or writing many keys costs one round trip per 100 keys per server instead of one per key.

They're for the higgs Orders live query, which now stores one memcached entry per order (restaumatic/restaumatic#13839). A client subscribing to a busy restaurant reads about 1000 entries.

Behaviour

getFirstMulti :: Cluster -> [Key] -> IO (Map Key (Value, Flags, Version))

  • Asks each key from the servers in the order getFirst uses (orderServersForKey). A key that is missing on one server is asked from the next one.
  • Keys that no server has are left out of the result.
  • A server whose request fails is skipped, as in getFirst. The first exception is thrown when no server answered.

setAllMulti :: Cluster -> [(Key, Value, Flags, Expiration)] -> IO [Either SomeException [(Key, Status)]]

  • Writes to every alive server, like setAll.
  • Returns one result per server: the keys that the server didn't store with their status, or the exception if the request failed.

Both:

  • Send 100 requests per round trip. A bounded pipeline keeps the server from blocking on writing GETKQ responses while the client is still sending.
  • Run each round trip through the new serverOp' (the cluster's retryOp), so they keep the timeout, retry and failed-server handling.

Supporting changes:

  • Cluster: getAliveServers, orderServersForKey (getServersForKey now uses both), serverOp'.
  • Socket: sendMany writes several requests in one sendAll.
  • Adds a containers dependency for the Map result.

Known limitation, shared with set: dzGenericResponse expects an empty body, and memcached sends an error message in the body of a failed set. So a SETQ failure surfaces as a protocol error for that server, instead of a (key, status) entry.

Test plan

Checked against a real memcached (memcached-test:11211 in the devcontainer), with a throwaway script:

  • setAllMulti with 250 pairs (3 chunks, half of them 20 KB values) returns [Right []].
  • getFirstMulti with those 250 keys plus 50 missing ones returns the 250 with correct values and flags. getFirst returns the same value.
  • With a second, unreachable server (127.0.0.1:1): setAllMulti returns Left for it and Right [] for the live one, and getFirstMulti finds all 20 keys.
  • With only the unreachable server, getFirstMulti throws.
  • Library and test suite compile; no -Wall warnings in the changed modules.

🤖 Generated with Claude Code

Reading or writing hundreds of keys one at a time costs one network round
trip per key. These send the keys as pipelines of quiet gets and sets, and
go through serverOp' so they keep the cluster's retry, timeout and
failed-server handling.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
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