Skip to content

Commit 26d6e1c

Browse files
committed
fix(api): use ARLEN for get-next-index (RI-8219)
ARNEXT on Redis 8.8.0 GA returns 0 for every populated array — its documented summary 'returns the next index ARINSERT would use' is a different operation from what this endpoint exposes (the next safe write index after the highest populated slot). ARLEN already returns max_index + 1 for both dense and sparse arrays, which is exactly the value the endpoint, its response DTO description, and its integration tests promise. Swap the primitive, tighten the response shape (`index` is now non-nullable string), drop the unreachable '(exhausted)' unit case, and remove the now-unused ArNext enum member plus its ioredis builtin registration. Caught by the new oss-st-8 RTE on PR #6078, where every get-next-index Main case was returning index '0' regardless of array shape. Verified against redis:8.8-alpine: ARLEN k → 3 for a dense [0,1,2], 6 for sparse [0,_,_,_,_,5] — matches the test expectations.
1 parent 0409c61 commit 26d6e1c

7 files changed

Lines changed: 10 additions & 35 deletions

File tree

redisinsight/api/src/modules/browser/array/array.controller.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ export class ArrayController extends BrowserBaseController {
124124
@HttpCode(200)
125125
@ApiOperation({
126126
description:
127-
'Get the next index that ARINSERT would use (read-only) — ARNEXT.',
127+
'Get the next safe write index (equivalent to the array length) — ARLEN.',
128128
})
129129
@ApiRedisParams()
130130
@ApiOkResponse({ type: GetArrayNextIndexResponse })

redisinsight/api/src/modules/browser/array/array.service.spec.ts

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -434,7 +434,7 @@ describe('ArrayService', () => {
434434
},
435435
{
436436
name: 'getNextIndex',
437-
command: BrowserToolArrayCommands.ArNext,
437+
command: BrowserToolArrayCommands.ArLen,
438438
reply: 7,
439439
expected: mockGetArrayNextIndexResponse,
440440
stringValue: mockArrayNextIndex,
@@ -481,21 +481,6 @@ describe('ArrayService', () => {
481481
});
482482
});
483483

484-
describe('getNextIndex (exhausted)', () => {
485-
it('should surface null index when ARNEXT returns nil', async () => {
486-
when(mockStandaloneRedisClient.sendCommand)
487-
.calledWith([BrowserToolArrayCommands.ArNext, mockKeyDto.keyName])
488-
.mockResolvedValue(null);
489-
490-
const result = await service.getNextIndex(
491-
mockBrowserClientMetadata,
492-
mockKeyDto,
493-
);
494-
495-
expect(result).toEqual({ keyName: mockKeyDto.keyName, index: null });
496-
});
497-
});
498-
499484
describe('getElement', () => {
500485
beforeEach(() => {
501486
when(mockStandaloneRedisClient.sendCommand)

redisinsight/api/src/modules/browser/array/array.service.ts

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,7 @@ import {
2020
} from 'src/modules/browser/utils';
2121
import { KeyDto } from 'src/modules/browser/keys/dto';
2222
import { ARRAY_RANGE_MAX_ELEMENTS } from 'src/modules/browser/array/constants';
23-
import {
24-
toIndexString,
25-
toRequiredIndexString,
26-
} from 'src/modules/browser/array/utils';
23+
import { toRequiredIndexString } from 'src/modules/browser/array/utils';
2724
import {
2825
ArrayCreationMode,
2926
ArrayElement,
@@ -314,17 +311,14 @@ export class ArrayService {
314311
await checkIfKeyNotExists(keyName, client);
315312

316313
const reply = await client.sendCommand([
317-
BrowserToolArrayCommands.ArNext,
314+
BrowserToolArrayCommands.ArLen,
318315
keyName,
319316
]);
320317

321-
// ARNEXT returns nil when the insertion cursor is exhausted; toIndexString
322-
// passes that through as null so clients can distinguish absence from a
323-
// real index.
324318
this.logger.debug('Succeed to get array next index.', clientMetadata);
325319
return plainToInstance(GetArrayNextIndexResponse, {
326320
keyName,
327-
index: toIndexString(reply),
321+
index: toRequiredIndexString(reply),
328322
});
329323
} catch (error) {
330324
this.logger.error(

redisinsight/api/src/modules/browser/array/dto/get.array-next-index.response.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,10 @@ import { KeyResponse } from 'src/modules/browser/keys/dto';
44
export class GetArrayNextIndexResponse extends KeyResponse {
55
@ApiProperty({
66
description:
7-
'Next index that ARINSERT would use. Unsigned 64-bit integer as string. ' +
8-
'Null when the array is exhausted and no further insertion is possible.',
7+
'Next safe write index (equivalent to the array length). ' +
8+
'Unsigned 64-bit integer as string.',
99
type: String,
1010
example: '7',
11-
nullable: true,
1211
})
13-
index: string | null;
12+
index: string;
1413
}

redisinsight/api/src/modules/browser/array/utils.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,8 @@
33
// string so the unsigned 64-bit contract is preserved on the wire.
44
//
55
// Returns `null` for nil replies so callers can distinguish absence from a
6-
// real value (e.g. ARNEXT returns nil when the insertion cursor is exhausted).
7-
// Without this guard `String(null)` / `String(undefined)` would emit the
8-
// literal strings "null" / "undefined" and corrupt downstream JSON.
6+
// real value. Without this guard `String(null)` / `String(undefined)` would
7+
// emit the literal strings "null" / "undefined" and corrupt downstream JSON.
98
export const toIndexString = (value: unknown): string | null => {
109
if (value === null || value === undefined) return null;
1110
if (typeof value === 'string') return value;

redisinsight/api/src/modules/browser/constants/browser-tool-commands.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,6 @@ export enum BrowserToolArrayCommands {
123123
ArCount = 'arcount',
124124
ArGetRange = 'argetrange',
125125
ArScan = 'arscan',
126-
ArNext = 'arnext',
127126
}
128127

129128
export type BrowserToolCommands =

redisinsight/api/src/modules/redis/client/ioredis/ioredis.client.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,6 @@ export abstract class IoredisClient extends RedisClient {
5757
client.addBuiltinCommand(BrowserToolArrayCommands.ArCount);
5858
client.addBuiltinCommand(BrowserToolArrayCommands.ArGetRange);
5959
client.addBuiltinCommand(BrowserToolArrayCommands.ArScan);
60-
client.addBuiltinCommand(BrowserToolArrayCommands.ArNext);
6160
}
6261

6362
static prepareCommandOptions(options: IRedisClientCommandOptions): any {

0 commit comments

Comments
 (0)