...
This commit is contained in:
		
							
								
								
									
										259
									
								
								instructions/redis_lists.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										259
									
								
								instructions/redis_lists.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,259 @@ | ||||
|  | ||||
| # 1) Data model & basics | ||||
|  | ||||
| * A **queue** is a List at key `queue:<name>`. | ||||
| * Common patterns: | ||||
|  | ||||
|   * **Producer**: `LPUSH queue item` (or `RPUSH`) | ||||
|   * **Consumer (non-blocking)**: `RPOP queue` (or `LPOP`) | ||||
|   * **Consumer (blocking)**: `BRPOP queue timeout` (or `BLPOP`) | ||||
| * If a key doesn’t exist, it’s treated as an **empty list**; push **creates** the list; when the **last element is popped, the key is deleted**. ([Redis][1]) | ||||
|  | ||||
| --- | ||||
|  | ||||
| # 2) Commands to implement (queues via Lists) | ||||
|  | ||||
| ## LPUSH / RPUSH | ||||
|  | ||||
| Prepend/append one or more elements. Create the list if it doesn’t exist. | ||||
| **Return**: Integer = new length of the list. | ||||
|  | ||||
| **Syntax** | ||||
|  | ||||
| ``` | ||||
| LPUSH key element [element ...] | ||||
| RPUSH key element [element ...] | ||||
| ``` | ||||
|  | ||||
| **RESP (example)** | ||||
|  | ||||
| ``` | ||||
| *3\r\n$5\r\nLPUSH\r\n$5\r\nqueue\r\n$5\r\njob-1\r\n | ||||
| :1\r\n | ||||
| ``` | ||||
|  | ||||
| Refs: semantics & multi-arg ordering. ([Redis][1]) | ||||
|  | ||||
| ### LPUSHX / RPUSHX (optional but useful) | ||||
|  | ||||
| Like LPUSH/RPUSH, **but only if the list exists**. | ||||
| **Return**: Integer = new length (0 if key didn’t exist). | ||||
|  | ||||
| ``` | ||||
| LPUSHX key element [element ...] | ||||
| RPUSHX key element [element ...] | ||||
| ``` | ||||
|  | ||||
| Refs: command index. ([Redis][2]) | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## LPOP / RPOP | ||||
|  | ||||
| Remove & return one (default) or **up to COUNT** elements since Redis 6.2. | ||||
| If the list is empty or missing, **Null** is returned (Null Bulk or Null Array if COUNT>1). | ||||
| **Return**: | ||||
|  | ||||
| * No COUNT: Bulk String or Null Bulk. | ||||
| * With COUNT: Array of Bulk Strings (possibly empty) or Null Array if key missing. | ||||
|  | ||||
| **Syntax** | ||||
|  | ||||
| ``` | ||||
| LPOP key [count] | ||||
| RPOP key [count] | ||||
| ``` | ||||
|  | ||||
| **RESP (no COUNT)** | ||||
|  | ||||
| ``` | ||||
| *2\r\n$4\r\nRPOP\r\n$5\r\nqueue\r\n | ||||
| $5\r\njob-1\r\n            # or $-1\r\n if empty | ||||
| ``` | ||||
|  | ||||
| **RESP (COUNT=2)** | ||||
|  | ||||
| ``` | ||||
| *3\r\n$4\r\nLPOP\r\n$5\r\nqueue\r\n$1\r\n2\r\n | ||||
| *2\r\n$5\r\njob-2\r\n$5\r\njob-3\r\n   # or *-1\r\n if key missing | ||||
| ``` | ||||
|  | ||||
| Refs: LPOP w/ COUNT; general pop semantics. ([Redis][3]) | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## BLPOP / BRPOP (blocking consumers) | ||||
|  | ||||
| Block until an element is available in any of the given lists or until `timeout` (seconds, **double**, `0` = forever). | ||||
| **Return** on success: **Array \[key, element]**. | ||||
| **Return** on timeout: **Null Array**. | ||||
|  | ||||
| **Syntax** | ||||
|  | ||||
| ``` | ||||
| BLPOP key [key ...] timeout | ||||
| BRPOP key [key ...] timeout | ||||
| ``` | ||||
|  | ||||
| **RESP** | ||||
|  | ||||
| ``` | ||||
| *3\r\n$5\r\nBRPOP\r\n$5\r\nqueue\r\n$1\r\n0\r\n  # block forever | ||||
|  | ||||
| # Success reply | ||||
| *2\r\n$5\r\nqueue\r\n$5\r\njob-4\r\n | ||||
|  | ||||
| # Timeout reply | ||||
| *-1\r\n | ||||
| ``` | ||||
|  | ||||
| **Implementation notes** | ||||
|  | ||||
| * If any listed key is non-empty at call time, reply **immediately** from the first non-empty key **by the command’s key order**. | ||||
| * Otherwise, put the client into a **blocked state** (register per-key waiters). On any `LPUSH/RPUSH` to those keys, **wake the earliest waiter** and serve it atomically. | ||||
| * If timeout expires, return **Null Array** and clear the blocked state. | ||||
|   Refs: timeout semantics and return shape. ([Redis][4]) | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## LMOVE / BLMOVE (atomic move; replaces RPOPLPUSH/BRPOPLPUSH) | ||||
|  | ||||
| Atomically **pop from one side** of `source` and **push to one side** of `destination`. | ||||
|  | ||||
| * Use for **reliable queues** (move to a *processing* list). | ||||
| * `BLMOVE` blocks like `BLPOP` when `source` is empty. | ||||
|  | ||||
| **Syntax** | ||||
|  | ||||
| ``` | ||||
| LMOVE source destination LEFT|RIGHT LEFT|RIGHT | ||||
| BLMOVE source destination LEFT|RIGHT LEFT|RIGHT timeout | ||||
| ``` | ||||
|  | ||||
| **Return**: Bulk String element moved, or Null if `source` empty (LMOVE); `BLMOVE` blocks/Null on timeout. | ||||
|  | ||||
| **RESP (LMOVE RIGHT->LEFT)** | ||||
|  | ||||
| ``` | ||||
| *5\r\n$5\r\nLMOVE\r\n$6\r\nsource\r\n$3\r\ndst\r\n$5\r\nRIGHT\r\n$4\r\nLEFT\r\n | ||||
| $5\r\njob-5\r\n | ||||
| ``` | ||||
|  | ||||
| **Notes** | ||||
|  | ||||
| * Prefer `LMOVE/BLMOVE` over deprecated `RPOPLPUSH/BRPOPLPUSH`. | ||||
| * Pattern: consumer `LMOVE queue processing RIGHT LEFT` → work → `LREM processing 1 <elem>` to ACK; a reaper can requeue stale items. | ||||
|   Refs: LMOVE/BLMOVE behavior and reliable-queue pattern; deprecation of RPOPLPUSH. ([Redis][5]) | ||||
|  | ||||
| *(Compat: you can still implement `RPOPLPUSH source dest` and `BRPOPLPUSH source dest timeout`, but mark them deprecated and map to LMOVE/BLMOVE.)* ([Redis][6]) | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## LLEN (length) | ||||
|  | ||||
| Useful for metrics/backpressure. | ||||
|  | ||||
| ``` | ||||
| LLEN key | ||||
| ``` | ||||
|  | ||||
| **RESP** | ||||
|  | ||||
| ``` | ||||
| *2\r\n$4\r\nLLEN\r\n$5\r\nqueue\r\n | ||||
| :3\r\n | ||||
| ``` | ||||
|  | ||||
| Refs: list overview mentioning LLEN. ([Redis][7]) | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## LREM (ack for “reliable” processing) | ||||
|  | ||||
| Remove occurrences of `element` from the list (head→tail scan). | ||||
| Use `count=1` to ACK a single processed item from `processing`. | ||||
|  | ||||
| ``` | ||||
| LREM key count element | ||||
| ``` | ||||
|  | ||||
| **RESP** | ||||
|  | ||||
| ``` | ||||
| *4\r\n$4\r\nLREM\r\n$9\r\nprocessing\r\n$1\r\n1\r\n$5\r\njob-5\r\n | ||||
| :1\r\n | ||||
| ``` | ||||
|  | ||||
| Refs: reliable pattern mentions LREM to ACK. ([Redis][5]) | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## LTRIM (bounded queues / retention) | ||||
|  | ||||
| Keep only `[start, stop]` range; everything else is dropped. | ||||
| Use to cap queue length after pushes. | ||||
|  | ||||
| ``` | ||||
| LTRIM key start stop | ||||
| ``` | ||||
|  | ||||
| **RESP** | ||||
|  | ||||
| ``` | ||||
| *4\r\n$5\r\nLTRIM\r\n$5\r\nqueue\r\n$2\r\n0\r\n$3\r\n999\r\n | ||||
| +OK\r\n | ||||
| ``` | ||||
|  | ||||
| Refs: list overview includes LTRIM for retention. ([Redis][7]) | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## LRANGE / LINDEX (debugging / peeking) | ||||
|  | ||||
| * `LRANGE key start stop` → Array of elements (non-destructive). | ||||
| * `LINDEX key index` → one element or Null. | ||||
|  | ||||
| These aren’t required for queue semantics, but handy. ([Redis][7]) | ||||
|  | ||||
| --- | ||||
|  | ||||
| # 3) Errors & types | ||||
|  | ||||
| * Wrong type: `-WRONGTYPE Operation against a key holding the wrong kind of value\r\n` | ||||
| * Non-existing key: | ||||
|  | ||||
|   * Push: creates the list (returns new length). | ||||
|   * Pop (non-blocking): returns **Null**. | ||||
|   * Blocking pop: **Null Array** on timeout. ([Redis][1]) | ||||
|  | ||||
| --- | ||||
|  | ||||
| # 4) Blocking engine (implementation sketch) | ||||
|  | ||||
| 1. **Call time**: scan keys in user order. If a non-empty list is found, pop & reply immediately. | ||||
| 2. **Otherwise**: register the client as **blocked** on those keys with `deadline = now + timeout` (or infinite). | ||||
| 3. **On push to any key**: if waiters exist, **wake one** (FIFO) and serve its pop **atomically** with the push result. | ||||
| 4. **On timer**: for each blocked client whose deadline passed, reply `Null Array` and clear state. | ||||
| 5. **Connection close**: remove from any wait queues. | ||||
|  | ||||
| Refs for timeout/block semantics. ([Redis][4]) | ||||
|  | ||||
| --- | ||||
|  | ||||
| # 5) Reliable queue pattern (recommended) | ||||
|  | ||||
| * **Consume**: `LMOVE queue processing RIGHT LEFT` (or `BLMOVE ... 0`). | ||||
| * **Process** the job. | ||||
| * **ACK**: `LREM processing 1 <job>` when done. | ||||
| * **Reaper**: auxiliary task that detects stale jobs (e.g., track job IDs + timestamps in a ZSET) and requeues them. (Lists don’t include timestamps; pairing with a ZSET is standard practice.) | ||||
|   Refs: LMOVE doc’s pattern. ([Redis][5]) | ||||
|  | ||||
| --- | ||||
|  | ||||
| # 6) Minimal test matrix | ||||
|  | ||||
| * Push/pop happy path (both ends), with/without COUNT.  | ||||
| * Blocking pop: immediate availability, block + timeout, wake on push, multiple keys order, FIFO across multiple waiters. | ||||
| * LMOVE/BLMOVE: RIGHT→LEFT pipeline, block + wake, cross-list atomicity, ACK via LREM. | ||||
| * Type errors and key deletion on last pop. | ||||
|  | ||||
| @@ -13,6 +13,8 @@ echo "2️⃣  Running Comprehensive Redis Integration Tests (13 tests)..." | ||||
| echo "----------------------------------------------------------------" | ||||
| cargo test --test redis_integration_tests -- --nocapture | ||||
| cargo test --test redis_basic_client -- --nocapture | ||||
| cargo test --test debug_hset -- --nocapture | ||||
| cargo test --test debug_hset_simple -- --nocapture | ||||
|  | ||||
| echo "" | ||||
| echo "3️⃣  Running All Tests..." | ||||
|   | ||||
| @@ -36,6 +36,9 @@ pub enum Cmd { | ||||
|     Client(Vec<String>), | ||||
|     ClientSetName(String), | ||||
|     ClientGetName, | ||||
|     // List commands | ||||
|     LPush(String, Vec<String>), | ||||
|     RPush(String, Vec<String>), | ||||
|     Unknow(String), | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -18,6 +18,7 @@ async fn start_test_server(test_name: &str) -> (Server, u16) { | ||||
|     let option = DBOption { | ||||
|         dir: test_dir, | ||||
|         port, | ||||
|         debug: true, | ||||
|     }; | ||||
|      | ||||
|     let server = Server::new(option).await; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user