From f2c67624ff3fb08e398a06788692c0e333036d59 Mon Sep 17 00:00:00 2001 From: Ryan Gang Date: Thu, 13 Jun 2024 16:49:15 +0530 Subject: [PATCH] feat: add placeholder stage descriptions --- internal/test_helpers/course_definition.yml | 366 ++++++++++++++------ 1 file changed, 251 insertions(+), 115 deletions(-) diff --git a/internal/test_helpers/course_definition.yml b/internal/test_helpers/course_definition.yml index 1151a765..b248ce5a 100644 --- a/internal/test_helpers/course_definition.yml +++ b/internal/test_helpers/course_definition.yml @@ -90,8 +90,7 @@ extensions: [xrange-command]: https://redis.io/commands/xrange/ stages: - - legacy_slug: "init" - slug: "jm1" + - slug: "jm1" concept_slugs: [ "network-protocols", @@ -130,8 +129,7 @@ stages: default port that Redis uses. tester_source_code_url: "https://github.com/codecrafters-io/redis-tester/blob/a58b9d58b33870fe26a164c0e323f809275a7250/internal/test_bind.go#L11" - - legacy_slug: "ping-pong" - slug: "rg2" + - slug: "rg2" concept_slugs: [ "network-protocols", @@ -183,8 +181,7 @@ stages: protocol](https://redis.io/topics/protocol) to encode the reply. tester_source_code_url: "https://github.com/codecrafters-io/redis-tester/blob/a58b9d58b33870fe26a164c0e323f809275a7250/internal/test_ping_pong.go#L9" - - legacy_slug: "ping-pong-multiple" - slug: "wy1" + - slug: "wy1" concept_slugs: [ "network-protocols", @@ -240,8 +237,7 @@ stages: [PING](https://redis.io/commands/ping) commands sent by the same client. tester_source_code_url: "https://github.com/codecrafters-io/redis-tester/blob/a58b9d58b33870fe26a164c0e323f809275a7250/internal/test_ping_pong.go#L35" - - legacy_slug: "concurrent-clients" - slug: "zu2" + - slug: "zu2" concept_slugs: [ "network-protocols", @@ -301,8 +297,7 @@ stages: like the official Redis implementation does. tester_source_code_url: "https://github.com/codecrafters-io/redis-tester/blob/a58b9d58b33870fe26a164c0e323f809275a7250/internal/test_ping_pong.go#L56" - - legacy_slug: "echo" - slug: "qq0" + - slug: "qq0" name: "Implement the ECHO command" difficulty: medium description_md: |- @@ -349,9 +344,7 @@ stages: specification](https://redis.io/topics/protocol). tester_source_code_url: "https://github.com/codecrafters-io/redis-tester/blob/a58b9d58b33870fe26a164c0e323f809275a7250/internal/test_echo.go#L11" - # TODO: Change this to use hyphens - - legacy_slug: "set_get" - slug: "la7" + - slug: "la7" name: "Implement the SET & GET commands" difficulty: medium description_md: |- @@ -405,8 +398,7 @@ stages: [GET](https://redis.io/commands/get) commands. tester_source_code_url: "https://github.com/codecrafters-io/redis-tester/blob/a58b9d58b33870fe26a164c0e323f809275a7250/internal/test_get_set.go#L11" - - legacy_slug: "expiry" - slug: "yz1" + - slug: "yz1" name: "Expiry" difficulty: medium description_md: |- @@ -468,8 +460,7 @@ stages: # Persistence - - legacy_slug: "rdb-config" - slug: "zg5" + - slug: "zg5" primary_extension_slug: "persistence-rdb" name: "RDB file config" difficulty: easy @@ -482,14 +473,14 @@ stages: An RDB file is a point-in-time snapshot of a Redis dataset. When RDB persistence is enabled, the Redis server syncs its in-memory state with an RDB file, by doing the following: - 1. On startup, the Redis server loads the data from the RDB file. - 2. While running, the Redis server periodically takes new snapshots of the dataset, in order to update the RDB file. + 1. On startup, the Redis server loads the data from the RDB file. + 2. While running, the Redis server periodically takes new snapshots of the dataset, in order to update the RDB file. - ### `dir` and `dbfilename` + ### `dir` and `dbfilename` The configuration parameters `dir` and `dbfilename` specify where an RDB file is stored: - - `dir` - the path to the directory where the RDB file is stored (example: `/tmp/redis-data`) - - `dbfilename` - the name of the RDB file (example: `rdbfile`) + - `dir` - the path to the directory where the RDB file is stored (example: `/tmp/redis-data`) + - `dbfilename` - the name of the RDB file (example: `rdbfile`) ### The `CONFIG GET` command @@ -522,8 +513,8 @@ stages: Your server must respond to each `CONFIG GET` command with a RESP array containing two elements: - 1. The parameter **name**, encoded as a [RESP Bulk string](https://redis.io/docs/latest/develop/reference/protocol-spec/#bulk-strings) - 2. The parameter **value**, encoded as a RESP Bulk string + 1. The parameter **name**, encoded as a [RESP Bulk string](https://redis.io/docs/latest/develop/reference/protocol-spec/#bulk-strings) + 2. The parameter **value**, encoded as a RESP Bulk string For example, if the value of `dir` is `/tmp/redis-files`, then the expected response to `CONFIG GET dir` is: @@ -533,57 +524,236 @@ stages: ### Notes - 1. You don't need to read the RDB file in this stage, you only need to store `dir` and `dbfilename`. Reading from the file will be covered in later stages. - 2. If your repository was created before 5th Oct 2023, it's possible that your `./spawn_redis_server.sh` script is not passing arguments to your program. To fix this, you'll need to edit `./spawn_redis_server.sh`. Check the [update CLI args PR](https://github.com/codecrafters-io/build-your-own-redis/pull/89/files) for details on how to do this. + - You don't need to read the RDB file in this stage, you only need to store `dir` and `dbfilename`. Reading from the file will be covered in later stages. + - If your repository was created before 5th Oct 2023, it's possible that your `./spawn_redis_server.sh` script is not passing arguments to your program. To fix this, you'll need to edit `./spawn_redis_server.sh`. Check the [update CLI args PR](https://github.com/codecrafters-io/build-your-own-redis/pull/89/files) for details on how to do this. marketing_md: | In this stage, you'll add support for reading the config values related to where RDB files are stored. You'll implement the `CONFIG GET` command. - - legacy_slug: "rdb-read-key" - slug: "jz6" + - slug: "jz6" primary_extension_slug: "persistence-rdb" name: "Read a key" difficulty: medium description_md: | - In this stage, you'll add support for reading a key from an RDB file. + In this stage, you'll add support for reading a single key from an RDB file. - To keep things simple, we'll start out by supporting RDB files that contain a single key. + ### RDB file format - Jan-Erik Rediger (author of [rdb-rs](https://rdb.fnordig.de/)) has a great [write-up](https://rdb.fnordig.de/file_format.html) - that explains the RDB file format in detail. We recommend using it as a reference when working on this stage. +
+ Click to expand/collapse + #### RDB file format overview - The tester will create an RDB file with a single key and execute your program like this: + Here are the different sections of the RDB file, in order: + + 1. Header section + 2. Metadata section + 3. Database section + 4. End of file section + + RDB files use special encodings to store different types of data. The ones relevant to this stage are "size encoding" and "string encoding." These are explained near the end of this page. + The following breakdown of the RDB file format is based on [Redis RDB File Format](https://rdb.fnordig.de/file_format.html) by Jan-Erik Rediger. We’ve only included the parts that are relevant to this stage. + + #### Header section + + RDB files begin with a header section, which looks something like this: ``` - ./spawn_redis_server.sh --dir --dbfilename + 52 45 44 49 53 30 30 30 37 // Magic string + version number (ASCII): "REDIS0007". ``` - It'll then send a `KEYS *` command to your server. + The header contains the magic string `REDIS`, followed by a four-character RDB version number. In this challenge, the test RDB files all use version 7. So, the header is always `REDIS0007`. - ```bash + #### Metadata section + + Next is the metadata section. It contains zero or more "metadata subsections," which each specify a single metadata attribute. Here's an example of a metadata subsection that specifies `redis-ver`: + ``` + FA // Indicates the start of a metadata subsection. + 09 72 65 64 69 73 2D 76 65 72 // The name of the metadata attribute (string encoded): "redis-ver". + 06 36 2E 30 2E 31 36 // The value of the metadata attribute (string encoded): "6.0.16". + ``` + + The metadata name and value are always string encoded. + + #### Database section + + Next is the database section. It contains zero or more "database subsections," which each describe a single database. Here's an example of a database subsection: + ``` + FE // Indicates the start of a database subsection. + 00 /* The index of the database (size encoded). + Here, the index is 0. */ + + FB // Indicates that hash table size information follows. + 02 /* The size of the hash table that stores the keys and values (size encoded). + Here, the key-value hash table size is 2. */ + 01 /* The size of the hash table that stores the expires of the keys (size encoded). + Here, the expire hash table size is 1. */ + + 00 /* The 1-byte flag that specifies the value’s type and encoding. + Here, the flag is 0, which means "string." */ + 06 66 6F 6F 62 61 72 // The name of the key (string encoded). Here, it's "foobar". + 06 62 61 7A 71 75 78 // The value (string encoded). Here, it's "bazqux". + + FC /* Indicates that this key has an expire, + and that the expire timestamp is expressed in milliseconds. */ + 15 72 E7 07 8F 01 00 00 /* The expire timestamp, expressed in Unix time, + stored as an 8-byte unsigned long, in little-endian (read right-to-left). + Here, the expire timestamp is 1713824559637. */ + 00 // Value type is string. + 03 66 6F 6F // Key name is "foo". + 03 62 61 72 // Value is "bar". + + FD /* Indicates that this key has an expire, + and that the expire timestamp is expressed in seconds. */ + 52 ED 2A 66 /* The expire timestamp, expressed in Unix time, + stored as an 4-byte unsigned integer, in little-endian (read right-to-left). + Here, the expire timestamp is 1714089298. */ + 00 // Value type is string. + 03 62 61 7A // Key name is "baz". + 03 71 75 78 // Value is "qux". + ``` + + Here's a more formal description of how each key-value pair is stored: + + 1. Optional expire information (one of the following): + * Timestamp in seconds: + 1. `FD` + 2. Expire timestamp in seconds (4-byte unsigned integer) + * Timestamp in milliseconds: + 1. `FC` + 2. Expire timestamp in milliseconds (8-byte unsigned long) + 2. Value type (1-byte flag) + 3. Key (string encoded) + 4. Value (encoding depends on value type) + + #### End of file section + + This section marks the end of the file. It looks something like this: + ``` + FF /* Indicates that the file is ending, + and that the checksum follows. */ + 89 3b b7 4e f8 0f 77 19 // An 8-byte CRC64 checksum of the entire file. + ``` + + #### Size encoding + + Size-encoded values specify the size of something. Here are some examples: + - The database indexes and hash table sizes are size encoded. + - String encoding begins with a size-encoded value that specifies the number of characters in the string. + - List encoding begins with a size-encoded value that specifies the number of elements in the list. + + The first two bits of a size-encoded value indicate how the value should be parsed. Here's a guide (bits are shown in both hexadecimal and binary): + ``` + /* If the first two bits are 0b00: + The size is the remaining 6 bits of the byte. + In this example, the size is 10: */ + 0A + 00001010 + + /* If the first two bits are 0b01: + The size is the next 14 bits + (remaining 6 bits in the first byte, combined with the next byte), + in big-endian (read left-to-right). + In this example, the size is 700: */ + 42 BC + 01000010 10111100 + + /* If the first two bits are 0b10: + Ignore the remaining 6 bits of the first byte. + The size is the next 4 bytes, in big-endian (read left-to-right). + In this example, the size is 17000: */ + 80 00 00 42 68 + 10000000 00000000 00000000 01000010 01101000 + + /* If the first two bits are 0b11: + The remaining 6 bits specify a type of string encoding. + See string encoding section. */ + ``` + + #### String encoding + + A string-encoded value consists of two parts: + 1. The size of the string (size encoded). + 2. The string. + + Here's an example: + ``` + /* The 0x0D size specifies that the string is 13 characters long. + The remaining characters spell out "Hello, World!". */ + 0D 48 65 6C 6C 6F 2C 20 57 6F 72 6C 64 21 + ``` + + For sizes that begin with `0b11`, the remaining 6 bits indicate a type of string format: + ``` + /* The 0xC0 size indicates the string is an 8-bit integer. + In this example, the string is "123". */ + C0 7B + + /* The 0xC1 size indicates the string is a 16-bit integer. + The remaining bytes are in little-endian (read right-to-left). + In this example, the string is "12345". */ + C1 39 30 + + /* The 0xC2 size indicates the string is a 32-bit integer. + The remaining bytes are in little-endian (read right-to-left), + In this example, the string is "1234567". */ + C2 87 D6 12 00 + + /* The 0xC3 size indicates that the string is compressed with the LZF algorithm. + You will not encounter LZF-compressed strings in this challenge. */ + C3 ... + ``` +
+ + + ### The `KEYS` command +
+ Click to expand/collapse + + The [`KEYS command`](https://redis.io/docs/latest/commands/keys/) returns all the keys that match a given pattern, as a RESP array: + ``` + $ redis-cli SET foo bar + OK + $ redis-cli SET baz qux + OK + $ redis-cli KEYS "f*" + 1) "foo" + ``` + + When the pattern is `*`, the command returns all the keys in the database: + ``` $ redis-cli KEYS "*" + 1) "baz" + 2) "foo" ``` - The response to `KEYS *` should be a RESP array with one element: the key. + In this stage, you must add support for the `KEYS` command. However, you only need to support the `*` pattern. +
- For example, let's say the RDB file contains a key called `foo`. The expected response will be: + ### Tests + The tester will create an RDB file with a single key and execute your program like this: ``` - *1\r\n$3\r\nfoo\r\n + $ ./spawn_redis_server.sh --dir --dbfilename ``` - - `*1\r\n` indicates that the array has one element - - `$3\r\nfoo\r\n` indicates that the first element is a bulk string with the value `foo` + It'll then send a `KEYS "*"` command to your server. + ``` + $ redis-cli KEYS "*" + ``` - **Note**: Remember, in this stage you only need to support RDB files that contain a single key, and you can ignore the value of the key. We'll - get to handling multiple keys and reading values in later stages. + Your server must respond with a RESP array that contains the key from the RDB file: + ``` + *1\r\n$3\r\nfoo\r\n + ``` + + ### Notes - **Note**: The `.rdb` file provided via `--dir`/`--dbfilename` might not exist. If the file doesn't exist, your program must treat it as if the database - is currently empty. + - The RDB file provided by `--dir` and `--dbfilename` might not exist. If the file doesn't exist, your program must treat the database as empty. + - RDB files use both little-endian and big-endian to store numbers. See the [MDN article on endianness](https://developer.mozilla.org/en-US/docs/Glossary/Endianness) to learn more. + - To generate an RDB file, use the [`SAVE` command](https://redis.io/docs/latest/commands/save/). marketing_md: | In this stage, you'll add support for reading a key from an RDB file that contains a single key-value pair. You'll do this by implementing the `KEYS *` command. - - legacy_slug: "rdb-read-string-value" - slug: "gc6" + - slug: "gc6" primary_extension_slug: "persistence-rdb" name: "Read a string value" difficulty: medium @@ -620,8 +790,7 @@ stages: marketing_md: | In this stage, you'll add support for reading the value of a key from an RDB file that contains a single key-value pair. - - legacy_slug: "rdb-read-multiple-keys" - slug: "jw4" + - slug: "jw4" primary_extension_slug: "persistence-rdb" name: "Read multiple keys" difficulty: medium @@ -654,8 +823,7 @@ stages: marketing_md: | In this stage, you'll add support for reading multiple keys from an RDB file. You'll do this by extending the `KEYS *` command to support multiple keys. - - legacy_slug: "rdb-read-multiple-string-values" - slug: "dq3" + - slug: "dq3" primary_extension_slug: "persistence-rdb" name: "Read multiple string values" difficulty: medium @@ -679,8 +847,7 @@ stages: marketing_md: | In this stage, you'll add support for reading multiple string values from an RDB file. - - legacy_slug: "rdb-read-value-with-expiry" - slug: "sm4" + - slug: "sm4" primary_extension_slug: "persistence-rdb" name: "Read value with expiry" difficulty: medium @@ -711,8 +878,7 @@ stages: # Replication - - legacy_slug: "repl-custom-port" - slug: "bw1" + - slug: "bw1" primary_extension_slug: "replication" name: "Configure listening port" difficulty: easy @@ -746,8 +912,7 @@ stages: marketing_md: | In this stage, you'll add support for parsing the `--port` flag and starting Redis on a custom port. - - legacy_slug: "repl-info" - slug: "ye5" + - slug: "ye5" primary_extension_slug: "replication" name: "The INFO command" difficulty: easy @@ -814,8 +979,7 @@ stages: marketing_md: | In this stage, you'll add support for the INFO command on the master. - - legacy_slug: "repl-info-replica" - slug: "hc6" + - slug: "hc6" primary_extension_slug: "replication" name: "The INFO command on a replica" difficulty: medium @@ -863,8 +1027,7 @@ stages: marketing_md: | In this stage, you'll add support for the --replicaof arg and INFO command on the replica. - - legacy_slug: "repl-id" - slug: "xc1" + - slug: "xc1" primary_extension_slug: "replication" name: "Initial Replication ID and Offset" difficulty: easy @@ -915,8 +1078,7 @@ stages: marketing_md: | In this stage, you'll add support for reading a key from an RDB file that contains a single key-value pair. You'll do this by implementing the `KEYS *` command. - - legacy_slug: "repl-replica-ping" - slug: "gl7" + - slug: "gl7" primary_extension_slug: "replication" name: "Send handshake (1/3)" difficulty: easy @@ -951,8 +1113,7 @@ stages: marketing_md: | In this stage, you'll add support for starting the handshake from the Replica side. - - legacy_slug: "repl-replica-replconf" - slug: "eh4" + - slug: "eh4" primary_extension_slug: "replication" name: "Send handshake (2/3)" difficulty: easy @@ -1009,8 +1170,7 @@ stages: marketing_md: | In this stage, you'll add support for continuing the handshake from the Replica side, by sending REPLCONF. - - legacy_slug: "repl-replica-psync" - slug: "ju6" + - slug: "ju6" primary_extension_slug: "replication" name: "Send handshake (3/3)" difficulty: medium @@ -1067,8 +1227,7 @@ stages: marketing_md: | In this stage, you'll add support for finishing the handshake from the Replica side, by sending PSYNC. - - legacy_slug: "repl-master-replconf" - slug: "fj0" + - slug: "fj0" primary_extension_slug: "replication" name: "Receive handshake (1/2)" difficulty: easy @@ -1107,8 +1266,7 @@ stages: marketing_md: | In this stage, you'll add support for starting the handshake from the master side, by accepting REPLCONF. - - legacy_slug: "repl-master-psync" - slug: "vm3" + - slug: "vm3" primary_extension_slug: "replication" name: "Receive handshake (2/2)" difficulty: easy @@ -1167,8 +1325,7 @@ stages: marketing_md: | In this stage, you'll add support for accepting PSYNC, and starting a FULLRESYNC. - - legacy_slug: "repl-master-psync-rdb" - slug: "cf8" + - slug: "cf8" primary_extension_slug: "replication" name: "Empty RDB Transfer" difficulty: easy @@ -1229,8 +1386,7 @@ stages: marketing_md: | In this stage, you'll add support for sending an empty RDB file to the replica. This is part of the "full resynchronization" process. - - legacy_slug: "repl-master-cmd-prop" - slug: "zn8" + - slug: "zn8" primary_extension_slug: "replication" name: "Single-replica propagation" difficulty: medium @@ -1300,8 +1456,7 @@ stages: marketing_md: | In this stage, you'll add support for finishing the sync handshake from the master side, by sending a RDB file. - - legacy_slug: "repl-multiple-replicas" - slug: "hd5" + - slug: "hd5" primary_extension_slug: "replication" name: "Multi Replica Command Propagation" difficulty: hard @@ -1337,8 +1492,7 @@ stages: marketing_md: | In this stage, you'll complete your implementation of Redis replication. - - legacy_slug: "repl-cmd-processing" - slug: "yg4" + - slug: "yg4" primary_extension_slug: "replication" name: "Command Processing" difficulty: hard @@ -1387,8 +1541,7 @@ stages: marketing_md: | In this stage, you'll add support for processing commands received by the replica from the master. - - legacy_slug: "repl-replica-getack" - slug: "xv6" + - slug: "xv6" primary_extension_slug: "replication" name: "ACKs with no commands" difficulty: easy @@ -1455,8 +1608,7 @@ stages: marketing_md: | In this stage, you'll add support for returning an ACK back to master as a response to GETACK. - - legacy_slug: "repl-replica-getack-nonzero" - slug: "yd3" + - slug: "yd3" primary_extension_slug: "replication" name: "ACKs with commands" difficulty: medium @@ -1531,8 +1683,7 @@ stages: marketing_md: | In this stage, you'll add support for returning an ACK back to master as a response to GETACK. - - legacy_slug: "repl-wait-zero-replicas" - slug: "my8" + - slug: "my8" primary_extension_slug: "replication" name: "WAIT with no replicas" difficulty: medium @@ -1569,8 +1720,7 @@ stages: marketing_md: | In this stage, you'll start implementing the WAIT command on your master. - - legacy_slug: "repl-wait-zero-offset" - slug: "tu8" + - slug: "tu8" primary_extension_slug: "replication" name: "WAIT with no commands" difficulty: medium @@ -1611,8 +1761,7 @@ stages: marketing_md: | In this stage, you'll continue implementing the WAIT command on your master. - - legacy_slug: "repl-wait" - slug: "na2" + - slug: "na2" primary_extension_slug: "replication" name: "WAIT with multiple commands" difficulty: hard @@ -1654,8 +1803,7 @@ stages: In this stage, you'll finish implementing the WAIT command on your master. # Streams - - legacy_slug: "streams-type" - slug: "cc3" + - slug: "cc3" primary_extension_slug: "streams" name: "The TYPE command" difficulty: easy @@ -1722,8 +1870,7 @@ stages: marketing_md: | In this stage, you'll add support for the `TYPE` command. - - legacy_slug: "streams-xadd" - slug: "cf6" + - slug: "cf6" primary_extension_slug: "streams" name: "Create a stream" difficulty: medium @@ -1803,8 +1950,7 @@ stages: marketing_md: | In this stage, you'll add support for creating a [Redis stream](https://redis.io/docs/data-types/streams/) using the `XADD` command. - - legacy_slug: "streams-xadd-validate-id" - slug: "hq8" + - slug: "hq8" primary_extension_slug: "streams" name: "Validating entry IDs" difficulty: easy @@ -1923,8 +2069,7 @@ stages: marketing_md: | In this stage, you'll enhance the `XADD` command by extending support for explicit IDs. - - legacy_slug: "streams-xadd-partial-autoid" - slug: "yh3" + - slug: "yh3" primary_extension_slug: "streams" name: "Partially auto-generated IDs" difficulty: medium @@ -1995,8 +2140,7 @@ stages: marketing_md: | In this stage, you'll enhance the `XADD` command by adding the option to use `*` as the sequence number. - - legacy_slug: "streams-xadd-full-autoid" - slug: "xu6" + - slug: "xu6" primary_extension_slug: "streams" name: "Fully auto-generated IDs" difficulty: medium @@ -2042,8 +2186,7 @@ stages: marketing_md: | In this stage, you'll enhance the `XADD` command by adding the option to use `*` as the entry ID. - - legacy_slug: "streams-xrange" - slug: "zx1" + - slug: "zx1" primary_extension_slug: "streams" name: "Query entries from stream" difficulty: medium @@ -2186,8 +2329,7 @@ stages: marketing_md: | In this stage, you'll add support for querying data from a stream using the `XRANGE` command. - - legacy_slug: "streams-xrange-min-id" - slug: "yp1" + - slug: "yp1" primary_extension_slug: "streams" name: "Query with -" difficulty: easy @@ -2276,8 +2418,7 @@ stages: marketing_md: | In this stage, you'll extend support for `XRANGE` to allow querying using `-`. - - legacy_slug: "streams-xrange-max-id" - slug: "fs1" + - slug: "fs1" primary_extension_slug: "streams" name: "Query with +" difficulty: easy @@ -2376,8 +2517,7 @@ stages: marketing_md: | In this stage, you'll extend support for `XRANGE` to allow querying using `+`. - - legacy_slug: "streams-xread" - slug: "um0" + - slug: "um0" primary_extension_slug: "streams" name: "Query single stream using XREAD" difficulty: medium @@ -2524,8 +2664,7 @@ stages: marketing_md: | In this stage, you'll add support to querying a stream using the `XREAD` command. - - legacy_slug: "streams-xread-multiple" - slug: "ru9" + - slug: "ru9" primary_extension_slug: "streams" name: "Query multiple streams using XREAD" difficulty: medium @@ -2611,8 +2750,7 @@ stages: marketing_md: | In this stage, you'll add extend support to `XREAD` to allow querying multiple streams. - - legacy_slug: "streams-xread-block" - slug: "bs1" + - slug: "bs1" primary_extension_slug: "streams" name: "Blocking reads" difficulty: hard @@ -2752,8 +2890,7 @@ stages: marketing_md: | In this stage, you'll add extend support to `XREAD` to allow querying multiple streams. - - legacy_slug: "streams-xread-block-no-timeout" - slug: "hw1" + - slug: "hw1" primary_extension_slug: "streams" name: "Blocking reads without timeout" difficulty: medium @@ -2870,8 +3007,7 @@ stages: marketing_md: | In this stage, you'll add extend support to `XREAD` to allow for the blocking command not timing out. - - legacy_slug: "streams-xread-block-max-id" - slug: "xu1" + - slug: "xu1" primary_extension_slug: "streams" name: "Blocking reads using $" difficulty: easy