Skip to content

Add LambdaInternalExtension#53465

Draft
hamburml wants to merge 1 commit intoquarkusio:mainfrom
hamburml:feature/AddLambdaInternalExtension
Draft

Add LambdaInternalExtension#53465
hamburml wants to merge 1 commit intoquarkusio:mainfrom
hamburml:feature/AddLambdaInternalExtension

Conversation

@hamburml
Copy link
Copy Markdown
Contributor

@hamburml hamburml commented Apr 6, 2026

What still needs to be done: Only call invocationFInished when all otel flushes are done in a correct way.

Log examples from AWS which show that registration works.

INIT_START Runtime Version: java:25.v19	Runtime Version ARN: arn:aws:lambda:eu-central-1::runtime:e6d4c82c9cda2359751b911122db4c442c1eccdc5efb2966018d6c34858dcf3e
__  ____  __  _____   ___  __ ____  ______ 
--/ __ \/ / / / _ | / _ \/ //_/ / / / __/ 
-/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \   
--\___\_\____/_/ |_/_/|_/_/|_|\____/___/   
2026-04-06 22:55:55,946 INFO  [io.quarkus] (main) lambda-exit 1.0.0-SNAPSHOT on JVM (powered by Quarkus 999-SNAPSHOT) started in 0.821s. 
2026-04-06 22:55:55,947 INFO  [io.quarkus] (main) Profile prod activated. 
2026-04-06 22:55:55,947 INFO  [io.quarkus] (main) Installed features: [amazon-lambda, cdi]
2026-04-06 22:55:55,951 INFO  [io.quarkus.amazon.lambda.runtime.LambdaInternalExtension] (main) Starting Lambda internal extension 'quarkus-internal-extension' against runtime API '169.254.100.1:9001'
2026-04-06 22:55:55,956 INFO  [io.quarkus.amazon.lambda.runtime.LambdaInternalExtension] (main) Registering Lambda internal extension 'quarkus-internal-extension' at http://169.254.100.1:9001/2020-01-01/extension/register with payload 
{
    "events": [
        "INVOKE"
    ]
}

2026-04-06 22:55:55,965 INFO  [io.quarkus.amazon.lambda.runtime.LambdaInternalExtension] (main) Waiting for register response for Lambda internal extension 'quarkus-internal-extension'
2026-04-06 22:55:55,968 INFO  [io.quarkus.amazon.lambda.runtime.LambdaInternalExtension] (main) Register response for Lambda internal extension 'quarkus-internal-extension': status=200 after 3 ms
2026-04-06 22:55:55,968 INFO  [io.quarkus.amazon.lambda.runtime.LambdaInternalExtension] (main) Lambda internal extension 'quarkus-internal-extension' registered successfully
2026-04-06 22:55:55,972 INFO  [io.quarkus.amazon.lambda.runtime.LambdaInternalExtension] (Quarkus Lambda Internal Extension) Lambda internal extension 'quarkus-internal-extension' event loop started
2026-04-06 22:55:55,972 INFO  [io.quarkus.amazon.lambda.runtime.LambdaInternalExtension] (Quarkus Lambda Internal Extension) Lambda internal extension 'quarkus-internal-extension' entering event/next loop
2026-04-06 22:55:55,973 INFO  [io.quarkus.amazon.lambda.runtime.LambdaInternalExtension] (Quarkus Lambda Internal Extension) Lambda internal extension 'quarkus-internal-extension' waiting for next event at http://169.254.100.1:9001/2020-01-01/extension/event/next
EXTENSION	Name: quarkus-internal-extension	State: Ready	Events: [INVOKE]
START RequestId: e13948f8-445e-4092-9842-1b718f860063 Version: $LATEST
2026-04-06 22:55:55,977 INFO  [io.quarkus.amazon.lambda.runtime.LambdaInternalExtension] (Quarkus Lambda Internal Extension) Lambda internal extension 'quarkus-internal-extension' received next event after 4 ms: {"eventType":"INVOKE","deadlineMs":1775516170977,"requestId":"e13948f8-445e-4092-9842-1b718f860063","invokedFunctionArn":"arn:aws:lambda:eu-central-1:859408712632:function:test","tracing":{"type":"X-Amzn-Trace-Id","value":"Root=1-69d439fa-614d436271a0e435598a8814;Parent=0d41e7b7650401c3;Sampled=0;Li...
2026-04-06 22:55:56,134 INFO  [de.hamburml.AmazonLambda] (main) hello
2026-04-06 22:55:56,135 INFO  [io.quarkus.amazon.lambda.runtime.LambdaInternalExtension] (Quarkus Lambda Internal Extension) Lambda internal extension 'quarkus-internal-extension' waiting for invocation completion requestId=e13948f8-445e-4092-9842-1b718f860063 (max 14842 ms)
2026-04-06 22:55:56,156 INFO  [io.quarkus.amazon.lambda.runtime.LambdaInternalExtension] (Quarkus Lambda Internal Extension) Lambda internal extension 'quarkus-internal-extension' invocation completed requestId=e13948f8-445e-4092-9842-1b718f860063
2026-04-06 22:55:56,156 INFO  [io.quarkus.amazon.lambda.runtime.LambdaInternalExtension] (Quarkus Lambda Internal Extension) Lambda internal extension 'quarkus-internal-extension' sleeping 500 ms after completion requestId=e13948f8-445e-4092-9842-1b718f860063
2026-04-06 22:55:56,657 INFO  [io.quarkus.amazon.lambda.runtime.LambdaInternalExtension] (Quarkus Lambda Internal Extension) Lambda internal extension 'quarkus-internal-extension' waiting for next event at http://169.254.100.1:9001/2020-01-01/extension/event/next
END RequestId: e13948f8-445e-4092-9842-1b718f860063
REPORT RequestId: e13948f8-445e-4092-9842-1b718f860063	Duration: 680.75 ms	Billed Duration: 1745 ms	Memory Size: 512 MB	Max Memory Used: 123 MB	Init Duration: 1063.46 ms	
START RequestId: f84604fd-aa78-4d28-ae02-2cb43b72b2c7 Version: $LATEST
2026-04-06 22:55:57,730 INFO  [io.quarkus.amazon.lambda.runtime.LambdaInternalExtension] (Quarkus Lambda Internal Extension) Lambda internal extension 'quarkus-internal-extension' received next event after 1073 ms: {"eventType":"INVOKE","deadlineMs":1775516172730,"requestId":"f84604fd-aa78-4d28-ae02-2cb43b72b2c7","invokedFunctionArn":"arn:aws:lambda:eu-central-1:859408712632:function:test","tracing":{"type":"X-Amzn-Trace-Id","value":"Root=1-69d439fd-7247c3da0d3f217b2f8cfb35;Parent=33db5cbe5a70e3ad;Sampled=0;Li...
2026-04-06 22:55:57,731 INFO  [io.quarkus.amazon.lambda.runtime.LambdaInternalExtension] (Quarkus Lambda Internal Extension) Lambda internal extension 'quarkus-internal-extension' waiting for invocation completion requestId=f84604fd-aa78-4d28-ae02-2cb43b72b2c7 (max 14999 ms)
2026-04-06 22:55:57,731 INFO  [de.hamburml.AmazonLambda] (main) hello
2026-04-06 22:55:57,731 INFO  [io.quarkus.amazon.lambda.runtime.LambdaInternalExtension] (Quarkus Lambda Internal Extension) Lambda internal extension 'quarkus-internal-extension' invocation completed requestId=f84604fd-aa78-4d28-ae02-2cb43b72b2c7
2026-04-06 22:55:57,732 INFO  [io.quarkus.amazon.lambda.runtime.LambdaInternalExtension] (Quarkus Lambda Internal Extension) Lambda internal extension 'quarkus-internal-extension' sleeping 500 ms after completion requestId=f84604fd-aa78-4d28-ae02-2cb43b72b2c7
2026-04-06 22:55:58,232 INFO  [io.quarkus.amazon.lambda.runtime.LambdaInternalExtension] (Quarkus Lambda Internal Extension) Lambda internal extension 'quarkus-internal-extension' waiting for next event at http://169.254.100.1:9001/2020-01-01/extension/event/next
END RequestId: f84604fd-aa78-4d28-ae02-2cb43b72b2c7
REPORT RequestId: f84604fd-aa78-4d28-ae02-2cb43b72b2c7	Duration: 503.57 ms	Billed Duration: 504 ms	Memory Size: 512 MB	Max Memory Used: 123 MB	
START RequestId: 29b2a6dc-bf63-49e0-836c-8e283a86e34a Version: $LATEST
2026-04-06 22:55:59,092 INFO  [io.quarkus.amazon.lambda.runtime.LambdaInternalExtension] (Quarkus Lambda Internal Extension) Lambda internal extension 'quarkus-internal-extension' received next event after 860 ms: {"eventType":"INVOKE","deadlineMs":1775516174092,"requestId":"29b2a6dc-bf63-49e0-836c-8e283a86e34a","invokedFunctionArn":"arn:aws:lambda:eu-central-1:859408712632:function:test","tracing":{"type":"X-Amzn-Trace-Id","value":"Root=1-69d439ff-74bd353735c3530134dd3c8d;Parent=7df0bf091a3394ae;Sampled=0;Li...
2026-04-06 22:55:59,093 INFO  [de.hamburml.AmazonLambda] (main) hello
2026-04-06 22:55:59,093 INFO  [io.quarkus.amazon.lambda.runtime.LambdaInternalExtension] (Quarkus Lambda Internal Extension) Lambda internal extension 'quarkus-internal-extension' waiting for invocation completion requestId=29b2a6dc-bf63-49e0-836c-8e283a86e34a (max 14999 ms)
2026-04-06 22:55:59,093 INFO  [io.quarkus.amazon.lambda.runtime.LambdaInternalExtension] (Quarkus Lambda Internal Extension) Lambda internal extension 'quarkus-internal-extension' invocation completed requestId=29b2a6dc-bf63-49e0-836c-8e283a86e34a
2026-04-06 22:55:59,093 INFO  [io.quarkus.amazon.lambda.runtime.LambdaInternalExtension] (Quarkus Lambda Internal Extension) Lambda internal extension 'quarkus-internal-extension' sleeping 500 ms after completion requestId=29b2a6dc-bf63-49e0-836c-8e283a86e34a
2026-04-06 22:55:59,594 INFO  [io.quarkus.amazon.lambda.runtime.LambdaInternalExtension] (Quarkus Lambda Internal Extension) Lambda internal extension 'quarkus-internal-extension' waiting for next event at http://169.254.100.1:9001/2020-01-01/extension/event/next
END RequestId: 29b2a6dc-bf63-49e0-836c-8e283a86e34a
REPORT RequestId: 29b2a6dc-bf63-49e0-836c-8e283a86e34a	Duration: 502.76 ms	Billed Duration: 503 ms	Memory Size: 512 MB	Max Memory Used: 123 MB	

…e). If set to true register the application as internal extension which allows quarkus to do post response stuff (otel flushes, still needs to be added).
@hamburml hamburml changed the title Add LambdaInternalExtension and internalExtensionConfig (default fals… Add LambdaInternalExtension Apr 6, 2026
Copy link
Copy Markdown
Member

@brunobat brunobat left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @hamburml.
If I understood well, the Idea is to register the lambda with an internal aws extension and allow an extra 500ms to flush all telemetry.
The lambda extension registration process seems complex. Don't they provide some utility for it?

invocationCompletion.computeIfAbsent(requestId, ignored -> new CompletableFuture<>()).complete(null);
}

private static void runLoop(String runtimeApi, String extensionName, String extensionId) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I'm not familiar with this AWS API... Why do you need this runLoop?

Copy link
Copy Markdown
Contributor Author

@hamburml hamburml Apr 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need the runLoop because the Extensions API is blocking. One call to /2020-01-01/extension/event/next waits for one lifecycle event, and after the extension finishes handling that event it must call Next again to signal completion and wait for the next event (or is shutdown if AWS Lambda decides this environment is not necessary anymore and scales down).

private LambdaInternalExtension() {
}

public static void startIfEnabled() {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this needs to run as a static code?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don’t think so. We mainly need a single coordinator, since the internal extension thread and the invocation tracking are runtime-wide in a Lambda execution environment. I used static state because it was the simplest way to get that i guess. We can change that.

@hamburml
Copy link
Copy Markdown
Contributor Author

hamburml commented Apr 7, 2026

If I understood well, the Idea is to register the lambda with an internal aws extension and allow an extra 500ms to flush all telemetry.

Yeah, that’s the general idea. We register an internal extension and use it to keep the invoke lifecycle open so post-response work, such as telemetry flushing, can complete before AWS Lambda freezes the environment. As I understood it, Lambda considers the invoke phase finished only after both the runtime and all registered extensions have signaled completion. For an internal extension, that effectively means sending a request to /2020-01-01/extension/event/next which tells Lambda that the internal extension is finished and we wait for the next event (https://docs.aws.amazon.com/lambda/latest/dg/runtimes-extensions-api.html#extensions-api-next).

The runtime thread signals completion for a given request id to the internal extension thread via invocationFinished once the post-response work is done. If flushing completes before the 500 ms cap, the extension thread can proceed earlier and return to /2020-01-01/extension/event/next, which signals to Lambda that the internal extension is also done for that invoke. So the function response may already have been sent, but Lambda still waits for the internal extension to finish before closing the invoke lifecycle.

The lambda extension registration process seems complex. Don't they provide some utility for it?

I couldn’t find an official AWS Java helper specifically for the Extensions API - considering that almost all AWS APIs are Rest APIs i am not sure if there is a need for it. AWS documents the extension flow as direct calls to /2020-01-01/extension/register and /2020-01-01/extension/event/next using AWS_LAMBDA_RUNTIME_API.

I did find this repo https://github.com/aws-samples/aws-lambda-extensions/blob/main/java-example-extension/src/main/java/example/ExtensionClient.java but yeah...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants