AWS lambda内存使用与python代码中的临时文件

kyl*_*215 6 python amazon-web-services aws-lambda

写入临时文件的数据是否会影响AWS lambda中的内存使用?在lambda函数中,我将文件流式传输到临时文件.在lambda日志中,我看到使用的最大内存大于下载的文件.奇怪的是,如果lambda被快速连续多次调用,则下载较小文件的调用仍会报告从下载较大文件的调用中使用的最大内存.我将并发限制设置为2.

当我在本地运行代码时,我的内存使用量大约为20MB.在lambda上它是180MB,大约是流式文件的大小.代码只是使用python请求库来传输文件下载,shutil.copyfileobj()写入tempfile.TemporaryFile(),然后通过管道传输给postgres"从stdin复制".

这使得/ tmp存储看起来像内存使用量,但没有发现任何提及.lambda文档中唯一提到的/ tmp是512mb的限制.

示例代码:

import sys
import json
import os
import io
import re
import traceback
import shutil
import tempfile

import boto3
import psycopg2
import requests


def handler(event, context):
    try:
        import_data(event["report_id"])
    except Exception as e:
        notify_failed(e, event)
        raise

def import_data(report_id):
    token = get_token()
    conn = psycopg2.connect(POSTGRES_DSN, connect_timeout=30)
    cur = conn.cursor()

    metadata = load_metadata(report_id, token)
    table = ensure_table(metadata, cur, REPLACE_TABLE)
    conn.commit()
    print(f"report {report_id}: downloading")
    with download_report(report_id, token) as f:
        print(f"report {report_id}: importing data")
        with conn, cur:
            cur.copy_expert(f"COPY {table} FROM STDIN WITH CSV HEADER", f)
        print(f"report {report_id}: data import complete")
    conn.close()


def download_report(report_id, token):
    url = f"https://some_url"
    params = {"includeHeader": True}
    headers = {"authorization": f"Bearer {token['access_token']}"}

    with requests.get(url, params=params, headers=headers, stream=True) as r:
        r.raise_for_status()
        tmp = tempfile.TemporaryFile()
        print("streaming contents to temporary file")
        shutil.copyfileobj(r.raw, tmp)
        tmp.seek(0)
        return tmp


if __name__ == "__main__":
    if len(sys.argv) > 1:
        handler({"report_id": sys.argv[1]}, None)
Run Code Online (Sandbox Code Playgroud)

更新:将代码更改为不使用临时文件但只是直接将下载流式传输到postgres copy命令后,内存使用情况得到修复.让我觉得/ tmp目录有助于记录内存使用情况.

Pet*_*rby 6

更新

注意:要回答这个问题,我使用了Lambdash,尽管我必须修改用于node8.10的lambda版本.Lambdash是一个简单的小库,可用于从本地终端在lambda上运行shell命令.

AWS Lambdas上的/ tmp目录作为循环设备安装.您可以通过(在执行lambdash的设置说明之后)运行以下命令来验证这一点:

./lambdash df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/xvda1       30G  4.0G   26G  14% /
/dev/loop0      526M  872K  514M   1% /tmp
/dev/loop1      6.5M  6.5M     0 100% /var/task
Run Code Online (Sandbox Code Playgroud)

根据https://unix.stackexchange.com/questions/278647/overhead-of-using-loop-mounted-images-under-linux,

通过循环设备访问的数据必须通过两个文件系统层,每个层都执行自己的缓存,因此数据最终缓存两次,浪费了大量内存(臭名昭着的"双缓存"问题)

但是,我的猜测/tmp实际上是保留在内存中.为了测试这个,我运行了以下命令:

./lambdash df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/xvda1       30G  4.0G   26G  14% /
/dev/loop0      526M  1.9M  513M   1% /tmp
/dev/loop1      6.5M  6.5M     0 100% /var/task

./lambdash dd if=/dev/zero of=/tmp/file.txt count=409600 bs=1024
409600+0 records in
409600+0 records out
419430400 bytes (419 MB) copied, 1.39277 s, 301 MB/s

./lambdash df -h
 Filesystem      Size  Used Avail Use% Mounted on
 /dev/xvda1       30G  4.8G   25G  17% /
 /dev/loop2      526M  401M  114M  78% /tmp
 /dev/loop3      6.5M  6.5M     0 100% /var/task

./lambdash df -h
 Filesystem      Size  Used Avail Use% Mounted on
 /dev/xvda1       30G  4.8G   25G  17% /
 /dev/loop2      526M  401M  114M  78% /tmp
 /dev/loop3      6.5M  6.5M     0 100% /var/task
Run Code Online (Sandbox Code Playgroud)

请记住,每次运行它时,lambda都会被执行.以下是Lambda Cloudwatch日志的输出:

07:06:30 START RequestId:4143f502-14a6-11e9-bce4-eff8b92bf218版本:$ LATEST 07:06:30 END RequestId:4143f502-14a6-11e9-bce4-eff8b92bf218 07:06:30报告RequestId:4143f502-14a6- 11e9-bce4-eff8b92bf218持续时间:3.60 ms结算时长:100 ms内存大小:1536 MB最大使用内存:30 MB

07:06:32 START RequestId:429eca30-14a6-11e9-9b0b-edfabd15c79f版本:$ LATEST 07:06:34 END RequestId:429eca30-14a6-11e9-9b0b-edfabd15c79f 07:06:34报告RequestId:429eca30-14a6- 11e9-9b0b-edfabd15c79f持续时间:1396.29 ms结算时长:1400 ms内存大小:1536 MB最大使用内存:430 MB

07:06:36 START RequestId:44a03f03-14a6-11e9-83cf-f375e336ed87版本:$ LATEST 07:06:36 END RequestId:44a03f03-14a6-11e9-83cf-f375e336ed87 07:06:36报告RequestId:44a03f03-14a6- 11e9-83cf-f375e336ed87持续时间:3.69毫秒收费持续时间:100毫秒内存大小:1536 MB最大使用内存:431 MB

07:06:38 START RequestId:4606381a-14a6-11e9-a32d-2956620824ab版本:$ LATEST 07:06:38 END RequestId:4606381a-14a6-11e9-a32d-2956620824ab 07:06:38报告RequestId:4606381a-14a6- 11e9-a32d-2956620824ab持续时间:3.63 ms结算时长:100 ms内存大小:1536 MB最大使用内存:431 MB

发生了什么事,这是什么意思?

lambda执行了4次.在第一次执行时,我显示了已安装的设备.在第二次执行时,我在/tmp目录中填充了一个文件,使用了允许的500Mb的401Mb.在随后的执行中,我列出了已安装的设备,显示了它们的可用空间.

第一次执行时的内存利用率为30Mb.后续执行的内存利用率在400Mb范围内.

这证实了/tmp利用确实有助于内存利用率.

原始答案

我的猜测是你正在观察的是python,或lambda容器本身,在写操作期间缓冲内存中的文件.

根据https://docs.python.org/3/library/functions.html#open,

buffering是一个可选的整数,用于设置缓冲策略.传递0以切换缓冲关闭(仅允许在二进制模式下),1选择行缓冲(仅在文本模式下可用),并且整数> 1以指示固定大小的块缓冲区的大小(以字节为单位).如果没有给出缓冲参数,则默认缓冲策略的工作方式如下:

二进制文件以固定大小的块缓冲; 使用启发式方法选择缓冲区的大小,尝试确定底层设备的"块大小"并回退到io.DEFAULT_BUFFER_SIZE.在许多系统上,缓冲区通常为4096或8192字节长."交互式"文本文件(isatty()返回True的文件)使用行缓冲.其他文本文件使用上述策略用于二进制文件.

tempfile.TemporaryFile()函数有一个关键字参数,buffering它基本上直接传递open给上述调用.

所以我的猜测是该tempfile.TemporaryFile()函数使用默认open()函数的缓冲设置.您可以尝试tempfile.TemporaryFile(buffering=0)禁用缓冲,或者tempfile.TemporaryFile(buffering=512)显式设置将数据写入文件时将使用的最大内存量.