Watch the world folder: auto spawn/upgrade, broker watch/unwatch, retire scan
ci / check (push) Failing after 7m36s
ci / check (push) Failing after 7m36s
The host watches the whole world folder recursively with the notify crate
(any writer counts, .sausage-tmp-* skipped, ~100 ms coalescing per path).
A new or changed actor/<id>/actor.wasm (or new folder actor/<id>) is
booted as at start once quiet: new = spawn (tested first), changed =
upgrade. The broker `scan` request is gone; the boot pass stays, plus a
full pass when the OS watcher overflows.
New broker requests watch/unwatch {path} (saved in actor.json `watches`)
deliver fs_changed {path, op: write|create|remove|rename, to?} from `fs`,
not routed, dropped when the mailbox is full; overflow sends every watcher
one fs_changed {path:"/actor", op:"rescan"}.
Card #91 (multi-host step 1).
This commit is contained in:
+39
-9
@@ -68,7 +68,7 @@ Each instance sees three folders:
|
||||
| `/actor` | read-only | the `actor/` folder of the world |
|
||||
|
||||
`/actor/<id>/` holds everything about an actor: `actor.wasm`, `actor.json`
|
||||
(installed version, running, subscriptions, tapped), `source/`, `public/`, `data/`,
|
||||
(installed version, running, subscriptions, tapped, watches), `source/`, `public/`, `data/`,
|
||||
`stderr.log`, and `versions/<n>/`. `ls /actor` lists all actors.
|
||||
|
||||
Files are plain POSIX files: a write is visible at once. To publish a file
|
||||
@@ -110,14 +110,15 @@ Any request can be answered with `{"type": "fs_error", "errno": "no-entry"}`.
|
||||
- No reply within 10 seconds, or an actor that does not exist, makes the
|
||||
operation fail (`io`, or `no-entry`). An actor that opens its own `fs/`
|
||||
waits for itself and fails after the timeout.
|
||||
- `fs` is reserved: no actor can have that ID.
|
||||
- `fs` is reserved: no actor can have that ID. It also sends `fs_changed`
|
||||
to watchers (see Watches).
|
||||
|
||||
## The broker
|
||||
|
||||
Requests to `broker`. The answer is a message whose `reply_to` is the ID of
|
||||
the request: the request's `type` in past tense (`spawned`, `upgraded`,
|
||||
`rolled_back`, `stopped`, `started`, `removed`, `subscribed`, `unsubscribed`,
|
||||
`tapping`, `untapped`, `scanned`, `exiting`), or `{"type": "error", "reason": "..."}`.
|
||||
`tapping`, `untapped`, `watching`, `unwatched`, `exiting`), or `{"type": "error", "reason": "..."}`.
|
||||
|
||||
| `type` | Fields | Effect |
|
||||
| --- | --- | --- |
|
||||
@@ -128,7 +129,7 @@ the request: the request's `type` in past tense (`spawned`, `upgraded`,
|
||||
| `remove` | `actor` | stop the actor (normal shutdown), then delete its whole folder `/actor/<id>/` (all versions, `data/`, `public/`, logs); answer `{"type": "removed", "actor"}`. Messages to it are then undeliverable, and the ID can be spawned again. An unknown actor or a native one (`broker`, `console`, `cargo`, `fs`) is an `error` |
|
||||
| `subscribe`, `unsubscribe` | `actor` | receive (or not) what `actor` publishes |
|
||||
| `tap`, `untap` | | receive (or not) a copy of every message the broker routes |
|
||||
| `scan` | | boot new or changed `actor.wasm` files |
|
||||
| `watch`, `unwatch` | `path` | receive (or not) `fs_changed` for every change of `path` or anything under it; answer `{"type": "watching" \| "unwatched", "path"}` with the path as an `/actor/...` path |
|
||||
| `exit` | | answer `exiting`, stop every actor, and end the `sausage` process (a wrapper like `nix run .#loop` starts it again, updated) |
|
||||
|
||||
Paths are `/actor/...`, or the sender's `/public/...` or `/data/...`.
|
||||
@@ -151,6 +152,31 @@ including its own), for debugging, logging, or a live view of the world:
|
||||
- Like subscriptions, a tap is saved in `actor.json` (`"tapped": true`) and
|
||||
survives restarts and upgrades. `untap` ends it.
|
||||
|
||||
**Watches.** The host watches the whole world folder with the OS file
|
||||
watcher: every change inside it counts, whoever makes it (actors, a 9P mount,
|
||||
a shell or `git` on the host). Actor-served files (`/actor/<id>/fs/`) are not
|
||||
on disk and do not count. After `watch {path}`, the sender gets for each
|
||||
change of `path` or a path under it (`/actor/a` covers `/actor/a/x`, not
|
||||
`/actor/ab`):
|
||||
|
||||
```json
|
||||
{"type": "fs_changed", "path": "/actor/notes/public/list.md", "op": "write"}
|
||||
```
|
||||
|
||||
- `op` is `write`, `create`, `remove`, or `rename` (then `to` is the new
|
||||
path; a watcher of either path gets it).
|
||||
- Changes of one path within about 100 ms become one event (a `write` after
|
||||
a `create` stays a `create`).
|
||||
- The host's temporary files (names starting with `.sausage-tmp-`) are
|
||||
skipped; renaming one onto a file is a `write` of that file.
|
||||
- Events come from `fs`. Like tap copies, they are not routed, a full
|
||||
mailbox drops them, and they never block the writer. Never answer them.
|
||||
- If the OS drops events, every watcher gets one
|
||||
`{"type": "fs_changed", "path": "/actor", "op": "rescan"}`: anything may
|
||||
have changed.
|
||||
- A watch is saved in `actor.json` (`"watches": [...]`) and survives
|
||||
restarts and upgrades. `unwatch` with the same path ends it.
|
||||
|
||||
**Versions.** A new version (from `spawn`, `upgrade`, or a changed
|
||||
`actor.wasm` on disk) is tested first, while the old version keeps running.
|
||||
Only when its tests pass does the broker copy it to `versions/<n>/`
|
||||
@@ -189,11 +215,15 @@ the delay up to 30 s. A new version that crashes 3 times within 30 seconds
|
||||
after an upgrade goes back to the previous version. The actor that asked for
|
||||
the upgrade gets `{"type": "rolled_back", "from", "to", "reason", "last_exit"}`.
|
||||
|
||||
**The folder is the truth.** At start, and on `scan`, the broker boots every
|
||||
`actor/<id>/actor.wasm`. If the file differs from the installed version, it
|
||||
is a new version, tested with the tests in `actor/<id>/source/`. If its
|
||||
tests fail, the file stays where it is, the reason goes to `stderr.log`, and
|
||||
the next scan tries again.
|
||||
**The folder is the truth.** At start, the broker boots every
|
||||
`actor/<id>/actor.wasm`. Afterwards, the file watcher reports a new or
|
||||
changed `actor/<id>/actor.wasm` (or a new folder `actor/<id>/`) once it is
|
||||
quiet for about 100 ms: a new file becomes a new actor, a changed one an
|
||||
upgrade, tested first with the tests in `actor/<id>/source/`. A file equal
|
||||
to the installed version changes nothing. If its tests fail, the file stays
|
||||
where it is, the reason goes to `stderr.log`, and the next change of the
|
||||
file tries again. When the OS drops events, the broker checks every actor
|
||||
folder again, as at start.
|
||||
|
||||
## The `cargo` actor
|
||||
|
||||
|
||||
Reference in New Issue
Block a user