Built and run by one person.

Why does my whole FastAPI app freeze when DuckDB runs a big query?

Because DuckDB runs inside your FastAPI process. Postgres is a separate server, so a heavy query there costs you one slow response. DuckDB is in-process, so a heavy query blocks the Python asyncio event loop and freezes all concurrent handling: health checks stop responding, rate-limit checks cannot execute, and timeout logic cannot fire. Your entire API is unresponsive for the duration of the query, not just the endpoint that triggered it.

The fix is to keep the loop free by running the query in another thread:

# WRONG - blocks the event loop
result = conn.execute(sql).fetchall()

# RIGHT - loop stays responsive
loop = asyncio.get_event_loop()
result = await loop.run_in_executor(None, execute_duckdb_query, sql)

Here is the part that catches people, and it is the reason this item matters more than it looks: without run_in_executor, your other protections do not work either. The timeout coroutine never gets a chance to run, because the loop it lives on is blocked. So a DuckDB backend needs three things together: run_in_executor so the timeout can fire at all, process-level kill so the engine actually stops, and concurrency limits so heavy queries do not pile up. Without the first, the other two are useless.

Full DuckDB checklist: https://www.tigzig.com/security/duckdb. Related: https://www.tigzig.com/agents-faq/how-to-secure-duckdb-behind-a-public-api.

← All Agents FAQ