1:"$Sreact.fragment"
2:I[15579,["/_next/static/chunks/fd0661f1506dcbc6.js","/_next/static/chunks/94245cbda44972fe.js","/_next/static/chunks/af778fff4a0f4be6.js","/_next/static/chunks/b096b037d08e2f31.js"],"Navigation"]
3:I[3013,["/_next/static/chunks/fd0661f1506dcbc6.js","/_next/static/chunks/94245cbda44972fe.js","/_next/static/chunks/af778fff4a0f4be6.js","/_next/static/chunks/b096b037d08e2f31.js"],""]
29:I[41451,["/_next/static/chunks/fd0661f1506dcbc6.js","/_next/static/chunks/94245cbda44972fe.js","/_next/static/chunks/af778fff4a0f4be6.js","/_next/static/chunks/b096b037d08e2f31.js"],"Footer"]
2a:I[47913,["/_next/static/chunks/316a3a63422f35de.js"],"OutletBoundary"]
2b:"$Sreact.suspense"
:HL["/blog/posts/threadpool-async-background-work/hero.jpg","image"]
:HL["/blog/posts/logo.png","image"]
0:{"buildId":"TlpKRvbES4zzM7LeczAM7","rsc":["$","$1","c",{"children":[[["$","$L2",null,{}],["$","main",null,{"className":"pt-20 md:pt-24","children":["$","article",null,{"children":[["$","header",null,{"className":"border-b border-border","children":["$","div",null,{"className":"container mx-auto px-6 py-14 md:py-20","children":[["$","$L3",null,{"href":"/blog","className":"mb-8 inline-flex items-center text-sm font-medium text-muted-foreground transition-colors hover:text-foreground","children":[["$","svg",null,{"xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-arrow-left mr-2 h-4 w-4","aria-hidden":"true","children":[["$","path","1l729n",{"d":"m12 19-7-7 7-7"}],["$","path","x3x0zl",{"d":"M19 12H5"}],"$undefined"]}],"Blog"]}],["$","div",null,{"className":"grid gap-8 md:grid-cols-[1fr_0.9fr] md:items-start","children":[["$","div",null,{"className":"max-w-3xl","children":[["$","div",null,{"className":"mb-5 flex flex-wrap items-center gap-3 text-sm text-muted-foreground","children":[["$","span",null,{"className":"rounded-md border border-primary/40 bg-primary/10 px-2.5 py-1 text-primary","children":"Concurrency"}],["$","span",null,{"children":"2026-W18"}],["$","span",null,{"aria-hidden":"true","children":"/"}],["$","span",null,{"children":"4 min read"}],null]}],["$","h1",null,{"className":"text-4xl font-bold leading-tight text-balance md:text-6xl","children":"Offloading Slow Background Work from an Async Web Server"}],["$","p",null,{"className":"mt-6 text-lg leading-relaxed text-muted-foreground md:text-xl","children":"A 30-second LLM call inside an async route ties up the event loop and starves every other request. `ThreadPoolExecutor` + `run_in_executor` is the right tool — and the executor must be sized to the downstream resource's real concurrency ceiling, not to throughput."}]]}],["$","div",null,{"className":"overflow-hidden rounded-lg border border-border bg-card","children":["$","div",null,{"className":"relative aspect-[16/9] overflow-hidden","children":[null,["$","img",null,{"src":"/blog/posts/threadpool-async-background-work/hero.jpg","alt":"A chef's hands plating a dish on a stainless-steel pass at a busy restaurant kitchen, sharp focus on the plate, background blurred with motion, no faces visible, warm overhead light, editorial.","className":"h-full w-full object-cover"}],["$","img",null,{"src":"/blog/posts/logo.png","alt":"","aria-hidden":"true","className":"pointer-events-none absolute right-4 top-4 h-[50px] w-[50px] mix-blend-screen"}]]}]}]]}]]}]}],["$","div",null,{"className":"container mx-auto px-6 py-12 md:py-16","children":["$","div",null,{"className":"grid gap-10 lg:grid-cols-[minmax(0,1fr)_280px] lg:items-start","children":[["$","div",null,{"className":"max-w-3xl text-muted-foreground","children":[[["$","h2","h2-0",{"className":"mt-12 text-2xl font-semibold leading-snug text-foreground first:mt-0","children":"The problem"}],"\n",["$","p","p-0",{"className":"mt-4 text-base leading-8 md:text-lg first:mt-0 first:text-xl first:leading-relaxed first:text-foreground md:first:text-2xl","children":["A FastAPI endpoint receives a request, saves some data, and needs to kick off a slow background operation (in this case, an LLM call that takes 30–120 seconds). The response must return immediately — the slow work can happen whenever. The naive solution, ",["$","code","code-0",{"className":"rounded bg-secondary px-1.5 py-0.5 text-[0.9em] text-foreground","children":"asyncio.create_task(slow_coroutine())"}],", only works if the slow work is an async function. If it calls a subprocess or blocks on I/O, it ties up the event loop thread."]}],"\n",["$","h2","h2-1",{"className":"mt-12 text-2xl font-semibold leading-snug text-foreground first:mt-0","children":"The approach"}],"\n",["$","p","p-1",{"className":"mt-4 text-base leading-8 md:text-lg first:mt-0 first:text-xl first:leading-relaxed first:text-foreground md:first:text-2xl","children":["$L4"," + ","$L5"," is the right tool: submit a regular synchronous function to a thread pool, get back a future, don't await it."]}],"\n","$L6","\n","$L7","\n","$L8","\n","$L9","\n","$La","\n","$Lb","\n","$Lc"],"$Ld",null]}],"$Le"]}]}]]}]}],"$Lf"],["$L10","$L11","$L12"],"$L13"]}],"loading":null,"isPartial":false}
4:["$","code","code-0",{"className":"rounded bg-secondary px-1.5 py-0.5 text-[0.9em] text-foreground","children":"ThreadPoolExecutor"}]
5:["$","code","code-1",{"className":"rounded bg-secondary px-1.5 py-0.5 text-[0.9em] text-foreground","children":"loop.run_in_executor"}]
6:["$","div","pre-0",{"style":{"background":"hsl(220, 13%, 18%)","color":"hsl(220, 14%, 71%)","textShadow":"0 1px rgba(0, 0, 0, 0.3)","fontFamily":"\"Fira Code\", \"Fira Mono\", Menlo, Consolas, \"DejaVu Sans Mono\", monospace","direction":"ltr","textAlign":"left","whiteSpace":"pre","wordSpacing":"normal","wordBreak":"normal","lineHeight":"1.5","MozTabSize":"2","OTabSize":"2","tabSize":"2","WebkitHyphens":"none","MozHyphens":"none","msHyphens":"none","hyphens":"none","padding":"1rem","margin":"0.5em 0","overflow":"auto","borderRadius":"0.5rem","marginTop":"1.25rem","marginBottom":0,"border":"1px solid hsl(var(--border))","fontSize":"0.875rem"},"children":["$","code",null,{"style":{"whiteSpace":"pre","fontFamily":"ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, Courier New, monospace"},"children":[false,[["$","span","code-segment-0",{"style":{},"children":["_executor "]}],["$","span","code-segment-1",{"className":"token","style":{"color":"hsl(207, 82%, 66%)"},"children":["="]}],["$","span","code-segment-2",{"style":{},"children":[" ThreadPoolExecutor"]}],["$","span","code-segment-3",{"className":"token","style":{"color":"hsl(220, 14%, 71%)"},"children":["("]}],["$","span","code-segment-4",{"style":{},"children":["max_workers"]}],["$","span","code-segment-5",{"className":"token","style":{"color":"hsl(207, 82%, 66%)"},"children":["="]}],["$","span","code-segment-6",{"className":"token","style":{"color":"hsl(29, 54%, 61%)"},"children":["2"]}],["$","span","code-segment-7",{"className":"token","style":{"color":"hsl(220, 14%, 71%)"},"children":[","]}],["$","span","code-segment-8",{"style":{},"children":[" thread_name_prefix"]}],["$","span","code-segment-9",{"className":"token","style":{"color":"hsl(207, 82%, 66%)"},"children":["="]}],["$","span","code-segment-10",{"className":"token","style":{"color":"hsl(95, 38%, 62%)"},"children":["\"bg-worker\""]}],["$","span","code-segment-11",{"className":"token","style":{"color":"hsl(220, 14%, 71%)"},"children":[")"]}],["$","span","code-segment-12",{"style":{},"children":["\n"]}],"\n",["$","span","code-segment-14",{"style":{},"children":[""]}],["$","span","code-segment-15",{"className":"token","style":{"color":"hsl(286, 60%, 67%)"},"children":["async"]}],["$","span","code-segment-16",{"style":{},"children":[" "]}],["$","span","code-segment-17",{"className":"token","style":{"color":"hsl(286, 60%, 67%)"},"children":["def"]}],["$","span","code-segment-18",{"style":{},"children":[" "]}],["$","span","code-segment-19",{"className":"token","style":{"color":"hsl(207, 82%, 66%)"},"children":["my_endpoint"]}],["$","span","code-segment-20",{"className":"token","style":{"color":"hsl(220, 14%, 71%)"},"children":["("]}],["$","span","code-segment-21",{"style":{},"children":["request"]}],["$","span","code-segment-22",{"className":"token","style":{"color":"hsl(220, 14%, 71%)"},"children":[")"]}],["$","span","code-segment-23",{"className":"token","style":{"color":"hsl(220, 14%, 71%)"},"children":[":"]}],["$","span","code-segment-24",{"style":{},"children":["\n"]}],["$","span","code-segment-25",{"style":{},"children":["    save_to_db"]}],["$","span","code-segment-26",{"className":"token","style":{"color":"hsl(220, 14%, 71%)"},"children":["("]}],["$","span","code-segment-27",{"style":{},"children":["data"]}],["$","span","code-segment-28",{"className":"token","style":{"color":"hsl(220, 14%, 71%)"},"children":[")"]}],["$","span","code-segment-29",{"style":{},"children":["\n"]}],["$","span","code-segment-30",{"style":{},"children":["    loop "]}],["$","span","code-segment-31",{"className":"token","style":{"color":"hsl(207, 82%, 66%)"},"children":["="]}],["$","span","code-segment-32",{"style":{},"children":[" asyncio"]}],["$","span","code-segment-33",{"className":"token","style":{"color":"hsl(220, 14%, 71%)"},"children":["."]}],["$","span","code-segment-34",{"style":{},"children":["get_running_loop"]}],["$","span","code-segment-35",{"className":"token","style":{"color":"hsl(220, 14%, 71%)"},"children":["("]}],["$","span","code-segment-36",{"className":"token","style":{"color":"hsl(220, 14%, 71%)"},"children":[")"]}],["$","span","code-segment-37",{"style":{},"children":["\n"]}],["$","span","code-segment-38",{"style":{},"children":["    loop"]}],["$","span","code-segment-39",{"className":"token","style":{"color":"hsl(220, 14%, 71%)"},"children":["."]}],["$","span","code-segment-40",{"style":{},"children":["run_in_executor"]}],["$","span","code-segment-41",{"className":"token","style":{"color":"hsl(220, 14%, 71%)"},"children":["("]}],"$L14","$L15","$L16","$L17","$L18","$L19","$L1a","$L1b","$L1c","$L1d","$L1e","$L1f","$L20","$L21","$L22","$L23","$L24","$L25","$L26","$L27"]]}]}]
7:["$","p","p-2",{"className":"mt-4 text-base leading-8 md:text-lg first:mt-0 first:text-xl first:leading-relaxed first:text-foreground md:first:text-2xl","children":"Two details that matter:"}]
8:["$","p","p-3",{"className":"mt-4 text-base leading-8 md:text-lg first:mt-0 first:text-xl first:leading-relaxed first:text-foreground md:first:text-2xl","children":[["$","strong","strong-0",{"children":["Use ",["$","code","code-0",{"className":"rounded bg-secondary px-1.5 py-0.5 text-[0.9em] text-foreground","children":"asyncio.get_running_loop()"}],", not ",["$","code","code-1",{"className":"rounded bg-secondary px-1.5 py-0.5 text-[0.9em] text-foreground","children":"asyncio.get_event_loop()"}],"."]}]," Inside an async function, there is always a running loop — ",["$","code","code-0",{"className":"rounded bg-secondary px-1.5 py-0.5 text-[0.9em] text-foreground","children":"get_running_loop()"}]," returns it directly and is the correct call. ",["$","code","code-1",{"className":"rounded bg-secondary px-1.5 py-0.5 text-[0.9em] text-foreground","children":"get_event_loop()"}]," is deprecated for this use in Python 3.10+ and raises a DeprecationWarning; in some 3.12+ configurations it can raise instead of warn."]}]
9:["$","p","p-4",{"className":"mt-4 text-base leading-8 md:text-lg first:mt-0 first:text-xl first:leading-relaxed first:text-foreground md:first:text-2xl","children":[["$","strong","strong-0",{"children":"Size the executor for concurrency, not throughput."}]," ",["$","code","code-0",{"className":"rounded bg-secondary px-1.5 py-0.5 text-[0.9em] text-foreground","children":"max_workers=2"}]," for an LLM background call means at most 2 simultaneous LLM subprocesses. A large thread pool here would just queue up LLM calls — more than 2–3 concurrent invocations is usually counterproductive. Match the pool size to the real concurrency ceiling of the downstream resource."]}]
a:["$","p","p-5",{"className":"mt-4 text-base leading-8 md:text-lg first:mt-0 first:text-xl first:leading-relaxed first:text-foreground md:first:text-2xl","children":["The future returned by ",["$","code","code-0",{"className":"rounded bg-secondary px-1.5 py-0.5 text-[0.9em] text-foreground","children":"run_in_executor"}]," is discarded here intentionally — this is a fire-and-forget pattern. Since the sync function catches its own exceptions and writes results directly to a shared store (Redis), the discard is safe. If you needed error propagation, you'd ",["$","code","code-1",{"className":"rounded bg-secondary px-1.5 py-0.5 text-[0.9em] text-foreground","children":"await"}]," the future or attach a callback."]}]
b:["$","h2","h2-2",{"className":"mt-12 text-2xl font-semibold leading-snug text-foreground first:mt-0","children":"What I learned"}]
c:["$","p","p-6",{"className":"mt-4 text-base leading-8 md:text-lg first:mt-0 first:text-xl first:leading-relaxed first:text-foreground md:first:text-2xl","children":["The ",["$","code","code-0",{"className":"rounded bg-secondary px-1.5 py-0.5 text-[0.9em] text-foreground","children":"run_in_executor"}]," fire-and-forget pattern is clean and easy to reason about, but it requires the sync function to be completely self-contained: catch your own exceptions, write results to a shared store, log failures internally. If you let exceptions escape uncaught from a discarded future, they go to the asyncio exception handler (or are silently dropped, depending on Python version). The discipline of \"if you fire and forget, handle your own errors\" makes this pattern composable without surprises."]}]
d:["$","div",null,{"className":"mt-14 border-t border-border pt-8","children":["$","$L3",null,{"href":"/platform","className":"inline-flex items-center text-sm font-medium text-primary transition-colors hover:text-primary/80","children":["Start a build",["$","svg",null,{"xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-arrow-right ml-2 h-4 w-4","aria-hidden":"true","children":[["$","path","1ays0h",{"d":"M5 12h14"}],["$","path","xquz4c",{"d":"m12 5 7 7-7 7"}],"$undefined"]}]]}]}]
e:["$","aside",null,{"className":"hidden lg:block","children":["$","div",null,{"className":"sticky top-24 space-y-4","children":[["$","h3",null,{"className":"text-xs font-semibold uppercase tracking-wider text-primary","children":["More in ","Concurrency"]}],["$","div",null,{"className":"space-y-4","children":[["$","$L3","bcrypt-async-event-loop",{"href":"/blog/bcrypt-async-event-loop","className":"block overflow-hidden rounded-lg border border-border bg-card transition-colors hover:border-primary/40","children":[["$","div",null,{"className":"relative aspect-[16/9] overflow-hidden bg-secondary/70","children":[["$","img",null,{"src":"/blog/posts/bcrypt-async-event-loop/hero.jpg","alt":"A solitary traffic light glowing red over an empty asphalt road at dusk, no cars, no people, sharp editorial photography.","className":"h-full w-full object-cover","loading":"lazy","decoding":"async"}],["$","img",null,{"src":"/blog/posts/logo.png","alt":"","aria-hidden":"true","className":"pointer-events-none absolute right-2 top-2 h-[24px] w-[24px] mix-blend-screen"}]]}],["$","div",null,{"className":"p-4","children":[["$","p",null,{"className":"text-xs font-medium text-primary","children":"Concurrency"}],["$","p",null,{"className":"mt-1 text-xs text-muted-foreground","children":["2026-W20"," · ","4 min"]}],["$","p",null,{"className":"mt-2 line-clamp-2 text-sm font-semibold leading-snug text-foreground","children":"bcrypt in an Async Codebase — The Three Places It Bites You"}]]}]]}],["$","$L3","toctou-async-database-handlers",{"href":"/blog/toctou-async-database-handlers","className":"block overflow-hidden rounded-lg border border-border bg-card transition-colors hover:border-primary/40","children":[["$","div",null,{"className":"relative aspect-[16/9] overflow-hidden bg-secondary/70","children":[["$","img",null,{"src":"/blog/posts/toctou-async-database-handlers/hero.jpg","alt":"A close-up macro shot of two identical brass keys sliding into the same antique brass door lock simultaneously against a dark wooden door, dramatic side lighting, no people, sharp editorial focus.","className":"h-full w-full object-cover","loading":"lazy","decoding":"async"}],["$","img",null,{"src":"/blog/posts/logo.png","alt":"","aria-hidden":"true","className":"pointer-events-none absolute right-2 top-2 h-[24px] w-[24px] mix-blend-screen"}]]}],["$","div",null,{"className":"p-4","children":[["$","p",null,{"className":"text-xs font-medium text-primary","children":"Concurrency"}],["$","p",null,{"className":"mt-1 text-xs text-muted-foreground","children":["2026-W20"," · ","4 min"]}],["$","p",null,{"className":"mt-2 line-clamp-2 text-sm font-semibold leading-snug text-foreground","children":"TOCTOU Races in Async Database Handlers"}]]}]]}],["$","$L3","async-sqlite-fastapi",{"href":"/blog/async-sqlite-fastapi","className":"block overflow-hidden rounded-lg border border-border bg-card transition-colors hover:border-primary/40","children":[["$","div",null,{"className":"relative aspect-[16/9] overflow-hidden bg-secondary/70","children":[["$","img",null,{"src":"/blog/posts/async-sqlite-fastapi/hero.jpg","alt":"A single brass funnel on a worn wooden workbench with one droplet caught mid-fall above its narrow neck, sharp focus on the droplet, soft side light, no people, editorial.","className":"h-full w-full object-cover","loading":"lazy","decoding":"async"}],["$","img",null,{"src":"/blog/posts/logo.png","alt":"","aria-hidden":"true","className":"pointer-events-none absolute right-2 top-2 h-[24px] w-[24px] mix-blend-screen"}]]}],["$","div",null,{"className":"p-4","children":[["$","p",null,{"className":"text-xs font-medium text-primary","children":"Concurrency"}],["$","p",null,{"className":"mt-1 text-xs text-muted-foreground","children":["2026-W20"," · ","4 min"]}],["$","p",null,{"className":"mt-2 line-clamp-2 text-sm font-semibold leading-snug text-foreground","children":"Blocking SQLite in an Async FastAPI Route"}]]}]]}]]}],["$","$L3",null,{"href":"/blog?category=concurrency","className":"inline-flex items-center text-sm font-medium text-primary transition-colors hover:text-primary/80","children":["Read all in ","Concurrency","$L28"]}]]}]}]
f:["$","$L29",null,{}]
10:["$","script","script-0",{"src":"/_next/static/chunks/94245cbda44972fe.js","async":true}]
11:["$","script","script-1",{"src":"/_next/static/chunks/af778fff4a0f4be6.js","async":true}]
12:["$","script","script-2",{"src":"/_next/static/chunks/b096b037d08e2f31.js","async":true}]
13:["$","$L2a",null,{"children":["$","$2b",null,{"name":"Next.MetadataOutlet","children":"$@2c"}]}]
14:["$","span","code-segment-42",{"style":{},"children":["_executor"]}]
15:["$","span","code-segment-43",{"className":"token","style":{"color":"hsl(220, 14%, 71%)"},"children":[","]}]
16:["$","span","code-segment-44",{"style":{},"children":[" slow_sync_function"]}]
17:["$","span","code-segment-45",{"className":"token","style":{"color":"hsl(220, 14%, 71%)"},"children":[","]}]
18:["$","span","code-segment-46",{"style":{},"children":[" arg1"]}]
19:["$","span","code-segment-47",{"className":"token","style":{"color":"hsl(220, 14%, 71%)"},"children":[","]}]
1a:["$","span","code-segment-48",{"style":{},"children":[" arg2"]}]
1b:["$","span","code-segment-49",{"className":"token","style":{"color":"hsl(220, 14%, 71%)"},"children":[")"]}]
1c:["$","span","code-segment-50",{"style":{},"children":["\n"]}]
1d:["$","span","code-segment-51",{"style":{},"children":["    "]}]
1e:["$","span","code-segment-52",{"className":"token","style":{"color":"hsl(286, 60%, 67%)"},"children":["return"]}]
1f:["$","span","code-segment-53",{"style":{},"children":[" JSONResponse"]}]
20:["$","span","code-segment-54",{"className":"token","style":{"color":"hsl(220, 14%, 71%)"},"children":["("]}]
21:["$","span","code-segment-55",{"className":"token","style":{"color":"hsl(220, 14%, 71%)"},"children":["{"]}]
22:["$","span","code-segment-56",{"className":"token","style":{"color":"hsl(95, 38%, 62%)"},"children":["\"ok\""]}]
23:["$","span","code-segment-57",{"className":"token","style":{"color":"hsl(220, 14%, 71%)"},"children":[":"]}]
24:["$","span","code-segment-58",{"style":{},"children":[" "]}]
25:["$","span","code-segment-59",{"className":"token","style":{"color":"hsl(29, 54%, 61%)"},"children":["True"]}]
26:["$","span","code-segment-60",{"className":"token","style":{"color":"hsl(220, 14%, 71%)"},"children":["}"]}]
27:["$","span","code-segment-61",{"className":"token","style":{"color":"hsl(220, 14%, 71%)"},"children":[")"]}]
28:["$","svg",null,{"xmlns":"http://www.w3.org/2000/svg","width":24,"height":24,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-arrow-right ml-2 h-4 w-4","aria-hidden":"true","children":[["$","path","1ays0h",{"d":"M5 12h14"}],["$","path","xquz4c",{"d":"m12 5 7 7-7 7"}],"$undefined"]}]
2c:null
