Django WeasyPrint эффективно использует память при больших наборах данных

Я использую WeasyPrint в Django для создания PDF-файла. Однако при обработке около 11 000 записей он потребляет все доступные ресурсы, выделенные модулю Kubernetes. В результате модуль перезапускается, и я так и не получаю сгенерированный PDF-файл по электронной почте.

Существуют ли:

  1. Есть ли какие-нибудь облегченные библиотеки PDF, которые могут более эффективно создавать PDF-файлы для тысяч записей?
  2. Есть ли какие-либо методы оптимизации в WeasyPrint (или в целом) для сокращения использования ресурсов и успешного создания PDF-файла?

Я столкнулся с такой же проблемой, когда WeasyPrint потреблял более 8 ГБ оперативной памяти для больших PDF-файлов. Вот что у меня сработало

Решение 1: Переключился на ReportLab (Это решило проблему)

Я заменил WeasyPrint на ReportLab, и объем используемой памяти сократился с 8 ГБ до менее чем 500 МБ:

python

from reportlab.platypus import SimpleDocTemplate, Table
import io

def generate_pdf(queryset):
    buffer = io.BytesIO()
    doc = SimpleDocTemplate(buffer)
    
    elements = []
    # This iterator prevents loading all records at once
    for batch in queryset.iterator(chunk_size=1000):
        data = [[record.field1, record.field2] for record in batch]
        elements.append(Table(data))
    
    doc.build(elements)
    return buffer.getvalue()

Сгенерировал PDF-файл объемом 11 000 записей за 45 секунд, используя всего 400 МБ оперативной памяти. Стиль более простой, но на самом деле он завершенный.

Решение 2: Обработка фрагментов (Работает, но медленнее)

Когда мне приходилось использовать WeasyPrint для создания сложных макетов, я обрабатывал их по частям:

python

from PyPDF2 import PdfMerger
import gc

def generate_large_pdf(queryset):
    merger = PdfMerger()
    
    # Process 500 records at a time
    for i in range(0, queryset.count(), 500):
        chunk = queryset[i:i+500]
        html = render_to_string('template.html', {'records': chunk})
        pdf = HTML(string=html).write_pdf()
        
        merger.append(io.BytesIO(pdf))
        
        # This is crucial - forces garbage collection
        del html, pdf
        gc.collect()
    
    output = io.BytesIO()
    merger.write(output)
    return output.getvalue()

Это позволило сохранить объем памяти менее 1 ГБ, но заняло 3-4 минуты для 11 000 записей.

Что не сработало:

  • Увеличение объема оперативной памяти модуля до 16 ГБ - просто отсрочило сбой

  • Использование флага --optimize-size в WeasyPrint - минимальное улучшение

  • Пытаюсь использовать wkhtmltopdf - те же проблемы с памятью

Моя рекомендация: Используйте ReportLab для PDF-файлов с большим объемом данных, используйте WeasyPrint только для сложных требований к дизайну с небольшими наборами данных (>1000 записей).

Вернуться на верх