Lambdas stop invoking after a period of time - aws-lambda

Here's my setup:
A Python 3.6 lambda function, which I want to keep pre-warmed at a certain concurrency level (say, 10). The lambda's initialization is painful enough that I don't want to inflict this cost on visitors at random. I call these lambdas "workers"
A Node lambda function which runs every 5 minutes to try to pre-warm 10 instances. It uses the Event invocation type for 9 of them, and RequestResponse for 1. There's only either one or zero of this lambda running at any one time. I call this a "warmer".
I followed the guidelines at [https://www.jeremydaly.com/lambda-warmer-optimize-aws-lambda-function-cold-starts/], namely:
Don’t ping more often than every 5 minutes
Invoke the function directly (i.e. don’t use API Gateway to invoke it)
Pass in a test payload that can be identified as such
Create handler logic that replies accordingly without running the whole function
Here's a problem: this works great for several minutes. Then, as I watch the logs, I start to get timeouts from my worker lambda invocations. The timeouts quickly take over all the invocations that the warmer is trying to launch.
Now, no worker lambdas are prewarmed any more. But the warmer keeps on trying, on a Cloudwatch event cron schedule, suffering 100% timeouts. Finally, Lambda stops trying to launch my worker lambdas at all. It feels like some aspect of Lambda's getting its state scrambled. The only way to recover is to re-deploy the lambda. That buys me another hour with pre-warmed lambdas working.
Questions:
How do I get visibility into why my worker lambdas start timing out, and then become completely non-responsive?
What is the definition of a "Concurrent Execution"? On the main Lambda dashboard it shows me this chart of them. Yet, it seems to have more than twice as many Concurrent Executions as I'm requesting.
Here's the warmup lambda code (Node):
// warmer
"use strict";
/** Generated by Serverless WarmUP Plugin at ${new Date().toISOString()} */
const aws = require("aws-sdk");
aws.config.region = "${this.options.region}";
const lambda = new aws.Lambda({httpOptions: {timeout: 60000}});
const functionNames = ${JSON.stringify(functionNames)};
const delay = ms => new Promise(res => setTimeout(res, ms))
const concurrency = 10;
module.exports.warmUp = async (event, context, callback) => {
console.log("Warm Up Start");
const invokes = await Promise.all(functionNames.map(async (functionName) => {
let invocations = [];
try {
for(let i=1;i <= concurrency;i++){
let params = {
FunctionName: functionName,
InvocationType: (i===concurrency)?'RequestResponse': 'Event',
LogType: 'None',
Qualifier: process.env.SERVERLESS_ALIAS || "$LATEST",
Payload: JSON.stringify({
source: 'serverless-plugin-warmup',
'__WARMER_INVOCATION__': i,
'__WARMER_CONCURRENCY__': concurrency,
'__WARMER_REQUESTED__': new Date().toISOString(),
})
};
invocations.push(lambda.invoke(params).promise())
}
return await delay(75).then(Promise.all(invocations.map(p => p.catch(e => e)))
.then(results => console.log('results', results))
.catch(e => {
console.log(e);
return e;
}
))
} catch (e) {
console.log(\`Warm Up Invoke Error: \${functionName}\`, e);
return false;
}
}));
console.log(\`Warm Up Finished\`);
}
And here's the worker lambda (Python):
source = event.get('source')
if source == 'serverless-plugin-warmup':
time.sleep(0.05)
print(event)
return lambda_gateway_response(200, {"status": "lambda warmup"})

It was the warmer (Node) lambda going haywire, even though all the logs pointed at the worker (Python) lambdas. After setting context.callbackWaitsForEmptyEventLoop = false, the problem disappeared.

Related

Expecting a Promise *not* to complete, in Jest

I have the following need to test whether something does not happen.
While testing something like that may be worth a discussion (how long wait is long enough?), I hope there would exist a better way in Jest to integrate with test timeouts. So far, I haven't found one, but let's begin with the test.
test ('User information is not distributed to a project where the user is not a member', async () => {
// Write in 'userInfo' -> should NOT turn up in project 1.
//
await collection("userInfo").doc("xyz").set({ displayName: "blah", photoURL: "https://no-such.png" });
// (firebase-jest-testing 0.0.3-beta.3)
await expect( eventually("projects/1/userInfo/xyz", o => !!o, 800 /*ms*/) ).resolves.toBeUndefined();
// ideally:
//await expect(prom).not.toComplete; // ..but with cancelling such a promise
}, 9999 /*ms*/ );
The eventually returns a Promise and I'd like to check that:
within the test's normal timeout...
such a Promise does not complete (resolve or reject)
Jest provides .resolves and .rejects but nothing that would combine the two.
Can I create the anticipated .not.toComplete using some Jest extension mechanism?
Can I create a "run just before the test would time out" (with ability to make the test pass or fail) trigger?
I think the 2. suggestion might turn handy, and can create a feature request for such, but let's see what comments this gets..
Edit: There's a further complexity in that JS Promises cannot be cancelled from outside (but they can time out, from within).
I eventually solved this with a custom matcher:
/*
* test-fns/matchers/timesOut.js
*
* Usage:
* <<
* expect(prom).timesOut(500);
* <<
*/
import { expect } from '#jest/globals'
expect.extend({
async timesOut(prom, ms) { // (Promise of any, number) => { message: () => string, pass: boolean }
// Wait for either 'prom' to complete, or a timeout.
//
const [resolved,error] = await Promise.race([ prom, timeoutMs(ms) ])
.then(x => [x])
.catch(err => [undefined,err] );
const pass = (resolved === TIMED_OUT);
return pass ? {
message: () => `expected not to time out in ${ms}ms`,
pass: true
} : {
message: () => `expected to time out in ${ms}ms, but ${ error ? `rejected with ${error}`:`resolved with ${resolved}` }`,
pass: false
}
}
})
const timeoutMs = (ms) => new Promise((resolve) => { setTimeout(resolve, ms); })
.then( _ => TIMED_OUT);
const TIMED_OUT = Symbol()
source
The good side is, this can be added to any Jest project.
The down side is, one needs to separately mention the delay (and guarantee Jest's time out does not happen before).
Makes the question's code become:
await expect( eventually("projects/1/userInfo/xyz") ).timesOut(300)
Note for Firebase users:
Jest does not exit to OS level if Firestore JS SDK client listeners are still active. You can prevent it by unsubscribing to them in afterAll - but this means keeping track of which listeners are alive and which not. The firebase-jest-testing library does this for you, under the hood. Also, this will eventually ;) get fixed by Firebase.

Is it safe to call an async function during lambda nodejs init?

I would like to know whether it is safe to make HTTP(S) requests during the Init phase of a NodeJS Lambda function. In particular, I would like to make calls to AWS SSM GetParameter using #aws-sdk/client-ssm or AWS KMS Decrypt using #aws-sdk/client-kms to load secrets that will be used within the handler.
I have found one example online of someone creating a Promise outside of the handler and then awaiting it within the handler (Async Initialisation of a Lambda Handler), but I haven’t seen this approach endorsed in the official Lambda sample applications. None of the official examples do any work outside of the handler.
According to AWS Lambda execution environment: Lambda execution environment lifecycle, “Lambda freezes the execution environment when the runtime and each extension have completed and there are no pending events.” AWS Lambda Runtime API: Next invocation elaborates on the http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/next endpoint, “Do not set a timeout on the GET call. Between when Lambda bootstraps the runtime and when the runtime has an event to return, the runtime process may be frozen for several seconds.” I take this to mean that Lambda will signal the process with SIGSTOP at the time of the next call if Provisioned Concurrency is enabled as well as between requests.
In addition, when I look at lambcli’s lambda sources (docker run -it --rm lambci/lambda:build-nodejs12.x cat /var/runtime/Runtime.js), I see that scheduleIteration calls setImmediate(() => this.handleOnce()…) which calls this.client.nextInvocation so I don’t see a way to delay the nextInvocation call.
Questions:
In the Lambda nodejs runtime, is it possible to perform a HTTP request and await its response entirely within the Function init phase?
If you make a request outside of the handler, will the server time out the connection, resulting in Connection Closed errors when the handler awaits the response?
Is there a better recommended way to perform one-time initialization of secrets?
While I'm not able to fully answer all of your questions, I found this blog post that describes a possible solution: https://barker.tech/aws-lambda-nodejs-async-init-88b5c24af567
So in the end, the answers would be:
Yes, it is possible - see linked blog post.
Yes, it will time out. At least, this is what I experienced an issue when I tried to establish a MongoDB-connection outside the function handler with Provisioned Concurrency configured.
I can't really help you with that one...
AWS recently added top level await support in node14 and newer lambdas: https://aws.amazon.com/blogs/compute/using-node-js-es-modules-and-top-level-await-in-aws-lambda/ . Using this you can simply make the init phase wait by using top level await like so:
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
console.log("start init");
await sleep(1000);
console.log("end init");
export const handler = async (event) => {
return {
statusCode: 200,
body: JSON.stringify('Hello from Lambda!'),
};
};
This works great if you are using ES modules. If for some reason you are stuck using commonjs (e.g. because your tooling like jest or ts-node doesn't yet fully support ES modules) then you can make your commonjs module look like an es module by making it export a Promise that waits on your initialization rather than exporting an object. Like so:
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
const main = async () => {
console.log("start init");
await sleep(1000);
console.log("end init");
const handler = async (event) => {
return {
statusCode: 200,
body: JSON.stringify('Hello from Lambda!'),
};
};
return { handler };
};
# note we aren't exporting main here, but rather the result
# of calling main() which is a promise resolving to {handler}:
module.exports = main();

AWS Lambda call Lambda

I'm trying to call one Lambda function from another one that I have. I set up my permissions so that is not problem.
My problem is that the function doesn't wait for the Invoke function to complete and return NULL all the time.
Here is the code I'm using:
const AWS = require('aws-sdk');
exports.handler = async (event, context, callback) => {
var lambda = new AWS.Lambda({region: 'us-east-1', apiVersion: '2015-03-31'});
var params = {
FunctionName: 'testFunction',
InvocationType: 'RequestResponse'
}
lambda.invoke(params, function(err, data){
console.log(err);
console.log('here');
}).promise().then(data=> { callback(null, {message:'done'}); });
};
The {message:'done'} its never shown. I was recommended to use invokeAsync but that function is deprecated by AWS.
I know the problem is that the function is running lambda.invoke as synchronously because if I add callback(null, {message:'done'}); outside of the lambda.invoke function then I can see the console.logs working.
Any help?
TL;DR - Remove "async" in line 3, and it should work.
Your issue seems to be caused by the async keyword here. I have recreated this and deployed it to Lambda to run on Node v8.10 (but pointing it to invoke one of my own lambda functions of course).
Why are you using "async" here anyway? The async keyword declaration defines an asynchronous function and returns an AsyncFunction object. AWS Lambda is expected a function, not an AsyncFunction, and your "null" result is probably just Lambda immediately giving up because it can't find a regular function. Also, async is almost exclusively used with await (at least is was in 99% of the cases I've seen), and since your code isn't using await at all I don't see any reason to use async either.

Firebase Functions take 1 second to start any function

I have a Firebase function. It makes 3 simple calls to Firebase Database.
I put some logs to profile a little, ran function 5 times to ensure it is not a cold start and here is what I get on 6th run:
8:10:34.133 am - Function execution started
8:10:34.133 am - Billing account not configured.....
8:10:35.284 am - MyFunction start and make 3 database calls
8:10:35.456 am - MyFunction database results obtained
8:10:35.461 am - MyFunction execution finished
So, it takes just 250ms to make database calls, but
It takes almost 1 second from the alleged function start to the execution of the first line.
My question is – is it really the case, which would make Firebase Functions unusable for a serverless API, or I am doing something wrong?
Function is an https trigger. Written with Express. Only CORS is applied.
Setup for functions:
const unsecure = express();
unsecure.get("/myFunc", require("./core/myFunc.f.js"))
MyFunc.f.js:
const functions = require("firebase-functions");
const admin = require("firebase-admin");
try {
admin.initializeApp(
Object.assign({}, functions.config().firebase, {
credential: admin.credential.cert(
),
storageBucket: ""
})
);
} catch (e) {
//can be initialized only once
}
const database = admin.database()
module.exports = (request, response) => {
console.log("/myFunc started and calling database")
Promise.all([
database.ref("database_node").child("default").once("value"),
database.ref("database_node").child("no_code").once("value"),
database.ref("database_node").child("test_code").once("value")
]).then(values => {
console.log("/myFunc got database results: " + JSON.stringify(values))
response.status(200).send({})
})
}

How fast / efficient is Bluebird's timeout?

The following example times out in most cases (outputs timed out):
Promise = require('bluebird');
new Promise(resolve => {
setTimeout(resolve, 1000);
})
.timeout(1001)
.then(() => {
console.log('finished');
})
.catch(error => {
if (error instanceof Promise.TimeoutError) {
console.log('timed out');
} else {
console.log('other error');
}
});
Does this mean that the Bluebird's promise overhead takes longer than 1ms?
I see it often time out even if I use .timeout(1002).
The main reason for asking - I'm trying to figure what the safe threshold is, which gets more important with smaller timeouts.
Using Bluebird 3.5.0, under Node.js 8.1.2
I have traced your bug in Bluebird's code. Consider this:
const p = new Promise(resolve => setTimeout(resolve, 1000));
const q = p.timeout(1001); // Bluebird spawns setTimeout(fn, 1001) deep inside
That looks rather innocent, yeah? Though, not in this case. Internally, Bluebird implemented it something like (not actually valid JS; timeout clearing logic is omitted):
Promise.prototype.timeout = function(ms) {
const original = this;
let result = original.then(); // Looks like noop
setTimeout(() => {
if result.isPending() {
make result rejected with TimeoutError; // Pseudocode
}
}, ms);
return result;
}
Bug was presence of line ret.isPending(). It resulted in brief time when original.isPending() === false and ret.isPending() === true because "resolved" status didn't propagate yet from original to children. Your code hit that extremely short period and BOOM, you had race condition.
I think what's going on here is that there's a race between the time the rest of the promise chain takes and the timer from the .timeout(). Since they are both so close in timing, sometimes one wins and sometimes the other wins - they are racy. When I run this code that logs the sequence of events, I get different ordering on different runs. The exact output order is unpredictable (e.g. racy).
const Promise = require('bluebird');
let buffer = [];
function log(x) {
buffer.push(x);
}
new Promise(resolve => {
setTimeout(() => {
log("hit my timeout");
resolve();
}, 1000);
}).timeout(1001).then(() => {
log('finished');
}).catch(error => {
if (error instanceof Promise.TimeoutError) {
log('timed out');
} else {
log('other error');
}
});
setTimeout(() => {
console.log(buffer.join("\n"));
}, 2000);
Sometimes this outputs:
hit my timeout
finished
And, sometimes it outputs:
hit my timeout
timed out
As has been mentioned in the comments, if .then() was always executed via microtask (which should precede any macrotasks), then one would think that the .then() would precede the setTimeout() from the .timeout(), but things are apparently not that simple.
Since the details of promise .then() scheduling is not mandated by specification (only that the stack is clear of application code) a code design should not assume a specific scheduling algorithm. Thus, a timeout this close to the execution of the async operation it is following can be racy and thus unpredictable.
If you could explain exactly what problem you're trying to solve, we could probably offer more concrete advice about what to do. No timers in Javascript are precise to the ms because JS is single threaded and all timer events have to go through the event queue and they only call their callbacks when their event gets serviced (not exactly when the timer fired). That said, timer events will always be served in order so a setTimeout(..., 1000) will always come before setTimeout(..., 1001), even though there may not be exactly 1ms delta between the executing of the two callbacks.

Resources