-
Notifications
You must be signed in to change notification settings - Fork 492
perf(parser): lazy-load envelopes to reduce Lambda cold start latency by ~900ms #8405
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
base: develop
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -308,3 +308,30 @@ def handler(event: SqsModel, _: LambdaContext): | |
| assert parsed_event[0].version == "version" | ||
|
|
||
| handler(event, LambdaContext()) | ||
|
|
||
|
|
||
| def test_parser_import_does_not_eagerly_load_envelopes(): | ||
| """Importing parse from parser __init__ must not eagerly load all envelope modules. | ||
|
|
||
| Envelopes are only needed when envelope= is passed to parse()/event_parser(). | ||
| Eager loading all 16 envelopes adds ~900ms to Lambda cold start for functions | ||
| that only use parse() without an envelope. | ||
| """ | ||
| import sys | ||
|
|
||
| # Remove any previously cached parser modules to simulate a fresh import | ||
| parser_modules = [key for key in sys.modules if "aws_lambda_powertools.utilities.parser.envelopes" in key] | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. One concern with this test: by the time it runs, the conftest and this module have already imported envelope classes. Removing entries from Could we run this check in a fresh subprocess instead? I would also cover the existing public imports for |
||
| for mod in parser_modules: | ||
| del sys.modules[mod] | ||
|
|
||
| # Also remove the parser __init__ itself so __getattr__ is exercised | ||
| sys.modules.pop("aws_lambda_powertools.utilities.parser", None) | ||
|
|
||
| # Re-import — only parse is needed, envelopes should NOT be loaded | ||
| from aws_lambda_powertools.utilities.parser import parse # noqa: F401 | ||
|
|
||
| envelope_modules_loaded = [key for key in sys.modules if "aws_lambda_powertools.utilities.parser.envelopes" in key] | ||
| assert not envelope_modules_loaded, ( | ||
| f"Envelope modules were eagerly loaded on parser import: {envelope_modules_loaded}. " | ||
| "This adds significant Lambda cold start latency for functions not using envelopes." | ||
| ) | ||
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.
Hi Erez, thank you for profiling this and documenting the cold-start impact. I really like the direction of this change.
I pulled the branch locally and tried the existing public import in a fresh Python process:
It currently ends in a
RecursionError.__getattr__("envelopes")imports the same attribute from the same package, which invokes__getattr__again.from ...parser import *fails for the same reason.Could we load the submodule with
importlib.import_module(f"{__name__}.envelopes")and cache the resolved value inglobals()? I tested that approach locally and it preserves both the lazy import and the existing public API.