@volter/teams 0.1.57 → 0.1.59
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@volter/teams",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.59",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Volter Teams, the neutral team plane: machine identity and daemon core, the local door and server link, admission from machine grants, product doors, and the Teams server's neutral routes (company RFC 0008).",
|
|
6
6
|
"license": "MIT",
|
package/server/core.mjs
CHANGED
|
@@ -80,6 +80,10 @@ export async function maintain(api){const now=api.clock.nowMs();
|
|
|
80
80
|
AND NOT EXISTS (SELECT 1 FROM session_artifacts a WHERE a.team_id=u.team_id AND a.upload_id=u.id)
|
|
81
81
|
AND NOT EXISTS (SELECT 1 FROM continuations c WHERE c.team_id=u.team_id AND instr(c.value_json,u.id)>0)
|
|
82
82
|
AND NOT EXISTS (SELECT 1 FROM captures c WHERE c.team_id=u.team_id AND c.state='staging' AND instr(c.begin_json,u.id)>0)`,params:[upload.team_id,upload.id]}]);
|
|
83
|
+
const unusedBlobs = await api.store.query(`SELECT team_id,sha256 FROM artifact_blobs b
|
|
84
|
+
WHERE NOT EXISTS (SELECT 1 FROM artifact_chunk_refs r WHERE r.team_id=b.team_id AND r.sha256=b.sha256) LIMIT 50`);
|
|
85
|
+
for (const blob of unusedBlobs) await api.store.transaction([{sql:`DELETE FROM artifact_blobs AS b WHERE team_id=? AND sha256=?
|
|
86
|
+
AND NOT EXISTS (SELECT 1 FROM artifact_chunk_refs r WHERE r.team_id=b.team_id AND r.sha256=b.sha256)`,params:[blob.team_id,blob.sha256]}]);
|
|
83
87
|
// Every capture writes its session a new version (its messages are carried, not copied: schema 9), so versions pile
|
|
84
88
|
// up (hundreds per session) until listing them outgrew a Worker's memory. Kept: each session's current version, the newest
|
|
85
89
|
// version from each of its sources (the next capture and a correlation read that one), and every version an
|
|
@@ -87,6 +91,10 @@ export async function maintain(api){const now=api.clock.nowMs();
|
|
|
87
91
|
const stale=await api.store.query(`SELECT v.team_id,v.session_id,v.revision FROM session_versions v JOIN sessions s ON s.team_id=v.team_id AND s.id=v.session_id
|
|
88
92
|
WHERE v.revision<>s.revision
|
|
89
93
|
AND EXISTS (SELECT 1 FROM session_versions n WHERE n.team_id=v.team_id AND n.session_id=v.session_id AND n.revision>v.revision AND json_extract(n.source_projection_json,'$.source_id') IS json_extract(v.source_projection_json,'$.source_id'))
|
|
94
|
+
AND (NOT EXISTS (SELECT 1 FROM session_artifacts a WHERE a.team_id=v.team_id AND a.session_id=v.session_id AND a.revision=v.revision AND a.provenance='capture')
|
|
95
|
+
OR EXISTS (SELECT 1 FROM session_artifacts a JOIN session_versions n ON n.team_id=a.team_id AND n.session_id=a.session_id AND n.revision=a.revision
|
|
96
|
+
WHERE a.team_id=v.team_id AND a.session_id=v.session_id AND a.revision>v.revision AND a.provenance='capture'
|
|
97
|
+
AND json_extract(n.source_projection_json,'$.source_id') IS json_extract(v.source_projection_json,'$.source_id')))
|
|
90
98
|
AND NOT EXISTS (SELECT 1 FROM cursors c, json_each(c.snapshot_json) j WHERE c.team_id=v.team_id AND c.kind IN ('list','search') AND c.expires_at>? AND json_extract(j.value,'$[0]')=v.session_id AND json_extract(j.value,'$[1]')=v.revision)
|
|
91
99
|
AND NOT EXISTS (SELECT 1 FROM cursors c WHERE c.team_id=v.team_id AND c.kind='history' AND c.expires_at>? AND json_extract(c.snapshot_json,'$.session_id')=v.session_id AND json_extract(c.snapshot_json,'$.revision')=v.revision)
|
|
92
100
|
LIMIT ${VERSION_PRUNE_BATCH}`,[now,now]);
|
|
@@ -111,7 +119,7 @@ export async function maintain(api){const now=api.clock.nowMs();
|
|
|
111
119
|
// Presence reconciliation can INSERT. Never put it ahead of the deletes
|
|
112
120
|
// that make room when the database is full.
|
|
113
121
|
await reconcileMachinePresence(api);
|
|
114
|
-
const result={pruned_versions:stale.length,pruned_parts:parts.length,pruned_idempotency:expired.length,pruned_uploads:orphaned.length,backlog:orphaned.length>=10||expired.length>=1000||stale.length>=VERSION_PRUNE_BATCH||parts.length>=PART_PRUNE_BATCH};
|
|
122
|
+
const result={pruned_versions:stale.length,pruned_parts:parts.length,pruned_idempotency:expired.length,pruned_uploads:orphaned.length,pruned_blobs:unusedBlobs.length,backlog:unusedBlobs.length>=50||orphaned.length>=10||expired.length>=1000||stale.length>=VERSION_PRUNE_BATCH||parts.length>=PART_PRUNE_BATCH};
|
|
115
123
|
if(result.pruned_versions||result.pruned_parts||result.pruned_idempotency||result.pruned_uploads)console.log(`maintain: pruned ${result.pruned_uploads} unreachable bundles, ${result.pruned_idempotency} expired replay records, ${result.pruned_parts} parts, ${result.pruned_versions} versions${result.backlog?' (backlog)':''}`);
|
|
116
124
|
return result;}
|
|
117
125
|
/** Session versions `maintain` removes per run: small enough for one Durable Object alarm; a backlog runs again in seconds. */
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// New captures share immutable chunk contents. Legacy uploads stay readable
|
|
2
|
+
// without copying their bytes during migration; ordinary retention retires them.
|
|
3
|
+
export default `
|
|
4
|
+
CREATE TABLE artifact_blobs (
|
|
5
|
+
team_id TEXT NOT NULL, sha256 TEXT NOT NULL, bytes BLOB NOT NULL,
|
|
6
|
+
PRIMARY KEY(team_id,sha256), CHECK(length(bytes)<=1048576)
|
|
7
|
+
) STRICT;
|
|
8
|
+
CREATE TABLE artifact_chunk_refs (
|
|
9
|
+
team_id TEXT NOT NULL, upload_id TEXT NOT NULL, chunk_index INTEGER NOT NULL,
|
|
10
|
+
sha256 TEXT NOT NULL, PRIMARY KEY(team_id,upload_id,chunk_index),
|
|
11
|
+
FOREIGN KEY(team_id,upload_id) REFERENCES uploads(team_id,id) ON DELETE CASCADE,
|
|
12
|
+
FOREIGN KEY(team_id,sha256) REFERENCES artifact_blobs(team_id,sha256)
|
|
13
|
+
) STRICT;
|
|
14
|
+
CREATE INDEX artifact_chunk_refs_blob ON artifact_chunk_refs(team_id,sha256);
|
|
15
|
+
CREATE VIEW artifact_chunk_data AS
|
|
16
|
+
SELECT team_id,upload_id,chunk_index,sha256,bytes FROM artifact_chunks
|
|
17
|
+
UNION ALL
|
|
18
|
+
SELECT r.team_id,r.upload_id,r.chunk_index,r.sha256,b.bytes
|
|
19
|
+
FROM artifact_chunk_refs r JOIN artifact_blobs b ON b.team_id=r.team_id AND b.sha256=r.sha256;
|
|
20
|
+
`;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
// A carried-prefix scan must seek by ordinal rather than rescan every retained
|
|
2
|
+
// version for every message. Includes revision for the temporal row predicate.
|
|
3
|
+
export default `
|
|
4
|
+
CREATE INDEX messages_ordinal_version ON messages(team_id,session_id,ordinal,revision);
|
|
5
|
+
`;
|
|
@@ -9,7 +9,9 @@ import m007 from './007.mjs';
|
|
|
9
9
|
import m008 from './008.mjs';
|
|
10
10
|
import m009 from './009.mjs';
|
|
11
11
|
import m010 from './010.mjs';
|
|
12
|
-
|
|
12
|
+
import m011 from './011.mjs';
|
|
13
|
+
import m012 from './012.mjs';
|
|
14
|
+
export const MIGRATIONS = [m001, m002, m003, m004, m005, m006, m007, m008, m009, m010, m011, m012];
|
|
13
15
|
/** A message row's `until_revision` while its session's current version still holds it (schema 9). */
|
|
14
16
|
export const OPEN_REVISION = Number.MAX_SAFE_INTEGER;
|
|
15
17
|
/** SQL: message row `m` belongs to the version at revision ?1 (bind the revision twice). */
|