Support async function initialization with the .NET 6 managed runtime
Describe the Feature
Add support to Amazon.Lambda.RuntimeSupport for asynchronous initialization of .NET functions using the dotnetcore3.1 or dotnet6 managed runtimes.
Is your Feature Request related to a problem?
While investigating moving a .NET Lambda using a custom runtime with Amazon.Lambda.RuntimeSupport to use .NET 6 over to the new built-in managed runtime for .NET 6, I noticed that the managed until uses the same infrastructure to bootstrap the user code from the handler type given in the function configuration.
However, there is no support for asynchronously initializing such a function using the runtime in this way, which is possible with a custom runtime.
https://github.com/aws/aws-lambda-dotnet/blob/e29ba747e4f912be57811f20511e5a57e195fda7/Libraries/src/Amazon.Lambda.RuntimeSupport/Bootstrap/UserCodeInitializer.cs#L44-L48
This means that user code wishing to perform asynchronous initialization must perform it on the path of the first request, moving the "penalty" from the initialization code path to the handler code path. This also means that if initialization fails, the handler will attempt to process requests (and fail), rather than hard-fail due to initialization of the lambda itself failing.
Proposed Solution
Possible ways this could be achieved:
- An attribute that specifies a bootstrap type/method that is invoked if found.
- An interface that defines an async initialization method that is invoked if the user-specified handler function implements the interface.
If I recall correctly, the in-development annotations library has similar support planned for it.
Describe alternatives you've considered
None, other than staying on a custom runtime.
Additional Context
This was found in the process of migrating a .NET 6 Lambda function with a custom runtime to test out the new managed runtime for .NET 6 (dotnet6).
Environment
- [x] :wave: I may be able to implement this feature request
- [ ] :warning: This feature might incur a breaking change
This is a :rocket: Feature Request
@martincostello Thanks for submitting feature request. Feel free to contribute in the form of PR that could be reviewed by the team.
@ashishdhingra Any preference to either of the two suggested solutions (or something different entirely) before I start on something?
For now I would do the async work in the constructor of the type and block on the async work. At that point there is only a single thread going through so there should be no issues blocking on the async work. Plus you should take advantage of the init stage which is free although the init stage does have to complete in some time limit.
I like the idea of coming up with a way for a more idiomatic place for running async code.
I've pushed up a draft PR for this using a new interface in #1091.
This means that user code wishing to perform asynchronous initialization must perform it on the path of the first request, moving the "penalty" from the initialization code path to the handler code path.
This is half story, even if the initialization goes out of the first request. It has to be done at some point which is now during the the user code loading step. On a cold VM, this will happen during the first request which essentially have the same impact.
Although, I agree with more idiomatic way of initialization the function and this approach seems reasonable.
I'm thinking at this point this is something we should target in the design changes we make to Amazon.Lambda.RuntimeSupport in .NET 8 to support a first class async initialize.
I'm going to close this as something we aren't going to do. In the end somebody has to make the blocking async call and I would rather not add the complexity to Amazon.Lambda.RuntimeSupport.
Comments on closed issues are hard for our team to see. If you need more assistance, please either tag a team member or open a new issue that references this one. If you wish to keep having a conversation with other community members under this issue feel free to do so.