From 74b41f5c8366e6d4986c59ea3462290e3f0fef2b Mon Sep 17 00:00:00 2001 From: agra Date: Fri, 12 Jun 2026 22:33:24 +0300 Subject: [PATCH] distd: handlers run on a 4-worker pool; writes serialize (PLAN-HTTPZ A2) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit thread_pool_count = 4 turns on std.http's pooled dispatch: reads (store load + render per request, fully independent) now run in parallel across workers. POST/PUT stay under one mutex — every write is a whole-model load-modify-save on dist.db, so concurrent writers would lose updates; the old sequential loop serialized them by accident, this serializes them on purpose. --- src/server/distd.sx | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/server/distd.sx b/src/server/distd.sx index d2139bb..911d834 100644 --- a/src/server/distd.sx +++ b/src/server/distd.sx @@ -1015,6 +1015,11 @@ route :: (store_dir: string, req: *http.Request, resp: *http.Response) { // Per-request allocations land in std.http's per-dispatch arena. DistdCtx :: struct { store_dir: string; + // Writes serialize: every POST/PUT is a whole-model load-modify-save + // on dist.db, so two concurrent writers would lose updates. Reads + // run concurrently across the pool (per-request loads are + // independent; SQLite's busy_timeout covers reader/writer overlap). + write_mu: thread.Mutex = .{}; } distd_handle :: (req: *http.Request, resp: *http.Response, ctx: usize) { @@ -1030,7 +1035,10 @@ distd_handle :: (req: *http.Request, resp: *http.Response, ctx: usize) { } } if !refused { + is_write := req.method == "POST" or req.method == "PUT"; + if is_write { dctx.write_mu.lock(); } route(store_dir, req, resp); + if is_write { dctx.write_mu.unlock(); } } line := concat("distd: ", concat(req.method, concat(" ", concat(req.path, concat(" -> ", concat(int_to_string(resp.status), "\n")))))); slog(line); @@ -1042,6 +1050,7 @@ distd_handle :: (req: *http.Request, resp: *http.Response, ctx: usize) { // can't be opened. run_server :: (store_dir: string, port: i64) -> !ServeErr { dctx : DistdCtx = .{ store_dir = store_dir }; + if !dctx.write_mu.setup() { raise error.Bind; } cfg : http.Config = .{ port = port, max_conn = 256, @@ -1049,6 +1058,7 @@ run_server :: (store_dir: string, port: i64) -> !ServeErr { timeout_request_ms = 120000, // a large upload must complete within this timeout_keepalive_ms = 5000, request_count = 200, + thread_pool_count = 4, // reads in parallel; writes serialize above }; srv, se := http.Server.init(cfg, distd_handle, xx @dctx); if se { raise error.Bind; }