Использование лямбда-памяти AWS с временными файлами в коде Python - PullRequest
0 голосов
/ 23 декабря 2018

Вносят ли данные, записанные во временные файлы, использование памяти в лямбда-AWS?В лямбда-функции я передаю файл во временный файл.В лямбда-логах я вижу, что максимальный объем используемой памяти больше, чем файл, который был загружен.Странно, что если лямбда вызывается несколько раз в быстрой последовательности, то вызовы, которые загружали меньшие файлы, все же сообщают о максимальном объеме памяти, использованном из вызова, который загружал больший файл.У меня установлен предел параллелизма, равный 2.

Когда я запускаю код локально, использование памяти, как и ожидалось, составляет около 20 МБ.На лямбде это 180 МБ, что примерно соответствует размеру файла, который транслируется.Код просто использует библиотеку запросов Python для потоковой загрузки файла, shutil.copyfileobj () для записи в tempfile.TevenFile (), который затем передается в postgres «copy from stdin».

Это делает егопохоже, что хранилище / tmp учитывает использование памяти, но не нашло упоминания об этом.Единственное упоминание / tmp в лямбда-документации состоит в том, что существует ограничение в 512 МБ.

Пример кода:

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)

ОБНОВЛЕНИЕ: После изменения кода, чтобы использовать не временный файл, апросто передайте загрузку непосредственно в команду postgres copy, использование памяти было исправлено.Заставляет меня думать, что каталог / tmp вносит вклад в использование памяти в журнале.

Ответы [ 2 ]

0 голосов
/ 23 декабря 2018

Обновление

Примечание. Чтобы ответить на этот вопрос, я использовал Lambdash , хотя мне пришлось изменить лямбда-версию, которая используется для узла 8.10.Lambdash - это простая небольшая библиотека, которую вы можете использовать для запуска команд оболочки на лямбде с локального терминала.

Каталог / tmp на AWS Lambdas монтируется как устройство * loop .Это можно проверить, выполнив (следуя инструкциям по настройке 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

Согласно 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

Имейте в виду, что каждый раз, когда я запускал его, лямбда выполнялась.Ниже приведен вывод из журналов 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 мс Продолжительность счета: 100 мс Объем памяти: 1536 МБ Макс. Используемая память: 30 МБ

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-14a611e9-9b0b-edfabd15c79f Продолжительность: 1396,29 мс Расчетная продолжительность: 1400 мс Размер памяти: 1536 МБ Макс. Используемая память: 430 МБ

07: 06: 36 START RequestId: 44a03f03-14a6-11e9-83cf-f375e336ed87 Версия:$ ПОСЛЕДНЕЕ 07:06:36 КОНЕЦ RequestId: 44a03f03-14a6-11e9-83cf-f375e336ed87 07:06:36 ОТЧЕТ RequestId: 44a03f03-14a6-11e9-83cf-f375e336ed87 Продолжительность: 3,69 мс Объявленная продолжительность: 100 мс Размер памяти: 1536 МБМакс. Используемая память: 431 МБ

07: 06: 38 START RequestId: 4606381a-14a6-11e9-a32d-2956620824ab Версия: $ ПОСЛЕДНЯЯ 07:06:38 КОНЕЦ RequestId: 4606381a-14a6-11e9-a32d-2956620824ab 07:06:38 ОТЧЕТ ЗАПИСИ: 4606381a-14a6-11e5-a32832-a3232Продолжительность: 3,63 мс. Объявленная длительность: 100 мс. Объем памяти: 1536 МБ. Макс. Используемая память: 431 МБ

Что произошло и что это значит?

Лямбда была выполнена 4 раза.При первом исполнении я отображал навесные устройства.При втором выполнении я заполнил файл в каталоге /tmp, используя 401 МБ из 500 МБ.В последующих исполнениях я перечислял подключенные устройства, отображая их доступное пространство.

Использование памяти при первом выполнении составило 30 МБ.Использование памяти для последующих выполнений находилось в диапазоне 400 МБ.

Это подтверждает, что использование /tmp действительно способствует использованию памяти.

Оригинальный ответ

Я предполагаю, что то, что вы наблюдаете, это python или сам лямбда-контейнер, буферизующий файл в памяти во время операций записи.

В соответствии с https://docs.python.org/3/library/functions.html#open,

буферизация является необязательным целым числом, используемым для установки политики буферизации.Пропустите 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), чтобы явно установить максимальный объем памяти, который будет использоваться при записи данных в файл.

0 голосов
/ 23 декабря 2018

Использование /tmp не учитывается при использовании памяти.Единственный случай, когда это может быть коррелировано, это когда вы читаете содержимое файла в память.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...