Skip to content

Commit

Permalink
Node: Brpop Command (valkey-io#1113)
Browse files Browse the repository at this point in the history
Add BRPOP command to node
  • Loading branch information
avifenesh authored and shohamazon committed Apr 9, 2024
1 parent df045bf commit c62c77e
Show file tree
Hide file tree
Showing 7 changed files with 81 additions and 1 deletion.
3 changes: 2 additions & 1 deletion glide-core/src/protobuf/redis_request.proto
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,8 @@ enum RequestType {
Zrank = 90;
Rename = 91;
DBSize = 92;
Spop = 93;
Brpop = 93;
Spop = 94;
}

message Command {
Expand Down
1 change: 1 addition & 0 deletions glide-core/src/socket_listener.rs
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,7 @@ fn get_command(request: &Command) -> Option<Cmd> {
RequestType::Zrank => Some(cmd("ZRANK")),
RequestType::Rename => Some(cmd("RENAME")),
RequestType::DBSize => Some(cmd("DBSIZE")),
RequestType::Brpop => Some(cmd("BRPOP")),
RequestType::Spop => Some(cmd("SPOP")),
}
}
Expand Down
25 changes: 25 additions & 0 deletions node/src/BaseClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
StreamReadOptions,
StreamTrimOptions,
ZaddOptions,
createBrpop,
createDecr,
createDecrBy,
createDel,
Expand Down Expand Up @@ -1398,6 +1399,30 @@ export class BaseClient {
return this.createWritePromise(createRename(key, newKey));
}

/** Blocking list pop primitive.
* Pop an element from the tail of the first list that is non-empty,
* with the given keys being checked in the order that they are given.
* Blocks the connection when there are no elements to pop from any of the given lists.
* See https://redis.io/commands/brpop/ for more details.
* Note: BRPOP is a blocking command,
* see [Blocking Commands](https://github.com/aws/glide-for-redis/wiki/General-Concepts#blocking-commands) for more details and best practices.
*
* @param keys - The `keys` of the lists to pop from.
* @param timeout - The `timeout` in seconds.
* @returns - An `array` containing the `key` from which the element was popped and the value of the popped element,
* formatted as [key, value]. If no element could be popped and the timeout expired, returns Null.
*
* @example
* await client.brpop(["list1", "list2"], 5);
* ["list1", "element"]
*/
public brpop(
keys: string[],
timeout: number,
): Promise<[string, string] | null> {
return this.createWritePromise(createBrpop(keys, timeout));
}

/**
* @internal
*/
Expand Down
11 changes: 11 additions & 0 deletions node/src/Commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1071,6 +1071,17 @@ export function createTime(): redis_request.Command {
return createCommand(RequestType.Time, []);
}

/**
* @internal
*/
export function createBrpop(
keys: string[],
timeout: number,
): redis_request.Command {
const args = [...keys, timeout.toString()];
return createCommand(RequestType.Brpop, args);
}

export type StreamReadOptions = {
/**
* If set, the read request will block for the set amount of milliseconds or until the server has the required number of entries.
Expand Down
18 changes: 18 additions & 0 deletions node/src/Transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
StreamReadOptions,
StreamTrimOptions,
ZaddOptions,
createBrpop,
createClientGetName,
createClientId,
createConfigGet,
Expand Down Expand Up @@ -1183,6 +1184,23 @@ export class BaseTransaction<T extends BaseTransaction<T>> {
public rename(key: string, newKey: string): T {
return this.addAndReturn(createRename(key, newKey));
}

/** Blocking list pop primitive.
* Pop an element from the tail of the first list that is non-empty,
* with the given keys being checked in the order that they are given.
* Blocks the connection when there are no elements to pop from any of the given lists.
* See https://redis.io/commands/brpop/ for more details.
* Note: BRPOP is a blocking command,
* see [Blocking Commands](https://github.com/aws/glide-for-redis/wiki/General-Concepts#blocking-commands) for more details and best practices.
*
* @param keys - The `keys` of the lists to pop from.
* @param timeout - The `timeout` in seconds.
* Command Response - An `array` containing the `key` from which the element was popped and the value of the popped element,
* formatted as [key, value]. If no element could be popped and the timeout expired, returns Null.
*/
public brpop(keys: string[], timeout: number): T {
return this.addAndReturn(createBrpop(keys, timeout));
}
}

/**
Expand Down
20 changes: 20 additions & 0 deletions node/tests/SharedTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1820,6 +1820,26 @@ export function runBaseTests<Context>(config: {
await expect(client.zrank(key2, "member")).rejects.toThrow();
}, protocol);
},
);

it.each([ProtocolVersion.RESP2, ProtocolVersion.RESP3])(
`test brpop test_%p`,
async (protocol) => {
await runTest(async (client: BaseClient) => {
expect(
await client.rpush("brpop-test", ["foo", "bar", "baz"]),
).toEqual(3);
// Test basic usage
expect(await client.brpop(["brpop-test"], 0.1)).toEqual([
"brpop-test",
"baz",
]);
// Delete all values from list
expect(await client.del(["brpop-test"])).toEqual(1);
// Test null return when key doesn't exist
expect(await client.brpop(["brpop-test"], 0.1)).toEqual(null);
}, protocol);
},
config.timeout,
);

Expand Down
4 changes: 4 additions & 0 deletions node/tests/TestUtilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,10 @@ export async function transactionTest(
args.push("OK");
baseTransaction.exists([key10]);
args.push(1);
baseTransaction.rpush(key6, [field + "1", field + "2", field + "3"]);
args.push(3);
baseTransaction.brpop([key6], 0.1);
args.push([key6, field + "3"]);
return args;
}

Expand Down

0 comments on commit c62c77e

Please sign in to comment.