Падение Docker контейнера при выгрузке большого pandas.Dataframe в Excel

Дисклеймер: Я не являюсь опытным разработчиком. Мой проект - веб-сервис для рассылки отчётов. Использую Django, DRF, SQLAlchemy, pandas, openpyxl

Суть проекта:

  1. подключиться к БД
  2. сделать запрос
  3. получить результат в виде pandas.DataFrame (разделен на чанки)
  4. сохранить данные в xlsx/csv файл в сетевую папку

Проблема: при получении отчета большого объема (300'000+ строк результата) контейнер с сервисом падает. Всё что остаётся - это любоваться строкой "Watching for file changes with StatReloader" в логах.

Я выяснил, что проблемных моментов 2:

  1. формирование датафрейма из выгрузки
  2. запись этого датафрейма в файл

Добавил параметр chunksize в функцию pandas.read_sql_query(). Кажется, это решило проблему (1). Со второй - всё сложнее.

Сейчас пытаюсь делать так:

  1. Получаю список датафреймов
def get_chunks(url_object: URL, query: text) -> List[DataFrame]:
    """Получить результат запроса к базе в виде объекта Iterator[DataFrame]."""

    engine = create_engine(url_object)
    try:
        chunks_array = []
        rows_count = 0
        chunksize = 50000
        with engine.connect() as cnxn:
            for df in read_sql_query(query, cnxn, chunksize=chunksize):
                chunks_array.append(df)
                rows_count += chunksize
    except ...:
        ...
    return chunks_array, rows_count
def update_spreadsheet(path: str, df, starcol: int = 1, startrow: int = 1, sheet_name: str = "Sheet1"):
    """
    Произвести дозапись данных в Excel-файл с сохранением форматирования.

    :param path: путь до файла Excel
    :param df: датафрейм Pandas для записи
    :param starcol: стартовая колонка в таблице листа Excel, куда будут писаться данные
    :param startrow: стартовая строка в таблице листа Excel, куда будут писаться данные
    :param sheet_name: ммя листа в таблице Excel, куда будут писаться данные
    :return:
    """

    wb = ox.load_workbook(path)
    for row in range(0, len(df)):
        for col in range(0, len(df.iloc[row])):
            wb[sheet_name].cell(startrow + row, starcol + col).value = df.iloc[row][col]
    wb.save(path)


def make_spreadsheet(chunks: List[DataFrame], path: str) -> None:
    """
    Создать Excel файл.

    :param chunks: список датафремов
    :param path: абсолютный путь будущего файла
    :return:
    """

    first_empty_line = 1
    with ExcelWriter(path, engine="openpyxl", mode='w') as writer:
        df = chunks.pop(0)
        first_empty_line += len(df) + 1
        df.to_excel(writer, index=False, engine='openpyxl')

    while chunks:
        df = chunks.pop(0)
        update_spreadsheet(path, df, starcol=1, startrow=first_empty_line)
        first_empty_line += len(df)
        sleep(1)  # этот слип уже от безысходности добавил

По логам вижу, что происходит запись одного датайрема в файл, потом всё падает.

Основной вопрос: Как сделать процесс записи в файл менее затратным в плане оперативной памяти, чтобы контейнер не падал даже при больших выгрузках? Что можно сделать, чтобы файл большого объема мог формироваться без проблем?


Ответы (0 шт):