From a0ed1ae5ae270aad5349cdf69e0f77502e67939a Mon Sep 17 00:00:00 2001 From: gilesb Date: Fri, 9 Jan 2026 00:26:32 +0000 Subject: [PATCH] Make IPFS pinning non-blocking (fire-and-forget) IPFS pinning can take a long time if content needs to be fetched from the network. Changed all pin operations to run in background threads so they don't block the HTTP response. This fixes the 30s timeout issue when publishing assets. Co-Authored-By: Claude Opus 4.5 --- server.py | 41 ++++++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/server.py b/server.py index 0147ed4..9300076 100644 --- a/server.py +++ b/server.py @@ -1599,13 +1599,9 @@ async def update_asset(name: str, req: UpdateAssetRequest, user: User = Depends( updates["origin"] = req.origin if req.ipfs_cid is not None: updates["ipfs_cid"] = req.ipfs_cid - # Pin on IPFS - try: - import ipfs_client - if ipfs_client.is_available(): - ipfs_client.pin(req.ipfs_cid) - except Exception as e: - logger.warning(f"Failed to pin IPFS content {req.ipfs_cid}: {e}") + # Pin on IPFS (fire-and-forget, don't block) + import threading + threading.Thread(target=_pin_ipfs_async, args=(req.ipfs_cid,), daemon=True).start() # Update asset in database updated_asset = await db.update_asset(name, updates) @@ -1639,20 +1635,27 @@ async def update_asset(name: str, req: UpdateAssetRequest, user: User = Depends( return {"asset": updated_asset, "activity": activity} +def _pin_ipfs_async(cid: str): + """Pin IPFS content in background thread.""" + try: + import ipfs_client + if ipfs_client.is_available(): + ipfs_client.pin(cid) + logger.info(f"Pinned IPFS content: {cid}") + except Exception as e: + logger.warning(f"Failed to pin IPFS content {cid}: {e}") + + async def _register_asset_impl(req: RegisterRequest, owner: str): """Internal implementation for registering an asset.""" # Check if name exists if await db.asset_exists(req.name): raise HTTPException(400, f"Asset already exists: {req.name}") - # Pin content on IPFS if CID provided + # Pin content on IPFS if CID provided (fire-and-forget, don't block) if req.ipfs_cid: - try: - import ipfs_client - if ipfs_client.is_available(): - ipfs_client.pin(req.ipfs_cid) - except Exception as e: - logger.warning(f"Failed to pin IPFS content {req.ipfs_cid}: {e}") + import threading + threading.Thread(target=_pin_ipfs_async, args=(req.ipfs_cid,), daemon=True).start() # Create asset now = datetime.now(timezone.utc).isoformat() @@ -1794,14 +1797,10 @@ async def publish_cache(req: PublishCacheRequest, user: User = Depends(get_requi if await db.asset_exists(req.asset_name): raise HTTPException(400, f"Asset name already exists: {req.asset_name}") - # Pin content on IPFS if CID provided + # Pin content on IPFS if CID provided (fire-and-forget, don't block) if req.ipfs_cid: - try: - import ipfs_client - if ipfs_client.is_available(): - ipfs_client.pin(req.ipfs_cid) - except Exception as e: - logger.warning(f"Failed to pin IPFS content {req.ipfs_cid}: {e}") + import threading + threading.Thread(target=_pin_ipfs_async, args=(req.ipfs_cid,), daemon=True).start() # Create asset now = datetime.now(timezone.utc).isoformat()