-
Notifications
You must be signed in to change notification settings - Fork 2k
feat(client): add LMOVEM and BLMOVEM commands #3340
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
nkaradzhov
wants to merge
5
commits into
redis:master
Choose a base branch
from
nkaradzhov:move-between-lists
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
52ce442
feat(client): add LMOVEM and BLMOVEM commands
nkaradzhov cc911e2
test(client): gate LMOVEM/BLMOVEM specs to 8.10 and annotate @since
nkaradzhov 6bcaf3e
refactor(client): inline LMOVEM arg building
nkaradzhov 29581f7
refactor(client): dedupe LMOVEM/BLMOVEM options, fix docs, add tests
nkaradzhov 05594ba
fix(client): LMOVEM EXACTLY shortfall returns null, not an error
nkaradzhov File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,61 @@ | ||
| import { strict as assert } from 'node:assert'; | ||
| import testUtils, { GLOBAL, BLOCKING_MIN_VALUE } from '../test-utils'; | ||
| import BLMOVEM from './BLMOVEM'; | ||
| import { parseArgs } from './generic-transformers'; | ||
|
|
||
| describe('BLMOVEM', () => { | ||
| testUtils.isVersionGreaterThanHook([8, 10]); | ||
|
|
||
| describe('transformArguments', () => { | ||
| it('simple', () => { | ||
| assert.deepEqual( | ||
| parseArgs(BLMOVEM, 'source', 'destination', 'LEFT', 'RIGHT', 0), | ||
| ['BLMOVEM', 'source', 'destination', 'LEFT', 'RIGHT', '0'] | ||
| ); | ||
| }); | ||
|
|
||
| it('with COUNT and ORDER', () => { | ||
| assert.deepEqual( | ||
| parseArgs(BLMOVEM, 'source', 'destination', 'LEFT', 'LEFT', 0, { | ||
| COUNT: 3, | ||
| ORDER: 'OBO' | ||
| }), | ||
| ['BLMOVEM', 'source', 'destination', 'LEFT', 'LEFT', '0', 'COUNT', '3', 'OBO'] | ||
| ); | ||
| }); | ||
|
|
||
| it('with EXACTLY and ORDER', () => { | ||
| assert.deepEqual( | ||
| parseArgs(BLMOVEM, 'source', 'destination', 'LEFT', 'RIGHT', 0, { | ||
| EXACTLY: 2, | ||
| ORDER: 'BULK' | ||
| }), | ||
| ['BLMOVEM', 'source', 'destination', 'LEFT', 'RIGHT', '0', 'EXACTLY', '2', 'BULK'] | ||
| ); | ||
| }); | ||
| }); | ||
|
|
||
| testUtils.testAll('blMoveM - null on timeout', async client => { | ||
| assert.equal( | ||
| await client.blMoveM('{tag}source', '{tag}destination', 'LEFT', 'RIGHT', BLOCKING_MIN_VALUE), | ||
| null | ||
| ); | ||
| }, { | ||
| client: GLOBAL.SERVERS.OPEN, | ||
| cluster: GLOBAL.CLUSTERS.OPEN | ||
| }); | ||
|
|
||
| testUtils.testAll('blMoveM - with elements', async client => { | ||
| const [, reply] = await Promise.all([ | ||
| client.rPush('{tag}source', ['1', '2', '3']), | ||
| client.blMoveM('{tag}source', '{tag}destination', 'LEFT', 'LEFT', BLOCKING_MIN_VALUE, { | ||
| COUNT: 2, | ||
| ORDER: 'BULK' | ||
| }) | ||
| ]); | ||
| assert.deepEqual(reply, ['1', '2']); | ||
| }, { | ||
| client: GLOBAL.SERVERS.OPEN, | ||
| cluster: GLOBAL.CLUSTERS.OPEN | ||
| }); | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| import { CommandParser } from '../client/parser'; | ||
| import { RedisArgument, Command } from '../RESP/types'; | ||
| import { ListSide } from './generic-transformers'; | ||
| import LMOVEM, { LMoveMOptions, parseLMoveMOptions } from './LMOVEM'; | ||
|
|
||
| export default { | ||
| IS_READ_ONLY: false, | ||
| parseCommand( | ||
| parser: CommandParser, | ||
| source: RedisArgument, | ||
| destination: RedisArgument, | ||
| sourceSide: ListSide, | ||
| destinationSide: ListSide, | ||
| timeout: number, | ||
| options?: LMoveMOptions | ||
| ) { | ||
| parser.push('BLMOVEM'); | ||
| parser.pushKeys([source, destination]); | ||
| parser.push(sourceSide, destinationSide, timeout.toString()); | ||
| parseLMoveMOptions(parser, options); | ||
| }, | ||
| transformReply: LMOVEM.transformReply | ||
| } as const satisfies Command; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,145 @@ | ||
| import { strict as assert } from 'node:assert'; | ||
| import testUtils, { GLOBAL } from '../test-utils'; | ||
| import LMOVEM from './LMOVEM'; | ||
| import { parseArgs } from './generic-transformers'; | ||
|
|
||
| describe('LMOVEM', () => { | ||
| testUtils.isVersionGreaterThanHook([8, 10]); | ||
|
|
||
| describe('transformArguments', () => { | ||
| it('simple', () => { | ||
| assert.deepEqual( | ||
| parseArgs(LMOVEM, 'source', 'destination', 'LEFT', 'RIGHT'), | ||
| ['LMOVEM', 'source', 'destination', 'LEFT', 'RIGHT'] | ||
| ); | ||
| }); | ||
|
|
||
| it('with COUNT', () => { | ||
| assert.deepEqual( | ||
| parseArgs(LMOVEM, 'source', 'destination', 'LEFT', 'RIGHT', { | ||
| COUNT: 3 | ||
| }), | ||
| ['LMOVEM', 'source', 'destination', 'LEFT', 'RIGHT', 'COUNT', '3'] | ||
| ); | ||
| }); | ||
|
|
||
| it('with COUNT and ORDER', () => { | ||
| assert.deepEqual( | ||
| parseArgs(LMOVEM, 'source', 'destination', 'LEFT', 'LEFT', { | ||
| COUNT: 3, | ||
| ORDER: 'OBO' | ||
| }), | ||
| ['LMOVEM', 'source', 'destination', 'LEFT', 'LEFT', 'COUNT', '3', 'OBO'] | ||
| ); | ||
| }); | ||
|
|
||
| it('with EXACTLY and ORDER', () => { | ||
| assert.deepEqual( | ||
| parseArgs(LMOVEM, 'source', 'destination', 'LEFT', 'RIGHT', { | ||
| EXACTLY: 2, | ||
| ORDER: 'BULK' | ||
| }), | ||
| ['LMOVEM', 'source', 'destination', 'LEFT', 'RIGHT', 'EXACTLY', '2', 'BULK'] | ||
| ); | ||
| }); | ||
| }); | ||
|
|
||
| testUtils.testAll('lMoveM - null when source empty', async client => { | ||
| assert.equal( | ||
| await client.lMoveM('{tag}source', '{tag}destination', 'LEFT', 'RIGHT'), | ||
| null | ||
| ); | ||
| }, { | ||
| client: GLOBAL.SERVERS.OPEN, | ||
| cluster: GLOBAL.CLUSTERS.OPEN | ||
| }); | ||
|
|
||
| testUtils.testAll('lMoveM - COUNT with OBO ordering', async client => { | ||
| await client.rPush('{tag}source', ['1', '2', '3', '4', '5']); | ||
| assert.deepEqual( | ||
| await client.lMoveM('{tag}source', '{tag}destination', 'LEFT', 'LEFT', { | ||
| COUNT: 3, | ||
| ORDER: 'OBO' | ||
| }), | ||
| ['3', '2', '1'] | ||
| ); | ||
| assert.deepEqual( | ||
| await client.lRange('{tag}destination', 0, -1), | ||
| ['3', '2', '1'] | ||
| ); | ||
| assert.deepEqual( | ||
| await client.lRange('{tag}source', 0, -1), | ||
| ['4', '5'] | ||
| ); | ||
| }, { | ||
| client: GLOBAL.SERVERS.OPEN, | ||
| cluster: GLOBAL.CLUSTERS.OPEN | ||
| }); | ||
|
|
||
| testUtils.testAll('lMoveM - COUNT with BULK ordering', async client => { | ||
| await client.rPush('{tag}source', ['1', '2', '3', '4', '5']); | ||
| assert.deepEqual( | ||
| await client.lMoveM('{tag}source', '{tag}destination', 'LEFT', 'LEFT', { | ||
| COUNT: 3, | ||
| ORDER: 'BULK' | ||
| }), | ||
| ['1', '2', '3'] | ||
| ); | ||
| assert.deepEqual( | ||
| await client.lRange('{tag}destination', 0, -1), | ||
| ['1', '2', '3'] | ||
| ); | ||
| }, { | ||
| client: GLOBAL.SERVERS.OPEN, | ||
| cluster: GLOBAL.CLUSTERS.OPEN | ||
| }); | ||
|
|
||
| testUtils.testAll('lMoveM - no options moves a single element as an array', async client => { | ||
| await client.rPush('{tag}source', ['1', '2', '3']); | ||
| assert.deepEqual( | ||
| await client.lMoveM('{tag}source', '{tag}destination', 'LEFT', 'RIGHT'), | ||
| ['1'] | ||
| ); | ||
| assert.deepEqual( | ||
| await client.lRange('{tag}source', 0, -1), | ||
| ['2', '3'] | ||
| ); | ||
| }, { | ||
| client: GLOBAL.SERVERS.OPEN, | ||
| cluster: GLOBAL.CLUSTERS.OPEN | ||
| }); | ||
|
|
||
| testUtils.testAll('lMoveM - COUNT clamps to available elements', async client => { | ||
| await client.rPush('{tag}source', ['1', '2', '3']); | ||
| assert.deepEqual( | ||
| await client.lMoveM('{tag}source', '{tag}destination', 'LEFT', 'LEFT', { | ||
| COUNT: 10, | ||
| ORDER: 'BULK' | ||
| }), | ||
| ['1', '2', '3'] | ||
| ); | ||
| assert.equal(await client.exists('{tag}source'), 0); | ||
| }, { | ||
| client: GLOBAL.SERVERS.OPEN, | ||
| cluster: GLOBAL.CLUSTERS.OPEN | ||
| }); | ||
|
|
||
| testUtils.testAll('lMoveM - EXACTLY with too few elements moves nothing', async client => { | ||
| await client.rPush('{tag}source', ['1', '2']); | ||
| assert.equal( | ||
| await client.lMoveM('{tag}source', '{tag}destination', 'LEFT', 'RIGHT', { | ||
| EXACTLY: 3, | ||
| ORDER: 'BULK' | ||
| }), | ||
| null | ||
| ); | ||
| assert.deepEqual( | ||
| await client.lRange('{tag}source', 0, -1), | ||
| ['1', '2'] | ||
| ); | ||
| assert.equal(await client.exists('{tag}destination'), 0); | ||
| }, { | ||
| client: GLOBAL.SERVERS.OPEN, | ||
| cluster: GLOBAL.CLUSTERS.OPEN | ||
| }); | ||
| }); | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,59 @@ | ||
| import { CommandParser } from '../client/parser'; | ||
| import { RedisArgument, ArrayReply, BlobStringReply, NullReply, Command } from '../RESP/types'; | ||
| import { ListSide } from './generic-transformers'; | ||
|
|
||
| /** | ||
| * Ordering of the moved elements at the destination. | ||
| * | ||
| * - `OBO` (one-by-one): push each element as it is popped, reversing the | ||
| * moved block's relative order (stack semantics). | ||
| * - `BULK`: preserve the moved block's original relative order (queue semantics). | ||
| */ | ||
| export type LMoveMOrder = 'OBO' | 'BULK'; | ||
|
|
||
| export type LMoveMOptions = { | ||
| /** Move up to `COUNT` elements (fewer if the source has fewer). */ | ||
| COUNT: number; | ||
| ORDER?: LMoveMOrder; | ||
| } | { | ||
| /** Move exactly `EXACTLY` elements; if the source has fewer, move nothing and reply with null. */ | ||
| EXACTLY: number; | ||
| ORDER?: LMoveMOrder; | ||
| }; | ||
|
|
||
| /** | ||
| * Pushes the trailing `[<COUNT|EXACTLY> count [OBO|BULK]]` arguments shared by | ||
| * LMOVEM and BLMOVEM. Kept separate from the command bodies because BLMOVEM's | ||
| * `timeout` sits between the sides and these options, so only the tail is shared. | ||
| */ | ||
| export function parseLMoveMOptions(parser: CommandParser, options?: LMoveMOptions) { | ||
| if (options) { | ||
| if ('EXACTLY' in options) { | ||
| parser.push('EXACTLY', options.EXACTLY.toString()); | ||
| } else { | ||
| parser.push('COUNT', options.COUNT.toString()); | ||
| } | ||
|
|
||
| if (options.ORDER !== undefined) { | ||
| parser.push(options.ORDER); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| export default { | ||
| IS_READ_ONLY: false, | ||
| parseCommand( | ||
| parser: CommandParser, | ||
| source: RedisArgument, | ||
| destination: RedisArgument, | ||
| sourceSide: ListSide, | ||
| destinationSide: ListSide, | ||
| options?: LMoveMOptions | ||
| ) { | ||
| parser.push('LMOVEM'); | ||
| parser.pushKeys([source, destination]); | ||
| parser.push(sourceSide, destinationSide); | ||
| parseLMoveMOptions(parser, options); | ||
| }, | ||
| transformReply: undefined as unknown as () => ArrayReply<BlobStringReply> | NullReply | ||
| } as const satisfies Command; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.