-
Notifications
You must be signed in to change notification settings - Fork 60
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
Allow then-able Faraday responses for free client concurrency #68
Conversation
👋 I just wanted to reach out to see if there was anything I could add here to make this PR easier to review/accept? We're using this library quite heavily at GitHub and it would be great to be able to support concurrent RPC requests in the way described above, to make GitHub itself run faster 😀 🚀 Thanks in advance, and thank you for maintaining this library! 💖 |
Hi @theojulienne thanks for the contribution! We're excited that twirp-ruby is valuable to GitHub. I'm currently getting up to speed with how the IO library you're developing works and will complete this review soon. |
Hi @theojulienne, I thought about it a bit and I wonder if a more generic approach to extending I've done a code sketch with a small test here https://github.com/twitchtv/twirp-ruby/compare/main...rudle:rpc-with-blocks?expand=1. The idea I'm exploring is what if the generated RPC instance methods accepted an optional block that yielded the response? In this world, client code that wanted to use any promise library could do so in whatever way was natural for them. I'm not sure this meets your goals, so please do take a look at my suggestion. I have a weak preference to the block approach as long as it meets your needs because it's more on the happy path of the syntax of the language. While it's true that the |
Thanks so much for looking into this PR/idea! 🤔 It would be really interesting to do this with blocks, though I'm not sure how to do it while still being asynchronous. If I'm reading it right, the library code that converts a Faraday response to a Twirp
Because the last bit there after the HTTP call is executed synchronously in the same block, it's not possible to ever delay execution of the block or follow up code - essentially what we need is to somehow be deferring the execution of the block to some future point where the response is ready and then continue on with other tasks until then. Promises support this pattern by deferring the block until some other action resolves the promise, allowing multiple requests to be created and queued up but not have the work done until they complete. Additionally, we would want the It would be possible to pass a block into the Is there any sort of pattern for deferring (past the point of returning from |
Hi @theojulienne, good point about the block approach, thanks for considering it. I can't think of a more natural way of expressing this pattern, so this is probably the way to go while still keeping things backward compatible (all the way back to 1.9!). I'm supportive of this change landing and will ask one of my colleagues with write access to merge it soon. Thanks for the making a contribution to twirp-ruby! |
One more small licensing requirement from our side. Please confirm that you understand the license of this project by including this phrase in the PR description. "By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license." |
Thanks @rudle! By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license. (I put it in the PR description too) |
👋 I'm working on creating improved support for non-threaded concurrent IO workloads at GitHub, and am creating this PR since we use
twirp-ruby
and it would be great to see it support one small change that would make it easy to enable concurrent requests on a single thread.This PR adds support for executing the mutation from a Faraday response to
ClientResp
in a.then { ... }
block when the object supports#then
, as is common for promises. This allows extensions of Faraday such as iopromise-faraday to return a Promise of a response rather than an immediate response, and have twirp-ruby work exactly as expected.This doesn't introduce any dependencies on promise libraries (or any other libraries) or change the behaviour in the normal case.
When a provided Faraday client (with extended behaviour) returns a response supporting
.then { ... }
, it is used. In Ruby 2.6+, all objects support.then { ... }
but it behaves just like a Promise. In older Ruby (which thegemspec
indicates support for), this code simply runs the function as before and so should be fully compatible.The end result is a no-op change for folks using this library right now, but for those adopting IOPromise, this introduces free single-threaded concurrency of RPC calls using this gem - take the
HelloWorld
service from theexample
in this repo being slightly amended to be slow:And the client, providing the Twirp client an IOPromise-supporting Faraday that returns a promise (responding to
#then
):This results in concurrent requests, rather than sequential ones:
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.