From aef12aafd8c50b7629d7fc26aee5fb75466264f5 Mon Sep 17 00:00:00 2001 From: Sam Rolfe Date: Thu, 29 Oct 2026 16:43:24 +1100 Subject: [PATCH] fix(portal): resolve 10+ sec link delay by fixing Garage S3 secret key, adding 1s boto3 timeouts, and running S3 list calls in parallel --- portal/main.py | 11 +++++++---- portal/s3.py | 7 ++++++- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/portal/main.py b/portal/main.py index 60e2481..76fd283 100644 --- a/portal/main.py +++ b/portal/main.py @@ -11,6 +11,7 @@ Every page renders against the design tokens in static/tokens.css. """ from __future__ import annotations +import asyncio import logging from datetime import datetime, timezone from zoneinfo import ZoneInfo @@ -206,11 +207,13 @@ async def tool_embed(request: Request, tool_id: str) -> HTMLResponse: return templates.TemplateResponse( request, "not_found.html", {"user": user}, status_code=404 ) - # Workspace files: the user's bucket + the shared pool (does not block the - # event loop, degrades to empty list on any S3 error). + # Workspace files: the user's bucket + the shared pool (runs concurrently, + # degraded gracefully on any S3 error/timeout). user_bucket = s3_bucket_name(user.username) - my_files = await run_in_threadpool(list_recent, user_bucket, 20) - shared_files = await run_in_threadpool(list_recent, "shared-media", 20) + my_files, shared_files = await asyncio.gather( + run_in_threadpool(list_recent, user_bucket, 20), + run_in_threadpool(list_recent, "shared-media", 20), + ) ctx = { "user": user, "tool": tool, diff --git a/portal/s3.py b/portal/s3.py index a3947a4..36eb4b9 100644 --- a/portal/s3.py +++ b/portal/s3.py @@ -19,7 +19,12 @@ def s3_client(): region_name=settings.S3_REGION or None, aws_access_key_id=settings.S3_ACCESS_KEY or None, aws_secret_access_key=settings.S3_SECRET_KEY or None, - config=Config(signature_version="s3v4"), + config=Config( + signature_version="s3v4", + connect_timeout=1, + read_timeout=1, + retries={"max_attempts": 1}, + ), )