In the RESP protocol, most frames a client reads are replies to commands it sent. Explain what an out-of-band push frame is, why the older protocol version could not express one, and what capabilities it unlocked.
answer
- > prefix = out-of-band, not a reply (RESP3 only)
- RESP2 subscribe mode locks the connection to subscription commands
- push kinds: message, pmessage, subscribe, invalidate
- enables CLIENT TRACKING invalidation + pub/sub on one connection
- reader must type-check every frame; pushes can interleave
basics
~20 sA push frame (prefix >, RESP3 only) is a server-initiated message that is not a reply to any command. RESP2 had no such marker, so subscription messages looked like ordinary array replies and a subscribed connection had to be restricted to subscription commands. Push frames let a client demultiplex, so one connection can carry both commands and server notifications.
solid answer
~60 sIn RESP2 every frame a client reads is assumed to answer the next outstanding request, because replies arrive in request order. There is no type marker for "this is unsolicited". Pub/Sub messages were therefore delivered as ordinary `*` arrays, indistinguishable in shape from a reply — so Redis had to forbid a subscribed RESP2 connection from issuing normal commands (only SUBSCRIBE/UNSUBSCRIBE/PSUBSCRIBE/PUNSUBSCRIBE/PING/QUIT/RESET). Applications consequently kept a **second connection** for subscriptions. RESP3 adds the `>` prefix. A push frame is structurally an array but explicitly typed as out-of-band, so a client reader can route it to a listener callback while reply frames continue to satisfy waiting callers on the same connection. What that unlocks: - **Pub/Sub multiplexed** with ordinary commands on one connection. - **Server-pushed cache invalidation** — with `CLIENT TRACKING ON`, the server sends `invalidate` push messages naming keys the client has cached and must discard. - **Keyspace notifications and monitoring events** delivered without a dedicated subscriber connection. Ordering caveat: a push may arrive interleaved between replies, so client readers must handle it at any point in the stream.
code
text · 16 lines# --- RESP2: connection is locked once subscribed ---
SUBSCRIBE news
GET user:1
(error) ERR Can't execute 'get': only (P|S)SUBSCRIBE / (P|S)UNSUBSCRIBE / PING / QUIT / RESET are allowed in this context
# --- RESP3: HELLO 3 first, then both work on one connection ---
HELLO 3
SUBSCRIBE news
GET user:1 # allowed
# on the wire, a published message arrives as a PUSH frame:
>3\r\n$7\r\nmessage\r\n$4\r\nnews\r\n$5\r\nhello\r\n
# and a tracking invalidation likewise:
CLIENT TRACKING ON
>2\r\n$10\r\ninvalidate\r\n*1\r\n$6\r\nuser:1\r\ngo deeper
Know that RESP3 adds a frame type marked > for messages the server sends on its own, such as published Pub/Sub messages.
Explain why RESP2 had to restrict subscribed connections and how the explicit push marker lets a client separate notifications from replies.
Cover the capabilities it unlocked — multiplexed Pub/Sub and CLIENT TRACKING invalidation — plus reader-loop implications, interleaving, and the staleness window.
Discuss it as a protocol-level enabling change: giving the server a channel to notify clients turns caching from pure client polling into server-driven invalidation, with the accompanying eventual-consistency and output-buffer tradeoffs.
## The request/response assumption RESP is fundamentally a request/response protocol over a single TCP connection with **ordered replies**: the Nth reply corresponds to the Nth request. That property is what makes pipelining trivial — write ten requests, read ten replies, match by position. A client reader is therefore usually a simple loop: read one frame, hand it to the caller at the head of the pending queue. That model has no room for a frame the client did not ask for. ## How RESP2 coped, badly Pub/Sub needs exactly such frames: after `SUBSCRIBE news`, the server sends a frame whenever anyone publishes, at an arbitrary time. In RESP2 that frame is an ordinary array: ``` *3\r\n$7\r\nmessage\r\n$4\r\nnews\r\n$5\r\nhello\r\n ``` Shape-wise this is indistinguishable from, say, an `LRANGE` reply of three strings. A client reading frames in order cannot know whether the array it just read is the answer to its pending `GET` or a published message. Redis solved this by **constraining the connection**: once a RESP2 connection enters subscribe mode, it may only run subscription commands plus `PING`, `QUIT`, `RESET` (and `SUBSCRIBE`-family variants). Any other command returns an error. The practical cost: every application that both reads/writes keys and subscribes needs **two connections** and two pools, and Pub/Sub cannot be layered onto a general-purpose client without extra machinery. It also means the server has no general way to notify a client about anything at all. ## What RESP3's push type changes RESP3 (Redis 6.0+, opted into per connection with `HELLO 3`) introduces the `>` prefix: ``` >4\r\n$7\r\nmessage\r\n$4\r\nnews\r\n$5\r\nhello\r\n... ``` Structurally it is an array of N elements; semantically the `>` says **this is not a reply**. That single bit of type information is enough to restore the client's ability to demultiplex: - the reader loop reads a frame; - if it is `>`, dispatch it to a registered listener (by the first element, which names the kind: `message`, `pmessage`, `subscribe`, `unsubscribe`, `invalidate`, ...); - otherwise, complete the oldest outstanding request. The pending-request queue is untouched by push frames, so ordering guarantees for replies still hold. ## Capabilities it unlocked **1. Pub/Sub on a shared connection.** In RESP3 a subscribed connection is no longer restricted; a client can `SUBSCRIBE` and still run `GET`/`SET` on the same socket. This simplifies pooling, halves connection counts for subscriber-heavy apps, and lets libraries expose subscription as an ordinary API rather than a special connection mode. **2. Client-side caching (`CLIENT TRACKING`).** This is the flagship use. With tracking enabled, the server remembers which keys a connection has read and, when such a key changes, **pushes** an `invalidate` message listing the keys. The client keeps a local in-process cache and drops entries on invalidation, eliminating round trips entirely for hot keys. Variants exist — default mode tracks read keys per connection; `BCAST` mode pushes invalidations for key prefixes without per-key bookkeeping; `OPTIN`/`OPTOUT` control which reads are tracked; and `REDIRECT` sends invalidation pushes to a *different* connection id, which is how RESP2 clients can participate at all (they keep a second connection in subscribe mode receiving `__redis__:invalidate`). The push type is what makes the primary path clean. **3. Server-initiated notifications generally.** Keyspace notifications and similar event streams can be delivered without hijacking the connection, and the protocol now has a place for future server-to-client signalling. ## Implementation consequences for client authors - **Any frame may be a push.** The reader must check the type byte on every frame, not only when it believes a subscription is active — an invalidation can arrive between two replies in a pipeline. - **Thread-safety.** Dispatching a push to user code from the reader thread means user callbacks must not block the reader; libraries usually hand off to a queue or executor. - **Ordering.** A push is delivered in stream order relative to replies, but carries no guarantee about *when* relative to the key operation that caused it in another client — an invalidation is an eventual signal, and there is a genuine race window in which a client may serve a stale locally-cached value. Designs that need correctness under that race use short local TTLs alongside invalidation. - **Backpressure.** A client that ignores pushes still has them accumulate in its socket buffer, and the server accounts them against the client output buffer limits; a slow reader can be disconnected. ## The short version for an interview RESP2's frames were all replies, so unsolicited messages had to masquerade as replies and the connection had to be locked into subscribe mode. RESP3's `>` push type names unsolicited messages explicitly, which restores multiplexing and, more importantly, gives the server a channel to tell clients things — with invalidation-driven client-side caching being the capability that channel was really built for.
- How can a client that is stuck on RESP2 still receive cache-invalidation messages?By using the REDIRECT option of `CLIENT TRACKING`: tracking is enabled on the working connection but invalidation messages are delivered to a different connection id, which is held in subscribe mode on the special `__redis__:invalidate` channel. That second connection receives them as ordinary RESP2 Pub/Sub messages, sidestepping the missing push type at the cost of an extra connection and the bookkeeping to keep the two associated.
- What must a client library's read loop do differently once RESP3 is enabled?It must inspect the type byte of every frame rather than assuming the next frame answers the oldest pending request, because a push can arrive at any point — including between two replies of a pipeline. Push frames are routed to listeners and must not consume an entry from the pending-request queue. Libraries also avoid running user callbacks on the reader thread, since a blocking callback would stall reply delivery for the whole connection.
- Does receiving an invalidation push guarantee the client never serves stale data?No. The invalidation is an asynchronous signal delivered after the key changed, so there is a window between another client's write and this client's processing of the push during which the local cache still answers with the old value. Client-side caching is therefore an eventual-consistency optimisation; designs that cannot tolerate the window pair invalidation with a short local time-to-live, or simply do not cache the values where the staleness matters.
RESP2 is a counter where you only ever hand things back to the person who just asked; RESP3 adds a tannoy — announcements can go out at any moment and everyone knows they are announcements, not someone's order.
saying these in an interview costs you the question
- Believing RESP2 Pub/Sub messages are structurally distinguishable from command replies
- Claiming a RESP2 connection can freely mix subscriptions and normal commands
- Thinking push frames break reply ordering or consume a pending request slot
- Assuming client-side caching invalidation is synchronous and eliminates all staleness
- Saying RESP3 push is just a cosmetic rename of pub/sub message arrays