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"