-
-
Notifications
You must be signed in to change notification settings - Fork 208
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
fix: non-nullable schema receiving null input does not coerce for default types on object and arrays #670
Conversation
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.
IMHO if user is not sure that his data matches the schema he should use a validator that will provide him a nice error. This change adds another validation check to the hot path that doesn't have anything to do with a serialization. I don't want turn this library to validator.
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 should be skipping the object if it's null or undefined.
I really do get it, but it's the use of obj assuming it'll never be null that is just wrong IMO. A null check is very little cost for the value it brings. As far as i can see, there's a bunch of validation being done already, e.g. required fields. |
You mean just early exit the function? |
The rule is simple. If your object can be null you use the nullable keyword, if not you don't use it. If you don't trust your data you validate it. If you see some validation checks it doesn't mean you should bring more of them. |
Can't disagree, but It's pretty much just crashing as of now and it's hard to track down. How about we return an empty object |
Which pretty much described in a documentation: https://github.com/fastify/fast-json-stringify#security-notice
IMHO this looks even more dangerous to me, because if you don't notice that some of your code returns a null instead of a real object, you might have a problem if an empty object is also a valid response. From the application logic throwing an error is a correct behaviour. I just don't agree that this should be done in this library. |
From the other hand we have this paragraph (which I don't like either, but it doesn't matter) that describes how we coerce simple types if they are null, but schema is not nullable. And we support that. Maybe an empty object is a good idea. |
Well, again I really do agreee but we could argue the same about this why is it bothering about nullable if we're only trying to serialize stuff? Can't the nullable field just be ignored and serialize to null anything that happens to be null? It's not really clear to me why is it trying to behave differently on whether that field is set or not, doesn't the validator handle nulls itself? |
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
An obscure error from generated code is not useful. Either we throw or we consider null like undefined, but something we should change for the sake of DX. |
@rhighs please check this: #670 (comment) |
@ivan-tymoshenko Coercing into an empty object kinda makes sense but what's the added value? we'd still have to check for null. Raising an error is more appropriate IMO. In case we wanna choose the first route function input could be defaulted to be an empty object here
|
It would be consistent having that we already coerce null to empty string, zero etc. Why logic for object should be different? |
I agree. |
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
can you update the PR title?
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.
- Can you update the doc please? Add the object example here: https://github.com/fastify/fast-json-stringify#nullable-object.
- Can you check/update the same behaviour for array. It should return an empty array for the null value. If not I will create another PR later.
- Can you run the benchmarks please, as it affects the hot path?
On a Ryzen 7 5800X Locked at 4.2 GHz, 16GB RAM, 8 cores and 16 threads:
Note: I have a couple of services in the background running, which are taking their portion of memory... |
As per the array part it's quite easy as the code is pretty much the same. Previously it would throw an error if null was passed as it's check was done with |
array -> [] object -> []
Sorry my bad. I need to explain it better. There is no need to update the benchmarks in the readme. Just post the benchmarks before your change and after, so we can see if there is a performance drop. |
5458cc3
to
d4b9b06
Compare
Before:
After:
|
it seems we are losing quite a lot of performance :(. |
How's it even possible adding a check + reassignment causes a 3mln ops/sec drop? 🤨 |
That is a hot path ;), and it's probably allocating a huge amount of objects that needs to be collected thereafter. Try not allocating, but rather skipping the object rendering altogether. |
Ok this makes a lot of sense! However, the rendering part includes some bits of validation e.g. checking for required fields. Do you think we can ignore this in case of coercion from null to {}? |
Yes, it's better than crashing. |
This helps avoiding useless allocations. It also causes required fields checks to be skipped entirely as those are done at rendering. see: fastify#670
With new changes applied:
|
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
@ivan-tymoshenko PTAL |
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.
Perf looks good now.
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.
See comment above ^^^
This helps avoiding useless allocations. It also causes required fields checks to be skipped entirely as those are done at rendering. see: fastify#670
e49e80c
to
9552bb5
Compare
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
This PR is related to issue #669. The changes applied bring an error being thrown with a somewhat descriptive message whenever a non-nullable schema of type 'object' validates against a null value.
Before this change, the generated code would ignore checks on the input value, trying to access properties on a null value which of course results in the application throwing
cannot read <prop> of undefined
. To address this we simply add a check on the function head and throw a meaningful error if needed.Checklist
npm run test
andnpm run benchmark
and the Code of conduct