Does AWS Lambda charge for the time spent initializing code?

橙三吉。 提交于 2020-01-24 08:44:29

问题


If my Lambda function written in Python takes 1.8 seconds to initialize (during a cold start) and 400 ms to execute, am I charged for the 400 ms execution time or the entire 2.2 seconds of initialization + execution time?

From X-Ray, I see:

From CloudWatch logs, I see:

Duration: 404.42 ms Billed Duration: 500 ms Memory Size: 448 MB Max Memory Used: 113 MB

What I understand from this is that I was billed for 500ms of execution time, so does that mean code initialization (e.g. importing stuff) is free?


回答1:


So I decided to try and figure it out myself with a little experiment. I created a Lambda function using Python 2.7 with 128 MB of RAM, timeout of 15 seconds and active tracing enabled. I modified the sample code to add a 10 second sleep right after the import statement:

print "starting import"
import json
from time import sleep
sleep(10)
print "calling handler"

def lambda_handler(event, context):
    return {
        'statusCode': 200,
        'body': json.dumps('Hello from Lambda!')
    }

Since the Lambda started cold, I saw this in the X-ray output:

And I saw this in CloudWatch logs:

22:06:47 starting import
22:06:57 calling handler
22:06:58 START RequestId: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx Version: $LATEST
22:06:58 starting import
22:07:08 calling handler
22:07:08 END RequestId: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
22:07:08 REPORT RequestId: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx Duration: 10022.57 ms   Billed Duration: 10100 ms Memory Size: 128 MB   Max Memory Used: 19 MB

The function actually ran TWICE. After sleeping for 10 seconds the first time, it re-started when the handler method was called, essentially taking 20 seconds to finish execution but billing me for 10 seconds.

I ran it again, this time a warm-start and I got this:

X-ray output (warm start):

CloudWatch logs (warm start):

22:23:16 START RequestId: yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy Version: $LATEST
22:23:16 END RequestId: yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy
22:23:16 REPORT RequestId: yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy Duration: 6.97 ms   Billed Duration: 100 ms Memory Size: 128 MB Max Memory Used: 29 MB

Nothing suspicious there. I increased the function memory to 192 MB, saved it and reverted it back to 128 MB and saved it again to ensure that it'd start cold again and invoked it once again. The output of X-ray was the same as before but CloudWatch logs had something interesting:

22:30:13 starting import
22:30:24 START RequestId: zzzzzzzz-zzzz-zzzz-zzzz-zzzzzzzzzzzz Version: $LATEST
22:30:24 starting import
22:30:34 calling handler
22:30:34 END RequestId: zzzzzzzz-zzzz-zzzz-zzzz-zzzzzzzzzzzz
22:30:34 REPORT RequestId: zzzzzzzz-zzzz-zzzz-zzzz-zzzzzzzzzzzz Duration: 10010.85 ms   Billed Duration: 10100 ms Memory Size: 128 MB   Max Memory Used: 19 MB

It seems while my code was in the middle of sleeping for 10 seconds, Lambda cut it off and re-started it. The execution time was again 20 seconds but I was billed for 10 seconds. So I thought what if instead of 1 sleep statement, I add 15 one second sleeps?

Updated code:

print "starting import"
import json
from time import sleep
for i in range(1, 16):
    sleep(1)
    print "completed {}th sleep".format(i)

print "calling handler"
def lambda_handler(event, context):
    return {
        'statusCode': 200,
        'body': json.dumps('Hello from Lambda!')
    }

The function timed out!

X-ray output:

CloudWatch logs:

22:51:54 starting import
22:51:55 completed 1th sleep
22:51:56 completed 2th sleep
22:51:57 completed 3th sleep
22:51:58 completed 4th sleep
22:51:59 completed 5th sleep
22:52:00 completed 6th sleep
22:52:01 completed 7th sleep
22:52:02 completed 8th sleep
22:52:03 completed 9th sleep
22:52:04 START RequestId: 11111111-1111-1111-1111-111111111111 Version: $LATEST
22:52:04 starting import
22:52:05 completed 1th sleep
22:52:06 completed 2th sleep
22:52:07 completed 3th sleep
22:52:08 completed 4th sleep
22:52:09 completed 5th sleep
22:52:10 completed 6th sleep
22:52:11 completed 7th sleep
22:52:12 completed 8th sleep
22:52:13 completed 9th sleep
22:52:14 completed 10th sleep
22:52:15 completed 11th sleep
22:52:16 completed 12th sleep
22:52:17 completed 13th sleep
22:52:18 completed 14th sleep
22:52:19 END RequestId: 11111111-1111-1111-1111-111111111111
22:52:19 REPORT RequestId: 11111111-1111-1111-1111-111111111111 Duration: 15015.16 ms   Billed Duration: 15000 ms Memory Size: 192 MB   Max Memory Used: 19 MB
22:52:19
2019-03-29T22:52:19.621Z 11111111-1111-1111-1111-111111111111 Task timed out after 15.02 seconds
22:52:19 starting import
22:52:20 completed 1th sleep
22:52:21 completed 2th sleep
22:52:22 completed 3th sleep
22:52:23 completed 4th sleep
22:52:24 completed 5th sleep
22:52:25 completed 6th sleep
22:52:26 completed 7th sleep
22:52:27 completed 8th sleep
22:52:28 completed 9th sleep
22:52:29 completed 10th sleep

It actually executed for 25.8 seconds but then timed out and billed me for 15 seconds. The code that executes before the handler is called ran for about 9 seconds then Lambda cut it off and re-started the function but didn't finish and ultimately timed out after 25.8 seconds. If I increase the Lambda timeout to 16 seconds, it finished executing in 25.8 seconds (as shown in X-Ray) and billed me for 15100 ms.

So this leads me to believe that if the handler function isn't called within about 9-10 seconds after initialization, Lambda will restart the function. So what if the code initialization takes less than 10 seconds?

Updated code:

print "starting import"
import json
from time import sleep
for i in range(1, 10):
    sleep(1)
    print "completed {}th sleep".format(i)

print "calling handler"
def lambda_handler(event, context):
    return {
        'statusCode': 200,
        'body': json.dumps('Hello from Lambda!')
    }

I ran this function cold for about 10 times and my billed duration was always 100 ms. I even changed my lambda timeout to 1 second and it still finished executing successfully!

X-Ray output:

CloudWatch logs:

23:23:43 starting import
23:23:44 completed 1th sleep
23:23:45 completed 2th sleep
23:23:46 completed 3th sleep
23:23:47 completed 4th sleep
23:23:48 completed 5th sleep
23:23:49 completed 6th sleep
23:23:50 completed 7th sleep
23:23:51 completed 8th sleep
23:23:52 completed 9th sleep
23:23:52 calling handler
23:23:52 START RequestId: 22222222-2222-2222-2222-222222222222 Version: $LATEST
23:23:52 END RequestId: 22222222-2222-2222-2222-222222222222
23:23:52 REPORT RequestId: 22222222-2222-2222-2222-222222222222 Duration: 0.73 ms   Billed Duration: 100 ms Memory Size: 128 MB Max Memory Used: 44 MB

As Steve HOUEL rightfully pointed out, this leads me to believe that Lambda won't charge you for the time it takes to initialize your code (e.g. importing stuff) as long as it finishes in about 9 seconds. However, if it takes longer than that, Lambda re-starts your function and assuming you set a large enough timeout, function execution effectively takes 10 seconds + regular cold start execution time but you are still billed for just the cold start execution time without the added 10 seconds.




回答2:


It depends what you mean with initialisation time.

If you mean container startup, allocation, etc. you DO NOT pay for it.

If you mean code initialisation (requiring modules, connecting to DBs, etc), yes, you do pay for it.

I don't know about Python, but if you want to see it in action in NodeJS, import a module that has a blocking operation before exporting its functions.

For example, you can have this someModule.js file that contains the following code:

for (let i = 0; i < 10000000000; i++) {}
module.exports = {
    test: () => {}
}

The for loop is a blocking operation, therefore, module.exports will only be invoked once the loop is finished.

This means if you require('someModule) in your Handler, it will hang until someModule is done exporting everything.

const someModule = require('./someModule')

exports.handler = async event => {
    console.log(event)
}

You would then pay for the time taken to someModule to export its functions successfully.




回答3:


You will only pay for init if you spend more than 10s on it. In that case your init process will restart and you will start to pay for it.

But what you should know if that once your function has been warmed, you will not initialised it again (till around 45min of inactivity). You will then pay only the execution time.




回答4:


EDITED: Your experiment looks valid.

I recommend reading this excellent post which includes information about how billing works on AWS Lambda runtime.




回答5:


The things in Lambda you get for free are:

  • You get 1 Million requests per month.
  • 400,000 GB-seconds of compute time per month.

Duration is calculated from the time your code begins executing until it returns or otherwise terminates, rounded up to the nearest 100ms.

The price depends on the amount of memory you allocate to your function.

Lambda counts a request each time it starts executing in response to an event notification or invokes call, including test invokes from the console. Hence, you are charged for the total number of requests across all your functions.

Also, at the program startup some actions to be done such as importing libraries, setting up DB and initializing global variables and classes. You DO PAY for this part of the Lambda initialization.



来源:https://stackoverflow.com/questions/55412624/does-aws-lambda-charge-for-the-time-spent-initializing-code

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!