Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions course-definition.yml
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,17 @@ extensions:
[redis-persistence]: https://redis.io/docs/manual/persistence/
[rdb-file-format]: https://github.com/sripathikrishnan/redis-rdb-tools/blob/548b11ec3c81a603f5b321228d07a61a0b940159/docs/RDB_File_Format.textile

- slug: "pub-sub"
name: "Pub/Sub"
description_markdown : |-
In this challenge extension you'll add support for [Publish/Subscribe (Pub/Sub)][redis-pub-sub] to your Redis implementation.

Along the way, you'll learn commands like [SUBSCRIBE][subscribe-command], [PUBLISH][publish-command], and more.

[redis-pub-sub]: https://redis.io/docs/latest/develop/pubsub/
[subscribe-command]: https://redis.io/docs/latest/commands/subscribe/
[publish-command]: https://redis.io/docs/latest/commands/publish/

stages:
- slug: "jm1"
concept_slugs:
Expand Down Expand Up @@ -623,3 +634,46 @@ stages:
name: "Blocking retrieval with timeout"
difficulty: medium
marketing_md: In this stage, you will add support for a non-zero timeout duration for the `BLPOP` command.

# Pub-Sub
- slug: "mx3"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'll need to add the extension above to the extensions key too btw

primary_extension_slug: "pub-sub"
name: "Subscribe to a channel"
difficulty: easy
marketing_md: In this stage, you’ll add support for the `SUBSCRIBE` command.

- slug: "zc8"
primary_extension_slug: "pub-sub"
name: "Subscribe to multiple channels"
difficulty: easy
marketing_md: In this stage, you'll add support for subscribing to multiple channels using the `SUBSCRIBE` command.

- slug: "aw8"
primary_extension_slug: "pub-sub"
name: "Enter subscribed mode"
difficulty: medium
marketing_md: In this stage, you'll add support for marking a client as having entered Subscribed mode.

- slug: "lf1"
primary_extension_slug: "pub-sub"
name: "PING in subscribed mode"
difficulty: easy
marketing_md: In this stage, you'll add support for responding to `PING` when a client is in subscribed mode.

- slug: "hf2"
primary_extension_slug: "pub-sub"
name: "Publish a message"
difficulty: easy
marketing_md: In this stage, you'll add support for the `PUBLISH` command.

- slug: "dn4"
primary_extension_slug: "pub-sub"
name: "Deliver messages"
difficulty: hard
marketing_md: In this stage, you will add support for delivering published messages to subscribed clients.

- slug: "ze9"
primary_extension_slug: "pub-sub"
name: "Unsubscribe"
difficulty: medium
marketing_md: In this stage, you'll add support for the `UNSUBSCRIBE command`, which is used to unsubscribe from a channel.
51 changes: 51 additions & 0 deletions stage_descriptions/pub-sub-01-mx3.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
In this stage, you’ll add support for the `SUBSCRIBE` command.

### The `SUBSCRIBE` Command

[The `SUBSCRIBE` command](https://redis.io/docs/latest/commands/subscribe/) registers the client as a subscriber to a channel.

Example Usage:

```bash
$ redis-cli
> SUBSCRIBE mychan
1) "subscribe"
2) "mychan"
3) (integer) 1
Reading messages... (press Ctrl-C to quit or any key to type command)
```

The response is a RESP Array which contains three elements:

1. "subscribe" (as a RESP bulk string)
1. The channel name (as a RESP bulk string)
1. The number of channels this client has subscribed to so far (as a RESP integer)

### Tests

The tester will run your program like this:

```bash
$ ./your_program.sh
```

It will then send a `SUBSCRIBE` command with a channel name.

```
$ redis-cli SUBSCRIBE foo
```

The tester will then expect the response to be `["subscribe", "foo", 1]`, which is encoded in RESP as:

```
*3\r\n
$9\r\n
subscribe\r\n
$3\r\n
foo\r\n
:1\r\n
```

### Notes

- In this stage, you'll only need to handle a `SUBSCRIBE` command being sent once and from a single client. We'll get to handling multiple `SUBSCRIBE` commands in later stages.
62 changes: 62 additions & 0 deletions stage_descriptions/pub-sub-02-zc8.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
In this stage, you'll add support for subscribing to multiple channels using the `SUBSCRIBE` command.

### Multiple SUBSCRIBE commands

A client can send `SUBSCRIBE` multiple times to subscribe to different channels. For example:

```bash
$ redis-cli
> SUBSCRIBE channel_1
1) "subscribe"
2) "channel_1"
3) (integer) 1

(subscribed mode)> SUBSCRIBE channel_2
1) "subscribe"
2) "channel_2"
3) (integer) 2
```

A client can also subscribe to the same channel multiple times, but the total subscribed channels count (returned as the 3rd item in the response array) won't change.

For example:

```bash
$ redis-cli
> SUBSCRIBE channel_1
1) "subscribe"
2) "channel_1"
3) (integer) 1

# The count remains 1 since channel is same
(subscribed mode)> SUBSCRIBE channel_1
1) "subscribe"
2) "channel_1"
3) (integer) 1
```

The subscribed channels count is per-client, i.e. how many channels the current client is subscribed to.

### Tests

The tester will run your program like this:

```bash
$ ./your_program.sh
```

It will then spawn a client and send multiple `SUBSCRIBE` commands specifying a new channel each time.

```bash
$ redis-cli
> SUBSCRIBE foo
# Expect ["subscribe", "foo", 1]

> SUBSCRIBE bar
# Expect ["subscribe", "bar", 2]

> SUBSCRIBE bar
# Expect ["subscribe", "bar", 2]
```

The tester will repeat this with other clients and verify that the subscribed channel counts are maintained per-client.
66 changes: 66 additions & 0 deletions stage_descriptions/pub-sub-03-aw8.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
In this stage, you'll add support for marking a client as having entered Subscribed mode.

### Subscribe Mode

After a subscribe command is sent, a client enters "Subscribed mode". In this mode, only a subset of commands is allowed:

- `SUBSCRIBE`
- `UNSUBSCRIBE`
- `PSUBSCRIBE`
- `PUNSUBSCRIBE`
- `PING`
- `QUIT`

The server will reject any other commands with an error, example:

```
$ redis-cli
> SUBSCRIBE channel
1) "subscribe"
2) "channel"
3) (integer) 1

(subscribed mode)> ECHO hey
(error) ERR Can't execute 'echo': only (P|S)SUBSCRIBE / (P|S)UNSUBSCRIBE / PING / QUIT / RESET are allowed in this context
```

### Tests

The tester will run your program like this:

```bash
$ ./your_program.sh
```

It will then send a `SUBSCRIBE` command to your server to enter Subscribed mode:

```bash
$ redis-cli
> SUBSCRIBE foo
# Expecting ["subscribe", "foo", 1]
```

The tester will then send a series of commands, which might be allowed or un-allowed for subscribed mode.

For un-allowed commands (like `SET`, `GET`, and `ECHO`) the tester will verify that your server responds with the following error:

```
> SET key value
- ERR Can't execute 'set': only (P|S)SUBSCRIBE / (P|S)UNSUBSCRIBE / PING / QUIT / RESET are allowed in this context
```

The tester only verifies that error message starts with "Can't execute '<command_name>'", so you're free to use a flexible error message and not stick to the exact format that Redis uses.

For the `SUBSCRIBE` command, the tester will verify that the response is its usual response.
```bash
> SUBSCRIBE bar
# Expecting ["subscribe", "bar", 2] as RESP-encoded array
```

### Notes

- For un-allowed commands, the tester is lenient in checking error messages so you don't have to stick to the exact format Redis uses. The exact format it checks for is `Can't execute '<command>'` (case-insensitive). Examples of error message strings that will pass tests:
- `Can't execute 'set' in subscribed mode`
- `can't execute 'SET' when one or more subscriptions exist`

- In subscribed mode, `PING` has a different response (it doesn't respond with `+PONG\r\n`). We'll get to this in later stages.
44 changes: 44 additions & 0 deletions stage_descriptions/pub-sub-04-lf1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
In this stage, you'll add support for responding to `PING` when a client is in subscribed mode.

### `PING` in subscribed mode

If a ping command is issued from a client after it enters subscribed mode, Redis does not respond with the usual response (`+PONG\r\n`). Instead it responds with a RESP array of two elements:

1. "pong" (encoded as a bulk string)
2. "" (empty bulk string)

### Tests

The tester will run your program like this:

```bash
$ ./your_program.sh
```

It will then send a `SUBSCRIBE` command to your server to enter Subscribed mode:

```bash
$ redis-cli
> SUBSCRIBE foo
# Expecting ["subscribe", "foo", 1]
```

The tester will then send a `PING` command.
```
> PING
```

It will expect the response to be an RESP-encoded array `["ping", ""]`, which would look like this:
```
*2\r\n
$4\r\n
pong\r\n
$0\r\n
\r\n
```

The tester will also send a `PING` command using a separate client, which is not subscribed to any channels.
```
> PING
```
It will expect the response to be `+PONG\r\n`, which is "PONG" encoded as a RESP simple string.
56 changes: 56 additions & 0 deletions stage_descriptions/pub-sub-05-hf2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
In this stage, you'll add support for the `PUBLISH` command\.

### The `PUBLISH` Command

[The PUBLISH command](https://redis.io/docs/latest/commands/publish/) delivers a message to all clients subscribed to a channel.

```
> PUBLISH channel_name message_contents
(integer) 3
```

The response to the command is the number of clients that are subscribed to the channel.

In this stage, you will only implement responding back to the `PUBLISH` command. You don't need to deliver the message to clients yet — we'll get to this in later stages.

### Tests

The tester will run your program like this:

```bash
$ ./your_program.sh
```

It will then spawn multiple clients that listen on multiple channels.

```bash
# Client 1 subscribes to channel "foo"
$ redis-cli
> SUBSCRIBE foo

# Client 2 subscribes to channel "bar"
$ redis-cli
> SUBSCRIBE bar

# Client 3 subscribes to channel "bar"
$ redis-cli
> SUBSCRIBE bar
```

The tester will then publish messages to different channels using `PUBLISH` and check whether the response matches the number of clients subscribed to the channel.

```
$ redis-cli PUBLISH bar "msg"
(integer) 2
```

In the above example, the expected response is 2 (encoded as a RESP integer) since 2 clients are subscribed to channel bar.

Similarly for the `foo` channel

```
$ redis-cli PUBLISH foo "msg"
(integer) 1
```

The tester expects the response to be 1 (encoded as a RESP integer) since only 1 client is subscribed to it.
Loading
Loading