@haikit/postgres 0.13.0 → 0.15.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 +25 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +151 -15
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/index.ts +164 -16
package/README.md
CHANGED
|
@@ -52,10 +52,13 @@ With your own migration tool, apply the new `ALTER TABLE` from `schema` before
|
|
|
52
52
|
deploying. Rows written before the column existed are held to the window your
|
|
53
53
|
code declares.
|
|
54
54
|
|
|
55
|
-
**Upgrading to 0.
|
|
55
|
+
**Upgrading to 0.13:** `migrate()` adds `haikit_notices`, and `notice_seq` and
|
|
56
56
|
`noticed_through` to `haikit_conversations`, for `hai.notify`. As before, if
|
|
57
57
|
you call `migrate()` at startup there is nothing to do.
|
|
58
58
|
|
|
59
|
+
**Upgrading to 0.14:** `migrate()` adds `kind` to `haikit_notices`, and
|
|
60
|
+
`wakes` and `woke_through` to `haikit_conversations`, for wake notices.
|
|
61
|
+
|
|
59
62
|
Handles are numbered per conversation, so every conversation's digests start at
|
|
60
63
|
`ui_01`.
|
|
61
64
|
|
|
@@ -89,6 +92,27 @@ const store = pgStore(pool, {
|
|
|
89
92
|
A notice belongs to its conversation and goes when it does: deleting a
|
|
90
93
|
conversation deletes its notices. `sweepOrphans` never touches them.
|
|
91
94
|
|
|
95
|
+
## Wake turns across servers
|
|
96
|
+
|
|
97
|
+
With several app servers, the same `listen` connection carries a wake turn
|
|
98
|
+
from the server running it to the others, so a browser connected to any of
|
|
99
|
+
them sees the reply. `publish` sends each of the turn's frames as `NOTIFY`
|
|
100
|
+
on `haikit_turns`. A frame longer than one `NOTIFY` can hold (just under
|
|
101
|
+
8,000 bytes) goes as several, sent in one statement, and is put back
|
|
102
|
+
together on arrival. Frames are sent one at a time per conversation, each
|
|
103
|
+
committed before the next, so they arrive in order. Every store can publish;
|
|
104
|
+
only one given `listen` hears. A message past `MAX_PUBLISH_BYTES` (about
|
|
105
|
+
52 MB, far beyond any frame) is refused, not sent.
|
|
106
|
+
|
|
107
|
+
**Upgrading:** `migrate()` adds `wake_turns` to `haikit_conversations`, each
|
|
108
|
+
conversation's count of wake turns, which numbers them so a late frame from
|
|
109
|
+
an older turn can't take over from a newer one.
|
|
110
|
+
|
|
111
|
+
It is best effort, as `NOTIFY` is: a server whose listening connection is
|
|
112
|
+
down misses those frames, and a tab there sees the reply when it next
|
|
113
|
+
reconnects to a server that has it, or not at all. The frames themselves
|
|
114
|
+
are never stored, so there is nothing of them to clean up.
|
|
115
|
+
|
|
92
116
|
## Cleaning up
|
|
93
117
|
|
|
94
118
|
A turn that is overtaken mid-flight can leave payload rows that the surviving
|
package/dist/index.d.ts
CHANGED
|
@@ -49,6 +49,10 @@ export interface Listener {
|
|
|
49
49
|
}
|
|
50
50
|
/** The channel `putNotice` notifies on, with the conversation id as payload. */
|
|
51
51
|
export declare const NOTICE_CHANNEL = "haikit_notices";
|
|
52
|
+
/** The channel `publish` carries messages on, in pieces small enough for NOTIFY. */
|
|
53
|
+
export declare const TURN_CHANNEL = "haikit_turns";
|
|
54
|
+
/** The longest message `publish` takes, in UTF-8 bytes: what MAX_PIECES pieces of base64 hold. */
|
|
55
|
+
export declare const MAX_PUBLISH_BYTES: number;
|
|
52
56
|
/**
|
|
53
57
|
* The schema, one statement per entry. `migrate()` runs these; export them to
|
|
54
58
|
* your own migration tool instead if you have one.
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAML,KAAK,YAAY,EAClB,MAAM,cAAc,CAAC;AAEtB;;;;;;;;GAQG;AACH,MAAM,WAAW,SAAS;IACxB,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,GAAG,EAAE,CAAA;KAAE,CAAC,CAAC;CACnE;AAED,MAAM,WAAW,cAAc;IAC7B;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,QAAQ,CAAC;CACnB;AAED;;;GAGG;AACH,MAAM,WAAW,QAAQ;IACvB,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CAChF;AAID,gFAAgF;AAChF,eAAO,MAAM,cAAc,mBAAmB,CAAC;AAE/C;;;GAGG;AACH,eAAO,MAAM,MAAM,EAAE,SAAS,MAAM,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAML,KAAK,YAAY,EAClB,MAAM,cAAc,CAAC;AAEtB;;;;;;;;GAQG;AACH,MAAM,WAAW,SAAS;IACxB,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,GAAG,EAAE,CAAA;KAAE,CAAC,CAAC;CACnE;AAED,MAAM,WAAW,cAAc;IAC7B;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,QAAQ,CAAC;CACnB;AAED;;;GAGG;AACH,MAAM,WAAW,QAAQ;IACvB,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CAChF;AAID,gFAAgF;AAChF,eAAO,MAAM,cAAc,mBAAmB,CAAC;AAE/C,oFAAoF;AACpF,eAAO,MAAM,YAAY,iBAAiB,CAAC;AAc3C,kGAAkG;AAClG,eAAO,MAAM,iBAAiB,QAA2C,CAAC;AAE1E;;;GAGG;AACH,eAAO,MAAM,MAAM,EAAE,SAAS,MAAM,EAiEnC,CAAC;AAEF,8FAA8F;AAC9F,wBAAsB,OAAO,CAAC,EAAE,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAI1D;AAmED,wBAAgB,OAAO,CAAC,EAAE,EAAE,SAAS,EAAE,OAAO,GAAE,cAAmB,GAAG,YAAY,CAoWjF;AAED,MAAM,WAAW,YAAY;IAC3B;;;;OAIG;IACH,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,YAAY,CAAC,EAAE,EAAE,SAAS,EAAE,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CAYrG"}
|
package/dist/index.js
CHANGED
|
@@ -14,6 +14,19 @@ import { ConversationBusy, StaleLease, } from "@haikit/core";
|
|
|
14
14
|
const LEASE_MS = 120_000;
|
|
15
15
|
/** The channel `putNotice` notifies on, with the conversation id as payload. */
|
|
16
16
|
export const NOTICE_CHANNEL = "haikit_notices";
|
|
17
|
+
/** The channel `publish` carries messages on, in pieces small enough for NOTIFY. */
|
|
18
|
+
export const TURN_CHANNEL = "haikit_turns";
|
|
19
|
+
/**
|
|
20
|
+
* The most of a message one NOTIFY carries. Postgres refuses payloads of 8000
|
|
21
|
+
* bytes or more; a piece is ASCII, so this leaves room for its header.
|
|
22
|
+
*/
|
|
23
|
+
const PIECE = 7_000;
|
|
24
|
+
/** How long half a message waits for the rest before it is given up on. */
|
|
25
|
+
const PIECE_TIMEOUT_MS = 30_000;
|
|
26
|
+
/** The most pieces one message may claim to have: about 52 MB of message, far past any frame. */
|
|
27
|
+
const MAX_PIECES = 10_000;
|
|
28
|
+
/** The longest message `publish` takes, in UTF-8 bytes: what MAX_PIECES pieces of base64 hold. */
|
|
29
|
+
export const MAX_PUBLISH_BYTES = Math.floor((PIECE * MAX_PIECES * 3) / 4);
|
|
17
30
|
/**
|
|
18
31
|
* The schema, one statement per entry. `migrate()` runs these; export them to
|
|
19
32
|
* your own migration tool instead if you have one.
|
|
@@ -32,11 +45,19 @@ export const schema = [
|
|
|
32
45
|
-- the last notice number given out, and the last one its history took in
|
|
33
46
|
notice_seq integer NOT NULL DEFAULT 0,
|
|
34
47
|
noticed_through integer NOT NULL DEFAULT 0,
|
|
48
|
+
-- when recent wake turns started, and the last notice that may no longer start one
|
|
49
|
+
wakes jsonb NOT NULL DEFAULT '[]',
|
|
50
|
+
woke_through integer NOT NULL DEFAULT 0,
|
|
51
|
+
-- how many wake turns it has started, to tell an older turn's frames from a newer's
|
|
52
|
+
wake_turns integer NOT NULL DEFAULT 0,
|
|
35
53
|
updated_at timestamptz NOT NULL DEFAULT now()
|
|
36
54
|
)`,
|
|
37
55
|
// a table created before notices existed gains the columns
|
|
38
56
|
`ALTER TABLE haikit_conversations ADD COLUMN IF NOT EXISTS notice_seq integer NOT NULL DEFAULT 0`,
|
|
39
57
|
`ALTER TABLE haikit_conversations ADD COLUMN IF NOT EXISTS noticed_through integer NOT NULL DEFAULT 0`,
|
|
58
|
+
`ALTER TABLE haikit_conversations ADD COLUMN IF NOT EXISTS wakes jsonb NOT NULL DEFAULT '[]'`,
|
|
59
|
+
`ALTER TABLE haikit_conversations ADD COLUMN IF NOT EXISTS woke_through integer NOT NULL DEFAULT 0`,
|
|
60
|
+
`ALTER TABLE haikit_conversations ADD COLUMN IF NOT EXISTS wake_turns integer NOT NULL DEFAULT 0`,
|
|
40
61
|
// Append-only, and never an orphan: a notice belongs to a conversation that
|
|
41
62
|
// exists, and goes when it does.
|
|
42
63
|
`CREATE TABLE IF NOT EXISTS haikit_notices (
|
|
@@ -48,8 +69,11 @@ export const schema = [
|
|
|
48
69
|
payload jsonb NOT NULL,
|
|
49
70
|
model text,
|
|
50
71
|
handle text,
|
|
72
|
+
-- 'wake', or NULL for a passive notice
|
|
73
|
+
kind text,
|
|
51
74
|
PRIMARY KEY (conversation_id, seq)
|
|
52
75
|
)`,
|
|
76
|
+
`ALTER TABLE haikit_notices ADD COLUMN IF NOT EXISTS kind text`,
|
|
53
77
|
// Write-once. Nothing about a payload changes after insert — everything that
|
|
54
78
|
// does lives on the fenced conversation row (see Conversation.frozen).
|
|
55
79
|
`CREATE TABLE IF NOT EXISTS haikit_payloads (
|
|
@@ -87,9 +111,10 @@ export async function migrate(db) {
|
|
|
87
111
|
const CONVERSATION_COLUMNS = `
|
|
88
112
|
id, status, messages::text AS messages, handles::text AS handles,
|
|
89
113
|
frozen::text AS frozen, pending::text AS pending, lease_token, noticed_through,
|
|
114
|
+
wakes::text AS wakes, woke_through, wake_turns,
|
|
90
115
|
(extract(epoch FROM lease_until) * 1000)::float8 AS lease_until_ms`;
|
|
91
116
|
const NOTICE_COLUMNS = `
|
|
92
|
-
conversation_id, seq, name, version, payload::text AS payload, model, handle,
|
|
117
|
+
conversation_id, seq, name, version, payload::text AS payload, model, handle, kind,
|
|
93
118
|
(extract(epoch FROM created_at) * 1000)::float8 AS created_at_ms`;
|
|
94
119
|
// The window is read as text too: 'Infinity' is how "never" is stored, and
|
|
95
120
|
// drivers need not agree on turning it into a number.
|
|
@@ -108,6 +133,9 @@ const toConversation = (row) => ({
|
|
|
108
133
|
leaseUntil: row.lease_until_ms == null ? null : Number(row.lease_until_ms),
|
|
109
134
|
leaseToken: row.lease_token,
|
|
110
135
|
noticedThrough: Number(row.noticed_through ?? 0),
|
|
136
|
+
wakes: row.wakes == null ? [] : json(row.wakes),
|
|
137
|
+
wokeThrough: Number(row.woke_through ?? 0),
|
|
138
|
+
wakeTurns: Number(row.wake_turns ?? 0),
|
|
111
139
|
});
|
|
112
140
|
const toNotice = (row) => ({
|
|
113
141
|
conversationId: row.conversation_id,
|
|
@@ -118,6 +146,7 @@ const toNotice = (row) => ({
|
|
|
118
146
|
payload: json(row.payload),
|
|
119
147
|
model: row.model,
|
|
120
148
|
...(row.handle == null ? {} : { handle: row.handle }),
|
|
149
|
+
...(row.kind === "wake" ? { kind: "wake" } : {}),
|
|
121
150
|
});
|
|
122
151
|
const toPayload = (row) => ({
|
|
123
152
|
handle: row.handle,
|
|
@@ -140,20 +169,118 @@ export function pgStore(db, options = {}) {
|
|
|
140
169
|
if (!Number.isFinite(leaseMs) || leaseMs <= 0) {
|
|
141
170
|
throw new RangeError(`leaseMs must be a positive, finite number of milliseconds (got ${leaseMs})`);
|
|
142
171
|
}
|
|
143
|
-
// One LISTEN for the whole store, taken when something first
|
|
144
|
-
// each notification handed to whoever
|
|
172
|
+
// One LISTEN per channel for the whole store, taken when something first
|
|
173
|
+
// needs it, and each notification handed to whoever wants that conversation.
|
|
145
174
|
const waiting = new Map();
|
|
146
|
-
|
|
147
|
-
const
|
|
148
|
-
.
|
|
175
|
+
const listening = new Map();
|
|
176
|
+
const listenOn = (listener, channel, onNotify) => {
|
|
177
|
+
let started = listening.get(channel);
|
|
178
|
+
if (!started) {
|
|
179
|
+
started = listener.listen(channel, onNotify).catch((err) => {
|
|
180
|
+
// try again next time, rather than never
|
|
181
|
+
listening.delete(channel);
|
|
182
|
+
throw err;
|
|
183
|
+
});
|
|
184
|
+
listening.set(channel, started);
|
|
185
|
+
}
|
|
186
|
+
return started;
|
|
187
|
+
};
|
|
188
|
+
const listen = (listener) => listenOn(listener, NOTICE_CHANNEL, (id) => {
|
|
149
189
|
for (const wake of waiting.get(id) ?? [])
|
|
150
190
|
wake();
|
|
151
|
-
})
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
191
|
+
});
|
|
192
|
+
// ── publish / subscribe, over NOTIFY ───────────────────────────────────
|
|
193
|
+
// A message goes out as pieces of at most PIECE base64 characters, each
|
|
194
|
+
// `<conversation> <message id> <index> <count> <piece>`, all in one
|
|
195
|
+
// statement, and is put back together on the way in. Messages from this
|
|
196
|
+
// store are sent one at a time per conversation, each committed before the
|
|
197
|
+
// next starts, so they arrive in the order they were published.
|
|
198
|
+
const hearing = new Map();
|
|
199
|
+
const partial = new Map();
|
|
200
|
+
const sending = new Map();
|
|
201
|
+
const onTurn = (payload) => {
|
|
202
|
+
const [conversationId, id, index, count, piece] = String(payload).split(" ");
|
|
203
|
+
if (!hearing.has(conversationId))
|
|
204
|
+
return;
|
|
205
|
+
// The channel is shared, and anything may NOTIFY on it: a piece that
|
|
206
|
+
// doesn't hold together is dropped, never let throw out of the listener.
|
|
207
|
+
const n = Number(count);
|
|
208
|
+
const i = Number(index);
|
|
209
|
+
if (!id || !Number.isSafeInteger(n) || n < 1 || n > MAX_PIECES || !Number.isSafeInteger(i) || i < 0 || i >= n)
|
|
210
|
+
return;
|
|
211
|
+
const known = partial.get(id);
|
|
212
|
+
if (known && known.pieces.length !== n)
|
|
213
|
+
return;
|
|
214
|
+
let entry = known;
|
|
215
|
+
if (!entry) {
|
|
216
|
+
// A message whose other pieces never come (a listening connection that
|
|
217
|
+
// dropped part way) is given up on after a while, whatever else arrives.
|
|
218
|
+
const expire = setTimeout(() => partial.delete(id), PIECE_TIMEOUT_MS);
|
|
219
|
+
// and is no reason to keep the process alive
|
|
220
|
+
expire.unref?.();
|
|
221
|
+
entry = { pieces: new Array(n), got: 0, expire };
|
|
222
|
+
}
|
|
223
|
+
if (entry.pieces[i] === undefined)
|
|
224
|
+
entry.got++;
|
|
225
|
+
entry.pieces[i] = piece ?? "";
|
|
226
|
+
if (entry.got < n)
|
|
227
|
+
return void partial.set(id, entry);
|
|
228
|
+
clearTimeout(entry.expire);
|
|
229
|
+
partial.delete(id);
|
|
230
|
+
let message;
|
|
231
|
+
try {
|
|
232
|
+
message = new TextDecoder("utf-8", { fatal: true }).decode(Uint8Array.from(atob(entry.pieces.join("")), (c) => c.charCodeAt(0)));
|
|
233
|
+
}
|
|
234
|
+
catch {
|
|
235
|
+
return; // not base64, or not UTF-8 once decoded: not one of ours
|
|
236
|
+
}
|
|
237
|
+
for (const hear of [...(hearing.get(conversationId) ?? [])])
|
|
238
|
+
hear(message);
|
|
239
|
+
};
|
|
240
|
+
async function publish(conversationId, message) {
|
|
241
|
+
const bytes = new TextEncoder().encode(String(message));
|
|
242
|
+
// Refused here rather than sent and dropped by every listener, and before
|
|
243
|
+
// any of the work of encoding it.
|
|
244
|
+
if (bytes.length > MAX_PUBLISH_BYTES) {
|
|
245
|
+
throw new RangeError(`message too long to publish: ${bytes.length} bytes, over the ${MAX_PUBLISH_BYTES}-byte maximum`);
|
|
246
|
+
}
|
|
247
|
+
let binary = "";
|
|
248
|
+
for (const b of bytes)
|
|
249
|
+
binary += String.fromCharCode(b);
|
|
250
|
+
const encoded = btoa(binary);
|
|
251
|
+
const id = newToken().replaceAll("-", "");
|
|
252
|
+
const count = Math.max(1, Math.ceil(encoded.length / PIECE));
|
|
253
|
+
const pieces = Array.from({ length: count }, (_, i) => `${conversationId} ${id} ${i} ${count} ${encoded.slice(i * PIECE, (i + 1) * PIECE)}`);
|
|
254
|
+
const before = sending.get(conversationId) ?? Promise.resolve();
|
|
255
|
+
const sent = before.then(() => db.query(`SELECT pg_notify('${TURN_CHANNEL}', piece) FROM jsonb_array_elements_text($1::jsonb) AS piece`, [
|
|
256
|
+
JSON.stringify(pieces),
|
|
257
|
+
]));
|
|
258
|
+
const settled = sent.then(() => { }, () => { });
|
|
259
|
+
sending.set(conversationId, settled);
|
|
260
|
+
try {
|
|
261
|
+
await sent;
|
|
262
|
+
}
|
|
263
|
+
finally {
|
|
264
|
+
if (sending.get(conversationId) === settled)
|
|
265
|
+
sending.delete(conversationId);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
async function subscribe(conversationId, onMessage, signal) {
|
|
269
|
+
if (signal.aborted)
|
|
270
|
+
return;
|
|
271
|
+
const set = hearing.get(conversationId) ?? new Set();
|
|
272
|
+
set.add(onMessage);
|
|
273
|
+
hearing.set(conversationId, set);
|
|
274
|
+
signal.addEventListener("abort", () => {
|
|
275
|
+
set.delete(onMessage);
|
|
276
|
+
if (!set.size && hearing.get(conversationId) === set)
|
|
277
|
+
hearing.delete(conversationId);
|
|
278
|
+
}, { once: true });
|
|
279
|
+
// Resolves once LISTEN has taken hold, so nothing published after this
|
|
280
|
+
// resolves is missed. Best effort, as the contract says: a failed LISTEN
|
|
281
|
+
// is tried again next time, and this resolves all the same.
|
|
282
|
+
await listenOn(options.listen, TURN_CHANNEL, onTurn).catch(() => { });
|
|
283
|
+
}
|
|
157
284
|
async function* watch(conversationId, signal) {
|
|
158
285
|
// One wake-up stands for any number of notices: a step resolves once
|
|
159
286
|
// something has landed since the last, and the caller reads them all.
|
|
@@ -236,6 +363,9 @@ export function pgStore(db, options = {}) {
|
|
|
236
363
|
pending = $7::jsonb,
|
|
237
364
|
lease_until = to_timestamp($8::float8 / 1000),
|
|
238
365
|
noticed_through = $9::integer,
|
|
366
|
+
wakes = $10::jsonb,
|
|
367
|
+
woke_through = $11::integer,
|
|
368
|
+
wake_turns = $12::integer,
|
|
239
369
|
updated_at = now()
|
|
240
370
|
WHERE id = $1 AND lease_token = $2
|
|
241
371
|
RETURNING id`, [
|
|
@@ -249,6 +379,9 @@ export function pgStore(db, options = {}) {
|
|
|
249
379
|
conversation.pending == null ? null : JSON.stringify(conversation.pending),
|
|
250
380
|
conversation.leaseUntil,
|
|
251
381
|
conversation.noticedThrough ?? 0,
|
|
382
|
+
JSON.stringify(conversation.wakes ?? []),
|
|
383
|
+
conversation.wokeThrough ?? 0,
|
|
384
|
+
conversation.wakeTurns ?? 0,
|
|
252
385
|
]);
|
|
253
386
|
if (!rows.length)
|
|
254
387
|
await stale(conversation.id);
|
|
@@ -318,8 +451,8 @@ export function pgStore(db, options = {}) {
|
|
|
318
451
|
WHERE id = $1::text
|
|
319
452
|
RETURNING notice_seq
|
|
320
453
|
), stored AS (
|
|
321
|
-
INSERT INTO haikit_notices (conversation_id, seq, name, version, payload, model, handle)
|
|
322
|
-
SELECT $1::text, notice_seq, $2::text, $3::integer, $4::jsonb, $5::text, $6::text FROM next
|
|
454
|
+
INSERT INTO haikit_notices (conversation_id, seq, name, version, payload, model, handle, kind)
|
|
455
|
+
SELECT $1::text, notice_seq, $2::text, $3::integer, $4::jsonb, $5::text, $6::text, $7::text FROM next
|
|
323
456
|
RETURNING ${NOTICE_COLUMNS}
|
|
324
457
|
)
|
|
325
458
|
SELECT stored.*, pg_notify('${NOTICE_CHANNEL}', $1::text) FROM stored`, [
|
|
@@ -329,6 +462,7 @@ export function pgStore(db, options = {}) {
|
|
|
329
462
|
JSON.stringify(record.payload ?? null),
|
|
330
463
|
record.model,
|
|
331
464
|
record.handle ?? null,
|
|
465
|
+
record.kind === "wake" ? "wake" : null,
|
|
332
466
|
]);
|
|
333
467
|
if (!rows.length)
|
|
334
468
|
throw new Error(`conversation ${record.conversationId} does not exist`);
|
|
@@ -345,7 +479,9 @@ export function pgStore(db, options = {}) {
|
|
|
345
479
|
const exists = await db.query(`SELECT 1 FROM haikit_conversations WHERE id = $1`, [conversationId]);
|
|
346
480
|
return exists.rows.length ? [] : null;
|
|
347
481
|
},
|
|
348
|
-
|
|
482
|
+
// Anyone can publish; hearing it takes a LISTEN connection.
|
|
483
|
+
publish,
|
|
484
|
+
...(options.listen ? { watch, subscribe } : {}),
|
|
349
485
|
};
|
|
350
486
|
}
|
|
351
487
|
/**
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@haikit/postgres",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.0",
|
|
4
4
|
"description": "Postgres store adapter for haikit: durable conversations, fenced turn leases, write-once payloads. Bring your own driver.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"haikit",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"prepack": "npm --prefix ../.. run build"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@haikit/core": "0.
|
|
37
|
+
"@haikit/core": "0.15.0"
|
|
38
38
|
},
|
|
39
39
|
"publishConfig": {
|
|
40
40
|
"access": "public"
|
package/src/index.ts
CHANGED
|
@@ -62,6 +62,24 @@ const LEASE_MS = 120_000;
|
|
|
62
62
|
/** The channel `putNotice` notifies on, with the conversation id as payload. */
|
|
63
63
|
export const NOTICE_CHANNEL = "haikit_notices";
|
|
64
64
|
|
|
65
|
+
/** The channel `publish` carries messages on, in pieces small enough for NOTIFY. */
|
|
66
|
+
export const TURN_CHANNEL = "haikit_turns";
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* The most of a message one NOTIFY carries. Postgres refuses payloads of 8000
|
|
70
|
+
* bytes or more; a piece is ASCII, so this leaves room for its header.
|
|
71
|
+
*/
|
|
72
|
+
const PIECE = 7_000;
|
|
73
|
+
|
|
74
|
+
/** How long half a message waits for the rest before it is given up on. */
|
|
75
|
+
const PIECE_TIMEOUT_MS = 30_000;
|
|
76
|
+
|
|
77
|
+
/** The most pieces one message may claim to have: about 52 MB of message, far past any frame. */
|
|
78
|
+
const MAX_PIECES = 10_000;
|
|
79
|
+
|
|
80
|
+
/** The longest message `publish` takes, in UTF-8 bytes: what MAX_PIECES pieces of base64 hold. */
|
|
81
|
+
export const MAX_PUBLISH_BYTES = Math.floor((PIECE * MAX_PIECES * 3) / 4);
|
|
82
|
+
|
|
65
83
|
/**
|
|
66
84
|
* The schema, one statement per entry. `migrate()` runs these; export them to
|
|
67
85
|
* your own migration tool instead if you have one.
|
|
@@ -80,11 +98,19 @@ export const schema: readonly string[] = [
|
|
|
80
98
|
-- the last notice number given out, and the last one its history took in
|
|
81
99
|
notice_seq integer NOT NULL DEFAULT 0,
|
|
82
100
|
noticed_through integer NOT NULL DEFAULT 0,
|
|
101
|
+
-- when recent wake turns started, and the last notice that may no longer start one
|
|
102
|
+
wakes jsonb NOT NULL DEFAULT '[]',
|
|
103
|
+
woke_through integer NOT NULL DEFAULT 0,
|
|
104
|
+
-- how many wake turns it has started, to tell an older turn's frames from a newer's
|
|
105
|
+
wake_turns integer NOT NULL DEFAULT 0,
|
|
83
106
|
updated_at timestamptz NOT NULL DEFAULT now()
|
|
84
107
|
)`,
|
|
85
108
|
// a table created before notices existed gains the columns
|
|
86
109
|
`ALTER TABLE haikit_conversations ADD COLUMN IF NOT EXISTS notice_seq integer NOT NULL DEFAULT 0`,
|
|
87
110
|
`ALTER TABLE haikit_conversations ADD COLUMN IF NOT EXISTS noticed_through integer NOT NULL DEFAULT 0`,
|
|
111
|
+
`ALTER TABLE haikit_conversations ADD COLUMN IF NOT EXISTS wakes jsonb NOT NULL DEFAULT '[]'`,
|
|
112
|
+
`ALTER TABLE haikit_conversations ADD COLUMN IF NOT EXISTS woke_through integer NOT NULL DEFAULT 0`,
|
|
113
|
+
`ALTER TABLE haikit_conversations ADD COLUMN IF NOT EXISTS wake_turns integer NOT NULL DEFAULT 0`,
|
|
88
114
|
// Append-only, and never an orphan: a notice belongs to a conversation that
|
|
89
115
|
// exists, and goes when it does.
|
|
90
116
|
`CREATE TABLE IF NOT EXISTS haikit_notices (
|
|
@@ -96,8 +122,11 @@ export const schema: readonly string[] = [
|
|
|
96
122
|
payload jsonb NOT NULL,
|
|
97
123
|
model text,
|
|
98
124
|
handle text,
|
|
125
|
+
-- 'wake', or NULL for a passive notice
|
|
126
|
+
kind text,
|
|
99
127
|
PRIMARY KEY (conversation_id, seq)
|
|
100
128
|
)`,
|
|
129
|
+
`ALTER TABLE haikit_notices ADD COLUMN IF NOT EXISTS kind text`,
|
|
101
130
|
// Write-once. Nothing about a payload changes after insert — everything that
|
|
102
131
|
// does lives on the fenced conversation row (see Conversation.frozen).
|
|
103
132
|
`CREATE TABLE IF NOT EXISTS haikit_payloads (
|
|
@@ -136,10 +165,11 @@ export async function migrate(db: Queryable): Promise<void> {
|
|
|
136
165
|
const CONVERSATION_COLUMNS = `
|
|
137
166
|
id, status, messages::text AS messages, handles::text AS handles,
|
|
138
167
|
frozen::text AS frozen, pending::text AS pending, lease_token, noticed_through,
|
|
168
|
+
wakes::text AS wakes, woke_through, wake_turns,
|
|
139
169
|
(extract(epoch FROM lease_until) * 1000)::float8 AS lease_until_ms`;
|
|
140
170
|
|
|
141
171
|
const NOTICE_COLUMNS = `
|
|
142
|
-
conversation_id, seq, name, version, payload::text AS payload, model, handle,
|
|
172
|
+
conversation_id, seq, name, version, payload::text AS payload, model, handle, kind,
|
|
143
173
|
(extract(epoch FROM created_at) * 1000)::float8 AS created_at_ms`;
|
|
144
174
|
|
|
145
175
|
// The window is read as text too: 'Infinity' is how "never" is stored, and
|
|
@@ -161,6 +191,9 @@ const toConversation = (row: any): Conversation => ({
|
|
|
161
191
|
leaseUntil: row.lease_until_ms == null ? null : Number(row.lease_until_ms),
|
|
162
192
|
leaseToken: row.lease_token,
|
|
163
193
|
noticedThrough: Number(row.noticed_through ?? 0),
|
|
194
|
+
wakes: row.wakes == null ? [] : json(row.wakes),
|
|
195
|
+
wokeThrough: Number(row.woke_through ?? 0),
|
|
196
|
+
wakeTurns: Number(row.wake_turns ?? 0),
|
|
164
197
|
});
|
|
165
198
|
|
|
166
199
|
const toNotice = (row: any): NoticeRecord => ({
|
|
@@ -172,6 +205,7 @@ const toNotice = (row: any): NoticeRecord => ({
|
|
|
172
205
|
payload: json(row.payload),
|
|
173
206
|
model: row.model,
|
|
174
207
|
...(row.handle == null ? {} : { handle: row.handle }),
|
|
208
|
+
...(row.kind === "wake" ? { kind: "wake" as const } : {}),
|
|
175
209
|
});
|
|
176
210
|
|
|
177
211
|
const toPayload = (row: any): PayloadRecord => ({
|
|
@@ -199,20 +233,125 @@ export function pgStore(db: Queryable, options: PgStoreOptions = {}): StoreAdapt
|
|
|
199
233
|
throw new RangeError(`leaseMs must be a positive, finite number of milliseconds (got ${leaseMs})`);
|
|
200
234
|
}
|
|
201
235
|
|
|
202
|
-
// One LISTEN for the whole store, taken when something first
|
|
203
|
-
// each notification handed to whoever
|
|
236
|
+
// One LISTEN per channel for the whole store, taken when something first
|
|
237
|
+
// needs it, and each notification handed to whoever wants that conversation.
|
|
204
238
|
const waiting = new Map<string, Set<() => void>>();
|
|
205
|
-
|
|
206
|
-
const
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
// try again on the next watch, rather than never
|
|
213
|
-
listening = null;
|
|
239
|
+
const listening = new Map<string, Promise<unknown>>();
|
|
240
|
+
const listenOn = (listener: Listener, channel: string, onNotify: (payload: string) => void) => {
|
|
241
|
+
let started = listening.get(channel);
|
|
242
|
+
if (!started) {
|
|
243
|
+
started = listener.listen(channel, onNotify).catch((err) => {
|
|
244
|
+
// try again next time, rather than never
|
|
245
|
+
listening.delete(channel);
|
|
214
246
|
throw err;
|
|
215
|
-
})
|
|
247
|
+
});
|
|
248
|
+
listening.set(channel, started);
|
|
249
|
+
}
|
|
250
|
+
return started;
|
|
251
|
+
};
|
|
252
|
+
const listen = (listener: Listener) =>
|
|
253
|
+
listenOn(listener, NOTICE_CHANNEL, (id) => {
|
|
254
|
+
for (const wake of waiting.get(id) ?? []) wake();
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
// ── publish / subscribe, over NOTIFY ───────────────────────────────────
|
|
258
|
+
// A message goes out as pieces of at most PIECE base64 characters, each
|
|
259
|
+
// `<conversation> <message id> <index> <count> <piece>`, all in one
|
|
260
|
+
// statement, and is put back together on the way in. Messages from this
|
|
261
|
+
// store are sent one at a time per conversation, each committed before the
|
|
262
|
+
// next starts, so they arrive in the order they were published.
|
|
263
|
+
const hearing = new Map<string, Set<(message: string) => void>>();
|
|
264
|
+
const partial = new Map<string, { pieces: string[]; got: number; expire: ReturnType<typeof setTimeout> }>();
|
|
265
|
+
const sending = new Map<string, Promise<void>>();
|
|
266
|
+
|
|
267
|
+
const onTurn = (payload: string) => {
|
|
268
|
+
const [conversationId, id, index, count, piece] = String(payload).split(" ");
|
|
269
|
+
if (!hearing.has(conversationId)) return;
|
|
270
|
+
// The channel is shared, and anything may NOTIFY on it: a piece that
|
|
271
|
+
// doesn't hold together is dropped, never let throw out of the listener.
|
|
272
|
+
const n = Number(count);
|
|
273
|
+
const i = Number(index);
|
|
274
|
+
if (!id || !Number.isSafeInteger(n) || n < 1 || n > MAX_PIECES || !Number.isSafeInteger(i) || i < 0 || i >= n) return;
|
|
275
|
+
const known = partial.get(id);
|
|
276
|
+
if (known && known.pieces.length !== n) return;
|
|
277
|
+
let entry = known;
|
|
278
|
+
if (!entry) {
|
|
279
|
+
// A message whose other pieces never come (a listening connection that
|
|
280
|
+
// dropped part way) is given up on after a while, whatever else arrives.
|
|
281
|
+
const expire = setTimeout(() => partial.delete(id), PIECE_TIMEOUT_MS);
|
|
282
|
+
// and is no reason to keep the process alive
|
|
283
|
+
(expire as { unref?: () => void }).unref?.();
|
|
284
|
+
entry = { pieces: new Array<string>(n), got: 0, expire };
|
|
285
|
+
}
|
|
286
|
+
if (entry.pieces[i] === undefined) entry.got++;
|
|
287
|
+
entry.pieces[i] = piece ?? "";
|
|
288
|
+
if (entry.got < n) return void partial.set(id, entry);
|
|
289
|
+
clearTimeout(entry.expire);
|
|
290
|
+
partial.delete(id);
|
|
291
|
+
let message: string;
|
|
292
|
+
try {
|
|
293
|
+
message = new TextDecoder("utf-8", { fatal: true }).decode(
|
|
294
|
+
Uint8Array.from(atob(entry.pieces.join("")), (c) => c.charCodeAt(0)),
|
|
295
|
+
);
|
|
296
|
+
} catch {
|
|
297
|
+
return; // not base64, or not UTF-8 once decoded: not one of ours
|
|
298
|
+
}
|
|
299
|
+
for (const hear of [...(hearing.get(conversationId) ?? [])]) hear(message);
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
async function publish(conversationId: string, message: string) {
|
|
303
|
+
const bytes = new TextEncoder().encode(String(message));
|
|
304
|
+
// Refused here rather than sent and dropped by every listener, and before
|
|
305
|
+
// any of the work of encoding it.
|
|
306
|
+
if (bytes.length > MAX_PUBLISH_BYTES) {
|
|
307
|
+
throw new RangeError(
|
|
308
|
+
`message too long to publish: ${bytes.length} bytes, over the ${MAX_PUBLISH_BYTES}-byte maximum`,
|
|
309
|
+
);
|
|
310
|
+
}
|
|
311
|
+
let binary = "";
|
|
312
|
+
for (const b of bytes) binary += String.fromCharCode(b);
|
|
313
|
+
const encoded = btoa(binary);
|
|
314
|
+
const id = newToken().replaceAll("-", "");
|
|
315
|
+
const count = Math.max(1, Math.ceil(encoded.length / PIECE));
|
|
316
|
+
const pieces = Array.from({ length: count }, (_, i) =>
|
|
317
|
+
`${conversationId} ${id} ${i} ${count} ${encoded.slice(i * PIECE, (i + 1) * PIECE)}`,
|
|
318
|
+
);
|
|
319
|
+
const before = sending.get(conversationId) ?? Promise.resolve();
|
|
320
|
+
const sent = before.then(() =>
|
|
321
|
+
db.query(`SELECT pg_notify('${TURN_CHANNEL}', piece) FROM jsonb_array_elements_text($1::jsonb) AS piece`, [
|
|
322
|
+
JSON.stringify(pieces),
|
|
323
|
+
]),
|
|
324
|
+
);
|
|
325
|
+
const settled = sent.then(
|
|
326
|
+
() => {},
|
|
327
|
+
() => {},
|
|
328
|
+
);
|
|
329
|
+
sending.set(conversationId, settled);
|
|
330
|
+
try {
|
|
331
|
+
await sent;
|
|
332
|
+
} finally {
|
|
333
|
+
if (sending.get(conversationId) === settled) sending.delete(conversationId);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
async function subscribe(conversationId: string, onMessage: (message: string) => void, signal: AbortSignal) {
|
|
338
|
+
if (signal.aborted) return;
|
|
339
|
+
const set = hearing.get(conversationId) ?? new Set();
|
|
340
|
+
set.add(onMessage);
|
|
341
|
+
hearing.set(conversationId, set);
|
|
342
|
+
signal.addEventListener(
|
|
343
|
+
"abort",
|
|
344
|
+
() => {
|
|
345
|
+
set.delete(onMessage);
|
|
346
|
+
if (!set.size && hearing.get(conversationId) === set) hearing.delete(conversationId);
|
|
347
|
+
},
|
|
348
|
+
{ once: true },
|
|
349
|
+
);
|
|
350
|
+
// Resolves once LISTEN has taken hold, so nothing published after this
|
|
351
|
+
// resolves is missed. Best effort, as the contract says: a failed LISTEN
|
|
352
|
+
// is tried again next time, and this resolves all the same.
|
|
353
|
+
await listenOn(options.listen!, TURN_CHANNEL, onTurn).catch(() => {});
|
|
354
|
+
}
|
|
216
355
|
|
|
217
356
|
async function* watch(conversationId: string, signal: AbortSignal): AsyncIterable<void> {
|
|
218
357
|
// One wake-up stands for any number of notices: a step resolves once
|
|
@@ -302,6 +441,9 @@ export function pgStore(db: Queryable, options: PgStoreOptions = {}): StoreAdapt
|
|
|
302
441
|
pending = $7::jsonb,
|
|
303
442
|
lease_until = to_timestamp($8::float8 / 1000),
|
|
304
443
|
noticed_through = $9::integer,
|
|
444
|
+
wakes = $10::jsonb,
|
|
445
|
+
woke_through = $11::integer,
|
|
446
|
+
wake_turns = $12::integer,
|
|
305
447
|
updated_at = now()
|
|
306
448
|
WHERE id = $1 AND lease_token = $2
|
|
307
449
|
RETURNING id`,
|
|
@@ -316,6 +458,9 @@ export function pgStore(db: Queryable, options: PgStoreOptions = {}): StoreAdapt
|
|
|
316
458
|
conversation.pending == null ? null : JSON.stringify(conversation.pending),
|
|
317
459
|
conversation.leaseUntil,
|
|
318
460
|
conversation.noticedThrough ?? 0,
|
|
461
|
+
JSON.stringify(conversation.wakes ?? []),
|
|
462
|
+
conversation.wokeThrough ?? 0,
|
|
463
|
+
conversation.wakeTurns ?? 0,
|
|
319
464
|
],
|
|
320
465
|
);
|
|
321
466
|
if (!rows.length) await stale(conversation.id);
|
|
@@ -397,8 +542,8 @@ export function pgStore(db: Queryable, options: PgStoreOptions = {}): StoreAdapt
|
|
|
397
542
|
WHERE id = $1::text
|
|
398
543
|
RETURNING notice_seq
|
|
399
544
|
), stored AS (
|
|
400
|
-
INSERT INTO haikit_notices (conversation_id, seq, name, version, payload, model, handle)
|
|
401
|
-
SELECT $1::text, notice_seq, $2::text, $3::integer, $4::jsonb, $5::text, $6::text FROM next
|
|
545
|
+
INSERT INTO haikit_notices (conversation_id, seq, name, version, payload, model, handle, kind)
|
|
546
|
+
SELECT $1::text, notice_seq, $2::text, $3::integer, $4::jsonb, $5::text, $6::text, $7::text FROM next
|
|
402
547
|
RETURNING ${NOTICE_COLUMNS}
|
|
403
548
|
)
|
|
404
549
|
SELECT stored.*, pg_notify('${NOTICE_CHANNEL}', $1::text) FROM stored`,
|
|
@@ -409,6 +554,7 @@ export function pgStore(db: Queryable, options: PgStoreOptions = {}): StoreAdapt
|
|
|
409
554
|
JSON.stringify(record.payload ?? null),
|
|
410
555
|
record.model,
|
|
411
556
|
record.handle ?? null,
|
|
557
|
+
record.kind === "wake" ? "wake" : null,
|
|
412
558
|
],
|
|
413
559
|
);
|
|
414
560
|
if (!rows.length) throw new Error(`conversation ${record.conversationId} does not exist`);
|
|
@@ -429,7 +575,9 @@ export function pgStore(db: Queryable, options: PgStoreOptions = {}): StoreAdapt
|
|
|
429
575
|
return exists.rows.length ? [] : null;
|
|
430
576
|
},
|
|
431
577
|
|
|
432
|
-
|
|
578
|
+
// Anyone can publish; hearing it takes a LISTEN connection.
|
|
579
|
+
publish,
|
|
580
|
+
...(options.listen ? { watch, subscribe } : {}),
|
|
433
581
|
};
|
|
434
582
|
}
|
|
435
583
|
|