Падение Docker контейнера при выгрузке большого pandas.Dataframe в Excel
Дисклеймер: Я не являюсь опытным разработчиком. Мой проект - веб-сервис для рассылки отчётов. Использую Django, DRF, SQLAlchemy, pandas, openpyxl
Суть проекта:
- подключиться к БД
- сделать запрос
- получить результат в виде pandas.DataFrame (разделен на чанки)
- сохранить данные в xlsx/csv файл в сетевую папку
Проблема: при получении отчета большого объема (300'000+ строк результата) контейнер с сервисом падает. Всё что остаётся - это любоваться строкой "Watching for file changes with StatReloader" в логах.
Я выяснил, что проблемных моментов 2:
- формирование датафрейма из выгрузки
- запись этого датафрейма в файл
Добавил параметр chunksize в функцию pandas.read_sql_query(). Кажется, это решило проблему (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) # этот слип уже от безысходности добавил
По логам вижу, что происходит запись одного датайрема в файл, потом всё падает.
Основной вопрос: Как сделать процесс записи в файл менее затратным в плане оперативной памяти, чтобы контейнер не падал даже при больших выгрузках? Что можно сделать, чтобы файл большого объема мог формироваться без проблем?