-
Notifications
You must be signed in to change notification settings - Fork 1.8k
refactor(NODE-3337): unwrap WriteConcernError #2932
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
Conversation
@andymina we should be careful about removing the server error behavior of assigning all input object props to the error object; the function of the server error class is to wrap errors returned from the server, and I believe the original design was based around the idea that we might not always know what those properties are, so we just want to pass them on to the user so that they have that information from the server; removing this behavior is a potentially breaking change. The goal of the ticket was just to remove the explicit rewrapping of the write concern error, not necessarily change the actual implementation of the MongoServerError class. |
@dariakp Ahh, didn't realize that there were potentially more/new props coming in from the server. Initially I figured that the shape of MongoServerErrors that the driver needs would remain the same so I just explicitly retrieved them rather than relying on the loop. So sorry for any confusion! Will revert this change, ty :) |
src/error.ts
Outdated
this.ok = message.ok; | ||
this.topologyVersion = message.topologyVersion; | ||
|
||
const currentProps = Object.getOwnPropertyNames(this); |
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.
Is all this really necessary to simplify the multiple constructor calls in bulk/common? It looks like all this filtering is doing the same thing as the original for..in, but it's less efficient because of the multiple array function calls. Ideally, the server error class should not need to be touched at all to complete this ticket, and we definitely should not be adding new functionality to it unless absolutely necessary (referring to the kErrorLabels line above), so any logic that's specific to particular errors, and not all server errors, should live in those specific classes.
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.
Neal and I talked things over and I removed the tons of array calls that were there. I opted to put prop names into a set for better efficiency and it'll likely be easier to read/maintain than loads of if conditions.
src/bulk/common.ts
Outdated
*/ | ||
export class WriteConcernError { | ||
err: MongoServerError; | ||
err: { code: number; errmsg: string; errInfo?: Document }; |
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.
We technically have a breakage here, this is no longer instanceof MongoServerError
but this was just relying on the key mapping logic of the MongoServerError constructor.
I think its fair to make this change, all the properties are still accessible. Of course, up for discussion:
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 mean, technically, based on the spec, there shouldn't be an err property here at all, so we could consider saving err
as a symbol (and perhaps replacing err with a deprecated getter); any thoughts on this? This class never extended Error
, so losing the stack trace from a property that should never have even been exposed wouldn't really be an issue
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.
You want to map these props on to this class? I think that sounds good removes a layer of indirection (and we keep a getter to prevent breakage)
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.
something along the lines of defining a kErr
symbol (maybe more descriptive) set equal to the { code, errmsg, errInfo }
and then a public getter marked deprecated: get err() { return this[kErr] }
so that we hopefully don't break any existing functionality in case someone is trying to read directly from the current err prop
src/bulk/common.ts
Outdated
if (result.writeConcernError) { | ||
bulkResult.writeConcernErrors.push(new WriteConcernError(result.writeConcernError)); | ||
const { code, errmsg, errInfo } = result.writeConcernError; | ||
bulkResult.writeConcernErrors.push(new WriteConcernError({ code, errmsg, errInfo })); |
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 think we have to change this back to result.writeConcernError
, we might be losing properties in this destructuring.
We can make the constructor accept a more flexible type, it's still good that we have a better typed WriteConcernError
class, but incase there are more properties in certain server versions we should have them automatically pass through here.
src/bulk/common.ts
Outdated
) { | ||
mergeBatchResults(batch, bulkResult, undefined, err.result); | ||
|
||
// TODO: Remove multiple levels of wrapping (NODE-3337) |
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.
We can delete the TODO
src/bulk/common.ts
Outdated
super(error as Error); | ||
Object.assign(this, error); | ||
constructor( | ||
x: { message: string; code: number; writeErrors?: WriteError[] } | WriteConcernError | AnyError, |
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.
We can name this something a little more descriptive than x
now.
src/bulk/common.ts
Outdated
result: BulkWriteResult | ||
) { | ||
super(x); | ||
if (x instanceof Error) Object.assign(this, x); |
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 believe we put the assign behind the gate of instanceof Error
since that was filtering for MongoServerErrors while testing.
We should move the assign to be the last call of the constructor ungated. (Or remove it all together and see if tests fail, but I defer to the team on seeing if that's acceptable).
src/operations/insert.ts
Outdated
if (err || res == null) return callback(err); | ||
if (res.code) return callback(new MongoServerError(res)); | ||
if (res.writeErrors) return callback(new MongoServerError(res.writeErrors[0])); | ||
if (res.writeErrors) return callback(new MongoWriteConcernError(res.writeErrors[0])); |
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.
Is this always correct? I don't think a writeError is always in the category of a WriteConcernError, but I could be wrong, lets sync to make sure this is correct
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.
Capturing what spoke about in person: This has been reverted to MongoServerError since not every write error is a write concern error. The ideal solution would be to make this a WriteError
class, but that would call for some error hierarchy refactoring since WriteError
isn't a child of anything and that would be out of scope.
Neal requested reviews from my laptop lol |
src/bulk/common.ts
Outdated
*/ | ||
export class WriteConcernError { | ||
err: MongoServerError; | ||
err: { code: number; errmsg: string; errInfo?: Document }; |
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 mean, technically, based on the spec, there shouldn't be an err property here at all, so we could consider saving err
as a symbol (and perhaps replacing err with a deprecated getter); any thoughts on this? This class never extended Error
, so losing the stack trace from a property that should never have even been exposed wouldn't really be an issue
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.
One small request, and there is a lint error to fix, otherwise LGTM though! 👍
src/bulk/common.ts
Outdated
} | ||
|
||
/** | ||
* @internal |
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.
does it make sense to mark this internal if it's explicitly intended as a backcompat option for users?
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 suppose my thinking was it avoids breaking runtime code but intentionally makes it inaccessible to TS but I realize that could be nearly equal in breakage.
lets go with making this interface public and not introduce all these specialized exceptions. Sorry @andymina my advice steered you here!!
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.
No worries!
src/bulk/common.ts
Outdated
errmsg: string; | ||
errInfo?: Document; | ||
} { | ||
// WriteConcernErrorData has been inlined as the return to avoid making it public |
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.
what's the reasoning against making it public?
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 figured it would be yet another type clouding the namespace relating to an unclearly layered WriteConcernError/WriteError/BulkError space. But it's an 🧅 that can be argued otherwise
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.
LGTM 👍
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.
LGTM :)
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.
LGTM
Description
Unwrapped the nested errors seen in
handleMongoWriteConcernError
to be clearer and more readable.