Skip to content
This repository was archived by the owner on Nov 9, 2023. It is now read-only.

Commit 4f97224

Browse files
authored
Request retry count (#30)
1 parent c7d4a1a commit 4f97224

File tree

3 files changed

+84
-11
lines changed

3 files changed

+84
-11
lines changed

jest.config.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,10 @@ module.exports = {
2121
// An object that configures minimum threshold enforcement for coverage results
2222
coverageThreshold: {
2323
global: {
24-
branches: 78.94,
24+
branches: 86.95,
2525
functions: 100,
26-
lines: 96.27,
27-
statements: 96.27,
26+
lines: 97.51,
27+
statements: 97.51,
2828
},
2929
},
3030

src/createStreamMiddleware.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ interface IdMapValue {
1414
res: PendingJsonRpcResponse<unknown>;
1515
next: JsonRpcEngineNextCallback;
1616
end: JsonRpcEngineEndCallback;
17+
retryCount?: number;
1718
}
1819

1920
interface IdMap {
@@ -129,8 +130,20 @@ export default function createStreamMiddleware(options: Options = {}) {
129130
* Retry pending requests.
130131
*/
131132
function retryStuckRequests() {
132-
Object.values(idMap).forEach(({ req }) => {
133-
// TODO: limiting retries could be implemented here
133+
Object.values(idMap).forEach(({ req, retryCount = 0 }) => {
134+
// Avoid retrying requests without an id - they cannot have matching responses so retry logic doesn't apply
135+
// Check for retry count below ensure that a request is not retried more than 3 times
136+
if (!req.id) {
137+
return;
138+
}
139+
140+
if (retryCount >= 3) {
141+
throw new Error(
142+
`StreamMiddleware - Retry limit exceeded for request id "${req.id}"`,
143+
);
144+
}
145+
146+
idMap[req.id].retryCount = retryCount + 1;
134147
sendToStream(req);
135148
});
136149
}

src/index.test.ts

Lines changed: 66 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -109,17 +109,20 @@ describe('middleware and engine to stream', () => {
109109

110110
const RECONNECTED = 'CONNECTED';
111111
describe('retry logic in middleware connected to a port', () => {
112-
it('retries requests on reconnect message', async () => {
112+
let engineA: JsonRpcEngine | undefined;
113+
let messages: any[] = [];
114+
let messageConsumer: any;
115+
beforeEach(() => {
113116
// create guest
114-
const engineA = new JsonRpcEngine();
117+
engineA = new JsonRpcEngine();
115118
const jsonRpcConnection = createStreamMiddleware({
116119
retryOnMessage: RECONNECTED,
117120
});
118121
engineA.push(jsonRpcConnection.middleware);
119122

120123
// create port
121-
let messageConsumer = noop;
122-
const messages: any[] = [];
124+
messageConsumer = noop;
125+
messages = [];
123126
const extensionPort = {
124127
onMessage: {
125128
addListener: (cb: any) => {
@@ -143,15 +146,17 @@ describe('retry logic in middleware connected to a port', () => {
143146
clientSideStream
144147
.pipe(connectionStream as unknown as Duplex)
145148
.pipe(clientSideStream);
149+
});
146150

151+
it('retries requests on reconnect message', async () => {
147152
// request and expected result
148153
const req1 = { id: 1, jsonrpc, method: 'test' };
149154
const req2 = { id: 2, jsonrpc, method: 'test' };
150155
const res = { id: 1, jsonrpc, result: 'test' };
151156

152157
// Initially sent once
153-
const responsePromise1 = engineA.handle(req1);
154-
engineA.handle(req2);
158+
const responsePromise1 = engineA?.handle(req1);
159+
engineA?.handle(req2);
155160
await artificialDelay();
156161

157162
expect(messages).toHaveLength(2);
@@ -179,4 +184,59 @@ describe('retry logic in middleware connected to a port', () => {
179184

180185
expect(messages).toHaveLength(5);
181186
});
187+
188+
it('throw error when requests are retried more than 3 times', async () => {
189+
// request and expected result
190+
const req = { id: 1, jsonrpc, method: 'test' };
191+
192+
// Initially sent once, message count at 1
193+
engineA?.handle(req);
194+
await artificialDelay();
195+
expect(messages).toHaveLength(1);
196+
197+
// Reconnected, gets sent again message count increased to 2
198+
messageConsumer({
199+
method: RECONNECTED,
200+
});
201+
await artificialDelay();
202+
expect(messages).toHaveLength(2);
203+
204+
// Reconnected, gets sent again message count increased to 3
205+
messageConsumer({
206+
method: RECONNECTED,
207+
});
208+
await artificialDelay();
209+
expect(messages).toHaveLength(3);
210+
211+
// Reconnected, gets sent again message count increased to 4
212+
messageConsumer({
213+
method: RECONNECTED,
214+
});
215+
await artificialDelay();
216+
expect(messages).toHaveLength(4);
217+
218+
// Reconnected, error is thrrown when trying to resend request more that 3 times
219+
expect(() => {
220+
messageConsumer({
221+
method: RECONNECTED,
222+
});
223+
}).toThrow('StreamMiddleware - Retry limit exceeded for request id');
224+
});
225+
226+
it('does not retry if the request has no id', async () => {
227+
// request and expected result
228+
const req = { id: undefined, jsonrpc, method: 'test' };
229+
230+
// Initially sent once, message count at 1
231+
engineA?.handle(req);
232+
await artificialDelay();
233+
expect(messages).toHaveLength(1);
234+
235+
// Reconnected, but request is not re-submitted
236+
messageConsumer({
237+
method: RECONNECTED,
238+
});
239+
await artificialDelay();
240+
expect(messages).toHaveLength(1);
241+
});
182242
});

0 commit comments

Comments
 (0)