-
Notifications
You must be signed in to change notification settings - Fork 327
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
feat(reqresp): send status messages along with reqresp responses #11727
Changes from 3 commits
4f94128
9ba0959
74592d2
a512dc5
3d5898f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -22,6 +22,7 @@ import { type PeerScoring } from '../peer-manager/peer_scoring.js'; | |
import { ReqRespSubProtocol, RequestableBuffer } from './interface.js'; | ||
import { reqRespBlockHandler } from './protocols/block.js'; | ||
import { GoodByeReason, reqGoodbyeHandler } from './protocols/goodbye.js'; | ||
import { ReqRespStatus, prettyPrintReqRespStatus } from './status.js'; | ||
|
||
const PING_REQUEST = RequestableBuffer.fromBuffer(Buffer.from('ping')); | ||
|
||
|
@@ -126,10 +127,21 @@ describe('ReqResp', () => { | |
await sleep(500); | ||
|
||
// Default rate is set at 1 every 200 ms; so this should fire a few times | ||
const responses = []; | ||
for (let i = 0; i < 10; i++) { | ||
await nodes[0].req.sendRequestToPeer(nodes[1].p2p.peerId, ReqRespSubProtocol.PING, Buffer.from('ping')); | ||
// Response object contains the status (error flags) and data | ||
const response = await nodes[0].req.sendRequestToPeer( | ||
nodes[1].p2p.peerId, | ||
ReqRespSubProtocol.PING, | ||
Buffer.from('ping'), | ||
); | ||
responses.push(response); | ||
} | ||
|
||
// Check that one of the responses gets a rate limit response | ||
const rateLimitResponse = responses.find(response => response?.status === ReqRespStatus.RATE_LIMIT_EXCEEDED); | ||
expect(rateLimitResponse).toBeDefined(); | ||
|
||
// Make sure the error message is logged | ||
const errorMessage = `Rate limit exceeded for ${ReqRespSubProtocol.PING} from ${nodes[0].p2p.peerId.toString()}`; | ||
expect(loggerSpy).toHaveBeenCalledWith(expect.stringContaining(errorMessage)); | ||
|
@@ -343,7 +355,8 @@ describe('ReqResp', () => { | |
); | ||
|
||
// Expect the response to be a buffer of length 1 | ||
expect(response).toEqual(Buffer.from([0x0])); | ||
expect(response?.status).toEqual(ReqRespStatus.SUCCESS); | ||
expect(response?.data).toEqual(Buffer.from([0x0])); | ||
}); | ||
}); | ||
|
||
|
@@ -413,6 +426,8 @@ describe('ReqResp', () => { | |
const batchSize = 12; | ||
nodes = await createNodes(peerScoring, 3); | ||
|
||
const requesterLoggerSpy = jest.spyOn((nodes[0].req as any).logger, 'debug'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not a fan of hooking onto the logger to test for events, but I don't have a better recommendation that doesn't involve extending an |
||
|
||
await startNodes(nodes); | ||
await sleep(500); | ||
await connectToPeers(nodes); | ||
|
@@ -426,6 +441,11 @@ describe('ReqResp', () => { | |
|
||
const res = await nodes[0].req.sendBatchRequest(ReqRespSubProtocol.PING, requests); | ||
expect(res).toEqual(expectResponses); | ||
|
||
// Check that we did detect hitting a rate limit | ||
expect(requesterLoggerSpy).toHaveBeenCalledWith( | ||
expect.stringContaining(`${prettyPrintReqRespStatus(ReqRespStatus.RATE_LIMIT_EXCEEDED)}`), | ||
); | ||
}); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
/** | ||
* The error codes for the ReqResp protocol | ||
*/ | ||
export enum ReqRespStatus { | ||
SUCCESS = 0, | ||
RATE_LIMIT_EXCEEDED = 1, | ||
BADLY_FORMED_REQUEST = 2, | ||
UNKNOWN = 127, | ||
} | ||
|
||
export class ReqRespStatusError extends Error { | ||
/** | ||
* The status code | ||
*/ | ||
status: ReqRespStatus; | ||
|
||
constructor(status: ReqRespStatus) { | ||
super(`ReqResp Error: ${prettyPrintReqRespStatus(status)}`); | ||
this.status = status; | ||
} | ||
} | ||
|
||
/** | ||
* Parse the status chunk | ||
* @param chunk | ||
* @returns | ||
* | ||
* @throws ReqRespStatusError if the chunk is not valid | ||
*/ | ||
export function parseStatusChunk(chunk: Uint8Array): ReqRespStatus { | ||
if (chunk.length !== 1) { | ||
throw new ReqRespStatusError(ReqRespStatus.UNKNOWN); | ||
} | ||
|
||
const status = chunk[0]; | ||
// Check if status is a valid ReqRespStatus value | ||
if (!(status in ReqRespStatus)) { | ||
throw new ReqRespStatusError(ReqRespStatus.UNKNOWN); | ||
} | ||
return status as ReqRespStatus; | ||
} | ||
|
||
/** | ||
* Pretty print the ReqResp status | ||
* @param status | ||
* @returns | ||
*/ | ||
export function prettyPrintReqRespStatus(status: ReqRespStatus) { | ||
switch (status) { | ||
case ReqRespStatus.SUCCESS: | ||
return 'SUCCESS'; | ||
case ReqRespStatus.RATE_LIMIT_EXCEEDED: | ||
return 'RATE_LIMIT_EXCEEDED'; | ||
case ReqRespStatus.BADLY_FORMED_REQUEST: | ||
return 'BADLY_FORMED_REQUEST'; | ||
case ReqRespStatus.UNKNOWN: | ||
return 'UNKNOWN'; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
bumping rate limits here for the meantime!