@framers/agentos-ext-account-sources 0.4.0 → 0.6.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 +114 -3
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/sync/AccountSourceSync.d.ts +136 -0
- package/dist/sync/AccountSourceSync.d.ts.map +1 -0
- package/dist/sync/AccountSourceSync.js +387 -0
- package/dist/sync/AccountSourceSync.js.map +1 -0
- package/dist/sync/SourceQuota.d.ts +134 -0
- package/dist/sync/SourceQuota.d.ts.map +1 -0
- package/dist/sync/SourceQuota.js +336 -0
- package/dist/sync/SourceQuota.js.map +1 -0
- package/dist/sync/SqlAccountSourceSyncStore.d.ts +42 -2
- package/dist/sync/SqlAccountSourceSyncStore.d.ts.map +1 -1
- package/dist/sync/SqlAccountSourceSyncStore.js +109 -12
- package/dist/sync/SqlAccountSourceSyncStore.js.map +1 -1
- package/dist/sync/coordinatorState.d.ts +40 -0
- package/dist/sync/coordinatorState.d.ts.map +1 -0
- package/dist/sync/coordinatorState.js +22 -0
- package/dist/sync/coordinatorState.js.map +1 -0
- package/dist/sync/types.d.ts +1 -1
- package/dist/sync/types.d.ts.map +1 -1
- package/manifest.json +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Persistent weighted request quotas on PostgreSQL through `@framers/sql-storage-adapter`: three
|
|
3
|
+
* released core spend meters (a project's rolling minute, a mailbox's rolling minute and a project's UTC day),
|
|
4
|
+
* request slots that every worker shares and that expire after a process dies, and durable provider retry times.
|
|
5
|
+
* @module account-sources/sync/SourceQuota
|
|
6
|
+
*/
|
|
7
|
+
import { randomUUID } from 'node:crypto';
|
|
8
|
+
import { SqlSpendMeter, spendMeterDdl } from '@framers/agentos/safety/runtime';
|
|
9
|
+
import { SourceSyncFault } from './state.js';
|
|
10
|
+
class QuotaDeferred extends Error {
|
|
11
|
+
reason;
|
|
12
|
+
retryAt;
|
|
13
|
+
constructor(reason, retryAt) {
|
|
14
|
+
super('source_quota_deferred');
|
|
15
|
+
this.reason = reason;
|
|
16
|
+
this.retryAt = retryAt;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
class QuotaDuplicate extends Error {
|
|
20
|
+
constructor() { super('source_quota_duplicate'); }
|
|
21
|
+
}
|
|
22
|
+
class QuotaLate extends Error {
|
|
23
|
+
retryAt;
|
|
24
|
+
constructor(retryAt) {
|
|
25
|
+
super('source_quota_late');
|
|
26
|
+
this.retryAt = retryAt;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/** Milliseconds after the reservation's clock reading within which an attempt must start its call. */
|
|
30
|
+
const sendWindowMs = 1000;
|
|
31
|
+
/** Rows of each table one `compact` call deletes at most. */
|
|
32
|
+
const compactLimit = 1000;
|
|
33
|
+
const integer = (n) => Number.isSafeInteger(n) && n >= 0;
|
|
34
|
+
const key = (s) => typeof s === 'string' && s.length > 0 && s.length <= 512;
|
|
35
|
+
const dayEnd = (now) => (Math.floor(now / 86_400_000) + 1) * 86_400_000;
|
|
36
|
+
function tables(prefix = 'agentos_source_quota') {
|
|
37
|
+
if (!/^[a-z][a-z0-9_]{0,27}$/.test(prefix))
|
|
38
|
+
throw new Error('invalid_source_quota_prefix');
|
|
39
|
+
return { projects: `"${prefix}_projects"`, mailboxes: `"${prefix}_mailboxes"`,
|
|
40
|
+
attempts: `"${prefix}_attempts"`, pm: `${prefix}_pm`, mm: `${prefix}_mm`, pd: `${prefix}_pd` };
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* The quota tables' DDL under a prefix of a lower-case letter and at most 27 lower-case letters, digits or underscores
|
|
44
|
+
* (`agentos_source_quota` unless given), as seven scripts for the host's migration to run with `exec`: the project,
|
|
45
|
+
* mailbox and attempt tables, the attempts' index, then a core spend meter's tables and indexes for each budget under
|
|
46
|
+
* `<prefix>_pm`, `<prefix>_mm` and `<prefix>_pd`. Throws `invalid_source_quota_prefix` for another prefix.
|
|
47
|
+
*/
|
|
48
|
+
export function sourceQuotaSchema(prefix = 'agentos_source_quota') {
|
|
49
|
+
const t = tables(prefix);
|
|
50
|
+
return [
|
|
51
|
+
`CREATE TABLE IF NOT EXISTS ${t.projects} (project_key TEXT PRIMARY KEY,policy TEXT NOT NULL,retry_at BIGINT NOT NULL DEFAULT 0)`,
|
|
52
|
+
`CREATE TABLE IF NOT EXISTS ${t.mailboxes} (project_key TEXT NOT NULL,mailbox_key TEXT NOT NULL,
|
|
53
|
+
retry_at BIGINT NOT NULL DEFAULT 0,PRIMARY KEY(project_key,mailbox_key))`,
|
|
54
|
+
`CREATE TABLE IF NOT EXISTS ${t.attempts} (attempt_id TEXT PRIMARY KEY,project_key TEXT NOT NULL,
|
|
55
|
+
mailbox_key TEXT NOT NULL,token TEXT NOT NULL,send_by BIGINT NOT NULL,expires_at BIGINT NOT NULL,
|
|
56
|
+
started BOOLEAN NOT NULL DEFAULT FALSE,finished BOOLEAN NOT NULL DEFAULT FALSE)`,
|
|
57
|
+
`CREATE INDEX IF NOT EXISTS "${prefix}_active" ON ${t.attempts}(project_key,finished,expires_at)`,
|
|
58
|
+
spendMeterDdl(t.pm), spendMeterDdl(t.mm), spendMeterDdl(t.pd),
|
|
59
|
+
];
|
|
60
|
+
}
|
|
61
|
+
function checkedPolicy(policy) {
|
|
62
|
+
const weights = { list: policy.weights.list, read: policy.weights.read,
|
|
63
|
+
changes: policy.weights.changes, profile: policy.weights.profile, labels: policy.weights.labels };
|
|
64
|
+
const copy = { projectMinute: policy.projectMinute, mailboxMinute: policy.mailboxMinute,
|
|
65
|
+
projectDay: policy.projectDay, projectConcurrency: policy.projectConcurrency,
|
|
66
|
+
mailboxConcurrency: policy.mailboxConcurrency, timeoutMs: policy.timeoutMs,
|
|
67
|
+
slotGraceMs: policy.slotGraceMs, weights: Object.freeze(weights) };
|
|
68
|
+
const numbers = [copy.projectMinute, copy.mailboxMinute, copy.projectDay, copy.projectConcurrency,
|
|
69
|
+
copy.mailboxConcurrency, copy.timeoutMs, copy.slotGraceMs, ...Object.values(weights)];
|
|
70
|
+
if (!numbers.every((n) => integer(n) && n > 0) || copy.timeoutMs > 60_000
|
|
71
|
+
|| copy.slotGraceMs < copy.timeoutMs || copy.slotGraceMs > 120_000
|
|
72
|
+
|| copy.mailboxConcurrency > copy.projectConcurrency)
|
|
73
|
+
throw new Error('invalid_source_quota_policy');
|
|
74
|
+
return Object.freeze(copy);
|
|
75
|
+
}
|
|
76
|
+
/** Combines released meters; this class never reimplements their accounting SQL. */
|
|
77
|
+
export class SourceQuota {
|
|
78
|
+
db;
|
|
79
|
+
options;
|
|
80
|
+
t;
|
|
81
|
+
policy;
|
|
82
|
+
fingerprint;
|
|
83
|
+
/**
|
|
84
|
+
* Takes a PostgreSQL adapter with transactions and runs no DDL. Throws `source_quota_requires_postgres` for another
|
|
85
|
+
* adapter, `invalid_source_quota_prefix` for a bad prefix and `invalid_source_quota_policy` for a bad policy.
|
|
86
|
+
*/
|
|
87
|
+
constructor(db, options) {
|
|
88
|
+
this.db = db;
|
|
89
|
+
this.options = options;
|
|
90
|
+
if (db.kind !== 'postgres' || !db.capabilities.has('transactions'))
|
|
91
|
+
throw new Error('source_quota_requires_postgres');
|
|
92
|
+
this.t = tables(options.tablePrefix);
|
|
93
|
+
this.policy = checkedPolicy(options.policy);
|
|
94
|
+
this.fingerprint = JSON.stringify(this.policy);
|
|
95
|
+
}
|
|
96
|
+
async now(tx) {
|
|
97
|
+
const n = this.options.clock ? await this.options.clock(tx)
|
|
98
|
+
: Number((await tx.get('SELECT floor(extract(epoch from clock_timestamp()) * 1000)::bigint AS now'))?.now);
|
|
99
|
+
if (!integer(n) || n > 8_639_999_913_600_000)
|
|
100
|
+
throw new Error('invalid_source_quota_clock');
|
|
101
|
+
return n;
|
|
102
|
+
}
|
|
103
|
+
async project(tx, projectKey) {
|
|
104
|
+
if (!key(projectKey))
|
|
105
|
+
throw new Error('invalid_source_quota_key');
|
|
106
|
+
await tx.run(`INSERT INTO ${this.t.projects}(project_key,policy) VALUES($1,$2) ON CONFLICT DO NOTHING`, [projectKey, this.fingerprint]);
|
|
107
|
+
const row = await tx.get(`SELECT policy,retry_at FROM ${this.t.projects} WHERE project_key=$1 FOR UPDATE`, [projectKey]);
|
|
108
|
+
if (!row || row.policy !== this.fingerprint)
|
|
109
|
+
throw new Error('source_quota_policy_mismatch');
|
|
110
|
+
return row;
|
|
111
|
+
}
|
|
112
|
+
meter(tx, prefix, allowance, daily) {
|
|
113
|
+
return new SqlSpendMeter({ db: tx, tablePrefix: prefix, allowanceFor: () => allowance,
|
|
114
|
+
...(daily ? { periodOf: (now) => String(Math.floor(now / 86_400_000)) } : { windowMs: 60_000 }),
|
|
115
|
+
ensureSchema: false, requireShared: true, leaseMs: 120_000, maxAttemptsPerOperation: 1,
|
|
116
|
+
unknownAfterLease: 'consume', retry: { attempts: 1, baseDelayMs: 0, maxDelayMs: 0, deadlineMs: 1000 } });
|
|
117
|
+
}
|
|
118
|
+
async reserve(request) {
|
|
119
|
+
if (![request.projectKey, request.mailboxKey, request.attemptId].every(key)
|
|
120
|
+
|| !Object.hasOwn(this.policy.weights, request.method))
|
|
121
|
+
throw new SourceSyncFault('invalid_response');
|
|
122
|
+
return this.db.transaction(async (tx) => {
|
|
123
|
+
const project = await this.project(tx, request.projectKey);
|
|
124
|
+
const now = await this.now(tx);
|
|
125
|
+
if (await tx.get(`SELECT attempt_id FROM ${this.t.attempts} WHERE attempt_id=$1`, [request.attemptId]))
|
|
126
|
+
throw new QuotaDuplicate();
|
|
127
|
+
const mailbox = await tx.get(`SELECT retry_at FROM ${this.t.mailboxes}
|
|
128
|
+
WHERE project_key=$1 AND mailbox_key=$2`, [request.projectKey, request.mailboxKey]);
|
|
129
|
+
const retryAt = Math.max(Number(project.retry_at), Number(mailbox?.retry_at ?? 0));
|
|
130
|
+
if (retryAt > now)
|
|
131
|
+
throw new QuotaDeferred('provider_wait', retryAt);
|
|
132
|
+
const slots = await tx.all(`SELECT mailbox_key,expires_at FROM ${this.t.attempts} WHERE project_key=$1 AND finished=FALSE AND expires_at>$2`, [request.projectKey, now]);
|
|
133
|
+
const local = slots.filter((s) => s.mailbox_key === request.mailboxKey);
|
|
134
|
+
if (local.length >= this.policy.mailboxConcurrency) {
|
|
135
|
+
throw new QuotaDeferred('mailbox_concurrency', Math.min(...local.map((s) => Number(s.expires_at))));
|
|
136
|
+
}
|
|
137
|
+
if (slots.length >= this.policy.projectConcurrency) {
|
|
138
|
+
throw new QuotaDeferred('project_concurrency', Math.min(...slots.map((s) => Number(s.expires_at))));
|
|
139
|
+
}
|
|
140
|
+
const projectId = JSON.stringify([request.projectKey]);
|
|
141
|
+
const mailboxId = JSON.stringify([request.projectKey, request.mailboxKey]);
|
|
142
|
+
const meters = [
|
|
143
|
+
{ meter: this.meter(tx, this.t.pm, this.policy.projectMinute, false), accountId: projectId,
|
|
144
|
+
reason: 'project_minute', retryAt: now + 60_001 },
|
|
145
|
+
{ meter: this.meter(tx, this.t.mm, this.policy.mailboxMinute, false), accountId: mailboxId,
|
|
146
|
+
reason: 'mailbox_minute', retryAt: now + 60_001 },
|
|
147
|
+
{ meter: this.meter(tx, this.t.pd, this.policy.projectDay, true), accountId: projectId,
|
|
148
|
+
reason: 'project_day', retryAt: dayEnd(now) },
|
|
149
|
+
];
|
|
150
|
+
for (const entry of meters) {
|
|
151
|
+
const reserved = await entry.meter.reserve({ accountId: entry.accountId,
|
|
152
|
+
operationId: request.attemptId, units: this.policy.weights[request.method], now });
|
|
153
|
+
if (reserved.status !== 'reserved') {
|
|
154
|
+
if (reserved.reason !== 'allowance_exhausted')
|
|
155
|
+
throw new QuotaDuplicate();
|
|
156
|
+
// Propagate past the outer transaction: nested adapter calls have no savepoint.
|
|
157
|
+
throw new QuotaDeferred(entry.reason, entry.retryAt);
|
|
158
|
+
}
|
|
159
|
+
const settled = await entry.meter.settle({ operationId: request.attemptId, outcome: 'consumed', now });
|
|
160
|
+
if (settled.status !== 'settled' || settled.state !== 'consumed')
|
|
161
|
+
throw new Error('source_quota_settle_failed');
|
|
162
|
+
}
|
|
163
|
+
const permit = { token: randomUUID(), sendBy: now + sendWindowMs,
|
|
164
|
+
expiresAt: now + sendWindowMs + this.policy.timeoutMs + this.policy.slotGraceMs, timeoutMs: this.policy.timeoutMs };
|
|
165
|
+
await tx.run(`INSERT INTO ${this.t.attempts}(attempt_id,project_key,mailbox_key,token,send_by,expires_at)
|
|
166
|
+
VALUES($1,$2,$3,$4,$5,$6)`, [request.attemptId, request.projectKey, request.mailboxKey,
|
|
167
|
+
permit.token, permit.sendBy, permit.expiresAt]);
|
|
168
|
+
return permit;
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
async begin(request, permit) {
|
|
172
|
+
await this.db.transaction(async (tx) => {
|
|
173
|
+
const project = await this.project(tx, request.projectKey);
|
|
174
|
+
const now = await this.now(tx);
|
|
175
|
+
const mailbox = await tx.get(`SELECT retry_at FROM ${this.t.mailboxes}
|
|
176
|
+
WHERE project_key=$1 AND mailbox_key=$2`, [request.projectKey, request.mailboxKey]);
|
|
177
|
+
const wait = Math.max(Number(project.retry_at), Number(mailbox?.retry_at ?? 0));
|
|
178
|
+
if (wait > now)
|
|
179
|
+
throw new QuotaDeferred('provider_wait', wait);
|
|
180
|
+
const updated = await tx.run(`UPDATE ${this.t.attempts} SET started=TRUE
|
|
181
|
+
WHERE attempt_id=$1 AND project_key=$2 AND mailbox_key=$3 AND token=$4
|
|
182
|
+
AND started=FALSE AND finished=FALSE AND send_by>=$5 AND expires_at>$5`, [request.attemptId, request.projectKey, request.mailboxKey, permit.token, now]);
|
|
183
|
+
if (updated.changes === 1)
|
|
184
|
+
return;
|
|
185
|
+
// This run's own attempt, still unstarted, missed only the time conditions: its send window closed before the start.
|
|
186
|
+
const unstarted = await tx.get(`SELECT attempt_id FROM ${this.t.attempts}
|
|
187
|
+
WHERE attempt_id=$1 AND project_key=$2 AND mailbox_key=$3 AND token=$4 AND started=FALSE AND finished=FALSE`, [request.attemptId, request.projectKey, request.mailboxKey, permit.token]);
|
|
188
|
+
throw unstarted ? new QuotaLate(now + sendWindowMs) : new QuotaDuplicate();
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
async release(request, permit) {
|
|
192
|
+
await this.db.transaction(async (tx) => {
|
|
193
|
+
await this.project(tx, request.projectKey);
|
|
194
|
+
await tx.run(`UPDATE ${this.t.attempts} SET finished=TRUE WHERE attempt_id=$1 AND token=$2
|
|
195
|
+
AND project_key=$3 AND mailbox_key=$4`, [request.attemptId, permit.token, request.projectKey, request.mailboxKey]);
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Consume weighted capacity and call the transport once. No inline HTTP retries: a retry, after a deferral or a
|
|
200
|
+
* `late` answer among others, is a new `run` with a new attempt id.
|
|
201
|
+
*/
|
|
202
|
+
async run(request, send, parent) {
|
|
203
|
+
if (parent?.aborted)
|
|
204
|
+
throw new SourceSyncFault('cancelled');
|
|
205
|
+
let permit;
|
|
206
|
+
try {
|
|
207
|
+
permit = await this.reserve(request);
|
|
208
|
+
}
|
|
209
|
+
catch (error) {
|
|
210
|
+
if (error instanceof QuotaDeferred)
|
|
211
|
+
return { kind: 'deferred', reason: error.reason, retryAt: error.retryAt };
|
|
212
|
+
if (error instanceof QuotaDuplicate)
|
|
213
|
+
return { kind: 'duplicate' };
|
|
214
|
+
if (error instanceof SourceSyncFault)
|
|
215
|
+
throw error;
|
|
216
|
+
throw new SourceSyncFault('storage_unavailable');
|
|
217
|
+
}
|
|
218
|
+
const controller = new AbortController();
|
|
219
|
+
const abort = () => controller.abort();
|
|
220
|
+
parent?.addEventListener('abort', abort, { once: true });
|
|
221
|
+
let timer;
|
|
222
|
+
let began = false;
|
|
223
|
+
try {
|
|
224
|
+
try {
|
|
225
|
+
await this.begin(request, permit);
|
|
226
|
+
began = true;
|
|
227
|
+
}
|
|
228
|
+
catch (error) {
|
|
229
|
+
if (error instanceof QuotaDeferred)
|
|
230
|
+
return { kind: 'deferred', reason: error.reason, retryAt: error.retryAt };
|
|
231
|
+
if (error instanceof QuotaLate)
|
|
232
|
+
return { kind: 'late', retryAt: error.retryAt };
|
|
233
|
+
if (error instanceof QuotaDuplicate)
|
|
234
|
+
return { kind: 'duplicate' };
|
|
235
|
+
throw new SourceSyncFault('storage_unavailable');
|
|
236
|
+
}
|
|
237
|
+
if (parent?.aborted)
|
|
238
|
+
throw new SourceSyncFault('cancelled');
|
|
239
|
+
timer = setTimeout(abort, permit.timeoutMs);
|
|
240
|
+
// The callback must consume or cancel its body before resolving and honor abort.
|
|
241
|
+
const value = await send(controller.signal);
|
|
242
|
+
if (controller.signal.aborted)
|
|
243
|
+
throw new SourceSyncFault(parent?.aborted ? 'cancelled' : 'network');
|
|
244
|
+
return { kind: 'completed', value };
|
|
245
|
+
}
|
|
246
|
+
finally {
|
|
247
|
+
if (timer !== undefined)
|
|
248
|
+
clearTimeout(timer);
|
|
249
|
+
parent?.removeEventListener('abort', abort);
|
|
250
|
+
// Request units remain consumed even if begin, transport, parsing or cleanup fails.
|
|
251
|
+
try {
|
|
252
|
+
await this.release(request, permit);
|
|
253
|
+
}
|
|
254
|
+
catch {
|
|
255
|
+
if (began)
|
|
256
|
+
throw new SourceSyncFault('storage_unavailable');
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
/** A longer provider wait survives restarts, generation changes and shorter updates. */
|
|
261
|
+
async defer(scope, retryAt) {
|
|
262
|
+
if (!key(scope.projectKey) || (scope.mailboxKey !== undefined && !key(scope.mailboxKey))
|
|
263
|
+
|| !integer(retryAt) || retryAt > 8_639_999_913_600_000)
|
|
264
|
+
throw new SourceSyncFault('invalid_response');
|
|
265
|
+
try {
|
|
266
|
+
await this.db.transaction(async (tx) => {
|
|
267
|
+
await this.project(tx, scope.projectKey);
|
|
268
|
+
if (scope.mailboxKey === undefined) {
|
|
269
|
+
await tx.run(`UPDATE ${this.t.projects} SET retry_at=GREATEST(retry_at,$2) WHERE project_key=$1`, [scope.projectKey, retryAt]);
|
|
270
|
+
}
|
|
271
|
+
else {
|
|
272
|
+
await tx.run(`INSERT INTO ${this.t.mailboxes}(project_key,mailbox_key,retry_at) VALUES($1,$2,$3)
|
|
273
|
+
ON CONFLICT(project_key,mailbox_key) DO UPDATE SET retry_at=GREATEST(${this.t.mailboxes}.retry_at,excluded.retry_at)`, [scope.projectKey, scope.mailboxKey, retryAt]);
|
|
274
|
+
}
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
catch {
|
|
278
|
+
throw new SourceSyncFault('storage_unavailable');
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Remove expired attempt evidence after 24 hours and safely old meter records, at most 1,000 rows of each table a
|
|
283
|
+
* call. Daily totals remain. Answers what it removed; while `more` is true, a further call may remove more.
|
|
284
|
+
*/
|
|
285
|
+
async compact() {
|
|
286
|
+
try {
|
|
287
|
+
return await this.db.transaction(async (tx) => {
|
|
288
|
+
const now = await this.now(tx);
|
|
289
|
+
const before = Math.max(0, now - 86_400_000);
|
|
290
|
+
const attempts = (await tx.run(`DELETE FROM ${this.t.attempts} WHERE attempt_id IN (SELECT attempt_id FROM ${this.t.attempts} WHERE expires_at < $1 ORDER BY expires_at LIMIT ${compactLimit}) AND expires_at < $1`, [before])).changes;
|
|
291
|
+
const mailboxWaits = (await tx.run(`DELETE FROM ${this.t.mailboxes} WHERE (project_key,mailbox_key) IN (SELECT project_key,mailbox_key FROM ${this.t.mailboxes} WHERE retry_at <= $1 LIMIT ${compactLimit}) AND retry_at <= $1`, [now])).changes;
|
|
292
|
+
const pages = [attempts, mailboxWaits];
|
|
293
|
+
let reservations = 0;
|
|
294
|
+
let windows = 0;
|
|
295
|
+
for (const meter of [this.meter(tx, this.t.pm, this.policy.projectMinute, false),
|
|
296
|
+
this.meter(tx, this.t.mm, this.policy.mailboxMinute, false), this.meter(tx, this.t.pd, this.policy.projectDay, true)]) {
|
|
297
|
+
// A meter answers its settled reservations and its window rows deleted, each its own page of the limit.
|
|
298
|
+
const purged = await meter.purge({ before, limit: compactLimit });
|
|
299
|
+
reservations += purged.reservations;
|
|
300
|
+
windows += purged.periods;
|
|
301
|
+
pages.push(purged.reservations, purged.periods);
|
|
302
|
+
}
|
|
303
|
+
return { attempts, mailboxWaits, reservations, windows, more: pages.some((n) => n >= compactLimit) };
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
catch {
|
|
307
|
+
throw new SourceSyncFault('storage_unavailable');
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
/** Retry-After is never clamped down to the local exponential-backoff ceiling. */
|
|
312
|
+
export function sourceRetryAt(now, attempt, retryAfter, random = Math.random()) {
|
|
313
|
+
if (!integer(now) || !Number.isInteger(attempt) || attempt < 1 || attempt > 5
|
|
314
|
+
|| !Number.isFinite(random) || random < 0 || random > 1)
|
|
315
|
+
throw new SourceSyncFault('invalid_response');
|
|
316
|
+
const base = Math.min(60_000, 1000 * 2 ** (attempt - 1));
|
|
317
|
+
let at = now + Math.min(60_000, Math.floor(base * (1 + random)));
|
|
318
|
+
if (retryAfter !== null) {
|
|
319
|
+
const value = retryAfter.trim();
|
|
320
|
+
if (/^\d+$/.test(value)) {
|
|
321
|
+
const seconds = Number(value);
|
|
322
|
+
if (!integer(seconds) || !integer(now + seconds * 1000))
|
|
323
|
+
throw new SourceSyncFault('invalid_response');
|
|
324
|
+
at = Math.max(at, now + seconds * 1000);
|
|
325
|
+
}
|
|
326
|
+
else {
|
|
327
|
+
const parsed = Date.parse(value);
|
|
328
|
+
if (integer(parsed) && new Date(parsed).toUTCString() === value)
|
|
329
|
+
at = Math.max(at, parsed);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
if (!integer(at) || at > 8_639_999_913_600_000)
|
|
333
|
+
throw new SourceSyncFault('invalid_response');
|
|
334
|
+
return at;
|
|
335
|
+
}
|
|
336
|
+
//# sourceMappingURL=SourceQuota.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SourceQuota.js","sourceRoot":"","sources":["../../src/sync/SourceQuota.ts"],"names":[],"mappings":"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"}
|
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import type { StorageAdapter } from '@framers/sql-storage-adapter';
|
|
8
8
|
import { SqlSourceDerivationStore, type SourceDependency, type SourceDependencyState, type SourceDerivation, type SourceOwnerScope, type SourceInvalidation, type SourceFenceTicket } from '@framers/agentos/cognition/library';
|
|
9
|
-
import type { SourceSyncScope, SourceSyncState, SourceSyncPolicy, SourceSyncLease, SourceSyncTrigger, SourceSyncCheckpoint, SourceSyncCycleInput, SourceSyncPage, SourceSyncWork, SourceSyncDocument, SourceSyncMessageSpan, SourceSyncReadResult, SourceSyncWorkDisposition, SourceSyncTransition } from './types.js';
|
|
9
|
+
import type { SourceSyncScope, SourceSyncState, SourceSyncPolicy, SourceSyncLease, SourceSyncTrigger, SourceSyncCheckpoint, SourceSyncCycleInput, SourceSyncPage, SourceSyncWork, SourceSyncDocument, SourceSyncMessageSpan, SourceSyncReadResult, SourceSyncWorkDisposition, SourceSyncTransition, SourceSyncRetryReason } from './types.js';
|
|
10
|
+
import type { SourceSyncOperationAttempt } from './coordinatorState.js';
|
|
10
11
|
/**
|
|
11
12
|
* The provenance kept for one retained source: its dependency, message spans, activity time, character count and
|
|
12
13
|
* content hash, never its text.
|
|
@@ -189,8 +190,47 @@ export declare class SqlAccountSourceSyncStore<Prepared = unknown> {
|
|
|
189
190
|
*/
|
|
190
191
|
page(h: SourceSyncHandle, expected: SourceSyncCheckpoint, page: SourceSyncPage): Promise<SourceSyncCheckpoint | null>;
|
|
191
192
|
private putWork;
|
|
192
|
-
/**
|
|
193
|
+
/**
|
|
194
|
+
* Null id takes the first due item. Increment before external work; a crash leaves it pending.
|
|
195
|
+
*
|
|
196
|
+
* An item is due when it is pending or its retry time has come; without an id the first due item by source id is
|
|
197
|
+
* taken. A pending item that already has five attempts and no recorded result, as when a process died during each
|
|
198
|
+
* read, is not taken: it waits `pollMs` as a `storage_unavailable` retry, and the attempt after a wait at five
|
|
199
|
+
* attempts starts a new count.
|
|
200
|
+
*
|
|
201
|
+
* @returns The item with its attempt counted, or null when none is due or the handle is stale.
|
|
202
|
+
*/
|
|
193
203
|
takeWork(h: SourceSyncHandle, id?: string | null): Promise<SourceSyncWork | null>;
|
|
204
|
+
/**
|
|
205
|
+
* Counts an attempt at one provider operation of the open sync, its anchor read or a page, before the request is
|
|
206
|
+
* sent, under the connection's row lock. `operationKey` names the operation: a SHA-256 digest in lower-case hex
|
|
207
|
+
* that changes with the generation, cycle, revision and phase, so a new key starts a new count. A recorded wait
|
|
208
|
+
* that has not passed is answered as a deferral. Five attempts with no recorded wait, as when a process died during
|
|
209
|
+
* each, answer a `storage_unavailable` deferral `pollMs` away, and the attempt after a wait at five attempts starts
|
|
210
|
+
* a new count.
|
|
211
|
+
*
|
|
212
|
+
* @returns The attempt to make, from 1 to 5, or the deferral; null for a stale handle.
|
|
213
|
+
* @throws {SourceSyncFault} `invalid_response` for a key that is not 64 lower-case hex digits.
|
|
214
|
+
*/
|
|
215
|
+
reserveOperation(h: SourceSyncHandle, operationKey: string): Promise<SourceSyncOperationAttempt | null>;
|
|
216
|
+
/**
|
|
217
|
+
* Records the wait after a retriable failure of the attempt `reserveOperation` answered, when that operation and
|
|
218
|
+
* attempt are still the ones reserved.
|
|
219
|
+
*
|
|
220
|
+
* @returns Whether the wait was recorded: false for a stale handle or when another reservation replaced it.
|
|
221
|
+
* @throws {SourceSyncFault} `invalid_response` for a malformed key, an attempt outside 1 to 5, a `retryAt` that is
|
|
222
|
+
* not after the store's clock or a reason that is not retriable.
|
|
223
|
+
*/
|
|
224
|
+
deferOperation(h: SourceSyncHandle, operationKey: string, attempt: number, reason: SourceSyncRetryReason, retryAt: number): Promise<boolean>;
|
|
225
|
+
/**
|
|
226
|
+
* The earliest retry wait among the open cycle's work items: the cycle cannot commit its cursor before it.
|
|
227
|
+
*
|
|
228
|
+
* @returns The wait's reason and time, or null when no item waits or the handle is stale.
|
|
229
|
+
*/
|
|
230
|
+
pendingRetry(h: SourceSyncHandle): Promise<{
|
|
231
|
+
reason: SourceSyncRetryReason;
|
|
232
|
+
retryAt: number;
|
|
233
|
+
} | null>;
|
|
194
234
|
private currentWork;
|
|
195
235
|
private normalized;
|
|
196
236
|
private documentRows;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SqlAccountSourceSyncStore.d.ts","sourceRoot":"","sources":["../../src/sync/SqlAccountSourceSyncStore.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,EACL,wBAAwB,EACxB,KAAK,gBAAgB,EAAE,KAAK,qBAAqB,EAAE,KAAK,gBAAgB,EACxE,KAAK,gBAAgB,EAAE,KAAK,kBAAkB,EAAE,KAAK,iBAAiB,EACvE,MAAM,oCAAoC,CAAC;AAC5C,OAAO,KAAK,EACV,eAAe,EAAE,eAAe,EAAE,gBAAgB,EAAE,eAAe,EAAE,iBAAiB,EACtF,oBAAoB,EAAE,oBAAoB,EAAE,cAAc,EAAE,cAAc,EAC1E,kBAAkB,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,yBAAyB,EAC1F,oBAAoB,
|
|
1
|
+
{"version":3,"file":"SqlAccountSourceSyncStore.d.ts","sourceRoot":"","sources":["../../src/sync/SqlAccountSourceSyncStore.ts"],"names":[],"mappings":"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"}
|
|
@@ -278,23 +278,120 @@ export class SqlAccountSourceSyncStore {
|
|
|
278
278
|
VALUES($1,$2,$3,$4,$5::jsonb) ON CONFLICT(tenant_id,owner_id,connection_id,source_id)
|
|
279
279
|
DO UPDATE SET work=excluded.work`, [...params(scope), work.sourceId, JSON.stringify(work)]);
|
|
280
280
|
}
|
|
281
|
-
/**
|
|
281
|
+
/**
|
|
282
|
+
* Null id takes the first due item. Increment before external work; a crash leaves it pending.
|
|
283
|
+
*
|
|
284
|
+
* An item is due when it is pending or its retry time has come; without an id the first due item by source id is
|
|
285
|
+
* taken. A pending item that already has five attempts and no recorded result, as when a process died during each
|
|
286
|
+
* read, is not taken: it waits `pollMs` as a `storage_unavailable` retry, and the attempt after a wait at five
|
|
287
|
+
* attempts starts a new count.
|
|
288
|
+
*
|
|
289
|
+
* @returns The item with its attempt counted, or null when none is due or the handle is stale.
|
|
290
|
+
*/
|
|
282
291
|
async takeWork(h, id = null) {
|
|
283
292
|
return this.withHandle(h, async (tx, row) => {
|
|
284
293
|
const rows = await tx.all(`SELECT work FROM ${this.t.work}
|
|
285
294
|
WHERE tenant_id=$1 AND owner_id=$2 AND connection_id=$3
|
|
286
295
|
AND ($4::text IS NULL OR source_id=$4) ORDER BY source_id`, [...params(h.lease.scope), id]);
|
|
287
|
-
const
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
296
|
+
const now = this.now();
|
|
297
|
+
for (const { work } of rows) {
|
|
298
|
+
if (work.cycleId !== row.checkpoint?.cycleId || work.generation !== h.lease.generation)
|
|
299
|
+
continue;
|
|
300
|
+
if (work.disposition.kind !== 'pending' && work.disposition.kind !== 'retry')
|
|
301
|
+
continue;
|
|
302
|
+
if (work.disposition.kind === 'retry' && work.disposition.retryAt > now)
|
|
303
|
+
continue;
|
|
304
|
+
if (work.disposition.kind === 'pending' && work.attempts >= 5) {
|
|
305
|
+
await this.putWork(tx, h.lease.scope, { ...work, disposition: {
|
|
306
|
+
kind: 'retry', reason: 'storage_unavailable', retryAt: now + row.policy.pollMs,
|
|
307
|
+
} });
|
|
308
|
+
continue;
|
|
309
|
+
}
|
|
310
|
+
const attempts = work.disposition.kind === 'retry' && work.attempts >= 5 ? 1 : work.attempts + 1;
|
|
311
|
+
if (!validNumber(attempts))
|
|
312
|
+
throw new SourceSyncFault('invalid_response');
|
|
313
|
+
const next = { ...work, attempts, disposition: { kind: 'pending' } };
|
|
314
|
+
await this.putWork(tx, h.lease.scope, next);
|
|
315
|
+
return next;
|
|
316
|
+
}
|
|
317
|
+
return null;
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Counts an attempt at one provider operation of the open sync, its anchor read or a page, before the request is
|
|
322
|
+
* sent, under the connection's row lock. `operationKey` names the operation: a SHA-256 digest in lower-case hex
|
|
323
|
+
* that changes with the generation, cycle, revision and phase, so a new key starts a new count. A recorded wait
|
|
324
|
+
* that has not passed is answered as a deferral. Five attempts with no recorded wait, as when a process died during
|
|
325
|
+
* each, answer a `storage_unavailable` deferral `pollMs` away, and the attempt after a wait at five attempts starts
|
|
326
|
+
* a new count.
|
|
327
|
+
*
|
|
328
|
+
* @returns The attempt to make, from 1 to 5, or the deferral; null for a stale handle.
|
|
329
|
+
* @throws {SourceSyncFault} `invalid_response` for a key that is not 64 lower-case hex digits.
|
|
330
|
+
*/
|
|
331
|
+
async reserveOperation(h, operationKey) {
|
|
332
|
+
if (!/^[a-f0-9]{64}$/.test(operationKey))
|
|
333
|
+
throw new SourceSyncFault('invalid_response');
|
|
334
|
+
return this.withHandle(h, async (tx, row) => {
|
|
335
|
+
const found = await tx.get(`SELECT operation FROM ${this.t.connections} WHERE tenant_id=$1 AND owner_id=$2 AND connection_id=$3`, params(h.lease.scope));
|
|
336
|
+
const previous = found?.operation?.key === operationKey ? found.operation : null;
|
|
337
|
+
const now = this.now();
|
|
338
|
+
if (previous && previous.retryAt !== null && previous.retryAt > now) {
|
|
339
|
+
return { kind: 'deferred', reason: previous.reason, retryAt: previous.retryAt };
|
|
340
|
+
}
|
|
341
|
+
let attempts = previous?.attempts ?? 0;
|
|
342
|
+
if (attempts >= 5 && previous?.retryAt === null) {
|
|
343
|
+
const retryAt = now + row.policy.pollMs;
|
|
344
|
+
const pending = { key: operationKey, attempts,
|
|
345
|
+
retryAt, reason: 'storage_unavailable' };
|
|
346
|
+
await tx.run(`UPDATE ${this.t.connections} SET operation=$4::jsonb
|
|
347
|
+
WHERE tenant_id=$1 AND owner_id=$2 AND connection_id=$3`, [...params(h.lease.scope), JSON.stringify(pending)]);
|
|
348
|
+
return { kind: 'deferred', reason: pending.reason, retryAt };
|
|
349
|
+
}
|
|
350
|
+
if (attempts >= 5)
|
|
351
|
+
attempts = 0;
|
|
352
|
+
const next = { key: operationKey, attempts: attempts + 1,
|
|
353
|
+
retryAt: null, reason: 'storage_unavailable' };
|
|
354
|
+
await tx.run(`UPDATE ${this.t.connections} SET operation=$4::jsonb
|
|
355
|
+
WHERE tenant_id=$1 AND owner_id=$2 AND connection_id=$3`, [...params(h.lease.scope), JSON.stringify(next)]);
|
|
356
|
+
return { kind: 'attempt', attempt: next.attempts };
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* Records the wait after a retriable failure of the attempt `reserveOperation` answered, when that operation and
|
|
361
|
+
* attempt are still the ones reserved.
|
|
362
|
+
*
|
|
363
|
+
* @returns Whether the wait was recorded: false for a stale handle or when another reservation replaced it.
|
|
364
|
+
* @throws {SourceSyncFault} `invalid_response` for a malformed key, an attempt outside 1 to 5, a `retryAt` that is
|
|
365
|
+
* not after the store's clock or a reason that is not retriable.
|
|
366
|
+
*/
|
|
367
|
+
async deferOperation(h, operationKey, attempt, reason, retryAt) {
|
|
368
|
+
if (!/^[a-f0-9]{64}$/.test(operationKey) || !Number.isInteger(attempt) || attempt < 1 || attempt > 5
|
|
369
|
+
|| !validNumber(retryAt) || retryAt <= this.now()
|
|
370
|
+
|| !['network', 'rate_limited', 'provider_unavailable', 'storage_unavailable'].includes(reason)) {
|
|
371
|
+
throw new SourceSyncFault('invalid_response');
|
|
372
|
+
}
|
|
373
|
+
const result = await this.withHandle(h, async (tx) => {
|
|
374
|
+
const updated = await tx.run(`UPDATE ${this.t.connections}
|
|
375
|
+
SET operation=jsonb_build_object('key',$4::text,'attempts',$5::int,'retryAt',$6::bigint,'reason',$7::text)
|
|
376
|
+
WHERE tenant_id=$1 AND owner_id=$2 AND connection_id=$3
|
|
377
|
+
AND operation->>'key'=$4 AND (operation->>'attempts')::int=$5`, [...params(h.lease.scope), operationKey, attempt, retryAt, reason]);
|
|
378
|
+
return updated.changes === 1;
|
|
379
|
+
});
|
|
380
|
+
return result === true;
|
|
381
|
+
}
|
|
382
|
+
/**
|
|
383
|
+
* The earliest retry wait among the open cycle's work items: the cycle cannot commit its cursor before it.
|
|
384
|
+
*
|
|
385
|
+
* @returns The wait's reason and time, or null when no item waits or the handle is stale.
|
|
386
|
+
*/
|
|
387
|
+
async pendingRetry(h) {
|
|
388
|
+
return this.withHandle(h, async (tx, row) => {
|
|
389
|
+
const found = await tx.get(`SELECT work FROM ${this.t.work}
|
|
390
|
+
WHERE tenant_id=$1 AND owner_id=$2 AND connection_id=$3
|
|
391
|
+
AND work->>'cycleId'=$4 AND work->'disposition'->>'kind'='retry'
|
|
392
|
+
ORDER BY (work->'disposition'->>'retryAt')::bigint,source_id LIMIT 1`, [...params(h.lease.scope), row.checkpoint?.cycleId ?? '']);
|
|
393
|
+
const disposition = found?.work.disposition;
|
|
394
|
+
return disposition?.kind === 'retry' ? { reason: disposition.reason, retryAt: disposition.retryAt } : null;
|
|
298
395
|
});
|
|
299
396
|
}
|
|
300
397
|
async currentWork(tx, scope, cp, work) {
|