diff options
| author | eunhhu <[email protected]> | 2026-09-30 03:04:03 +0100 |
|---|---|---|
| committer | eunhhu <[email protected]> | 2026-09-30 03:04:03 +0100 |
| commit | a6df26a8499522033277ce4f3cd76eee155169ee (patch) | |
| tree | 7c619e72faff29cc2677a4219774f9d8afe799cc /serve/deliver.h | |
| parent | c9c0edf5a8fc9f79fff42064db09bc297e117a88 (diff) | |
| download | pure-a6df26a8499522033277ce4f3cd76eee155169ee.tar.gz pure-a6df26a8499522033277ce4f3cd76eee155169ee.zip | |
Bound what an app holds (--memory): refuse what would grow past it
pure-serve --memory BYTES gives the app a budget of state bytes: its
lists' items as they are stored, its texts, the outbox and the numbers
heard (docs/design/013-memory-budget.md). The count is deterministic,
the same on any device. An event after which the states hold more than
the budget, and more than before it, fails with the fault memory and
changes nothing; HTTP answers 503 memory. One that frees or keeps them
goes on, so an app over its budget can always shrink.
Sends waiting for their receivers are state: a full outbox refuses the
next send until some are stored, which is the backpressure a slow
receiver puts on clients. A delivery refused for memory is not heard:
the link keeps it (c9c0edf) and HTTP senders try again after 503.
An answer (--reply) now takes its send out of the outbox in its own
event (pure_app_answer), so it has the room its send held. Apart, an app
whose outbox filled the budget could never apply an answer that keeps
anything, and the send leaves only after its answer: both would stay for
good. An answer that still does not fit waits, with the wire's after it,
and is tried again every 100 ms: pure_wire_answer leaves it in place
until pure_wire_answered.
Checks: test-module memory_budget (refused and undone, freeing over the
budget, a full outbox, a delivery not heard, answers that fit or wait,
across a restart); check.py server_memory (a sender refused 503 memory
with its receiver down and taking buys once it is up; a receiver
restarted below what it keeps, refusing 7 times in 2 s, then taking all)
and server_answers_memory (answers wait while receipts fill the budget,
then apply once each; the API called once per order). The stats report
state_bytes and memory_refused.
Diffstat (limited to 'serve/deliver.h')
| -rw-r--r-- | serve/deliver.h | 12 |
1 files changed, 7 insertions, 5 deletions
diff --git a/serve/deliver.h b/serve/deliver.h index 71cc38e..a7ccac3 100644 --- a/serve/deliver.h +++ b/serve/deliver.h @@ -49,11 +49,10 @@ void pure_wire_close(PureWire *); * http_status is the answer's status. An answer that makes no message is * logged and the send tried again every 10 s. Answers go one per request. */ bool pure_wire_reply(PureWire *, uint32_t input, char *error, size_t size); -/* The owner's side: the next answer in order, or false. Taking it counts - * its send delivered (the owner applies the answer, then trims); what out - * points to is the wire's until the next call. The answer is applied with - * pure_app_deliver from the wire's source with the send's number, so an - * answer that comes again after a restart is applied once. */ +/* The owner's side: the next answer in order, or false; it stays the next, + * and what out points to the wire's, until pure_wire_answered. The answer + * is applied with pure_app_answer from the wire's source with the send's + * number, so an answer that comes again after a restart is applied once. */ typedef struct { uint32_t input; uint64_t seq; @@ -62,6 +61,9 @@ typedef struct { const char *source; } PureWireAnswer; bool pure_wire_answer(PureWire *, PureWireAnswer *out); +/* The next answer was applied, or refused by the app: it goes, and its send + * counts delivered (the owner trims it). */ +void pure_wire_answered(PureWire *); /* Counts for a host's report. */ void pure_wire_counts(PureWire *, uint64_t *delivered, uint64_t *attempts); #endif |
