From f1e02ded783bac0ee5023bdb77be2a0f55e9fcd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hartmut=20N=C3=B6renberg?= Date: Fri, 6 Mar 2026 21:54:03 +0100 Subject: [PATCH] feat(F1): wire MinIO STL cache into render_still + render_turntable_to_file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously the cache_service was only used in the generate_stl_cache Celery task. All render paths (render_still, render_turntable_to_file, render_turntable_task) only checked for a local file and converted from scratch if missing. Changes: - render_blender.py: add _stl_from_cache_or_convert() helper that checks MinIO cache before falling back to local STEP→STL conversion. Wire into render_still() and render_turntable_to_file() (both STL conversion blocks). - domains/rendering/tasks.py: wire MinIO cache check into render_turntable_task() inline before convert_step_to_stl(). All errors are non-fatal (falls back to fresh conversion). Now a STEP file converted on one worker is available to all workers via MinIO, avoiding redundant cadquery conversions on re-renders. Co-Authored-By: Claude Sonnet 4.6 --- backend/app/domains/rendering/tasks.py | 15 +++++++++++-- backend/app/services/render_blender.py | 30 ++++++++++++++++++++------ 2 files changed, 37 insertions(+), 8 deletions(-) diff --git a/backend/app/domains/rendering/tasks.py b/backend/app/domains/rendering/tasks.py index 9073cdb..1d1dc84 100644 --- a/backend/app/domains/rendering/tasks.py +++ b/backend/app/domains/rendering/tasks.py @@ -155,10 +155,21 @@ def render_turntable_task( scripts_dir = Path(os.environ.get("RENDER_SCRIPTS_DIR", "/render-scripts")) turntable_script = scripts_dir / "turntable_render.py" - # STL conversion + # STL conversion — try MinIO cache first, then convert locally stl_path = step.parent / f"{step.stem}_{stl_quality}.stl" if not stl_path.exists() or stl_path.stat().st_size == 0: - convert_step_to_stl(step, stl_path, stl_quality) + try: + from app.domains.products.cache_service import compute_step_hash, check_stl_cache + step_hash = compute_step_hash(str(step)) + cached = check_stl_cache(step_hash, stl_quality) + if cached: + stl_path.write_bytes(cached) + logger.info("STL restored from MinIO cache: %s", stl_path.name) + else: + convert_step_to_stl(step, stl_path, stl_quality) + except Exception as exc: + logger.warning("MinIO cache check failed (non-fatal): %s — falling back to conversion", exc) + convert_step_to_stl(step, stl_path, stl_quality) parts_dir = step.parent / f"{step.stem}_{stl_quality}_parts" if not (parts_dir / "manifest.json").exists(): try: diff --git a/backend/app/services/render_blender.py b/backend/app/services/render_blender.py index d1e9833..2dc58b7 100644 --- a/backend/app/services/render_blender.py +++ b/backend/app/services/render_blender.py @@ -17,6 +17,26 @@ logger = logging.getLogger(__name__) MIN_BLENDER_VERSION = (5, 0, 1) +def _stl_from_cache_or_convert(step_path: Path, stl_path: Path, quality: str) -> None: + """Try MinIO cache first, then fall back to local STEP→STL conversion.""" + # MinIO cache check (non-fatal — cache miss just means we convert normally) + try: + from app.domains.products.cache_service import compute_step_hash, check_stl_cache + step_hash = compute_step_hash(str(step_path)) + cached_bytes = check_stl_cache(step_hash, quality) + if cached_bytes: + stl_path.write_bytes(cached_bytes) + logger.info("STL restored from MinIO cache: %s (%d KB)", stl_path.name, len(cached_bytes) // 1024) + return + except Exception as exc: + logger.warning("MinIO cache check failed (non-fatal): %s", exc) + + # Local conversion + from app.services.step_processor import convert_step_to_stl + logger.info("STL cache miss — converting: %s", step_path.name) + convert_step_to_stl(step_path, stl_path, quality) + + def find_blender() -> str: """Locate the Blender binary via $BLENDER_BIN or PATH.""" env_bin = os.environ.get("BLENDER_BIN", "") @@ -210,10 +230,9 @@ def render_still( t_stl = time.monotonic() if not stl_path.exists() or stl_path.stat().st_size == 0: - logger.info("STL cache miss — converting: %s", step_path.name) - convert_step_to_stl(step_path, stl_path, stl_quality) + _stl_from_cache_or_convert(step_path, stl_path, stl_quality) else: - logger.info("STL cache hit: %s (%d KB)", stl_path.name, stl_path.stat().st_size // 1024) + logger.info("STL local hit: %s (%d KB)", stl_path.name, stl_path.stat().st_size // 1024) stl_size_bytes = stl_path.stat().st_size if stl_path.exists() else 0 if not (parts_dir / "manifest.json").exists(): @@ -394,10 +413,9 @@ def render_turntable_to_file( t_stl = time.monotonic() if not stl_path.exists() or stl_path.stat().st_size == 0: - logger.info("STL cache miss — converting: %s", step_path.name) - convert_step_to_stl(step_path, stl_path, stl_quality) + _stl_from_cache_or_convert(step_path, stl_path, stl_quality) else: - logger.info("STL cache hit: %s (%d KB)", stl_path.name, stl_path.stat().st_size // 1024) + logger.info("STL local hit: %s (%d KB)", stl_path.name, stl_path.stat().st_size // 1024) stl_size_bytes = stl_path.stat().st_size if stl_path.exists() else 0 if not (parts_dir / "manifest.json").exists():