-
-
Notifications
You must be signed in to change notification settings - Fork 4.2k
WIP: Support "stay open" mode for Lambda invocations #4185
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
Conversation
Hey @jamietanna ... Managed to test this over the weekend... And it works! import boto3
class LambdaExecutorReuseContainers(LambdaExecutorContainers):
def prepare_execution(self, func_details, env_vars): #, event_body, events_file_path):
func_arn = func_details.arn()
lambda_cwd = func_details.cwd
runtime = func_details.runtime
# Choose a port for this invocation
with self.docker_container_lock:
env_vars['_LAMBDA_SERVER_PORT'] = str(self.next_port + self.port_offset)
self.next_port = (self.next_port + 1) % self.max_port
# create/verify the docker container is running.
LOG.debug('Priming docker container with runtime "%s" and arn "%s".', runtime, func_arn)
self.prime_docker_container(func_details, env_vars, lambda_cwd)
def _execute(self, func_arn, func_details, event, *args, **kwargs):
if not LAMBDA_CONCURRENCY_LOCK.get(func_arn):
concurrency_lock = threading.RLock()
LAMBDA_CONCURRENCY_LOCK[func_arn] = concurrency_lock
with LAMBDA_CONCURRENCY_LOCK[func_arn]:
lambda_cwd = func_details.cwd
runtime = func_details.runtime
handler = func_details.handler
environment = self._prepare_environment(func_details)
# prepare event body
if not event:
LOG.warning('Empty event body specified for invocation of Lambda "%s"' % func_arn)
event = {}
# just prep the container, no need for cmd
self.prepare_execution(func_details, environment)
func_arn = func_details and func_details.arn()
lambda_docker_hostname = self.get_docker_container_hostname(func_arn)
# invoke lambda directly using http
invocation_result = self.invoke_lambda(func_arn, lambda_docker_hostname, event)
log_formatted = invocation_result.log_output.strip()
LOG.debug('Lambda %s result / log output:\n%s\n> %s' % (func_arn, invocation_result.result.strip(), log_formatted))
# store log output
_store_logs(func_details, log_formatted)
return invocation_result.result
def invoke_lambda(self, function_name, lambda_docker_hostname, event_body):
full_url=f"http://{lambda_docker_hostname}:9001"
client = boto3.client(
service_name='lambda',
region_name=config.DEFAULT_REGION,
endpoint_url=full_url
)
jsonBody = json.dumps(json_safe(event_body))
response = client.invoke(
FunctionName=function_name,
InvocationType='RequestResponse',
Payload=jsonBody,
LogType='Tail'
)
logs=base64.b64decode(response["LogResult"]).decode('utf-8')
responseBody=response["Payload"].read().decode('utf-8')
return InvocationResult(responseBody, logs) Also, in the # All 'docker' cmd's are expensive, only do the work if in DEBUG
if logging.getLogger().getEffectiveLevel() == logging.DEBUG:
container_network = self.get_docker_container_network(func_arn)
LOG.debug('Using entrypoint "%s" for container "%s" on network "%s".' % (entry_point, container_name, container_network)) Really good work :) |
4fd09cb
to
efab935
Compare
Thanks for the help @chaz-doyle-cko - I'd not even considered I've just updated the code with latest on HEAD, and will try and have a bit more of a go at this the next few days. @whummer this is still very WIP, so not expecting a full code review, but I was wondering if you had any thoughts about whether we should remove the scheduled |
I am gonna give this a spin too. I have a nodejs use case for this. |
Thanks @wesselvdv - I think it may currently be broken locally so bear with 😅 |
efab935
to
1f39d06
Compare
@wesselvdv you should be good to give it a go now, apologies for that! |
@jamietanna Thanks! Building it locally now. I do see one caveat in your approach, and that is your assumption that the images used are always EDIT: I ran it locally with one minor change in the |
I guess it would need a minor check to make sure that the correct port forward is put on the docker run. (lambci vs amazon images) |
Maybe scratch my previous comment, I was under the impression it was the base amazon image. But localstack is using it's own fused one with lambci. I reverted my change, and it still works. |
Is it possible that errors are not passed along correctly when using this branch? |
I've tested this by creating a (broken) Lambda and it returns in the response:
|
Maybe it’s my own incompetence but when using the aws sdk in nodejs I am not getting it back as such. I have to parse the Payload to deduce if it was an error, whereas normal the FunctionError property would be set as ‘Error’ which is what would use to assume it error’ed. |
What version of the |
It might be because the boto3 direct invocation isn't returning the error correctly... If I get a chance today I'll test with a lambda that just errors out... |
I am only getting it back as an invocation error instead of a function error. I'll see if I can whip up an example project somewhere this week. |
Final implementation merged in #4914 - thanks again everyone for contributing!! 🎉 |
Note that this is very draft and Work-in-Progress.
I've only verified this with a Java 8 Lambda, and have not updated any of the local tests.
This does, however, allow for some early feedback on the approach (albeit not very clean right now).
Closes #4123