@coda-console/kernel 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/docs/design.md +89 -18
- package/package.json +1 -1
- package/sim/host.mjs +12 -0
- package/sim/scenarios.mjs +206 -12
- package/sim/world.mjs +166 -19
- package/src/kernel.mjs +114 -13
- package/src/schema.mjs +35 -7
package/src/schema.mjs
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
// The kernel's tables and every statement it runs on them. Keeping the statements in one place lets a test check the
|
|
2
2
|
// query plan of each one: none of them may scan a table.
|
|
3
3
|
|
|
4
|
+
const UNENDED_CALL = "state IN ('sending', 'open')";
|
|
5
|
+
const UNENDED_RUN = "status IN ('working', 'input-required')";
|
|
6
|
+
|
|
4
7
|
// Entry n upgrades the schema from version n to n + 1. Migrations only go forward: the first entry is the 0.1.0 baseline,
|
|
5
8
|
// and no entry changes once released, since a database already past it would never run it again. A changed table adds
|
|
6
|
-
// an entry at the end.
|
|
9
|
+
// an entry at the end. An entry is SQL text, or a function of the host's now() giving it, for one that needs the time it
|
|
10
|
+
// runs at.
|
|
7
11
|
export const MIGRATIONS = [
|
|
8
12
|
`CREATE TABLE kernel_calls (
|
|
9
13
|
from_addr TEXT NOT NULL,
|
|
@@ -98,13 +102,20 @@ export const MIGRATIONS = [
|
|
|
98
102
|
CREATE INDEX kernel_outbox_lane ON kernel_outbox (lane, pos);
|
|
99
103
|
CREATE INDEX kernel_outbox_call ON kernel_outbox (call_from, call_id);
|
|
100
104
|
CREATE INDEX kernel_outbox_from ON kernel_outbox (from_addr, pos);`,
|
|
105
|
+
// 0.3.0: since when an ended call or run has been kept only to tell a repeat, by the host's clock, for forget() (9).
|
|
106
|
+
// A row that ended before this migration counts from it: by the host's clock, not SQLite's, as a simulated world's is
|
|
107
|
+
// not real; a letter about it still owed moves it on once confirmed. Like the rest of a migration, the backfill is no
|
|
108
|
+
// statement of SQL: it visits every row, once.
|
|
109
|
+
now => `ALTER TABLE kernel_calls ADD COLUMN quiet_at INTEGER;
|
|
110
|
+
ALTER TABLE kernel_runs ADD COLUMN quiet_at INTEGER;
|
|
111
|
+
UPDATE kernel_calls SET quiet_at = ${now} WHERE NOT (${UNENDED_CALL});
|
|
112
|
+
UPDATE kernel_runs SET quiet_at = ${now} WHERE NOT (${UNENDED_RUN});
|
|
113
|
+
CREATE INDEX kernel_calls_quiet ON kernel_calls (quiet_at);
|
|
114
|
+
CREATE INDEX kernel_runs_quiet ON kernel_runs (quiet_at);`,
|
|
101
115
|
];
|
|
102
116
|
|
|
103
117
|
export const META = 'CREATE TABLE IF NOT EXISTS kernel_meta (key TEXT PRIMARY KEY, value INTEGER NOT NULL)';
|
|
104
118
|
|
|
105
|
-
const UNENDED_CALL = "state IN ('sending', 'open')";
|
|
106
|
-
const UNENDED_RUN = "status IN ('working', 'input-required')";
|
|
107
|
-
|
|
108
119
|
export const SQL = {
|
|
109
120
|
version: "SELECT value FROM kernel_meta WHERE key = 'version'",
|
|
110
121
|
setVersion: "INSERT INTO kernel_meta (key, value) VALUES ('version', ?) ON CONFLICT (key) DO UPDATE SET value = excluded.value",
|
|
@@ -112,30 +123,46 @@ export const SQL = {
|
|
|
112
123
|
call: 'SELECT * FROM kernel_calls WHERE from_addr = ? AND id = ?',
|
|
113
124
|
addCall: `INSERT INTO kernel_calls (from_addr, id, to_addr, op, digest, input, parent, lane, by_from, by_id, awaited, memo, state)
|
|
114
125
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'sending')`,
|
|
126
|
+
// An imported call was taken already: open, at version 0, so that the first status its reread hears is newer.
|
|
127
|
+
adoptCall: `INSERT INTO kernel_calls (from_addr, id, to_addr, op, digest, input, parent, lane, by_from, by_id, awaited, memo, state, status, rev)
|
|
128
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'open', 'working', 0) RETURNING *`,
|
|
115
129
|
// A receipt says the callee took the call; the run it took is working at version 1 (C6).
|
|
116
130
|
openCall: "UPDATE kernel_calls SET state = 'open', status = 'working', rev = 1 WHERE from_addr = ? AND id = ? AND state = 'sending'",
|
|
117
131
|
hearCall: "UPDATE kernel_calls SET state = 'open', status = ?, rev = ?, asked = ?, asking = ?, progress = ? WHERE from_addr = ? AND id = ? RETURNING *",
|
|
118
|
-
endCall: 'UPDATE kernel_calls SET state = ?, result = ?, error = ?, progress = ?, owed = 1, local = ? WHERE from_addr = ? AND id = ?',
|
|
132
|
+
endCall: 'UPDATE kernel_calls SET state = ?, result = ?, error = ?, progress = ?, owed = 1, local = ?, quiet_at = ? WHERE from_addr = ? AND id = ?',
|
|
119
133
|
paidCall: 'UPDATE kernel_calls SET owed = 0 WHERE from_addr = ? AND id = ?',
|
|
120
134
|
owedCalls: 'SELECT from_addr, id FROM kernel_calls WHERE owed = 1',
|
|
121
135
|
openCalls: "SELECT * FROM kernel_calls WHERE state = 'open'",
|
|
122
136
|
detach: 'UPDATE kernel_calls SET awaited = 0 WHERE from_addr = ? AND id = ?',
|
|
123
137
|
// The caller ends the call itself and takes no result from it; a late outcome is kept apart (C12).
|
|
124
|
-
abandonCall: "UPDATE kernel_calls SET state = 'failed', error = ?, local = 1, abandoned = 1 WHERE from_addr = ? AND id = ?",
|
|
138
|
+
abandonCall: "UPDATE kernel_calls SET state = 'failed', error = ?, local = 1, abandoned = 1, quiet_at = ? WHERE from_addr = ? AND id = ?",
|
|
125
139
|
lateCall: 'UPDATE kernel_calls SET late = ?, owed = 1 WHERE from_addr = ? AND id = ?',
|
|
140
|
+
// A letter about an ended call or run has just left the outbox: a repeat of it may come until its retention (9).
|
|
141
|
+
quietCall: 'UPDATE kernel_calls SET quiet_at = ? WHERE from_addr = ? AND id = ? AND quiet_at IS NOT NULL',
|
|
126
142
|
awaiting: `SELECT * FROM kernel_calls WHERE by_from = ? AND by_id = ? AND awaited = 1 AND ${UNENDED_CALL}`,
|
|
127
143
|
calls: `SELECT * FROM kernel_calls WHERE from_addr = ? AND ${UNENDED_CALL} AND state = coalesce(?, state)`,
|
|
144
|
+
// An ended call whose handler has committed and about which no letter is owed, quiet since before the retention, is
|
|
145
|
+
// no longer needed to tell a repeat (9); an unended row is not quiet and is never taken.
|
|
146
|
+
forgetCalls: `DELETE FROM kernel_calls AS c WHERE quiet_at < ? AND owed = 0
|
|
147
|
+
AND NOT EXISTS (SELECT 1 FROM kernel_outbox AS o WHERE o.call_from = c.from_addr AND o.call_id = c.id)`,
|
|
128
148
|
|
|
129
149
|
run: 'SELECT * FROM kernel_runs WHERE from_addr = ? AND id = ?',
|
|
130
150
|
addRun: `INSERT INTO kernel_runs (from_addr, id, to_addr, op, digest, parent, awaited, root, status, rev)
|
|
131
151
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, 'working', 1)`,
|
|
152
|
+
// An imported run was taken at version 1, as a run is (C6); one waiting for input is on its first question. One that
|
|
153
|
+
// has ended is never quiet: it owes no outcome letter, so nothing here tells when its caller has the outcome (9).
|
|
154
|
+
adoptRun: `INSERT INTO kernel_runs (from_addr, id, to_addr, op, digest, parent, awaited, root, status, rev, asked, asking, result, error, progress)
|
|
155
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, 0, ?, 1, ?, ?, ?, ?, ?) RETURNING *`,
|
|
132
156
|
dropRun: 'DELETE FROM kernel_runs WHERE from_addr = ? AND id = ?',
|
|
133
157
|
haltRun: 'UPDATE kernel_runs SET cancel_asked = 1 WHERE from_addr = ? AND id = ?',
|
|
134
158
|
updateRun: 'UPDATE kernel_runs SET status = ?, asked = ?, asking = ?, progress = ?, rev = rev + 1 WHERE from_addr = ? AND id = ? RETURNING *',
|
|
135
159
|
// Without a progress of its own, an outcome carries the run's last one (C4).
|
|
136
|
-
endRun: `UPDATE kernel_runs SET status = ?, result = ?, error = ?, progress = coalesce(?, progress), rev = rev + 1
|
|
160
|
+
endRun: `UPDATE kernel_runs SET status = ?, result = ?, error = ?, progress = coalesce(?, progress), rev = rev + 1, quiet_at = ?
|
|
137
161
|
WHERE from_addr = ? AND id = ? RETURNING to_addr, root, progress`,
|
|
138
162
|
runs: `SELECT * FROM kernel_runs WHERE to_addr = ? AND ${UNENDED_RUN} AND status = coalesce(?, status)`,
|
|
163
|
+
quietRun: 'UPDATE kernel_runs SET quiet_at = ? WHERE from_addr = ? AND id = ? AND quiet_at IS NOT NULL',
|
|
164
|
+
forgetRuns: `DELETE FROM kernel_runs AS r WHERE quiet_at < ?
|
|
165
|
+
AND NOT EXISTS (SELECT 1 FROM kernel_outbox AS o WHERE o.call_from = r.from_addr AND o.call_id = r.id) RETURNING from_addr, id`,
|
|
139
166
|
|
|
140
167
|
timer: 'SELECT * FROM kernel_timers WHERE from_addr = ? AND id = ?',
|
|
141
168
|
timers: 'SELECT * FROM kernel_timers WHERE from_addr = ?',
|
|
@@ -152,6 +179,7 @@ export const SQL = {
|
|
|
152
179
|
// Only the number of an answer taken is kept, and which question it answered (C8, S8).
|
|
153
180
|
answer: 'SELECT 1 FROM kernel_answers WHERE from_addr = ? AND id = ?',
|
|
154
181
|
addAnswer: 'INSERT INTO kernel_answers (from_addr, id, run_from, run_id, asked) VALUES (?, ?, ?, ?, ?)',
|
|
182
|
+
forgetAnswers: 'DELETE FROM kernel_answers WHERE run_from = ? AND run_id = ?',
|
|
155
183
|
|
|
156
184
|
// A letter is identified by `seq` and ordered by `pos`: a letter that replaces another takes the other's `pos` (R3).
|
|
157
185
|
owe: `INSERT INTO kernel_outbox (pos, from_addr, to_addr, lane, kind, call_from, call_id, envelope)
|