@minnowdb/core 0.7.7 → 0.7.10
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/dist/engine/batch.d.ts +14 -1
- package/dist/engine/batch.js +23 -2
- package/dist/engine/buffered-writer.d.ts +7 -4
- package/dist/engine/client.d.ts +44 -27
- package/dist/engine/client.js +22 -10
- package/dist/engine/database.d.ts +40 -22
- package/dist/engine/database.js +489 -38
- package/dist/engine/keyed-live.js +55 -18
- package/dist/engine/live.d.ts +95 -8
- package/dist/engine/live.js +311 -148
- package/dist/engine/schema.d.ts +85 -2
- package/dist/engine/typed-live.d.ts +12 -1
- package/dist/engine/typed-live.js +89 -12
- package/dist/engine/worker-server.js +12 -5
- package/dist/worker-protocol/index.d.ts +1 -1
- package/dist/worker-protocol/index.js +1 -1
- package/package.json +1 -1
package/dist/engine/live.js
CHANGED
|
@@ -4,52 +4,8 @@ const DEFAULT_LIVE_QUERY_MAX_SUBSCRIPTIONS = 1024;
|
|
|
4
4
|
const MAX_LIVE_QUERY_GROUPS = 4096;
|
|
5
5
|
const MAX_LIVE_QUERY_SUBSCRIPTIONS = 16384;
|
|
6
6
|
const MAX_LIVE_QUERY_SETS_PER_DATABASE = 256;
|
|
7
|
+
const LIVE_QUERY_EXECUTION_CONCURRENCY = 8;
|
|
7
8
|
import { LiveQueryLimitError as LiveQueryLimitError2 } from "./errors.js";
|
|
8
|
-
const digestScratch = new DataView(new ArrayBuffer(8));
|
|
9
|
-
function digestResult(result) {
|
|
10
|
-
let hash = 2166136261;
|
|
11
|
-
const mixByte = (byte) => {
|
|
12
|
-
hash = Math.imul(hash ^ byte, 16777619) >>> 0;
|
|
13
|
-
};
|
|
14
|
-
const mixNumber = (value) => {
|
|
15
|
-
digestScratch.setFloat64(0, value);
|
|
16
|
-
for (let index = 0; index < 8; index += 1)
|
|
17
|
-
mixByte(digestScratch.getUint8(index));
|
|
18
|
-
};
|
|
19
|
-
const mixString = (value) => {
|
|
20
|
-
for (let index = 0; index < value.length; index += 1) {
|
|
21
|
-
const code = value.charCodeAt(index);
|
|
22
|
-
mixByte(code & 255);
|
|
23
|
-
mixByte(code >>> 8);
|
|
24
|
-
}
|
|
25
|
-
mixByte(255);
|
|
26
|
-
};
|
|
27
|
-
for (const column of result.columns)
|
|
28
|
-
mixString(column);
|
|
29
|
-
for (const domain of result.columnDomains)
|
|
30
|
-
mixString(JSON.stringify(domain));
|
|
31
|
-
for (const row of result.rows) {
|
|
32
|
-
for (const column of result.columns) {
|
|
33
|
-
const value = row[column] ?? null;
|
|
34
|
-
if (value === null)
|
|
35
|
-
mixByte(1);
|
|
36
|
-
else if (typeof value === "number") {
|
|
37
|
-
mixByte(2);
|
|
38
|
-
mixNumber(value);
|
|
39
|
-
} else if (typeof value === "string") {
|
|
40
|
-
mixByte(3);
|
|
41
|
-
mixString(value);
|
|
42
|
-
} else if (typeof value === "boolean")
|
|
43
|
-
mixByte(value ? 4 : 5);
|
|
44
|
-
else {
|
|
45
|
-
mixByte(6);
|
|
46
|
-
mixNumber(dateMilliseconds(value));
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
mixByte(254);
|
|
50
|
-
}
|
|
51
|
-
return hash;
|
|
52
|
-
}
|
|
53
9
|
function sameQueryValue(left, right) {
|
|
54
10
|
if (left instanceof Date || right instanceof Date) {
|
|
55
11
|
return left instanceof Date && right instanceof Date && Object.is(dateMilliseconds(left), dateMilliseconds(right));
|
|
@@ -67,12 +23,15 @@ function sameResult(left, right) {
|
|
|
67
23
|
return false;
|
|
68
24
|
}
|
|
69
25
|
}
|
|
26
|
+
const columns = left.columns;
|
|
70
27
|
for (let rowIndex = 0; rowIndex < left.rows.length; rowIndex += 1) {
|
|
71
28
|
const leftRow = left.rows[rowIndex];
|
|
72
29
|
const rightRow = right.rows[rowIndex];
|
|
73
30
|
if (leftRow === void 0 || rightRow === void 0)
|
|
74
31
|
return false;
|
|
75
|
-
|
|
32
|
+
if (leftRow === rightRow)
|
|
33
|
+
continue;
|
|
34
|
+
for (const column of columns) {
|
|
76
35
|
if (!sameQueryValue(leftRow[column] ?? null, rightRow[column] ?? null))
|
|
77
36
|
return false;
|
|
78
37
|
}
|
|
@@ -148,6 +107,9 @@ function sameProbe(left, right) {
|
|
|
148
107
|
function versionOrdinal(version) {
|
|
149
108
|
return version ?? -1;
|
|
150
109
|
}
|
|
110
|
+
function newerProbe(left, right) {
|
|
111
|
+
return versionOrdinal(right.manifestVersion) > versionOrdinal(left.manifestVersion) ? right : left;
|
|
112
|
+
}
|
|
151
113
|
function boundedLiveLimit(value, maximum, label) {
|
|
152
114
|
if (!Number.isSafeInteger(value) || value < 1 || value > maximum) {
|
|
153
115
|
throw new RangeError(`Live query ${label} limit must be between 1 and ${String(maximum)}`);
|
|
@@ -157,6 +119,28 @@ function boundedLiveLimit(value, maximum, label) {
|
|
|
157
119
|
function catalogChangedBetween(previous, current) {
|
|
158
120
|
return versionOrdinal(current.manifestVersion) < versionOrdinal(previous.manifestVersion) || current.schemaEpoch !== previous.schemaEpoch;
|
|
159
121
|
}
|
|
122
|
+
function isResultSubscriber(subscriber) {
|
|
123
|
+
return subscriber.kind === "result" && !subscriber.closed;
|
|
124
|
+
}
|
|
125
|
+
function isObserver(subscriber) {
|
|
126
|
+
return subscriber.kind === "observer" && !subscriber.closed;
|
|
127
|
+
}
|
|
128
|
+
function groupExecutes(group) {
|
|
129
|
+
for (const subscriber of group.subscribers) {
|
|
130
|
+
if (subscriber.closed)
|
|
131
|
+
continue;
|
|
132
|
+
if (subscriber.kind === "result" || subscriber.options.suppressUnchanged === true)
|
|
133
|
+
return true;
|
|
134
|
+
}
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
function groupMemoizes(group) {
|
|
138
|
+
for (const subscriber of group.subscribers) {
|
|
139
|
+
if (!subscriber.closed && subscriber.kind === "observer")
|
|
140
|
+
return true;
|
|
141
|
+
}
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
160
144
|
class LiveQuerySet {
|
|
161
145
|
#host;
|
|
162
146
|
#channel;
|
|
@@ -166,8 +150,11 @@ class LiveQuerySet {
|
|
|
166
150
|
#groups = /* @__PURE__ */ new Map();
|
|
167
151
|
#opening = /* @__PURE__ */ new Map();
|
|
168
152
|
#groupsByTable = /* @__PURE__ */ new Map();
|
|
153
|
+
#lagging = /* @__PURE__ */ new Set();
|
|
169
154
|
#maxGroups;
|
|
170
155
|
#maxSubscriptions;
|
|
156
|
+
#sharedResults;
|
|
157
|
+
#incremental;
|
|
171
158
|
#stats = {
|
|
172
159
|
hints: 0,
|
|
173
160
|
versionChecks: 0,
|
|
@@ -177,10 +164,16 @@ class LiveQuerySet {
|
|
|
177
164
|
zoneSkips: 0,
|
|
178
165
|
notificationsSuppressed: 0,
|
|
179
166
|
invalidations: 0,
|
|
167
|
+
maintained: 0,
|
|
168
|
+
groupsVisited: 0,
|
|
169
|
+
retainedRows: 0,
|
|
180
170
|
sharedExecutions: 0,
|
|
181
171
|
lastSweepMs: 0
|
|
182
172
|
};
|
|
183
173
|
#lastProbe;
|
|
174
|
+
#pendingProbe;
|
|
175
|
+
#executing = 0;
|
|
176
|
+
#executionWaiters = [];
|
|
184
177
|
#sweepChain = Promise.resolve();
|
|
185
178
|
#sweepQueued = false;
|
|
186
179
|
#pollTimer;
|
|
@@ -194,6 +187,8 @@ class LiveQuerySet {
|
|
|
194
187
|
}
|
|
195
188
|
this.#maxGroups = boundedLiveLimit(options.maxGroups ?? DEFAULT_LIVE_QUERY_MAX_GROUPS, MAX_LIVE_QUERY_GROUPS, "group");
|
|
196
189
|
this.#maxSubscriptions = boundedLiveLimit(options.maxSubscriptions ?? DEFAULT_LIVE_QUERY_MAX_SUBSCRIPTIONS, MAX_LIVE_QUERY_SUBSCRIPTIONS, "subscription");
|
|
190
|
+
this.#sharedResults = options.sharedResults === true;
|
|
191
|
+
this.#incremental = options.incremental !== false;
|
|
197
192
|
this.#host = host;
|
|
198
193
|
if (options.channel !== void 0) {
|
|
199
194
|
this.#channel = options.channel;
|
|
@@ -214,7 +209,22 @@ class LiveQuerySet {
|
|
|
214
209
|
}
|
|
215
210
|
}
|
|
216
211
|
get stats() {
|
|
217
|
-
|
|
212
|
+
let retainedRows = 0;
|
|
213
|
+
for (const group of this.#groups.values())
|
|
214
|
+
retainedRows += group.result?.rows.length ?? 0;
|
|
215
|
+
return { ...this.#stats, retainedRows };
|
|
216
|
+
}
|
|
217
|
+
#freshProbe() {
|
|
218
|
+
let pending = this.#pendingProbe;
|
|
219
|
+
if (pending === void 0) {
|
|
220
|
+
pending = Promise.resolve().then(() => {
|
|
221
|
+
if (this.#pendingProbe === pending)
|
|
222
|
+
this.#pendingProbe = void 0;
|
|
223
|
+
return this.#host.currentProbe();
|
|
224
|
+
});
|
|
225
|
+
this.#pendingProbe = pending;
|
|
226
|
+
}
|
|
227
|
+
return pending;
|
|
218
228
|
}
|
|
219
229
|
#throwIfClosed() {
|
|
220
230
|
if (this.#closed)
|
|
@@ -232,22 +242,25 @@ class LiveQuerySet {
|
|
|
232
242
|
let group;
|
|
233
243
|
let subscriber;
|
|
234
244
|
try {
|
|
235
|
-
|
|
245
|
+
const opened = await this.#getOrOpenGroup(query);
|
|
246
|
+
group = opened.group;
|
|
236
247
|
this.#throwIfClosed();
|
|
237
248
|
subscriber = {
|
|
238
249
|
kind: "result",
|
|
239
250
|
options,
|
|
240
251
|
delivered: false,
|
|
241
|
-
closed: false
|
|
252
|
+
closed: false,
|
|
253
|
+
seenDelivery: -1
|
|
242
254
|
};
|
|
243
255
|
group.subscribers.add(subscriber);
|
|
244
|
-
await this.
|
|
256
|
+
await this.#settleGroup(group, opened.fresh, true);
|
|
245
257
|
this.#throwIfClosed();
|
|
246
258
|
if (!subscriber.delivered) {
|
|
247
259
|
const result = group.result ?? (await this.#executeGroup(group)).result;
|
|
248
260
|
this.#throwIfClosed();
|
|
249
|
-
if (!subscriber.closed)
|
|
250
|
-
this.#deliverResult(subscriber, result);
|
|
261
|
+
if (!subscriber.closed) {
|
|
262
|
+
this.#deliverResult(group, subscriber, result, { ...group.seenProbe, initial: true });
|
|
263
|
+
}
|
|
251
264
|
}
|
|
252
265
|
} catch (error) {
|
|
253
266
|
if (group !== void 0 && subscriber !== void 0) {
|
|
@@ -267,7 +280,8 @@ class LiveQuerySet {
|
|
|
267
280
|
let group;
|
|
268
281
|
let subscriber;
|
|
269
282
|
try {
|
|
270
|
-
|
|
283
|
+
const opened = await this.#getOrOpenGroup(query);
|
|
284
|
+
group = opened.group;
|
|
271
285
|
this.#throwIfClosed();
|
|
272
286
|
subscriber = {
|
|
273
287
|
kind: "observer",
|
|
@@ -276,7 +290,7 @@ class LiveQuerySet {
|
|
|
276
290
|
closed: false
|
|
277
291
|
};
|
|
278
292
|
group.subscribers.add(subscriber);
|
|
279
|
-
await this.
|
|
293
|
+
await this.#settleGroup(group, opened.fresh, options.suppressUnchanged === true);
|
|
280
294
|
this.#throwIfClosed();
|
|
281
295
|
if (!subscriber.delivered) {
|
|
282
296
|
this.#deliverInvalidation(subscriber, { ...group.seenProbe, initial: true });
|
|
@@ -293,6 +307,22 @@ class LiveQuerySet {
|
|
|
293
307
|
}
|
|
294
308
|
return this.#subscriptionHandle(group, subscriber);
|
|
295
309
|
}
|
|
310
|
+
async #settleGroup(group, fresh, execute) {
|
|
311
|
+
if (execute && group.result === void 0) {
|
|
312
|
+
if (group.execution !== void 0) {
|
|
313
|
+
this.#stats.sharedExecutions += 1;
|
|
314
|
+
await group.execution;
|
|
315
|
+
} else {
|
|
316
|
+
await this.#executeGroup(group, {
|
|
317
|
+
probe: group.seenProbe,
|
|
318
|
+
memoize: groupMemoizes(group)
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
this.#throwIfClosed();
|
|
322
|
+
}
|
|
323
|
+
if (!fresh || this.#lagging.has(group))
|
|
324
|
+
await this.refresh();
|
|
325
|
+
}
|
|
296
326
|
#subscriptionHandle(group, subscriber) {
|
|
297
327
|
return {
|
|
298
328
|
dependencyTableIds: [...group.dependencies],
|
|
@@ -317,6 +347,7 @@ class LiveQuerySet {
|
|
|
317
347
|
if (this.#groups.get(group.key) !== group)
|
|
318
348
|
return;
|
|
319
349
|
this.#groups.delete(group.key);
|
|
350
|
+
this.#lagging.delete(group);
|
|
320
351
|
this.#unindexGroup(group, group.dependencies);
|
|
321
352
|
}
|
|
322
353
|
async #getOrOpenGroup(query) {
|
|
@@ -324,12 +355,12 @@ class LiveQuerySet {
|
|
|
324
355
|
const existing = this.#groups.get(key);
|
|
325
356
|
if (existing !== void 0) {
|
|
326
357
|
this.#stats.sharedExecutions += 1;
|
|
327
|
-
return existing;
|
|
358
|
+
return { group: existing, fresh: false };
|
|
328
359
|
}
|
|
329
360
|
const opening = this.#opening.get(key);
|
|
330
361
|
if (opening !== void 0) {
|
|
331
362
|
this.#stats.sharedExecutions += 1;
|
|
332
|
-
return opening;
|
|
363
|
+
return { group: await opening, fresh: false };
|
|
333
364
|
}
|
|
334
365
|
if (this.#groups.size + this.#opening.size >= this.#maxGroups) {
|
|
335
366
|
throw new LiveQueryLimitError("group", this.#maxGroups);
|
|
@@ -337,7 +368,7 @@ class LiveQuerySet {
|
|
|
337
368
|
const created = this.#openGroup(key, query);
|
|
338
369
|
this.#opening.set(key, created);
|
|
339
370
|
try {
|
|
340
|
-
return await created;
|
|
371
|
+
return { group: await created, fresh: true };
|
|
341
372
|
} finally {
|
|
342
373
|
this.#opening.delete(key);
|
|
343
374
|
}
|
|
@@ -351,13 +382,17 @@ class LiveQuerySet {
|
|
|
351
382
|
subscribers: /* @__PURE__ */ new Set(),
|
|
352
383
|
seenProbe: after,
|
|
353
384
|
result: void 0,
|
|
354
|
-
|
|
355
|
-
|
|
385
|
+
execution: void 0,
|
|
386
|
+
deliveries: 0,
|
|
387
|
+
maintenance: void 0,
|
|
388
|
+
unmaintainable: false
|
|
356
389
|
};
|
|
357
390
|
this.#groups.set(key, group);
|
|
358
391
|
this.#indexGroup(group, dependencies);
|
|
359
|
-
if (this.#lastProbe === void 0
|
|
392
|
+
if (this.#lastProbe === void 0)
|
|
360
393
|
this.#lastProbe = after;
|
|
394
|
+
else if (versionOrdinal(after.manifestVersion) < versionOrdinal(this.#lastProbe.manifestVersion) || after.schemaEpoch !== this.#lastProbe.schemaEpoch) {
|
|
395
|
+
this.#lagging.add(group);
|
|
361
396
|
}
|
|
362
397
|
return group;
|
|
363
398
|
}
|
|
@@ -388,12 +423,12 @@ class LiveQuerySet {
|
|
|
388
423
|
this.#indexGroup(group, next);
|
|
389
424
|
}
|
|
390
425
|
async #stableDependencies(query) {
|
|
391
|
-
let before = await this.#
|
|
426
|
+
let before = await this.#freshProbe();
|
|
392
427
|
this.#throwIfClosed();
|
|
393
428
|
for (let attempt = 0; attempt < 8; attempt += 1) {
|
|
394
|
-
const dependencies = await this.#host.dependencyTableIds(query);
|
|
429
|
+
const dependencies = await this.#host.dependencyTableIds(query, before);
|
|
395
430
|
this.#throwIfClosed();
|
|
396
|
-
const after = await this.#
|
|
431
|
+
const after = await this.#freshProbe();
|
|
397
432
|
this.#throwIfClosed();
|
|
398
433
|
if (!catalogChangedBetween(before, after))
|
|
399
434
|
return { dependencies, probe: after };
|
|
@@ -401,20 +436,34 @@ class LiveQuerySet {
|
|
|
401
436
|
}
|
|
402
437
|
throw new Error("Catalog kept changing while live-query dependencies were resolved");
|
|
403
438
|
}
|
|
404
|
-
async #executeGroup(group) {
|
|
439
|
+
async #executeGroup(group, context) {
|
|
405
440
|
if (group.execution !== void 0) {
|
|
406
441
|
this.#stats.sharedExecutions += 1;
|
|
407
442
|
return group.execution;
|
|
408
443
|
}
|
|
409
444
|
const execution = (async () => {
|
|
410
|
-
|
|
411
|
-
|
|
445
|
+
await this.#acquireExecutionSlot();
|
|
446
|
+
let executed;
|
|
447
|
+
let maintenance;
|
|
448
|
+
try {
|
|
449
|
+
if (this.#incremental && this.#host.executeMaintainable !== void 0 && !group.unmaintainable) {
|
|
450
|
+
const maintained = await this.#host.executeMaintainable(group.query, context);
|
|
451
|
+
if (maintained === void 0)
|
|
452
|
+
group.unmaintainable = true;
|
|
453
|
+
else {
|
|
454
|
+
executed = maintained.result;
|
|
455
|
+
maintenance = maintained.state;
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
executed ??= await this.#host.execute(group.query, context);
|
|
459
|
+
} finally {
|
|
460
|
+
this.#releaseExecutionSlot();
|
|
461
|
+
}
|
|
412
462
|
const previous = group.result;
|
|
413
|
-
const changed = previous === void 0 ||
|
|
414
|
-
|
|
415
|
-
group.
|
|
416
|
-
|
|
417
|
-
return { result: retained, changed };
|
|
463
|
+
const changed = previous === void 0 || !sameResult(previous, executed);
|
|
464
|
+
group.result = executed;
|
|
465
|
+
group.maintenance = maintenance;
|
|
466
|
+
return { result: executed, changed };
|
|
418
467
|
})();
|
|
419
468
|
group.execution = execution;
|
|
420
469
|
try {
|
|
@@ -424,11 +473,66 @@ class LiveQuerySet {
|
|
|
424
473
|
group.execution = void 0;
|
|
425
474
|
}
|
|
426
475
|
}
|
|
427
|
-
#
|
|
476
|
+
async #acquireExecutionSlot() {
|
|
477
|
+
if (this.#executing < LIVE_QUERY_EXECUTION_CONCURRENCY) {
|
|
478
|
+
this.#executing += 1;
|
|
479
|
+
return;
|
|
480
|
+
}
|
|
481
|
+
await new Promise((resolve) => {
|
|
482
|
+
this.#executionWaiters.push(resolve);
|
|
483
|
+
});
|
|
484
|
+
this.#executing += 1;
|
|
485
|
+
}
|
|
486
|
+
#releaseExecutionSlot() {
|
|
487
|
+
this.#executing -= 1;
|
|
488
|
+
this.#executionWaiters.shift()?.();
|
|
489
|
+
}
|
|
490
|
+
async #maintainGroup(group, tableIds, after, until, probe) {
|
|
491
|
+
const host = this.#host;
|
|
492
|
+
if (host.maintain === void 0 || group.result === void 0 || group.maintenance === void 0 || group.execution !== void 0) {
|
|
493
|
+
return void 0;
|
|
494
|
+
}
|
|
495
|
+
const retained = group.result;
|
|
496
|
+
const state = group.maintenance;
|
|
497
|
+
const verdict = { declined: false };
|
|
498
|
+
const execution = (async () => {
|
|
499
|
+
await this.#acquireExecutionSlot();
|
|
500
|
+
let maintained;
|
|
501
|
+
try {
|
|
502
|
+
maintained = await host.maintain?.(group.query, retained, state, tableIds, after, until, probe);
|
|
503
|
+
} catch {
|
|
504
|
+
maintained = void 0;
|
|
505
|
+
} finally {
|
|
506
|
+
this.#releaseExecutionSlot();
|
|
507
|
+
}
|
|
508
|
+
if (maintained === void 0) {
|
|
509
|
+
verdict.declined = true;
|
|
510
|
+
return { result: retained, changed: false };
|
|
511
|
+
}
|
|
512
|
+
group.result = maintained.result;
|
|
513
|
+
group.maintenance = maintained.state;
|
|
514
|
+
return {
|
|
515
|
+
result: maintained.result,
|
|
516
|
+
changed: maintained.changed,
|
|
517
|
+
...maintained.retained === void 0 ? {} : { retained: maintained.retained }
|
|
518
|
+
};
|
|
519
|
+
})();
|
|
520
|
+
group.execution = execution;
|
|
521
|
+
try {
|
|
522
|
+
const outcome = await execution;
|
|
523
|
+
return verdict.declined ? void 0 : outcome;
|
|
524
|
+
} finally {
|
|
525
|
+
if (group.execution === execution)
|
|
526
|
+
group.execution = void 0;
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
#deliverResult(group, subscriber, result, delivery, retained) {
|
|
428
530
|
if (subscriber.closed)
|
|
429
531
|
return;
|
|
430
532
|
subscriber.delivered = true;
|
|
431
|
-
subscriber.
|
|
533
|
+
const consecutive = subscriber.seenDelivery === group.deliveries - 1;
|
|
534
|
+
subscriber.options.onChange(this.#sharedResults ? result : cloneResult(result), retained !== void 0 && consecutive && !delivery.initial ? { ...delivery, retained } : delivery);
|
|
535
|
+
subscriber.seenDelivery = group.deliveries;
|
|
432
536
|
}
|
|
433
537
|
#deliverInvalidation(subscriber, invalidation) {
|
|
434
538
|
if (subscriber.closed)
|
|
@@ -456,6 +560,7 @@ class LiveQuerySet {
|
|
|
456
560
|
const groups = [...this.#groups.values()];
|
|
457
561
|
this.#groups.clear();
|
|
458
562
|
this.#groupsByTable.clear();
|
|
563
|
+
this.#lagging.clear();
|
|
459
564
|
for (const group of groups) {
|
|
460
565
|
const subscribers = [...group.subscribers];
|
|
461
566
|
group.subscribers.clear();
|
|
@@ -489,19 +594,39 @@ class LiveQuerySet {
|
|
|
489
594
|
#stillOpen() {
|
|
490
595
|
return !this.#closed;
|
|
491
596
|
}
|
|
597
|
+
async #sweepCandidates(last, current, changedSince) {
|
|
598
|
+
if (catalogChangedBetween(last, current))
|
|
599
|
+
return [...this.#groups.values()];
|
|
600
|
+
if (sameProbe(last, current))
|
|
601
|
+
return [...this.#lagging];
|
|
602
|
+
const changed = await changedSince(last.manifestVersion);
|
|
603
|
+
if (!this.#stillOpen())
|
|
604
|
+
return void 0;
|
|
605
|
+
if (changed === "all")
|
|
606
|
+
return [...this.#groups.values()];
|
|
607
|
+
const candidates = new Set(this.#lagging);
|
|
608
|
+
for (const tableId of changed) {
|
|
609
|
+
const groups = this.#groupsByTable.get(tableId);
|
|
610
|
+
if (groups === void 0)
|
|
611
|
+
continue;
|
|
612
|
+
for (const group of groups)
|
|
613
|
+
candidates.add(group);
|
|
614
|
+
}
|
|
615
|
+
return [...candidates];
|
|
616
|
+
}
|
|
492
617
|
async #sweep() {
|
|
493
618
|
if (this.#closed || this.#groups.size === 0)
|
|
494
619
|
return;
|
|
495
620
|
this.#stats.versionChecks += 1;
|
|
496
|
-
const current = await this.#
|
|
621
|
+
const current = await this.#freshProbe();
|
|
497
622
|
if (!this.#stillOpen())
|
|
498
623
|
return;
|
|
499
624
|
const last = this.#lastProbe ?? current;
|
|
500
|
-
|
|
501
|
-
if (sameProbe(last, current) && !anyLagging)
|
|
625
|
+
if (sameProbe(last, current) && this.#lagging.size === 0)
|
|
502
626
|
return;
|
|
503
627
|
const started = performance.now();
|
|
504
628
|
this.#stats.sweeps += 1;
|
|
629
|
+
const rerunsBefore = this.#stats.reruns;
|
|
505
630
|
const windowCache = /* @__PURE__ */ new Map();
|
|
506
631
|
const changedSince = (after) => {
|
|
507
632
|
const key = versionOrdinal(after);
|
|
@@ -512,95 +637,133 @@ class LiveQuerySet {
|
|
|
512
637
|
}
|
|
513
638
|
return pending;
|
|
514
639
|
};
|
|
515
|
-
|
|
640
|
+
const candidates = await this.#sweepCandidates(last, current, changedSince);
|
|
641
|
+
if (candidates === void 0)
|
|
642
|
+
return;
|
|
643
|
+
for (const group of candidates) {
|
|
516
644
|
if (!this.#stillOpen())
|
|
517
645
|
return;
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
646
|
+
await this.#sweepGroup(group, last, current, changedSince);
|
|
647
|
+
}
|
|
648
|
+
this.#lastProbe = current;
|
|
649
|
+
this.#stats.rerunsAvoided += this.#groups.size - (this.#stats.reruns - rerunsBefore);
|
|
650
|
+
this.#stats.lastSweepMs = performance.now() - started;
|
|
651
|
+
}
|
|
652
|
+
async #sweepGroup(group, last, current, changedSince) {
|
|
653
|
+
if (!this.#stillOpen())
|
|
654
|
+
return;
|
|
655
|
+
if (group.subscribers.size === 0 || this.#groups.get(group.key) !== group)
|
|
656
|
+
return;
|
|
657
|
+
this.#stats.groupsVisited += 1;
|
|
658
|
+
if (sameProbe(group.seenProbe, current)) {
|
|
659
|
+
this.#lagging.delete(group);
|
|
660
|
+
return;
|
|
661
|
+
}
|
|
662
|
+
const prior = this.#lagging.has(group) ? group.seenProbe : newerProbe(group.seenProbe, last);
|
|
663
|
+
const catalogChanged = catalogChangedBetween(prior, current);
|
|
664
|
+
if (catalogChanged) {
|
|
665
|
+
group.maintenance = void 0;
|
|
666
|
+
group.unmaintainable = false;
|
|
667
|
+
try {
|
|
668
|
+
await this.#refreshDependencies(group);
|
|
669
|
+
} catch (error) {
|
|
670
|
+
this.#lagging.add(group);
|
|
671
|
+
this.#notifyGroupError(group, error);
|
|
672
|
+
return;
|
|
529
673
|
}
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
674
|
+
}
|
|
675
|
+
let relevant = [];
|
|
676
|
+
let affected = catalogChanged;
|
|
677
|
+
if (!affected && prior.manifestVersion !== current.manifestVersion) {
|
|
678
|
+
const changed = await changedSince(prior.manifestVersion);
|
|
679
|
+
if (!this.#stillOpen())
|
|
680
|
+
return;
|
|
681
|
+
if (changed === "all")
|
|
682
|
+
affected = true;
|
|
683
|
+
else {
|
|
684
|
+
relevant = [...group.dependencies].filter((tableId) => changed.has(tableId));
|
|
685
|
+
affected = relevant.length > 0;
|
|
542
686
|
}
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
687
|
+
}
|
|
688
|
+
if (!affected) {
|
|
689
|
+
this.#settleProbe(group, current);
|
|
690
|
+
return;
|
|
691
|
+
}
|
|
692
|
+
if (!catalogChanged && relevant.length > 0 && current.manifestVersion !== null && this.#host.changeCanAffect !== void 0) {
|
|
693
|
+
let canAffect;
|
|
694
|
+
try {
|
|
695
|
+
canAffect = await this.#host.changeCanAffect(group.query, relevant, prior.manifestVersion, current.manifestVersion);
|
|
696
|
+
} catch {
|
|
697
|
+
canAffect = true;
|
|
547
698
|
}
|
|
548
|
-
if (!
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
699
|
+
if (!this.#stillOpen())
|
|
700
|
+
return;
|
|
701
|
+
if (!canAffect) {
|
|
702
|
+
this.#stats.zoneSkips += 1;
|
|
703
|
+
this.#settleProbe(group, current);
|
|
704
|
+
return;
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
try {
|
|
708
|
+
let execution;
|
|
709
|
+
if (groupExecutes(group)) {
|
|
710
|
+
if (group.result === void 0 && group.execution !== void 0) {
|
|
711
|
+
this.#lagging.add(group);
|
|
556
712
|
return;
|
|
557
|
-
if (!canAffect) {
|
|
558
|
-
this.#stats.rerunsAvoided += 1;
|
|
559
|
-
this.#stats.zoneSkips += 1;
|
|
560
|
-
group.seenProbe = current;
|
|
561
|
-
continue;
|
|
562
713
|
}
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
if (
|
|
569
|
-
|
|
570
|
-
|
|
714
|
+
if (!catalogChanged && relevant.length > 0 && current.manifestVersion !== null) {
|
|
715
|
+
execution = await this.#maintainGroup(group, relevant, prior.manifestVersion, current.manifestVersion, current);
|
|
716
|
+
if (!this.#stillOpen() || this.#groups.get(group.key) !== group)
|
|
717
|
+
return;
|
|
718
|
+
}
|
|
719
|
+
if (execution !== void 0)
|
|
720
|
+
this.#stats.maintained += 1;
|
|
721
|
+
else {
|
|
571
722
|
this.#stats.reruns += 1;
|
|
572
|
-
execution = await this.#executeGroup(group
|
|
573
|
-
|
|
574
|
-
|
|
723
|
+
execution = await this.#executeGroup(group, {
|
|
724
|
+
probe: current,
|
|
725
|
+
memoize: groupMemoizes(group)
|
|
726
|
+
});
|
|
727
|
+
if (!this.#stillOpen() || this.#groups.get(group.key) !== group)
|
|
728
|
+
return;
|
|
575
729
|
}
|
|
576
|
-
|
|
730
|
+
}
|
|
731
|
+
const invalidation = { ...current, initial: false };
|
|
732
|
+
if (execution?.changed === true)
|
|
733
|
+
group.deliveries += 1;
|
|
734
|
+
for (const subscriber of [...group.subscribers]) {
|
|
735
|
+
if (isObserver(subscriber)) {
|
|
736
|
+
if (execution !== void 0 && !execution.changed && subscriber.options.suppressUnchanged === true) {
|
|
737
|
+
this.#stats.notificationsSuppressed += 1;
|
|
738
|
+
return;
|
|
739
|
+
}
|
|
577
740
|
try {
|
|
578
|
-
this.#deliverInvalidation(
|
|
741
|
+
this.#deliverInvalidation(subscriber, invalidation);
|
|
579
742
|
this.#stats.invalidations += 1;
|
|
580
743
|
} catch (error) {
|
|
581
|
-
|
|
744
|
+
subscriber.options.onError?.(error);
|
|
582
745
|
}
|
|
583
|
-
}
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
}
|
|
593
|
-
} else {
|
|
594
|
-
this.#stats.notificationsSuppressed += resultSubscribers.length;
|
|
746
|
+
} else if (isResultSubscriber(subscriber) && execution !== void 0) {
|
|
747
|
+
if (!execution.changed) {
|
|
748
|
+
this.#stats.notificationsSuppressed += 1;
|
|
749
|
+
return;
|
|
750
|
+
}
|
|
751
|
+
try {
|
|
752
|
+
this.#deliverResult(group, subscriber, execution.result, invalidation, execution.retained);
|
|
753
|
+
} catch (error) {
|
|
754
|
+
subscriber.options.onError?.(error);
|
|
595
755
|
}
|
|
596
756
|
}
|
|
597
|
-
group.seenProbe = current;
|
|
598
|
-
} catch (error) {
|
|
599
|
-
this.#notifyGroupError(group, error);
|
|
600
757
|
}
|
|
758
|
+
this.#settleProbe(group, current);
|
|
759
|
+
} catch (error) {
|
|
760
|
+
this.#lagging.add(group);
|
|
761
|
+
this.#notifyGroupError(group, error);
|
|
601
762
|
}
|
|
602
|
-
|
|
603
|
-
|
|
763
|
+
}
|
|
764
|
+
#settleProbe(group, probe) {
|
|
765
|
+
group.seenProbe = probe;
|
|
766
|
+
this.#lagging.delete(group);
|
|
604
767
|
}
|
|
605
768
|
#notifyGroupError(group, error) {
|
|
606
769
|
for (const subscriber of group.subscribers) {
|