Devlog

Librarian Architecture — Idle Is Work, Traffic Is Yield

Why the crawler should eat all available compute when the library is empty — and graciously step aside when someone walks in.

The frustration

The server is an 8GB Hetzner box at £30/month. For most of the day no-one's on it, and the thing that reads the web for VINE — the crawler — is throttled down to a polite trickle. When someone finally walks in and talks to her, that's when the crawler suddenly decides to open its bag and eat 5GB of RAM trying to build indexes, and the chat times out.

Wrong way round. Entirely wrong way round.

The fix in one sentence

When the library is empty, the librarian is knee-deep in the stacks. When someone walks in, she looks up, puts the book down, and helps.

Idle is work. Traffic is yield.

What that actually looks like

Four states, named the way a real librarian would describe her day:

  • Reading — nobody here, full compute, all caps lifted, reading everything
  • Glancing — someone just looked in, she glanced up but kept working
  • Serving — patrons are asking questions, she's paused her own work
  • Rush — the place is rammed, everything non-essential is stopped

The state is a position on a 0–8 axis — not a switch. As the load fraction rises, the position drifts toward the next basin. Each subsystem that holds memory asks "how much can I have, right now?" and the answer scales smoothly with the position. Nothing branches. Nothing flips.

Why it has to be a surface, not a switch

A switch gives you the same chat-timeout bug we just lived through. Between reading and serving there's got to be a ramp — her pausing the page, marking her place, folding the corner. If that transition is a cliff edge you pay for it at exactly the worst moment.

Free-RAM safety floor

One override, sitting on top of everything: if the OS has less than 500MB free, we force rush regardless of how many people are in the building. Caches don't get to eat the swap file just because they feel like reading.

What's landed today

  • The state machine stub, with geometric basin settling (no elif chains anywhere — they kept trying to creep in, I kept taking them out)
  • The free-RAM override
  • Wiring through the text store + forager so they ask the librarian before allocating

What's next

  • Tell the frontend the honest numbers instead of the capped ones
  • Image observer (a separate post)
  • Storage migration — the 3.3GB crawler text file has to stop being rewritten on every save. Append-only or bust.

The librarian metaphor is the one I keep coming back to in this project. It's not decoration. It's the design.