Skip to content

Commit 9519004

Browse files
committed
fix redis#2665 - handle errors in multi/pipeline replies
1 parent d6d2064 commit 9519004

File tree

3 files changed

+44
-6
lines changed

3 files changed

+44
-6
lines changed

packages/client/lib/client/index.spec.ts

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import testUtils, { GLOBAL, waitTillBeenCalled } from '../test-utils';
33
import RedisClient, { RedisClientType } from '.';
44
import { RedisClientMultiCommandType } from './multi-command';
55
import { RedisCommandRawReply, RedisModules, RedisFunctions, RedisScripts } from '../commands';
6-
import { AbortError, ClientClosedError, ClientOfflineError, ConnectionTimeoutError, DisconnectsClientError, SocketClosedUnexpectedlyError, WatchError } from '../errors';
6+
import { AbortError, ClientClosedError, ClientOfflineError, ConnectionTimeoutError, DisconnectsClientError, ErrorReply, MultiErrorReply, SocketClosedUnexpectedlyError, WatchError } from '../errors';
77
import { defineScript } from '../lua-script';
88
import { spy } from 'sinon';
99
import { once } from 'events';
@@ -602,6 +602,26 @@ describe('Client', () => {
602602
...GLOBAL.SERVERS.OPEN,
603603
minimumDockerVersion: [6, 2] // CLIENT INFO
604604
});
605+
606+
testUtils.testWithClient('should handle error replies (#2665)', async client => {
607+
await assert.rejects(
608+
client.multi()
609+
.set('key', 'value')
610+
.hGetAll('key')
611+
.exec(),
612+
err => {
613+
console.log(err);
614+
assert.ok(err instanceof MultiErrorReply);
615+
assert.equal(err.replies.length, 2);
616+
assert.deepEqual(err.errorIndexes, [1]);
617+
assert.ok(err.replies[1] instanceof ErrorReply);
618+
return true;
619+
}
620+
);
621+
}, {
622+
...GLOBAL.SERVERS.OPEN,
623+
minimumDockerVersion: [6, 2] // CLIENT INFO
624+
});
605625
});
606626

607627
testUtils.testWithClient('scripts', async client => {

packages/client/lib/errors.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,3 +63,14 @@ export class ErrorReply extends Error {
6363
this.stack = undefined;
6464
}
6565
}
66+
67+
export class MultiErrorReply extends ErrorReply {
68+
replies: Array<ErrorReply>;
69+
errorIndexes: Array<number>;
70+
71+
constructor(replies: Array<ErrorReply>, errorIndexes: Array<number>) {
72+
super(`${errorIndexes.length} commands failed, see .replies and .errorIndexes for more information`);
73+
this.replies = replies;
74+
this.errorIndexes = errorIndexes;
75+
}
76+
}

packages/client/lib/multi-command.ts

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { fCallArguments } from './commander';
22
import { RedisCommand, RedisCommandArguments, RedisCommandRawReply, RedisFunction, RedisScript } from './commands';
3-
import { WatchError } from './errors';
3+
import { ErrorReply, MultiErrorReply, WatchError } from './errors';
44

55
export interface RedisMultiQueuedCommand {
66
args: RedisCommandArguments;
@@ -79,9 +79,16 @@ export default class RedisMultiCommand {
7979
}
8080

8181
transformReplies(rawReplies: Array<RedisCommandRawReply>): Array<RedisCommandRawReply> {
82-
return rawReplies.map((reply, i) => {
83-
const { transformReply, args } = this.queue[i];
84-
return transformReply ? transformReply(reply, args.preserve) : reply;
85-
});
82+
const errorIndexes: Array<number> = [],
83+
replies = rawReplies.map((reply, i) => {
84+
if (reply instanceof ErrorReply) {
85+
errorIndexes.push(i);
86+
}
87+
const { transformReply, args } = this.queue[i];
88+
return transformReply ? transformReply(reply, args.preserve) : reply;
89+
});
90+
91+
if (errorIndexes.length) throw new MultiErrorReply(replies, errorIndexes);
92+
return replies;
8693
}
8794
}

0 commit comments

Comments
 (0)