-
Notifications
You must be signed in to change notification settings - Fork 1.4k
fix: handle NaN and Infinity float values in gRPC #4631
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: handle NaN and Infinity float values in gRPC #4631
Conversation
2fa5e78
to
fdd70b2
Compare
Couple of things:
|
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.
Thank you for the PR and sorry for the slow reply 🙇
I have left some comments around the code.
On the whole idea I am still not particularly certain this is ... what should be done, if anything.
Trying to figure out how this works in other cases I found protocolbuffers/protobuf-javascript#49 which seems to suggest that this isn't really supported. But maybe I am getting something wrong.
From the test it seems that this just works on the way back which seems awfully convient.
Hey @mstoykov, thanks a lot for the review! I’ve made some changes based on your comments. There’s one part I didn't change yet because I’m not fully sure I understand it correctly, but we can keep discussing it in the open conversation. Regarding the issue you mentioned: it looks like an older open issue. From what I saw, the code has since changed and now properly asserts both I also tested it locally using the dummy.proto from the issue, for both client and server (wire format). Additionally, the ProtoJSON documentation confirms that both values are officially supported. Just a heads-up: I might be a bit slower next week if any further changes are needed — apologies in advance. Thanks again for the great feedback. 🙏 EDIT: Tests are failing, don't know if related to my changes but the |
Hi there, sorry for the delay on this! It's been a while since I could get back to this PR (I haven't had much time recently). I've made the requested corrections. Please take a look and let me know if everything looks good (I feel like there should be a better way of handling the copy for sure) or if there's anything else that needs to be addressed. Thank you! 🙇 |
adbc0f9
to
0a673e0
Compare
Hi @ariasmn, Sorry for the late review, but finally I've been able to properly test it. I think it's close to be ready to be merged, but there's one extra thing that we should fix, which is that if For instance:
Can you take a look at this and try to fix it, please? Thanks! |
…ring-float' into fix/grpc-special-string-float
Conflicts: internal/js/modules/k6/grpc/client_test.go
Hi @joanlopez, Thank you for catching that one! I implemented a fix by following what's done in the k6/vendor/github.com/grafana/sobek/builtin_json.go Lines 366 to 372 in 602467d
Let me know if my choice of wording for variables/comments is odd, I can fix them no problem. Thanks! |
} | ||
|
||
// Handle special float values. | ||
if f, ok := v.Export().(float64); ok { |
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.
As @mstoykov suggested before, could we use ExportType()
here instead of Export()
, as the latter is a much more expensive operation.
In case it's a float64
, I guess you'll need to call v.Export()
anyway, but in all the other cases, it won't be called that early.
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.
Or maybe even something like:
if f, ok := v.Export().(float64); ok { | |
if v.ExportType().Kind() == reflect.Float64 { | |
f := v.ToFloat() | |
switch { | |
case math.IsNaN(f): | |
return runtime.ToValue("NaN"), nil | |
case math.IsInf(f, 1): | |
return runtime.ToValue("Infinity"), nil | |
case math.IsInf(f, -1): | |
return runtime.ToValue("-Infinity"), nil | |
} | |
return v, nil | |
} |
So, you don't need Export()
at all.
Wdyt? @mstoykov
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 seems better.
For the record - the idea is that if you are using Export
it will do a lot of work in some of the cases. If the end type is flaot64 - that probably is fine. But it also might be a big object that you just fully transformed from JS to go type. And then you just throw it away because it isn't a float64, just to repeat the operation each time.
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.
Sorry about that, I resolved the previous conversation thinking I had already implemented the change, totally my bad.
Thanks for the explanation and sorry for the inconvenience!
Let me know if there's anything else I can do on my end.
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.
Sure, no problem @ariasmn, that's why we have a review process, we're all humans and sometimes it happens!
Looks good now! 👌🏻 Thanks! 🙌🏻
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! Thanks for the changes and going through figuring it out @ariasmn 🙇
Fixes (?): #3990
The problem was in this specific line:
k6/internal/js/modules/k6/grpc/client.go
Line 363 in a74d803
This
MarshalJSON
implemented in Sobek uses a copy of JSON.stringify() under the hood, and per JSON RFC, neitherInfinity
orNaN
as values are accepted as valid numbers.However, since we rely on ProtoJSON, which does support special strings
"Infinity"
and"NaN"
, I guess that the idea would be to "normalize" them as a string format before marshaling, which is what this PR implements.