skip to content

A Redis client issues SUBSCRIBE news.sports and also PSUBSCRIBE news.* on the same connection, then a publisher runs PUBLISH news.sports hello. How many messages does that client receive, and why?

level: middleimportance: should knowfreq 30%

answer

  1. two registries: exact table + pattern list
  2. no per-client de-duplication on publish
  3. message push AND pmessage push
  4. overlapping patterns duplicate too
  5. PUBLISH count = deliveries, not clients

basics

~20 s

Two: one message push from the exact subscription and one pmessage push from the pattern. Redis delivers once per matching subscription and never de-duplicates per client, so overlapping subscriptions mean duplicate payloads the application must handle.

solid answer

~50 s

The client gets **two** deliveries for the single publish: a three-element `message` push for the exact `news.sports` subscription, and a four-element `pmessage` push for the `news.*` pattern. The reason is that Redis resolves a publish against two independent registries: a dictionary of exact channel subscriptions and a list of pattern subscriptions. It delivers to each match it finds and does not check whether the same connection already received the payload. The same happens with two overlapping patterns: `PSUBSCRIBE news.*` plus `PSUBSCRIBE *.sports` yields two `pmessage` pushes, distinguishable only by their pattern field. `PUBLISH` returns the number of receivers, and that count includes pattern matches, so it counts deliveries rather than distinct clients. In practice I avoid the overlap by design: subscribe to the pattern **or** the exact channel, not both. Where overlap is unavoidable I de-duplicate in the handler using a message ID from the payload, since the protocol offers nothing to correlate the two pushes.

code

text · 15 lines
text
# subscriber connection registers BOTH
SUBSCRIBE news.sports
PSUBSCRIBE news.*

# publisher
PUBLISH news.sports "goal"
(integer) 2          # counts deliveries, not distinct clients

# the single subscriber connection receives TWO pushes
1) "message"         2) "news.sports"   3) "goal"
1) "pmessage"        2) "news.*"        3) "news.sports"   4) "goal"

# server-side introspection
PUBSUB CHANNELS      # 1) "news.sports"   (exact subscriptions only)
PUBSUB NUMPAT        # (integer) 1        (patterns registered, not clients)

go deeper

for a junior

Answer two, and say it is because the exact subscription and the pattern subscription are matched independently.

for a middle

Describe the two registries and the missing de-duplication step, and note that two overlapping patterns duplicate as well while re-registering the same pattern does not.

for a senior

Move to consequences and remedies: idempotent handlers, double-counted metrics, extra fanout bytes, subscription-topology rules, and PUBSUB introspection instead of trusting the PUBLISH count.

for a principal

Treat channel naming and subscription topology as a governed interface: who may register broad patterns, how duplicates and losses are both absorbed by idempotent consumers, and what the fanout amplification costs at peak publish rates.

## What Redis does on PUBLISH A publish resolves against two separate structures inside the server: 1. **Exact channel table** - a hash map from channel name to the list of clients subscribed to that literal name. One lookup, then a write per subscriber. 2. **Pattern list** - all registered patterns across all clients. Redis walks it and glob-matches each pattern against the published channel name, writing to the owning client for each match. Nothing joins these two passes. There is no per-publish set of 'clients already served', because maintaining one would cost more than the duplicate does in the common case. So a connection that appears in both structures receives the payload twice, in two differently shaped pushes. ## The two overlap shapes **Channel plus pattern overlap.** `SUBSCRIBE news.sports` and `PSUBSCRIBE news.*` on one connection: one `message` push (`message`, `news.sports`, payload) and one `pmessage` push (`pmessage`, `news.*`, `news.sports`, payload). A handler that only reads the payload will process the event twice. **Pattern plus pattern overlap.** `PSUBSCRIBE news.*` and `PSUBSCRIBE *.sports`: two `pmessage` pushes that differ only in the pattern element. This is the sneakier variant, because it usually appears after someone adds a second, broader tap without auditing the existing ones. What does **not** duplicate: re-issuing the identical subscription. `PSUBSCRIBE news.*` twice on the same connection is idempotent; the pattern is registered once, the confirmation count does not grow, and one delivery arrives. Duplication requires *distinct* subscriptions that both match. ## Ordering between the duplicates Both deliveries are written to the same client's output buffer during the same publish, so they arrive back-to-back on that connection, but you should not build logic on which comes first. Treat them as two arrivals of the same event, distinguished only by push kind and pattern. ## What PUBLISH's return value means `PUBLISH` replies with an integer 'number of clients that received the message', and that number counts each pattern match as well. With one client holding both subscriptions above, `PUBLISH news.sports hello` returns `2`. This makes the reply a poor health check for 'how many consumers do I have', because it conflates deliveries with distinct consumers. In a cluster it is worse still, since ordinary `PUBLISH` is broadcast across the shards and the count reflects what the receiving node observed. If you need a consumer census, use `PUBSUB CHANNELS`, `PUBSUB NUMSUB` for exact channels and `PUBSUB NUMPAT` for the number of registered patterns, keeping in mind `NUMPAT` counts patterns, not clients. ## Practical consequences - **At-least-twice processing on a bus that promises at-most-once.** The irony is real: Pub/Sub can drop a message entirely on disconnect, and simultaneously hand the same message to a client twice through overlap. Handlers should be idempotent regardless. - **Metrics double counting.** A monitoring consumer that adds a broad pattern tap alongside existing exact subscriptions inflates every counter derived from it. - **Amplified fanout cost.** Each duplicate is a real write into the client output buffer, so overlap multiplies the bytes the server pushes at high publish rates. ## How to avoid or handle it 1. **Design it away.** Pick one addressing style per consumer: either the specific channels or the pattern above them. Encode this as a rule in the shared client wrapper so consumers cannot register both. 2. **Audit patterns.** Before adding a pattern, check the existing patterns and channels for the same connection; `PUBSUB CHANNELS` and `PUBSUB NUMPAT` help at the server level. 3. **De-duplicate on a message ID.** If your payloads carry a UUID or monotonic ID, keep a small recently-seen set in the consumer. This also protects against reconnect-driven redelivery in your own retry code. 4. **Split connections.** Where a service genuinely needs both a targeted feed and a broad audit tap, run them on separate connections with separate handlers, so the duplication is explicit and each path has its own semantics. ## What an interviewer is listening for The number (two), the mechanism (two independent registries, no per-client de-duplication), the extension to overlapping patterns, and the practical answer that handlers must be idempotent or the subscription topology must be designed to avoid overlap.

  • Does subscribing to the same pattern twice on one connection cause two deliveries?
    No. Registering an identical pattern again is idempotent: the pattern is stored once for that client, the confirmation reply's subscription count does not increase, and a matching publish is delivered once. Duplication requires two distinct subscriptions that both match the published channel, such as an exact channel plus a covering pattern, or two overlapping patterns.
  • What does the integer returned by PUBLISH actually tell you?
    It is the number of deliveries the node performed for that publish, counting exact-channel subscribers and pattern matches alike, so one client holding both an exact and a pattern subscription contributes 2. It is therefore not a count of distinct consumers, and it says nothing about whether anyone processed the message. Use PUBSUB NUMSUB and PUBSUB NUMPAT for introspection instead.

saying these in an interview costs you the question

  • 'Redis de-duplicates so the client gets it once'
  • Assuming only the pattern delivery arrives because it is more specific or was registered later
  • Thinking two overlapping patterns collapse into one delivery
  • Reading the PUBLISH return value as a count of distinct subscribers
  • Relying on a fixed arrival order between the message and pmessage pushes

context