v3.5.1
A patch for the two things the Lodestar cutover found on 2026-09-06. Every nest serving more than
one SQL permit should restart onto this; nothing changes on disk.
Concurrent /sql execution segfaulted the nest (#1165)
Twenty-five segfaults a minute at four permits under the dashboard's load, three an hour at two, none
at one. The cause was not memory. Every DuckDB instance a process opened - and the analytical path
opens one per concurrent query, because the connection cache lends its connection out for the
query's duration - spilled to the same default temp_directory, .tmp under the nest, with file
names indexed per directory rather than per instance. Two instances overwrote each other's spill
blocks; the reader tripped an assertion in DuckDB's temporary file manager and the process died on a
free. Each instance now spills to a private directory, nuthatch-duckdb-<pid>-<seq> under the
system temp dir, created exclusively so a directory left by a dead process whose PID the kernel
handed back is never inherited, admitted to allowed_directories before the configuration is locked,
and removed with the connection. Directories left by processes that no longer exist are swept at the
first open. Measured on the production nest at four permits: an hour of the dashboard's traffic and
a two-minute concurrent replay, no fault.
A concurrent harness now runs in CI: four threads, twenty-five queries each, a filtered ORDER BY
over a sealed segment holding rows on both sides of the filter, every result compared row for row
with the same query run alone.
Every /sql request re-bound every authored view (#1183)
A SELECT 1 cost 1.24 s and 32,000 file opens on the Lodestar nest. define_views had been narrowed
to the base tables a statement can reach (#896); define_nest_views had not, and on the pooled
connection every CREATE OR REPLACE VIEW in views/*.sql bound in full against the base tables the
previous request left behind, re-reading every segment footer behind every table it touches. The
authored views are now defined from the same reachability set, so a statement pays for the views it
reads and nothing else. On the production nest the floor went from 1.24 s to 20 ms and the
dashboard's 47 statements from 158 s to 89 s serially; what remains is the views' own compute, #1186.