-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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
Support for gRPC protocol #1623
Conversation
This comment has been minimized.
This comment has been minimized.
Hey @na-- Hope you had a good weekend? |
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 can't find any major problems. I commented in two places on things that I think will be better but we can fix them later as well
timeout := 60 * time.Second | ||
|
||
ctx = metadata.NewOutgoingContext(ctx, metadata.New(nil)) | ||
for k, v := range params { |
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 am more and more of the opinion that https://github.com/loadimpact/k6/blob/master/js/modules/k6/http/request.go#L248-L351 should be split and moved to utility functions and then reused here and in in the ws
code.
But this should be done in a different PR
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.
definitely a separate PR
|
||
var response Response | ||
response.Headers = header | ||
response.Trailers = trailer | ||
|
||
marshaler := protojson.MarshalOptions{EmitUnpopulated: true} | ||
|
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 do think it will be more readable if this is moved before the Invoke so taht if err != nil
can be directly after the Invoke.
Also making the ctx should probably be directly before the Invoke and you can skip the header/trailer varaible and directly use response.Headers/response.Trailers as far as I can see
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.
If I directly use response.Headers
/response.Trailers
this results in issues with casting, as one is map[string][]string
and the other is metadata.MD
which is an alias to map[string][]string
. If I change the Response
struct to use metadata.MD
then this will not serialize correctly via goja (unmarshals to an empty object {}
)
close(errc) | ||
}() | ||
|
||
if err := <-errc; err != nil { |
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.
Would canceling the ctx ... make the goroutine Dial return faster?
Maybe we can drop the goroutine ... have the transportCreds
remember the first error they get and cancel the context on it?
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.
Possibly. We wound have to attach a context cancel function to the transportCreds
object at the beginning, and in the event of an error call cancel()
, and then store the TLS error on the transportCreds
to later retrieve the error to check if the error was caused by TLS (as appose to some other context cancellation).
In my opinion this last part of checking an error field on a struct is not idiomatic Go and is prone to being missed (hence why I don't like the builder pattern in Go, you have to remember to check that error field).
At this point I'm still in-favour of the error channel an goroutine as I think it reads cleaner.
js/modules/k6/grpc/client.go
Outdated
|
||
// MethodInfo holds information on any parsed method descriptors that can be used by the goja VM | ||
type MethodInfo struct { | ||
grpc.MethodInfo |
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.
grpc.MethodInfo | |
grpc.MethodInfo `json:"-" js:"-"` |
I think I was a bit overzealous with the suggestion for code reuse here 😞 Without this change, if you run something like:
let methods = client.load([], "samples/grpc_server/route_guide.proto");
console.log(JSON.stringify(methods, null, 4));
you'd get this:
[
{
"method_info": {
"name": "GetFeature",
"is_client_stream": false,
"is_server_stream": false
},
"name": "GetFeature",
"is_client_stream": false,
"is_server_stream": false,
"package": "main",
"service": "RouteGuide",
"full_method": "/main.RouteGuide/GetFeature"
},
{
"method_info": {
"name": "ListFeatures",
"is_client_stream": false,
"is_server_stream": true
},
"name": "ListFeatures",
"is_client_stream": false,
"is_server_stream": true,
"package": "main",
"service": "RouteGuide",
"full_method": "/main.RouteGuide/ListFeatures"
},
// ...
]
but yeah, I'm not sure if the anonymous struct reuse is worth it at this point 🤷♂️
js/modules/k6/grpc/client.go
Outdated
case int64: | ||
timeout = time.Duration(float64(t)) * time.Millisecond | ||
case float64: | ||
timeout = time.Duration(t) * time.Millisecond |
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 https://play.golang.org/p/gsOWwDPPAV3 for the rationale
case int64: | |
timeout = time.Duration(float64(t)) * time.Millisecond | |
case float64: | |
timeout = time.Duration(t) * time.Millisecond | |
case int64: | |
timeout = time.Duration(t) * time.Millisecond | |
case float64: | |
timeout = time.Duration(t * float64(time.Millisecond)) |
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.
Very interesting.
js/modules/k6/grpc/client.go
Outdated
case int64: | ||
timeout = time.Duration(float64(t)) * time.Millisecond | ||
case float64: | ||
timeout = time.Duration(t) * time.Millisecond |
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 https://play.golang.org/p/gsOWwDPPAV3 for the rationale
case int64: | |
timeout = time.Duration(float64(t)) * time.Millisecond | |
case float64: | |
timeout = time.Duration(t) * time.Millisecond | |
case int64: | |
timeout = time.Duration(t) * time.Millisecond | |
case float64: | |
timeout = time.Duration(t * float64(time.Millisecond)) |
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.
Besides the few minor nitpicks, LGTM! 🎉 Awesome 🎊
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.
Stellar work @rogchap! 👏
I don't have any gRPC experience, but this was easy to read (minus the vendor
changes 😆) and use. Don't have any code comments, just spotted a couple of typos.
Though would you mind doing some cleanup/squashing of commits before merging?
Regarding squashing - I was thinking we can squash this in just a single commit when merging the PR, through the GitHub interface? I know it has 250k LoC difference, but 99% of that is And if @rogchap doesn't modify the commits and force push in this PR, we would be able to look up the change history or the rationale behind some code here, just in case. Sort of the best of both worlds with this type of a change, I think? So... here's my suggestion on how to proceed:
Any objections? btw, regarding the actual gRPC module name - we still haven't decided it. So we'll likely merge this PR with the current |
Only a few commits in this PR 😉 Sounds like a plan; for what it's worth: in our company we have set the GitHub settings in all repos to only allow squash+merge from our PRs, so seldom need to manually squash/rebase. Will address all comments; 11pm here in the future (Sydney) so will take a look in the morning. |
👍 Sounds good! btw for now we're tentatively going with |
Ok will update to Just my 2¢ on the subject:
|
I can sympathize, but we'd like to support a lot of other protocols (e.g.
We will support
This is one of the main issues we're struggling right now in our internal discussions... The other one is how to make clear to users that an API's stability is The current 2 top contender strategies are versioned paths, like you proposed ( The biggest drawback to versioned paths for me, especially if we go with |
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.
🎉
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.
🎉
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!
fixes #441
Initial implementation only supports Unary requests.
Example JavaScript API: