diff --git a/hosts/ocaml/bin/sx_server.ml b/hosts/ocaml/bin/sx_server.ml index c780c18f..d3bdcfe7 100644 --- a/hosts/ocaml/bin/sx_server.ml +++ b/hosts/ocaml/bin/sx_server.ml @@ -1796,16 +1796,34 @@ let http_mode port = ignore (env_bind env "expand-components?" (NativeFn ("expand-components?", fun _args -> Bool true))); (* Inject shell statics *) http_inject_shell_statics env; - (* Pre-warm: render key pages to trigger JIT compilation of page functions, - aser, render-to-html, and shell components. Discards results. *) + (* Response cache — path → full HTTP response string. + Populated during pre-warm, serves cached responses in <0.1ms. + Thread-safe: reads are lock-free (Hashtbl.find_opt is atomic for + immutable values), writes happen only during single-threaded startup. *) + let response_cache : (string, string) Hashtbl.t = Hashtbl.create 128 in + + let cache_response path = + match http_render_page env path with + | Some html -> + let resp = http_response html in + Hashtbl.replace response_cache path resp; + Printf.eprintf "[cache] %s → %d bytes\n%!" path (String.length html) + | None -> + Printf.eprintf "[cache] %s → not found\n%!" path + in + + (* Pre-warm + cache all key pages *) let warmup_paths = ["/sx/"; "/sx/(geography)"; "/sx/(geography.(reactive.(examples)))"; - "/sx/(applications.(sxtp))"; "/sx/(geography.(cek))"] in + "/sx/(applications.(sxtp))"; "/sx/(geography.(cek))"; + "/sx/(language)"; "/sx/(applications)"; + "/sx/(geography.(reactive))"; "/sx/(geography.(hypermedia))"; + ] in let wt0 = Unix.gettimeofday () in - List.iter (fun path -> - ignore (http_render_page env path) - ) warmup_paths; + List.iter cache_response warmup_paths; let wt1 = Unix.gettimeofday () in - Printf.eprintf "[sx-http] Pre-warmed %d pages in %.3fs\n%!" (List.length warmup_paths) (wt1 -. wt0); + Printf.eprintf "[sx-http] Pre-warmed + cached %d pages in %.3fs (%d cached)\n%!" + (List.length warmup_paths) (wt1 -. wt0) (Hashtbl.length response_cache); + (* Write full response to a socket *) let write_response client response = let resp_bytes = Bytes.of_string response in @@ -1838,9 +1856,17 @@ let http_mode port = let is_sx = path = "/" || path = "/sx/" || path = "/sx" || (String.length path > 4 && String.sub path 0 4 = "/sx/") in if is_sx then - match http_render_page env path with - | Some html -> http_response html - | None -> http_response ~status:404 "

Not Found

" + (* Check cache first *) + match Hashtbl.find_opt response_cache path with + | Some cached -> cached + | None -> + (* Cache miss — render, cache, return *) + (match http_render_page env path with + | Some html -> + let resp = http_response html in + Hashtbl.replace response_cache path resp; + resp + | None -> http_response ~status:404 "

Not Found

") else http_response ~status:404 "

Not Found

" end