diff --git a/docs/examples/site/CODE-LOGIC.md b/docs/examples/site/CODE-LOGIC.md index e52df55..22f5dbe 100644 --- a/docs/examples/site/CODE-LOGIC.md +++ b/docs/examples/site/CODE-LOGIC.md @@ -9,7 +9,7 @@ samples now read the same way. | file | layer | what it owns | | --- | --- | --- | | `types.wo` | MODEL | the `Chapter` `@table`, the `ChapterLink` projection, `Chapters.links()`, and `seed_if_empty()` | -| `content.wo` | MODEL (content) | the nine chapter bodies as fragment-returning functions, plus `seed_chapters()` | +| `content.wo` | MODEL (content) | the ten chapter bodies as fragment-returning functions, plus `seed_chapters()` | | `layout/app.wo` | VIEW (chrome) | `AppShell` — the component that fills writeonce-view's `Layout` — the two named widths, and `html_error` | | `layout/header.wo`, `layout/footer.wo` | VIEW (chrome) | the shared nav bar (brand = mark + wordmark) and footer | | `layout/logo.wo` | VIEW (chrome) | the mark as inline SVG, plus the `
` links | diff --git a/docs/examples/site/README.md b/docs/examples/site/README.md index eff6c72..c8c5833 100644 --- a/docs/examples/site/README.md +++ b/docs/examples/site/README.md @@ -50,7 +50,7 @@ MVC, laid out exactly like the program template | this app | layer | | --- | --- | | `types.wo` | MODEL — the `Chapter` `@table`, and seed-if-empty | -| `content.wo` | the nine chapter bodies + `seed_chapters()` | +| `content.wo` | the ten chapter bodies + `seed_chapters()` | | `layout/` | the chrome: `AppShell` (+ the two named widths), header, footer, `html_error` | | `home/`, `chapter/`, `install/`, `packages/` | one module per feature: its `view.wo` (components: fields in, Text out) and its `controller.wo` (query the model, fill the components, answer a `Resp`) | | `admin/`, `health/`, `favicon/` | controller-only features — a redirect, a text probe, an SVG | diff --git a/docs/examples/site/content.wo b/docs/examples/site/content.wo index 1d0486a..42c94eb 100644 --- a/docs/examples/site/content.wo +++ b/docs/examples/site/content.wo @@ -52,6 +52,20 @@ fn ch_tables() -> Text { return b; } +fn ch_storage() -> Text { + let b = el("p", "leading-relaxed mb-4", + "Two optional@table keys decide where a table's rows LIVE. Both default " .. "to today's behaviour, so every table above keeps working untouched. " .. "durable: false keeps a table entirely in RAM — a full table in-process, " .. "same indexes, same @unique, same queries — that is simply empty after a " .. "restart. That is what a session store or a rate-limit counter wants: losing it on " .. "reboot is correct, and paying to make it durable is waste.");
+ b = b .. code_block("@table(name: \"sessions\", durable: false)\nclass Session {\n sid: Text @unique\n hits: Int\n}\n\n@table(name: \"catalog\", resident: keys, index: [sku])\nclass Product {\n sku: Text @unique\n price: Int\n blurb: Text\n}");
+ b = b .. el("p", "leading-relaxed mt-4 mb-4",
+ "resident: keys is the other direction: only the KEY stays in memory. The " .. "row body lives in the write-ahead log and is read back on demand, so a table can be " .. "far larger than RAM without a page-fault cliff. Measured on a product catalogue, the " .. "resident set is 2.55× smaller than the same table fully resident. " .. "Such a table has nowhere to keep its rows without a log, so declaring it and starting " .. "without WO_DATA is REFUSED at startup rather than silently ignored.");
+ b = b .. el("p", "leading-relaxed mb-4",
+ "Reads and writes are unchanged — the storage mode is not a different API:");
+ b = b .. code_block("let hits = from x in Product where x.sku == \"a-1\" take 1 select x;\nif len(hits) == 1 {\n hits[0].price = 1299; -- an ordinary assignment\n}");
+ b = b .. el("p", "leading-relaxed mt-4",
+ "Underneath, that assignment appends a delta — which field changed, plus a " .. "back-pointer to the row's previous record — instead of rewriting the whole row. A read " .. "walks that chain backward, newest wins. Left alone the chain would grow forever on a " .. "hot row, so it is BOUNDED: past 16 links an update writes a full row image and the " .. "chain restarts at zero. A row updated a million times still costs a bounded number of " .. "record reads, on read and on replay alike.");
+ return b;
+}
+
fn ch_actors() -> Text {
let b = el("p", "leading-relaxed mb-4",
"Concurrency is actors on fibers: spawn makes an actor from a class with a " .. "receive method, send delivers one message at a time, and " .. "ownership MOVES with the message — no locks, no shared mutable state, no data races " .. "by construction. Blocking calls park the fiber; the shard serves others meanwhile. " .. "One VM per core by default; mailboxes are bounded (a full one is a catchable trap).");
@@ -94,7 +108,8 @@ pub fn seed_chapters() {
insert Chapter { slug: "classes", ord: 4, title: "Classes and interfaces", body: ch_classes() };
insert Chapter { slug: "optionals", ord: 5, title: "Optionals and traps", body: ch_optionals() };
insert Chapter { slug: "tables", ord: 6, title: "@table: the built-in database", body: ch_tables() };
- insert Chapter { slug: "actors", ord: 7, title: "Actors and fibers", body: ch_actors() };
- insert Chapter { slug: "deps", ord: 8, title: "Dependencies", body: ch_deps() };
- insert Chapter { slug: "serving", ord: 9, title: "Serving the web (this site)", body: ch_serving() };
+ insert Chapter { slug: "storage", ord: 7, title: "Storage modes: durable and resident", body: ch_storage() };
+ insert Chapter { slug: "actors", ord: 8, title: "Actors and fibers", body: ch_actors() };
+ insert Chapter { slug: "deps", ord: 9, title: "Dependencies", body: ch_deps() };
+ insert Chapter { slug: "serving", ord: 10, title: "Serving the web (this site)", body: ch_serving() };
}
diff --git a/docs/examples/site/home/view.wo b/docs/examples/site/home/view.wo
index a4b3d4c..ae853d7 100644
--- a/docs/examples/site/home/view.wo
+++ b/docs/examples/site/home/view.wo
@@ -24,7 +24,7 @@ pub class HomePage {
-- why-cards (2x2 grid, collapses on small screens)
let cards = card("One binary", "woc build emits a self-contained executable: VM, your bytecode, the database engine. Deploys are a file copy; the runtime swaps code in place.");
cards = cards .. card("Memory safety, no tax", "Rust-shaped ownership checked at compile time; where ownership cannot express the shape, the compiler infers GC — per shard, no global pause.");
- cards = cards .. card("The database is built in", "Every class is a table. Inserts are WAL-logged before they acknowledge; restart replays. No server to operate, no connection string.");
+ cards = cards .. card("The database is built in", "Every class is a table. Inserts are WAL-logged before they acknowledge; restart replays. No server to operate, no connection string — and a table can declare itself RAM-only, or keep only its keys in memory and outgrow RAM.");
cards = cards .. card("Actors on every core", "spawn returns an address, send moves ownership. Fibers park on io_uring instead of blocking threads — no async/await, ever.");
let grid = el("div", "grid grid-cols-2 gap-6 mb-8", cards);
diff --git a/scripts/site-accept.sh b/scripts/site-accept.sh
index 00712cf..640c111 100755
--- a/scripts/site-accept.sh
+++ b/scripts/site-accept.sh
@@ -119,6 +119,8 @@ expect "tailwind sheet inlined" "$(hit /)" 200 ".btn{"
expect "chapter renders a code sample" "$(hit /ch/hello)" 200 "fn main"
expect "escaped interpolation visible" "$(hit /ch/values)" 200 '${port}'
expect "unknown chapter is a 404 page" "$(hit /ch/nope)" 404 "No such chapter"
+expect "storage chapter documents both modes" "$(hit /ch/storage)" 200 "resident: keys"
+expect "storage chapter states the chain bound" "$(hit /ch/storage)" 200 "past 16 links"
expect "install guide renders" "$(hit /install)" 200 "tar -C /usr/local"
expect "packages index lists both" "$(hit /packages)" 200 "/packages/porch"
expect "package detail shows its dep" "$(hit /packages/view)" 200 "writeonce-view"