# Pub/Sub

> Publish messages to channels and receive them with SUBSCRIBE, PSUBSCRIBE and sharded SSUBSCRIBE, in RESP2 or RESP3.

URL: https://docs.hotkv.com/develop/pubsub/


Pub/Sub delivers messages from publishers to every client currently subscribed
to a channel. It is fire and forget: a message goes to the subscribers that
are connected at that moment, and nothing is stored. A subscriber that is
offline, or reconnecting, misses it. When you need delivery that survives a
disconnect, or replay of past messages, use a [stream](https://docs.hotkv.com/data-types/streams/index.md)
with consumer groups instead.

## Channels

[PUBLISH](https://docs.hotkv.com/commands/publish/index.md) sends a message to a channel and replies with
the number of clients that received it. [SUBSCRIBE](https://docs.hotkv.com/commands/subscribe/index.md)
puts a connection in subscribed mode and starts delivering that channel's
messages to it.

In one terminal:

```
127.0.0.1:6379> SUBSCRIBE orders
1) "subscribe"
2) "orders"
3) (integer) 1
```

In another:

```
127.0.0.1:6379> PUBLISH orders "order:1187 created"
(integer) 1
```

The subscriber receives:

```
1) "message"
2) "orders"
3) "order:1187 created"
```

Each subscription confirmation carries the number of channels and patterns
the connection is now subscribed to. [UNSUBSCRIBE](https://docs.hotkv.com/commands/unsubscribe/index.md)
with no arguments leaves every channel.

## Patterns

[PSUBSCRIBE](https://docs.hotkv.com/commands/psubscribe/index.md) subscribes to every channel whose name
matches a glob pattern (`*`, `?`, `[...]`). Messages arrive as `pmessage`
with the pattern that matched as well as the channel:

```
127.0.0.1:6379> PSUBSCRIBE orders.*
1) "psubscribe"
2) "orders.*"
3) (integer) 1
1) "pmessage"
2) "orders.*"
3) "orders.eu"
4) "order:1188 created"
```

A connection subscribed to a channel and to a pattern that matches it gets
the message twice, once of each kind. [PUNSUBSCRIBE](https://docs.hotkv.com/commands/punsubscribe/index.md)
removes pattern subscriptions.

## Subscribed mode

Once a RESP2 connection has at least one subscription, it may only run
`SUBSCRIBE`, `PSUBSCRIBE`, `SSUBSCRIBE`, their `UNSUBSCRIBE` counterparts,
`PING`, `QUIT` and `RESET`. Anything else replies with an error:

```
(error) ERR Can't execute 'get': only (P|S)SUBSCRIBE / (P|S)UNSUBSCRIBE / PING / QUIT / RESET are allowed in this context
```

> **Note**
> A RESP3 connection (after `HELLO 3`) can run any command while it is
> subscribed, because its messages arrive as push frames that a client tells
> apart from replies. A RESP2 connection cannot, so use a separate connection for
> subscriptions there, which every HotKV SDK already does.

## Sharded Pub/Sub

[SSUBSCRIBE](https://docs.hotkv.com/commands/ssubscribe/index.md), [SUNSUBSCRIBE](https://docs.hotkv.com/commands/sunsubscribe/index.md)
and [SPUBLISH](https://docs.hotkv.com/commands/spublish/index.md) work like their unsharded versions, but a
shard channel's name is hashed to a slot exactly like a key. In a cluster:

- `SPUBLISH` and `SSUBSCRIBE` must be sent to a node of the shard that serves
  the channel's slot; elsewhere they get a `MOVED` redirect, as a key would.
  Several channels in one `SSUBSCRIBE` must hash to the same slot, or the
  reply is `CROSSSLOT`.
- A message published with `SPUBLISH` reaches only the primary and replicas
  of that shard, not the whole cluster, so sharded Pub/Sub scales with the
  number of shards.
- When the slot moves to another shard (resharding or failover), its
  subscribers are unsubscribed: they receive an `sunsubscribe` message and
  should subscribe again, which routes them to the new owner.

Messages arrive as `smessage`:

```
127.0.0.1:6379> SSUBSCRIBE orders:{eu}
1) "ssubscribe"
2) "orders:{eu}"
3) (integer) 1
1) "smessage"
2) "orders:{eu}"
3) "order:1189 created"
```

Unsharded `PUBLISH` in a cluster is forwarded to every node, so a subscriber
on any node receives it, but every node has to handle every message. Prefer
sharded Pub/Sub for high-volume channels in a cluster. On a standalone server
both kinds behave the same.

## RESP3 push messages

On a RESP3 connection (after `HELLO 3`), subscription confirmations and
messages arrive as push frames (`>` on the wire) instead of plain arrays.
Their contents are the same: a kind (`message`, `pmessage`, `smessage`,
`subscribe`, ...), then the channel and payload. Client libraries use the push
type to tell messages apart from ordinary replies. See
[RESP3](https://docs.hotkv.com/develop/resp3/index.md).

## Inspecting Pub/Sub

[PUBSUB](https://docs.hotkv.com/commands/pubsub/index.md) reports the current state of the server you are
connected to:

| Subcommand | Returns |
| --- | --- |
| [PUBSUB CHANNELS](https://docs.hotkv.com/commands/pubsub-channels/index.md) `[pattern]` | Channels with at least one subscriber |
| [PUBSUB NUMSUB](https://docs.hotkv.com/commands/pubsub-numsub/index.md) `[channel ...]` | Subscriber count for each channel |
| [PUBSUB NUMPAT](https://docs.hotkv.com/commands/pubsub-numpat/index.md) | Number of pattern subscriptions |
| [PUBSUB SHARDCHANNELS](https://docs.hotkv.com/commands/pubsub-shardchannels/index.md) `[pattern]` | Shard channels with subscribers |
| [PUBSUB SHARDNUMSUB](https://docs.hotkv.com/commands/pubsub-shardnumsub/index.md) `[channel ...]` | Subscriber count for each shard channel |

The count `PUBLISH` replies with, and these subcommands, cover the node you
are connected to only.

## Using the SDKs

**Python**

```python
import hotkv

client = hotkv.HotKV(host="localhost", port=6379)

with client.pubsub() as sub:
    sub.subscribe("orders")
    client.publish("orders", "order:1187 created")
    for message in sub.listen():
        print(message.channel, message.data)
        break
```

`pubsub()` opens a dedicated connection. `psubscribe()` and `ssubscribe()`
work the same way, and `get_message(timeout=...)` polls instead of blocking.
**Node.js**

```ts
import { HotKV } from "@hotkv/client";

const client = new HotKV({ url: "hotkv://127.0.0.1:6379" });

const sub = client.subscribe("orders");
sub.on("message", (msg) => {
  console.log(msg.channel, msg.message);
});

await client.publish("orders", "order:1187 created");
```

`client.psubscribe()` and `client.ssubscribe()` return the same kind of
`PubSub` object, which you can also read with `for await`. Call
`sub.close()` when done.
**Go**

```go
sub := client.Subscribe(ctx, "orders")
defer sub.Close()

for msg := range sub.Channel() {
    fmt.Println(msg.Channel, msg.Payload)
}
```
**Java**

```java
import com.hotkv.PubSub;
import com.hotkv.PubSubMessage;

try (PubSub sub = client.pubSub()) {
    sub.addListener(message -> {
        if (message.kind() == PubSubMessage.Kind.MESSAGE) {
            System.out.println(message.channel() + ": " + message.payload());
        }
    });
    sub.subscribe("orders");
    client.publish("orders", "order:1187 created");
}
```

## In a cluster

`PUBLISH` is forwarded to the subscribers of the channel on every node, so a
subscriber can connect to any node. The number it returns counts only the
subscribers on the node you sent it to. Sharded Pub/Sub (`SPUBLISH`,
`SSUBSCRIBE`) keeps a channel on the node that serves its slot, and other nodes
answer `MOVED`. See [Pub/Sub in a cluster](https://docs.hotkv.com/clustering/keys-and-commands/index.md#pubsub).

## Compatibility

Commands, message formats and replies follow Redis 7, including sharded
Pub/Sub. The one difference is subscribed mode on RESP3 connections, described
above.