Skip to content

Instantly share code, notes, and snippets.

@IronSavior
Last active February 19, 2022 10:18
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save IronSavior/fe644f11a3fe509338c02443a8bfa369 to your computer and use it in GitHub Desktop.
Save IronSavior/fe644f11a3fe509338c02443a8bfa369 to your computer and use it in GitHub Desktop.
Require AWS Lambda handler to invoke callback before exit (prevent Node exit until handler invokes callback)
const lambda_handler = require('lambda-handler');
exports.handler = lambda_handler(( event, ctx, done ) => {
// This will log a handled error to CloudWatch and return the error to AWS Lambda
throw 'BOOM!';
});
const lambda_handler = require('lambda-handler');
exports.handler = lambda_handler(( event, ctx, done ) => {
// This invocation will return "Complete!" to AWS Lambda as the function's output.
done(null, 'Complete!');
});
const lambda_handler = require('lambda-handler');
exports.handler = lambda_handler(( event, ctx, done ) => {
// This handler doesn't change the event loop. Normally Lambda would immediately exit
// with null output, but wrapping it in lambda_handler() prevents that. This handler will
// eventually time out and be killed by AWS Lambda.
});
"use strict";
const util = require('util');
// Ensure logged inputs, outputs, and context are complete
const inspect = (obj, opts = {}) => util.inspect(obj, Object.assign({depth: 10}, opts));
const info = console.info.bind(console);
const error = console.error.bind(console);
// Wrap the given handler with standard logging, error handling, and prevent Node exit until callback is invoked.
// @returns {Function} Augmented handler function
module.exports = handler => (...args) => invoke_handler(handler, ...args);
// Invoke AWS Lambda handler and ensure that Node will not exit until the handler explicitly invokes its callback
// @param handler {Function} Handler function
// @param event {Object} AWS Lambda input event (forwarded to handler)
// @param context {Object} AWS Lambda context (forwarded to handler)
// @param done {Function} AWS Lambda completion callback
// @returns value returned by handler
function invoke_handler( handler, event, context, done ){
info('Event:', inspect(event));
let finished = false;
prevent_exit_until_finished();
try{
const rv = handler(event, context, finish);
if( rv && rv.then !== undefined && typeof rv.then === 'function' ){
return rv.then(output => finish(null, output), e => finish(e));
}
return rv;
}
catch(e){
finish(e);
}
// Ensures the Node event loop never ends until handler explicitly signals completion via callback
// @param interval_ms {Integer} Timeout interval in milliseconds between checks
// @returns {void}
function prevent_exit_until_finished( interval_ms = 25 ){
if( finished ) return;
setTimeout(prevent_exit_until_finished, interval_ms);
}
// Invoked by handler to signal completion
// @param err {Object} Handler error (null when successfully completed)
// @param output {Object} Output returned to AWS Lambda
// @returns {void}
function finish( err, output ){
if( finished ) return;
finished = true;
if( err ) error('Function handler error:', err);
else info('Function handler output:', inspect(output));
done(...arguments);
}
}
@anthonyroach
Copy link

We came across this gist while trying to find information about lambda seemingly exiting early and returning null data to the caller. It looks like this gist is designed to work around the exact issue we are seeing. Is this a known issue with AWS lambda? Any context for this gist?

@IronSavior
Copy link
Author

@anthonyroach You described exactly the problem I was trying to solve. I had an AWS StepFunctions workflow that would sometimes have the entire state blown away because Lambda mistakenly exited and captured a null output. I could sometimes see the logged events from where an earlier execution of a function became frozen by the Lambda runtime and was later awakened. I never had that problem again since using this hack.

I'm guessing there must be a bug in the Lambda Node runtime that infrequently allows the process to become frozen even when you have callbacks registered. I only encountered this strange behavior when I had hundreds of concurrent Lambda executions.

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