aboutsummaryrefslogtreecommitdiffstats
path: root/serve/serve.c
Commit message (Collapse)AuthorAgeFilesLines
...
* Stop reading a connection at a short readeunhhu2026-09-271-3/+17
| | | | | | | | | | | | | | | | | | After answering, a connection went back to recv for its next request and got EAGAIN nearly every time: a failed syscall a request. A recv that returns less than it asked for has emptied the socket, and the edge-triggered epoll reports the next bytes when they come (epoll(7); as nginx does), so the connection now waits for that; a peer that hung up is read to its end as before. Small keep-alive GETs, 64 connections, five rounds in rotation (Raspberry Pi 5): 131.9k/s before this step, 139.9k after. With the three steps before it (a burst's keys in one look, the clock once a round, the head without printf): 116.1k -> 139.9k/s, p99 672 -> 512 us, above the 127.0k of 2026-09-26's build that the app model had since slowed. App reads: the total 111.5k -> 125.4k/s, a 20-row page 86.2k -> 94.3k; 1 MiB reads and durable posts unchanged.
* Write an answer's head without printfeunhhu2026-09-271-9/+45
| | | | | | | | | Three snprintf calls a response (the head, its ETag and a delivery's batch size) spent a sixth of a small read's user time in printf's format scanning. The head is now written part by part, the same bytes as before, clipped at the end of its buffer where snprintf's would-be length could have sent bytes past it.
* Read the clock once a roundeunhhu2026-09-271-8/+11
| | | | | | | | | | A connection's deadline was counted from a fresh clock_gettime on every read and write, three times a request, and every event took the wall clock again: 5.7% of a small read's user time. A connection thread now reads the monotonic clock once after each epoll_wait and deadlines count from that (they are 10 s by default and swept every second), and a round's events share the time it began.
* Find a burst's keys in one lookeunhhu2026-09-271-4/+3
| | | | | | | | | | | | | | | A message for a document scanned the burst's pending keys with strcmp to see whether its key was there, and the host scanned the burst's earlier requests the same way before each one: both quadratic in the burst (64 concurrent reads, about 31 compares each, twice). The app now indexes its pending documents by key (open addressing over an FNV-1a hash, 1 KiB), answers pure_app_pending in one look, and the host asks it; whether the current event's document is pending is a flag instead of a scan by pointer. Bursts split where they did, except that on an owner, which applies a round's reads first, an edit of a key read in the same round now splits the burst at the host rather than the app saving on its own. A unit test checks the index at 200 keys.
* Seal delivered sends to the receiver's key (086)eunhhu2026-09-271-4/+87
| | | | | | | | | | | | | | | | | | | | --seal OUTPUT=KEY seals each request's body whole to the receiving node's public key with HPKE (RFC 9180, base mode: X25519, HKDF-SHA256, AES-128-GCM where the CPU has AES, else ChaCha20-Poly1305), bound to the request's source, first number and count; X-Pure-Seal names the suite. A receiver's --key is a raw 32-byte file, made 0600 when missing, held in OpenSSL's secure heap; it opens the body where it lies (400 seal when it does not open) and zeroes the plain text when the request's ticket goes. --sealed-only refuses unsealed deliveries (403 unsealed). The check puts a recording relay between two apps: every order applies once, no body shows an item or a field name, a flipped bit is refused, a replayed request applies nothing twice, and a seal to another key does not open. Its hosts choose their own ports (--port 0). Sealed sends drain at 233-235k a second against 469-470k plain (Raspberry Pi 5): three X25519 operations a request, 164 us each. The plain request path measured the same as before.
* Lay Pure out as a core with the parts selected around iteunhhu2026-09-271-0/+1820
The app model, the tree store and the HTTP host lived in prototypes/connections among the earlier document runtime, the store was known only by its format's name, and Vega compiled six Pure sources by path. Now, as the README's principle has it: core/ runtime, flow and the app model (module*.c), with what the core asks of storage (state.h, store.h) store/ the tree store (PURETRE3, PUREREC1) serve/ pure-serve: HTTP host, outbound delivery, JSON in place cli/ pure-run, pure-edit-module A top-level make builds libpure.a (app model and store, for hosts to link) and the hosts; make check runs the store's and the app model's tests. The earlier runtime still builds from prototypes/connections against core/ and store/ (check, check-tree and check-core pass). Design 009 records the rule: parts by role behind libraries; git repositories split by coupling, so the store stays here while its contract moves with the core, and Vega stays apart.