-
Notifications
You must be signed in to change notification settings - Fork 235
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(js-connectors): Proper JS error propagation #4186
Conversation
The way it is implemented: 1. On TS side, we catch all errors from connector methods. In case we encounterd an error, we store it on per-connector error registry and return it's numeric id to the engine. 2. Engine propogates that numeric id all the way throuhg quaint up to query-engine-node-api, where it becomes new kind of error, `ExternalError` with a code of P2036 and error id in the meta field. 3. Client is then expected to pick the error from the registry and re-throw it. This PR implements this for smoke-tests, similar implementation needs to be done in the client. Implementation is done this way, rather than propogating `napi::Error` instance, to avoid dependencies on napi types in all intermediate crates other than `js-connectors` and `query-engine-node-api`. To better accomodate this pattern, `Result` type was introduced to the connectors. It allows to explicitly indicate if particular call succeeded or failed via return value. On Rust side, those values get parsed and converted into `std::result::Result` values. On Rust side, `AsyncJsFunction` wrapper over `ThreadsafeFunction` is introduced. It handles promises and JS result type conversions automatically and simplifies `Proxy` code. This also lays the foundation for supporting "Known" errors - JsError type could be extended in a future with new error types, that could be converted into standard prisma errors with normal error codes. Fix #prisma/team-orm#260
I am also happy to discuss naming of some of the things this PR introduces. In particuallary, I am not 100% with those names:
|
if (error.error_code === 'P2036') { | ||
const jsError = this.connector.errorRegistry.consumeError(error.meta.id) | ||
if (!jsError) { | ||
throw new Error(`Something went wrong. Engine reported external error with id ${error.meta.id}, but it was not registered.`) | ||
} | ||
throw jsError.error |
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.
This is rougly what Prisma client has to do to get errors too.
ArgType: ToNapiValue + 'static, | ||
ReturnType: FromNapiValue + 'static, | ||
{ | ||
unsafe fn from_napi_value(napi_env: napi::sys::napi_env, napi_val: napi::sys::napi_value) -> napi::Result<Self> { |
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.
This PR is pretty heavy on the usage of this trait because it plays nicely with napi-rs build-in methods for automatic conversions and there is no safe alternative. In all cases we immediately wrap it into safe wrappers and work with them afterwards. I beleive safe alternative could be added to napi-rs
pretty easily, so if you think it's a problem I can try to send them a PR.
CodSpeed Performance ReportMerging #4186 will not alter performanceComparing Summary
|
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.
🚀
The way it is implemented:
encounterd an error, we store it on per-connector error registry and
return it's numeric id to the engine.
query-engine-node-api, where it becomes new kind of error,
ExternalError
with a code of P2036 and error id in the meta field.re-throw it. This PR implements this for smoke-tests, similar
implementation needs to be done in the client.
Implementation is done this way, rather than propogating
napi::Error
instance, to avoid dependencies on napi types in all intermediate crates
other than
js-connectors
andquery-engine-node-api
.To better accomodate this pattern,
Result
type was introduced to theconnectors. It allows to explicitly indicate if particular call
succeeded or failed via return value. On Rust side, those values get
parsed and converted into
std::result::Result
values.On Rust side,
AsyncJsFunction
wrapper overThreadsafeFunction
isintroduced. It handles promises and JS result type conversions
automatically and simplifies
Proxy
code.This also lays the foundation for supporting "Known" errors - JsError
type could be extended in a future with new error types, that could be
converted into standard prisma errors with normal error codes.
Fix #prisma/team-orm#260