fix(async): chiamate bloccanti in asyncio.to_thread (#36 punto 1)
kaleido (pio.to_image), WeasyPrint (write_pdf), pdfplumber (extract_text_from_pdf) e la scrittura del CSV di produzione giravano sincrone dentro handler async: un PDF pesante o un run grande fermava il worker per tutte le altre richieste. Ora avvolte in await asyncio.to_thread(...) in report_service.py, ai_service.py e production_export_service.py. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
"""Report generation service — PDF reports via Jinja2 + Plotly/Kaleido + WeasyPrint."""
|
||||
import asyncio
|
||||
import base64
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
@@ -204,6 +205,11 @@ def _generate_control_chart_svg(chart_data, subtask_info: dict | None) -> str:
|
||||
return svg_bytes.decode("utf-8")
|
||||
|
||||
|
||||
def _render_pdf(html_content: str) -> bytes:
|
||||
"""Blocking WeasyPrint render — always call via asyncio.to_thread."""
|
||||
return HTML(string=html_content).write_pdf()
|
||||
|
||||
|
||||
def _generate_histogram_svg(hist_data, subtask_info: dict | None) -> str:
|
||||
"""Generate histogram SVG using Plotly + Kaleido."""
|
||||
if not hist_data.bins:
|
||||
@@ -300,9 +306,13 @@ async def generate_spc_report(
|
||||
)
|
||||
histogram = compute_histogram(values)
|
||||
|
||||
# 3. Generate SVG charts
|
||||
control_chart_svg = _generate_control_chart_svg(control_chart, subtask_info)
|
||||
histogram_svg = _generate_histogram_svg(histogram, subtask_info)
|
||||
# 3. Generate SVG charts (kaleido shells out and blocks: off the event loop)
|
||||
control_chart_svg = await asyncio.to_thread(
|
||||
_generate_control_chart_svg, control_chart, subtask_info
|
||||
)
|
||||
histogram_svg = await asyncio.to_thread(
|
||||
_generate_histogram_svg, histogram, subtask_info
|
||||
)
|
||||
|
||||
# 4. Get company info and recipe info
|
||||
company = await _get_company_info(db)
|
||||
@@ -336,8 +346,8 @@ async def generate_spc_report(
|
||||
n_measurements=len(measurements),
|
||||
)
|
||||
|
||||
# 7. Convert HTML to PDF
|
||||
pdf_bytes = HTML(string=html_content).write_pdf()
|
||||
# 7. Convert HTML to PDF (WeasyPrint blocks: off the event loop)
|
||||
pdf_bytes = await asyncio.to_thread(_render_pdf, html_content)
|
||||
return pdf_bytes
|
||||
|
||||
|
||||
@@ -424,6 +434,6 @@ async def generate_measurement_report(
|
||||
n_measurements=len(measurements),
|
||||
)
|
||||
|
||||
# 8. Convert to PDF
|
||||
pdf_bytes = HTML(string=html_content).write_pdf()
|
||||
# 8. Convert to PDF (WeasyPrint blocks: off the event loop)
|
||||
pdf_bytes = await asyncio.to_thread(_render_pdf, html_content)
|
||||
return pdf_bytes
|
||||
|
||||
Reference in New Issue
Block a user