Skip to content

Commit c99c17a

Browse files
alex-arzola-impYury-Fridlyandacarbonetto
authored andcommitted
Node - Added BLPOP command (valkey-io#1223)
* Node - Added BLPOP command * Addressed PR comments * Ran prettier * Added unit test and ran prettier * PR comments. Signed-off-by: Yury-Fridlyand <yury.fridlyand@improving.com> * Added same-slot requirement for BLPOP and addressed PR comments * Fixed branch conflicts * Addressed PR commands * Update docs and test, fix merge errors. Signed-off-by: Yury-Fridlyand <yury.fridlyand@improving.com> * Update doc. Signed-off-by: Yury-Fridlyand <yury.fridlyand@improving.com> --------- Signed-off-by: Yury-Fridlyand <yury.fridlyand@improving.com> Co-authored-by: Yury-Fridlyand <yury.fridlyand@improving.com> Co-authored-by: Andrew Carbonetto <andrew.carbonetto@improving.com>
1 parent 3326b4f commit c99c17a

File tree

6 files changed

+122
-4
lines changed

6 files changed

+122
-4
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
* Python: Added PFADD command ([#1315](https://github.com/aws/glide-for-redis/pull/1315))
2020
* Python: Added ZMSCORE command ([#1357](https://github.com/aws/glide-for-redis/pull/1357))
2121
* Python: Added HRANDFIELD command ([#1334](https://github.com/aws/glide-for-redis/pull/1334))
22+
* Node: Added BLPOP command ([#1223](https://github.com/aws/glide-for-redis/pull/1223))
2223
* Python: Added XADD, XTRIM commands ([#1320](https://github.com/aws/glide-for-redis/pull/1320))
2324
* Python: Added ZRANGESTORE command ([#1377](https://github.com/aws/glide-for-redis/pull/1377))
2425

node/src/BaseClient.ts

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import {
2121
StreamReadOptions,
2222
StreamTrimOptions,
2323
ZaddOptions,
24+
createBlpop,
2425
createBrpop,
2526
createDecr,
2627
createDecrBy,
@@ -2190,13 +2191,16 @@ export class BaseClient {
21902191
* with the given keys being checked in the order that they are given.
21912192
* Blocks the connection when there are no elements to pop from any of the given lists.
21922193
* See https://redis.io/commands/brpop/ for more details.
2193-
* Note: BRPOP is a blocking command,
2194+
*
2195+
* Notes:
2196+
* 1. `BRPOP` is a blocking command,
21942197
* see [Blocking Commands](https://github.com/aws/glide-for-redis/wiki/General-Concepts#blocking-commands) for more details and best practices.
2198+
* 2. When in cluster mode, all `keys` must map to the same `hash slot`.
21952199
*
21962200
* @param keys - The `keys` of the lists to pop from.
21972201
* @param timeout - The `timeout` in seconds.
21982202
* @returns - An `array` containing the `key` from which the element was popped and the value of the popped element,
2199-
* formatted as [key, value]. If no element could be popped and the timeout expired, returns Null.
2203+
* formatted as [key, value]. If no element could be popped and the timeout expired, returns `null`.
22002204
*
22012205
* @example
22022206
* ```typescript
@@ -2212,6 +2216,35 @@ export class BaseClient {
22122216
return this.createWritePromise(createBrpop(keys, timeout));
22132217
}
22142218

2219+
/** Blocking list pop primitive.
2220+
* Pop an element from the head of the first list that is non-empty,
2221+
* with the given `keys` being checked in the order that they are given.
2222+
* Blocks the connection when there are no elements to pop from any of the given lists.
2223+
* See https://redis.io/commands/blpop/ for more details.
2224+
*
2225+
* Notes:
2226+
* 1. `BLPOP` is a blocking command,
2227+
* see [Blocking Commands](https://github.com/aws/glide-for-redis/wiki/General-Concepts#blocking-commands) for more details and best practices.
2228+
* 2. When in cluster mode, all `keys` must map to the same `hash slot`.
2229+
*
2230+
* @param keys - The `keys` of the lists to pop from.
2231+
* @param timeout - The `timeout` in seconds.
2232+
* @returns - An `array` containing the `key` from which the element was popped and the value of the popped element,
2233+
* formatted as [key, value]. If no element could be popped and the timeout expired, returns `null`.
2234+
*
2235+
* @example
2236+
* ```typescript
2237+
* const result = await client.blpop(["list1", "list2"], 5);
2238+
* console.log(result); // Output: ['list1', 'element']
2239+
* ```
2240+
*/
2241+
public blpop(
2242+
keys: string[],
2243+
timeout: number,
2244+
): Promise<[string, string] | null> {
2245+
return this.createWritePromise(createBlpop(keys, timeout));
2246+
}
2247+
22152248
/** Adds all elements to the HyperLogLog data structure stored at the specified `key`.
22162249
* Creates a new structure if the `key` does not exist.
22172250
* When no elements are provided, and `key` exists and is a HyperLogLog, then no operation is performed.

node/src/Commands.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1221,6 +1221,17 @@ export function createBrpop(
12211221
return createCommand(RequestType.Brpop, args);
12221222
}
12231223

1224+
/**
1225+
* @internal
1226+
*/
1227+
export function createBlpop(
1228+
keys: string[],
1229+
timeout: number,
1230+
): redis_request.Command {
1231+
const args = [...keys, timeout.toString()];
1232+
return createCommand(RequestType.Blpop, args);
1233+
}
1234+
12241235
export type StreamReadOptions = {
12251236
/**
12261237
* If set, the read request will block for the set amount of milliseconds or until the server has the required number of entries.

node/src/Transaction.ts

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
StreamReadOptions,
1515
StreamTrimOptions,
1616
ZaddOptions,
17+
createBlpop,
1718
createBrpop,
1819
createClientGetName,
1920
createClientId,
@@ -1243,18 +1244,35 @@ export class BaseTransaction<T extends BaseTransaction<T>> {
12431244
* with the given keys being checked in the order that they are given.
12441245
* Blocks the connection when there are no elements to pop from any of the given lists.
12451246
* See https://redis.io/commands/brpop/ for more details.
1246-
* Note: BRPOP is a blocking command,
1247+
* Note: `BRPOP` is a blocking command,
12471248
* see [Blocking Commands](https://github.com/aws/glide-for-redis/wiki/General-Concepts#blocking-commands) for more details and best practices.
12481249
*
12491250
* @param keys - The `keys` of the lists to pop from.
12501251
* @param timeout - The `timeout` in seconds.
12511252
* Command Response - An `array` containing the `key` from which the element was popped and the value of the popped element,
1252-
* formatted as [key, value]. If no element could be popped and the timeout expired, returns Null.
1253+
* formatted as [key, value]. If no element could be popped and the timeout expired, returns `null`.
12531254
*/
12541255
public brpop(keys: string[], timeout: number): T {
12551256
return this.addAndReturn(createBrpop(keys, timeout));
12561257
}
12571258

1259+
/** Blocking list pop primitive.
1260+
* Pop an element from the head of the first list that is non-empty,
1261+
* with the given `keys` being checked in the order that they are given.
1262+
* Blocks the connection when there are no elements to pop from any of the given lists.
1263+
* See https://redis.io/commands/blpop/ for more details.
1264+
* Note: `BLPOP` is a blocking command,
1265+
* see [Blocking Commands](https://github.com/aws/glide-for-redis/wiki/General-Concepts#blocking-commands) for more details and best practices.
1266+
*
1267+
* @param keys - The `keys` of the lists to pop from.
1268+
* @param timeout - The `timeout` in seconds.
1269+
* Command Response - An `array` containing the `key` from which the element was popped and the value of the popped element,
1270+
* formatted as [key, value]. If no element could be popped and the timeout expired, returns `null`.
1271+
*/
1272+
public blpop(keys: string[], timeout: number): T {
1273+
return this.addAndReturn(createBlpop(keys, timeout));
1274+
}
1275+
12581276
/** Adds all elements to the HyperLogLog data structure stored at the specified `key`.
12591277
* Creates a new structure if the `key` does not exist.
12601278
* When no elements are provided, and `key` exists and is a HyperLogLog, then no operation is performed.

node/tests/SharedTests.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2049,6 +2049,59 @@ export function runBaseTests<Context>(config: {
20492049
expect(await client.del(["brpop-test"])).toEqual(1);
20502050
// Test null return when key doesn't exist
20512051
expect(await client.brpop(["brpop-test"], 0.1)).toEqual(null);
2052+
// key exists, but it is not a list
2053+
await client.set("foo", "bar");
2054+
await expect(client.brpop(["foo"], 0.1)).rejects.toThrow();
2055+
2056+
// Same-slot requirement
2057+
if (client instanceof RedisClusterClient) {
2058+
try {
2059+
expect(
2060+
await client.brpop(["abc", "zxy", "lkn"], 0.1),
2061+
).toThrow();
2062+
} catch (e) {
2063+
expect((e as Error).message.toLowerCase()).toMatch(
2064+
"crossslot",
2065+
);
2066+
}
2067+
}
2068+
}, protocol);
2069+
},
2070+
config.timeout,
2071+
);
2072+
2073+
it.each([ProtocolVersion.RESP2, ProtocolVersion.RESP3])(
2074+
`test blpop test_%p`,
2075+
async (protocol) => {
2076+
await runTest(async (client: BaseClient) => {
2077+
expect(
2078+
await client.rpush("blpop-test", ["foo", "bar", "baz"]),
2079+
).toEqual(3);
2080+
// Test basic usage
2081+
expect(await client.blpop(["blpop-test"], 0.1)).toEqual([
2082+
"blpop-test",
2083+
"foo",
2084+
]);
2085+
// Delete all values from list
2086+
expect(await client.del(["blpop-test"])).toEqual(1);
2087+
// Test null return when key doesn't exist
2088+
expect(await client.blpop(["blpop-test"], 0.1)).toEqual(null);
2089+
// key exists, but it is not a list
2090+
await client.set("foo", "bar");
2091+
await expect(client.blpop(["foo"], 0.1)).rejects.toThrow();
2092+
2093+
// Same-slot requirement
2094+
if (client instanceof RedisClusterClient) {
2095+
try {
2096+
expect(
2097+
await client.blpop(["abc", "zxy", "lkn"], 0.1),
2098+
).toThrow();
2099+
} catch (e) {
2100+
expect((e as Error).message.toLowerCase()).toMatch(
2101+
"crossslot",
2102+
);
2103+
}
2104+
}
20522105
}, protocol);
20532106
},
20542107
config.timeout,

node/tests/TestUtilities.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,8 @@ export async function transactionTest(
218218
args.push(3);
219219
baseTransaction.brpop([key6], 0.1);
220220
args.push([key6, field + "3"]);
221+
baseTransaction.blpop([key6], 0.1);
222+
args.push([key6, field + "1"]);
221223
baseTransaction.pfadd(key11, ["a", "b", "c"]);
222224
args.push(1);
223225
return args;

0 commit comments

Comments
 (0)