Skip to content

non-async version? #317

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

Closed
DartMen opened this issue Apr 12, 2021 · 7 comments
Closed

non-async version? #317

DartMen opened this issue Apr 12, 2021 · 7 comments

Comments

@DartMen
Copy link

DartMen commented Apr 12, 2021

Would it be possible to have a lightweight 'blocking' version without Tokio/Hyper dependencies?

Consider the following repo names:
aws-lambda-rust-runtime
aws-lambda-rust-runtime-async

Here is some recent discussion about the need for async by John-Nagle:

Async is really needed only for a specific class of programs - those that are both I/O bound and need to maintain a large number of network connections. Outside of that niche, you don't really need it. We already have threads, after all. Not everyone is writing a web service.

rust-lang/wg-async#58

@softprops
Copy link
Contributor

A few items of note.

Async workloads. I think, and I haven't tried this it should be sufficiently easy to wrap sync workloads in async |move| { } or something of the like. You are correct in that not all workloads have async implementations and to some degree lambda, the hosting platform, is a provides your application with concurrency levers, but a large number of networked services interactions typically guide you in an async direction. For that reason I think it still makes sense as the default.

Async runtimes. The way this and other lambda runtimes work is through an internal api expects to be polled for events based on registered triggers, dispatches control to your function with those event payloads, and responds back with a response to that internal api for those triggers. In order to get the most performance and value out of your lambda task, a small semi ephemeral process that runs your function code, it's behoving to allow function invocations to make independent progress and not serialize their invocations. In a tasks lifetime, more than one invocation can be making progress at a time regardless of what form its workload takes on.

Since the runtime is compiled in as a runtime dependency and that needs to talk to the internal lambda api I don't think you can get away with a hyper free runtime. Since the runtime is able to be come more runtime efficient through using hyper in an async fashion I don't think you can get away with tokio dependencies.

However for sync workloads there might be some area for a helper interface though I don't know that it would provide much more than a wrapper around an async |move| { } closure

@bahildebrand
Copy link
Contributor

Can I ask if there is an underlying issue you're running into that sparked this request? You mention lightweight, are you finding your binary sizes too large? The reason I ask is that while it would be possible to support this request, it would be a significant amount of work, and we would need a pretty good use case to justify it.

As @softprops mentioned the current implementation is tied pretty heavily into the Tokio runtime, so we'd need to bring in another dependency other than Hyper/Tokio, and design an alternative path to service the Lambda Runtime API. I do, however, like the idea of a sync wrapper that @softprops mentioned. If that is feasible it could be a good middle ground here.

@DartMen
Copy link
Author

DartMen commented Apr 15, 2021

Ive been using async/futures extensively over the last years in many different languages and it's without a doubt a fantastic mechanism.

But I would rather have a choice to use it or not, like for example, just fetching the weather forecast synchronously once a day.

To run the following example, it needs to pull in many packages which increases not only build times, but also attack surface.

fn main() {
    println!("Hello, world!");
}

With lightweight, I meant, the bare-minimum packages needed to just be able to execute println!("Hello, world!");

The proposal for async |move| { } would defeat my request, as in this case, I would rather just keep using async/await anyway because I am kind of forced to do so?

It's not about just running synchronous code, but to run it with bare-minimum package requirements.

The Rust folks just released a blog post about the future of async which might add to the discussion, and considerations:
https://blog.rust-lang.org/2021/04/14/async-vision-doc-shiny-future.html

@bahildebrand
Copy link
Contributor

Unfortunately the bare minimum to execute a simple print statement will always be rather large for this library. Even if we were to take out our tokio/hyper dependency we would need to bring in another http client to service the runtime api. I haven't done any direct comparisons of other http libraries, but I would imagine they would be comparable in the number of depencies. Additionally some of the more popular libraries wrap the c version of curl which would bring it's own attack surface considerations.

@coltonweaver
Copy link
Contributor

Yeah after looking into this a good bit, it would require a decent amount of effort to get going. I'm definitely open to the idea of this, but I can't see this being a priority for some time.

@davidbarsky
Copy link
Contributor

davidbarsky commented Sep 9, 2021

While I don't maintain this library anymore (as I no longer work at Amazon and will be starting a new job soon), I figured that I should explain my thinking about making the runtime async-first. I'll first try to respond to @duaneking's points.

A thread is an expensive operation, and since we are do not need that thread, its seen as expensive and wasteful from an engineering excellence perspective.

I think that there's might be misunderstanding here: it is entirely possible (and encouraged, if you know what performance profile you need) to run your Lambda function on a single-threaded Tokio executor.

Speed should be your critical priority here, and forcing async code on people where that is a total waste when its not needed - you are forcing the current process to stop so it can yield and create that thread so its a huge delay you just do not need - makes it look like your goals are not aligned to these customer efforts to optimize for speed and reliability.

There are no threads being spawned in the background without your knowledge or action.

This library has a lot of dependencies that it does not need. Please allow us to not be forced into async hell or slow down our systems due to this architecture.

While I think that there's an opportunity to have this library adopt a sans-IO approach, I personally struggle to think of what dependencies can be safely removed without a non-trivial amount of engineering work. Can you let me know what dependencies you feel are unnecessary?

What should be happening is this should be split into 2 libs, one sync and one async, and the async lib simply uses the sync lib so people can use whatever. Simple, DRY, SOLID. Not this moist code.

While I think it'd be helpful to have both a synchronous and an asynchronous version of this library, I'll note that I believe that the synchronous library needs to be built atop of the asynchronous one (as opposed to your proposal, which has the layering flipped) in order to achieve the performance (namely, latency goals) that customers using an asynchronous version require.


I couldn't state this at the time when I was working on this library, but I knew that the AWS Rust SDK would be asynchronous-first, with synchronous clients added later. It didn't seem reasonable to me to ship this library as being synchronous-first, only to require customers to pull in a futures executor such as Tokio to interact with anything on AWS.

@calavera
Copy link
Contributor

hey folks, we've decided not to do this for the time being, if anything changes, I'll let you know.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants