Client blocking mechanism for keys in use (forkless)#3469
Client blocking mechanism for keys in use (forkless)#3469JimB123 merged 1 commit intovalkey-io:forklessfrom
Conversation
ea29a5b to
850572d
Compare
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## forkless #3469 +/- ##
============================================
+ Coverage 76.56% 76.60% +0.03%
============================================
Files 158 159 +1
Lines 79078 79464 +386
============================================
+ Hits 60548 60870 +322
- Misses 18530 18594 +64
🚀 New features to boost your workflow:
|
c37061b to
de6f63c
Compare
6aecfb9 to
5ba12b8
Compare
There was a problem hiding this comment.
Pull request overview
This PR introduces a new server-initiated blocking mode (BLOCKED_INUSE) intended to temporarily block clients from accessing keys held exclusively by internal/background operations, while preventing unbounded query buffer growth by removing the client read handler and later restoring it when unblocked.
Changes:
- Add
BLOCKED_INUSEand a block/unblock API (blockClientInUseOnKeys,unblockClientsInUseOnKey, etc.) backed by a key→clients mapping. - Remove client read handlers while blocked-in-use, restore them in
processUnblockedClients(), and add cron-based detection of remotely-closed connections while handlers are removed. - Extend
ConnectionTypewith anis_closinghook, implement it for socket/TLS, and add unit tests for the new API.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| src/blocked.c | Implements BLOCKED_INUSE mechanism, key↔client tracking, and read-handler restore on unblock; adds guardrails in generic unblock paths. |
| src/server.h | Adds BLOCKED_INUSE enum value and exports block-in-use public APIs. |
| src/networking.c | Integrates unlink cleanup for blocked-in-use clients; adjusts free/unlink ordering; ensures blocked-in-use clients aren’t unblocked via the generic path. |
| src/server.c | Initializes/releases block-in-use state; adds clientsCronTcpIsClosing() to reap connections closed while read handlers are removed. |
| src/connection.h | Adds ConnectionType.is_closing hook. |
| src/socket.c | Implements connSocketIsClosing() using TCP state via getsockopt (Linux/macOS) and wires it into socket connections. |
| src/tls.c | Wires TLS ConnectionType.is_closing to the socket implementation. |
| src/unix.c | Extends Unix connection type initializer with the new is_closing field (currently NULL). |
| src/rdma.c | Extends RDMA connection type initializer with the new is_closing field (currently NULL). |
| src/unit/wrappers.h | Adds wrappers for processPendingCommandAndInputBuffer and beforeNextClient to support mocking in tests. |
| src/unit/test_blockedInuse.cpp | Adds unit tests covering block-in-use API behavior. |
07fea81 to
3ac8035
Compare
murphyjacob4
left a comment
There was a problem hiding this comment.
I'm partial - but I find the new code much simpler! Thanks 🥇
| * Note: BLOCKED_INUSE never goes through unblockClient(). Blocking state is | ||
| * cleared via resetBlockInUseClientState() instead, and calling | ||
| * unblockClient() on a BLOCKED_INUSE client will trigger a serverPanic. |
There was a problem hiding this comment.
Can we update the comment?
fee1370 to
e27c633
Compare
e27c633 to
d8319a5
Compare
89e0317 to
e6896f4
Compare
df759ff to
77fea02
Compare
70c0d9f to
31303fd
Compare
A client blocking system (blockInUse) that prevents concurrent access to keys actively being modified by internal operations (e.g., bgIteration). The mechanism blocks clients attempting to access in-use keys and automatically unblocks them when keys become available. When a client is blocked by blockInUse, its read handler is removed so the event loop stops monitoring read events for that connection, preventing new commands from being buffered into c->querybuf while the client is waiting. The read handler is restored in processUnblockedClients() when the client is unblocked. To avoid leaking zombie file descriptors, clientsCronTcpIsClosing() is added to detect and free connections that were closed by the remote side while the read handler was removed. Signed-off-by: harrylin98 <harrylin980107@gmail.com>
0bcc488 to
2fa56b3
Compare
Summary
This is a building block for upcoming bgIteration that require exclusive key access without stalling the event loop.
This PR adds blockInUse, a server-initiated client blocking mechanism that prevents concurrent access to keys held exclusively by internal operations. When a client command targets a key that is currently in use, the client is blocked and its read handler is removed to prevent unbounded input buffering. Once the internal operation releases the key, the client is automatically resumed and its pending command is re-executed.
When a client is blocked by blockInUse, its read handler is removed so the event loop stops monitoring read events for that connection, preventing new commands from being buffered into c->querybuf while the client is waiting. Clients would otherwise continue buffering incoming commands during the entire blocking period, leading to unbounded memory growth across all blocked clients. The read handler is restored in processUnblockedClients() when the client is unblocked.
The tradeoff is that removing the read handler also makes the event loop blind to TCP disconnects on that client connection. To avoid leaking zombie file descriptors, clientsCronTcpIsClosing() is added to detect and free connections that were closed by the remote side while the read handler was removed.
Design decisions
Timeout
BLOCKED_INUSEclients do not time out. The timeout is explicitly set to zero before blocking, so clients remain blocked until the key is released by the internal operation.Module commands without
ALLOW_BLOCKModule commands registered without
ALLOW_BLOCKthat target a key held by an internal operation will receive aINUSE key is being processederror. This is a new failure mode.processUnblockedClientsrefactorReplaced the silent skip of blocked non-module clients with an assert, since a non-module client being both
blocked and in the unblocked list is a bug. Added read handler restoration unblocked clients; for other blocking types
this is a no-op.
Testing
Unit tests (src/unit/test_blocked.cpp) cover each public API.