Skip to content

[Lambda DevX] Support for Config Hot Reloading #11477

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

Merged
merged 10 commits into from
Nov 18, 2024

Conversation

MEPalma
Copy link
Contributor

@MEPalma MEPalma commented Sep 7, 2024

Motivation

Currently the configuration for Lambda Debug Mode is loaded on startup and cannot be changed until the next restart of LocalStack. This may not only be inconvenient whenever changes to the configuration may be required after startup, but also means that it is impossible for the user to provide configurations for Lambda functions for which the arn is unknown before startup. Such scenario may occur when using dynamic naming in cloud formation. Hence, these changes add initial support for the hot reloading of the Lambda Debug Mode configuration file. This is achieved by updating the config when changes are detected in the file, and stopping a lambda container after evaluation iff this is being debugged. Stopping the container after evaluation is encouraged by the difficulty/feasibility or reconfiguring the container (consider ports), and has the added benefit of unblocking the scenario of debugging the same lambda function more than once.

Changes

  • Added watch and reload logic to the Lambda Debug Mode session class
  • Add environment stop after environment execution iff the lambda function being run is target of the debug session

Testing

As the development of a testing stack for Lambdas debugging is being developed, I refer to manual testing using the Lambda Debug Mode sampled provided here: localstack-samples/localstack-pro-samples#253

@MEPalma MEPalma added the semver: minor Non-breaking changes which can be included in minor releases, but not in patch releases label Sep 7, 2024
@MEPalma MEPalma added this to the 3.8 milestone Sep 7, 2024
@MEPalma MEPalma self-assigned this Sep 7, 2024
Copy link

github-actions bot commented Sep 7, 2024

LocalStack Community integration with Pro

    2 files  ±0      2 suites  ±0   1h 42m 51s ⏱️ - 1m 10s
3 526 tests ±0  3 133 ✅ ±0  393 💤 ±0  0 ❌ ±0 
3 528 runs  ±0  3 133 ✅ ±0  395 💤 ±0  0 ❌ ±0 

Results for commit 6d3d796. ± Comparison against base commit 5b44747.

♻️ This comment has been updated with latest results.

Copy link
Member

@dfangl dfangl left a comment

Choose a reason for hiding this comment

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

I have a few comments, especially for the lifecycle of the lambda environment, and the ability to stop the watching thread.

Comment on lines 129 to 134
while True:
time.sleep(1)
epoch_last_modified = self._config_file_epoch_last_modified_or_now()
if epoch_last_modified > epoch_last_loaded:
epoch_last_loaded = epoch_last_modified
self._load_lambda_debug_mode_config()
Copy link
Member

Choose a reason for hiding this comment

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

I am not a fan of this endless loops, even though the thread is a daemon thread. I would prefer an orderly shutdown of the thread, by using an stopping event as loop condition, and shutting it down on shutdown of the lambda service.

Copy link
Contributor Author

@MEPalma MEPalma Sep 18, 2024

Choose a reason for hiding this comment

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

changed to SERVICE_PLUGINS.is_running("lambda"), and also synched the start to the starts of the lambda provider

self._watch_thread = Thread(
target=self._watch_logic, args=(), daemon=True, name="LambdaDebugModeConfigWatch"
)
TMP_THREADS.append(self._watch_thread)
Copy link
Member

Choose a reason for hiding this comment

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

Appending it to TMP_THREADS accomplishes nothing here, as it is unstoppable.
Instead, as seen in the other comment, I would prefer shutting it down when the lambda service shuts down.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I removed this

@@ -75,7 +76,10 @@ def get_environment(

try:
yield execution_environment
execution_environment.release()
if is_lambda_debug_timeout_enabled_for(lambda_arn=function_version.qualified_arn):
self.stop_environment(execution_environment)
Copy link
Member

Choose a reason for hiding this comment

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

Is this something the users expect? This would effectively stop the possibility of leaving the debugger attached for multiple invocations, which should be theoretically possible currently, right?

Does it make sense, maybe, to only stop the environment if something actually changed for it?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Leaving the debugger attached is definitely the behaviour we envision. However, this is not an experience we can offer consistently at the moment. I cannot get this to work for python for example. Currently it is impossible to debug a lambda function more than once.
I am of the idea of providing this behaviour to unblock, whilst we continue the work on integrating more tightly the Lambda Debug Mode and the debuggers. What do you think?

Copy link
Member

Choose a reason for hiding this comment

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

That is fine by me, let's keep it as a temporary workaround!

@MEPalma MEPalma requested a review from dfangl October 1, 2024 18:05
@MEPalma MEPalma modified the milestones: 3.8, 4.0 Oct 1, 2024
Copy link
Member

@dfangl dfangl left a comment

Choose a reason for hiding this comment

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

Looks good now, just a minor comment regarding the shutdown, I am not too happy with the polling, let's just have an internal event which get's set on_before_stop.

@@ -75,7 +76,10 @@ def get_environment(

try:
yield execution_environment
execution_environment.release()
if is_lambda_debug_timeout_enabled_for(lambda_arn=function_version.qualified_arn):
self.stop_environment(execution_environment)
Copy link
Member

Choose a reason for hiding this comment

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

That is fine by me, let's keep it as a temporary workaround!

self._initialised_event.set()

# Monitor for file changes whilst the lambda service is running.
while SERVICE_PLUGINS.is_running("lambda"):
Copy link
Member

Choose a reason for hiding this comment

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

Not too happy with this here. could we have an internal shutdown / running state (for example a threading Event) which is checked here, and call stop() triggering the event in on_before_stop, just as we do the ensure_running now?

@MEPalma MEPalma merged commit 6e0af69 into master Nov 18, 2024
31 checks passed
@MEPalma MEPalma deleted the MEP-LambdaDevX-hot_reload_init branch November 18, 2024 16:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
semver: minor Non-breaking changes which can be included in minor releases, but not in patch releases
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants