-
-
Notifications
You must be signed in to change notification settings - Fork 289
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: add syncing_status query parameter to eth/v1/node/health #5790
Changes from 6 commits
5f25f03
0cbccc2
c4e4ace
ab7d42e
50d91e9
a127714
e1cbec4
0f2444e
900743a
3abda75
f07b469
35ab535
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 |
---|---|---|
@@ -1,32 +1,9 @@ | ||
import {ChainForkConfig} from "@lodestar/config"; | ||
import {Api, ReqTypes, routesData, getReturnTypes, getReqSerializers} from "../routes/node.js"; | ||
import {ServerRoutes, getGenericJsonServer} from "../../utils/server/index.js"; | ||
import {HttpStatusCode} from "../../utils/client/httpStatusCode.js"; | ||
import {ServerApi} from "../../interfaces.js"; | ||
|
||
export function getRoutes(config: ChainForkConfig, api: ServerApi<Api>): ServerRoutes<ServerApi<Api>, ReqTypes> { | ||
// All routes return JSON, use a server auto-generator | ||
const serverRoutes = getGenericJsonServer<ServerApi<Api>, ReqTypes>( | ||
{routesData, getReturnTypes, getReqSerializers}, | ||
config, | ||
api | ||
); | ||
|
||
return { | ||
...serverRoutes, | ||
|
||
getHealth: { | ||
...serverRoutes.getHealth, | ||
handler: async (req, res) => { | ||
try { | ||
await api.getHealth(); | ||
res.raw.writeHead(HttpStatusCode.OK); | ||
res.raw.end(); | ||
} catch (e) { | ||
res.raw.writeHead(HttpStatusCode.INTERNAL_SERVER_ERROR); | ||
res.raw.end(); | ||
} | ||
}, | ||
}, | ||
}; | ||
return getGenericJsonServer<ServerApi<Api>, ReqTypes>({routesData, getReturnTypes, getReqSerializers}, config, api); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -26,7 +26,7 @@ export function getFetchOptsSerializer<Fn extends (...args: any) => any, ReqType | |
return { | ||
url: urlFormater(req.params ?? {}), | ||
method: routeDef.method, | ||
query: req.query, | ||
query: Object.keys(req.query ?? {}).length ? req.query : undefined, | ||
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. Fixes appending '?' to URL if query params are omitted, e.g. I would assume most/(all) HTTP servers will ignore this anyways, but it might be misleading if req.url is logged on the server. |
||
body: req.body as unknown, | ||
headers: req.headers, | ||
routeId, | ||
|
@@ -80,14 +80,17 @@ export function generateGenericJsonClient< | |
} | ||
} catch (err) { | ||
if (err instanceof HttpError) { | ||
return {ok: false, error: {code: err.status, message: err.message, operationId: routeId}} as ReturnType< | ||
Api[keyof Api] | ||
>; | ||
return { | ||
ok: false, | ||
status: err.status, | ||
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. Assuming this was just missed previously as the code here is not really type-safe, didn't find a quick solution to fix that but we should always set status in client response. |
||
error: {code: err.status, message: err.message, operationId: routeId}, | ||
} as ReturnType<Api[keyof Api]>; | ||
} | ||
|
||
if (err instanceof TimeoutError) { | ||
return { | ||
ok: false, | ||
status: HttpStatusCode.INTERNAL_SERVER_ERROR, | ||
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. This status code is kinda wrong but 408 would also be wrong as it would indicate a server timeout whereas this error happens due to client timeout. If server doesn't respond within client timeout we can assume a server issue, setting 500 here is the most accurate. Alternative would be to just throw the error here but this makes the error logs less informative if a timeout happens. |
||
error: {code: HttpStatusCode.INTERNAL_SERVER_ERROR, message: err.message, operationId: routeId}, | ||
} as ReturnType<Api[keyof Api]>; | ||
} | ||
|
Original file line number | Diff line number | Diff line change | ||
---|---|---|---|---|
|
@@ -66,10 +66,18 @@ export function getNodeApi( | |||
return {data: sync.getSyncStatus()}; | ||||
}, | ||||
|
||||
async getHealth(_req, res) { | ||||
async getHealth(options, _req, res) { | ||||
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. Uhmm I didn't noticed that request and response objects where being leaked to the API implementation. I see it was introduced by #4994 but why not keep the interaction in the Fastify's response object in 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. Looked at this as well but the only "clean way" to achieve this without too many type hacks is approach used in #3349 but in this case the If you think passing response object to API implementation is not desired we should update types accordingly and use the other approach to implementing this. 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. @nazarhussain any opinions here? 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. Passing We can define a data structure for the implementation of API endpoints, that way we can handle the status code at the server implementation side. So all implementation handlers should return;
And if 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. Health API is really the only one right now that needs to modify the status code. The interface of response is limited to setting the status code lodestar/packages/api/src/interfaces.ts Line 27 in 7b5fc63
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. In that case we can make the response status code structure as optional. e.g.
This will require no refactor on API implementation side and minimum change on the server side. 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. True, that could be a good pattern to give some API implementations more control over the response. Maybe we want to allow to set Proposed type would look like this then () => T | {body: T | null, status: number} 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. So will you make the change in this PR or separate? 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. Better to do it in this PR, allows to remove the workaround in tests, should reduce overall diff 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. Request and response objects are no longer passed to API implementation. After looking into updating return type to something like Better to address in a separate PR. |
||||
// Custom value passed via `syncing_status` query parameter | ||||
const syncingStatus = options?.syncingStatus; | ||||
|
||||
// Syncing status must be a valid HTTP status code within range 100-599 | ||||
if (syncingStatus != null && (syncingStatus < 100 || syncingStatus > 599)) { | ||||
throw new ApiError(400, `Invalid syncing status code: ${syncingStatus}`); | ||||
} | ||||
|
||||
if (sync.getSyncStatus().isSyncing) { | ||||
// 206: Node is syncing but can serve incomplete data | ||||
res?.code(routes.node.NodeHealth.SYNCING); | ||||
res?.code(syncingStatus ?? routes.node.NodeHealth.SYNCING); | ||||
} else { | ||||
// 200: Node is ready | ||||
res?.code(routes.node.NodeHealth.READY); | ||||
|
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.
I would recommend moving the range check in the schema.
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.
Do you have an example API that does this? Based on current types this is not possible as far as I can see, we only support pre-defined schemas
lodestar/packages/api/src/utils/schema.ts
Line 25 in a127714
We enforce basic validation through schema but the error response and logging is not ideal in my opinion
JSON API response
Schema validation error
API error
Error log
Schema validation error
API error
In both cases as it is right now, it is better to use API error. Schema validation could be made more flexible but there are not that many values in the spec that enforce things like minimum / maximum.