@rocicorp/zero 1.11.0-canary.11 → 1.11.0-canary.13
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/out/analyze-query/src/analyze-cli.js +2 -2
- package/out/analyze-query/src/analyze-cli.js.map +1 -1
- package/out/shared/src/ring-buffer.d.ts +2 -0
- package/out/shared/src/ring-buffer.d.ts.map +1 -1
- package/out/shared/src/ring-buffer.js +4 -0
- package/out/shared/src/ring-buffer.js.map +1 -1
- package/out/zero/package.js +1 -1
- package/out/zero/package.js.map +1 -1
- package/out/zero-cache/src/server/replicator.d.ts.map +1 -1
- package/out/zero-cache/src/server/replicator.js +26 -13
- package/out/zero-cache/src/server/replicator.js.map +1 -1
- package/out/zero-cache/src/services/change-source/common/backfill-manager.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/common/backfill-manager.js +169 -19
- package/out/zero-cache/src/services/change-source/common/backfill-manager.js.map +1 -1
- package/out/zero-cache/src/services/change-source/custom/change-source.js +2 -2
- package/out/zero-cache/src/services/change-source/custom/change-source.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer-http.d.ts +45 -6
- package/out/zero-cache/src/services/change-streamer/change-streamer-http.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer-http.js +118 -33
- package/out/zero-cache/src/services/change-streamer/change-streamer-http.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer-service.js +2 -2
- package/out/zero-cache/src/services/change-streamer/change-streamer-service.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer.d.ts +10 -17
- package/out/zero-cache/src/services/change-streamer/change-streamer.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/schema/tables.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/schema/tables.js +15 -4
- package/out/zero-cache/src/services/change-streamer/schema/tables.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/snapshot-message.d.ts +92 -0
- package/out/zero-cache/src/services/change-streamer/snapshot-message.d.ts.map +1 -0
- package/out/zero-cache/src/services/change-streamer/snapshot-message.js +42 -0
- package/out/zero-cache/src/services/change-streamer/snapshot-message.js.map +1 -0
- package/out/zero-cache/src/services/change-streamer/snapshot-reservations.d.ts +1 -1
- package/out/zero-cache/src/services/change-streamer/snapshot-reservations.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/snapshot-reservations.js +1 -1
- package/out/zero-cache/src/services/change-streamer/snapshot-reservations.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/snapshot.d.ts +7 -89
- package/out/zero-cache/src/services/change-streamer/snapshot.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/snapshot.js +26 -98
- package/out/zero-cache/src/services/change-streamer/snapshot.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/subscribe.d.ts +358 -0
- package/out/zero-cache/src/services/change-streamer/subscribe.d.ts.map +1 -0
- package/out/zero-cache/src/services/change-streamer/subscribe.js +68 -0
- package/out/zero-cache/src/services/change-streamer/subscribe.js.map +1 -0
- package/out/zero-cache/src/services/litestream/commands.d.ts +2 -3
- package/out/zero-cache/src/services/litestream/commands.d.ts.map +1 -1
- package/out/zero-cache/src/services/litestream/commands.js +3 -2
- package/out/zero-cache/src/services/litestream/commands.js.map +1 -1
- package/out/zero-cache/src/services/replicator/change-processor.js +12 -1
- package/out/zero-cache/src/services/replicator/change-processor.js.map +1 -1
- package/out/zero-cache/src/services/replicator/incremental-sync.d.ts +2 -1
- package/out/zero-cache/src/services/replicator/incremental-sync.d.ts.map +1 -1
- package/out/zero-cache/src/services/replicator/incremental-sync.js +13 -7
- package/out/zero-cache/src/services/replicator/incremental-sync.js.map +1 -1
- package/out/zero-cache/src/services/replicator/replicator.d.ts +2 -1
- package/out/zero-cache/src/services/replicator/replicator.d.ts.map +1 -1
- package/out/zero-cache/src/services/replicator/replicator.js +2 -2
- package/out/zero-cache/src/services/replicator/replicator.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/client-handler.d.ts +19 -2
- package/out/zero-cache/src/services/view-syncer/client-handler.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/client-handler.js +8 -1
- package/out/zero-cache/src/services/view-syncer/client-handler.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/cvr-store.d.ts +19 -1
- package/out/zero-cache/src/services/view-syncer/cvr-store.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/cvr-store.js +34 -11
- package/out/zero-cache/src/services/view-syncer/cvr-store.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/cvr.d.ts +6 -2
- package/out/zero-cache/src/services/view-syncer/cvr.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/cvr.js +9 -5
- package/out/zero-cache/src/services/view-syncer/cvr.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/pipeline-driver.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/pipeline-driver.js +44 -11
- package/out/zero-cache/src/services/view-syncer/pipeline-driver.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/snapshotter.d.ts +21 -1
- package/out/zero-cache/src/services/view-syncer/snapshotter.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/snapshotter.js +14 -6
- package/out/zero-cache/src/services/view-syncer/snapshotter.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/view-syncer.js +28 -4
- package/out/zero-cache/src/services/view-syncer/view-syncer.js.map +1 -1
- package/out/zero-cache/src/types/streams.d.ts +33 -7
- package/out/zero-cache/src/types/streams.d.ts.map +1 -1
- package/out/zero-cache/src/types/streams.js +93 -50
- package/out/zero-cache/src/types/streams.js.map +1 -1
- package/out/zero-cache/src/types/subscription.d.ts +11 -0
- package/out/zero-cache/src/types/subscription.d.ts.map +1 -1
- package/out/zero-cache/src/types/subscription.js +56 -35
- package/out/zero-cache/src/types/subscription.js.map +1 -1
- package/out/zero-cache/src/types/ws.js +1 -31
- package/out/zero-cache/src/types/ws.js.map +1 -1
- package/out/zero-cache/src/workers/connection.js +3 -3
- package/out/zero-cache/src/workers/connection.js.map +1 -1
- package/out/zero-cache/src/workers/syncer.js +2 -2
- package/out/zero-cache/src/workers/syncer.js.map +1 -1
- package/out/zero-client/src/client/version.js +1 -1
- package/out/zero-client/src/client/zero.d.ts.map +1 -1
- package/out/zero-client/src/client/zero.js +5 -5
- package/out/zero-client/src/client/zero.js.map +1 -1
- package/out/zero-protocol/src/inspect-down.d.ts +81 -81
- package/out/zql/src/builder/builder.d.ts.map +1 -1
- package/out/zql/src/builder/builder.js +6 -4
- package/out/zql/src/builder/builder.js.map +1 -1
- package/out/zql/src/ivm/cap.d.ts +2 -1
- package/out/zql/src/ivm/cap.d.ts.map +1 -1
- package/out/zql/src/ivm/cap.js +3 -0
- package/out/zql/src/ivm/cap.js.map +1 -1
- package/out/zql/src/ivm/exists.d.ts +2 -0
- package/out/zql/src/ivm/exists.d.ts.map +1 -1
- package/out/zql/src/ivm/exists.js +3 -0
- package/out/zql/src/ivm/exists.js.map +1 -1
- package/out/zql/src/ivm/fan-in.d.ts +5 -1
- package/out/zql/src/ivm/fan-in.d.ts.map +1 -1
- package/out/zql/src/ivm/fan-in.js +7 -0
- package/out/zql/src/ivm/fan-in.js.map +1 -1
- package/out/zql/src/ivm/fan-out.d.ts +3 -0
- package/out/zql/src/ivm/fan-out.d.ts.map +1 -1
- package/out/zql/src/ivm/fan-out.js +4 -0
- package/out/zql/src/ivm/fan-out.js.map +1 -1
- package/out/zql/src/ivm/filter-operators.d.ts +2 -0
- package/out/zql/src/ivm/filter-operators.d.ts.map +1 -1
- package/out/zql/src/ivm/filter-operators.js +9 -0
- package/out/zql/src/ivm/filter-operators.js.map +1 -1
- package/out/zql/src/ivm/filter.d.ts +4 -1
- package/out/zql/src/ivm/filter.d.ts.map +1 -1
- package/out/zql/src/ivm/filter.js +3 -0
- package/out/zql/src/ivm/filter.js.map +1 -1
- package/out/zql/src/ivm/flipped-join.d.ts +4 -3
- package/out/zql/src/ivm/flipped-join.d.ts.map +1 -1
- package/out/zql/src/ivm/flipped-join.js +73 -22
- package/out/zql/src/ivm/flipped-join.js.map +1 -1
- package/out/zql/src/ivm/join-utils.d.ts +24 -7
- package/out/zql/src/ivm/join-utils.d.ts.map +1 -1
- package/out/zql/src/ivm/join-utils.js +87 -4
- package/out/zql/src/ivm/join-utils.js.map +1 -1
- package/out/zql/src/ivm/join.d.ts +4 -4
- package/out/zql/src/ivm/join.d.ts.map +1 -1
- package/out/zql/src/ivm/join.js +55 -64
- package/out/zql/src/ivm/join.js.map +1 -1
- package/out/zql/src/ivm/memory-source.d.ts.map +1 -1
- package/out/zql/src/ivm/memory-source.js +33 -23
- package/out/zql/src/ivm/memory-source.js.map +1 -1
- package/out/zql/src/ivm/operator.d.ts +39 -10
- package/out/zql/src/ivm/operator.d.ts.map +1 -1
- package/out/zql/src/ivm/operator.js +9 -4
- package/out/zql/src/ivm/operator.js.map +1 -1
- package/out/zql/src/ivm/push-accumulated.d.ts +6 -1
- package/out/zql/src/ivm/push-accumulated.d.ts.map +1 -1
- package/out/zql/src/ivm/push-accumulated.js +47 -2
- package/out/zql/src/ivm/push-accumulated.js.map +1 -1
- package/out/zql/src/ivm/skip.d.ts +2 -1
- package/out/zql/src/ivm/skip.d.ts.map +1 -1
- package/out/zql/src/ivm/skip.js +3 -0
- package/out/zql/src/ivm/skip.js.map +1 -1
- package/out/zql/src/ivm/take-gate.d.ts +24 -2
- package/out/zql/src/ivm/take-gate.d.ts.map +1 -1
- package/out/zql/src/ivm/take-gate.js +26 -1
- package/out/zql/src/ivm/take-gate.js.map +1 -1
- package/out/zql/src/ivm/take.d.ts +2 -1
- package/out/zql/src/ivm/take.d.ts.map +1 -1
- package/out/zql/src/ivm/take.js +59 -40
- package/out/zql/src/ivm/take.js.map +1 -1
- package/out/zql/src/ivm/union-fan-in.d.ts +3 -0
- package/out/zql/src/ivm/union-fan-in.d.ts.map +1 -1
- package/out/zql/src/ivm/union-fan-in.js +56 -5
- package/out/zql/src/ivm/union-fan-in.js.map +1 -1
- package/out/zql/src/ivm/union-fan-out.d.ts +2 -1
- package/out/zql/src/ivm/union-fan-out.d.ts.map +1 -1
- package/out/zql/src/ivm/union-fan-out.js +5 -0
- package/out/zql/src/ivm/union-fan-out.js.map +1 -1
- package/out/zql/src/query/measure-push-operator.d.ts +2 -1
- package/out/zql/src/query/measure-push-operator.d.ts.map +1 -1
- package/out/zql/src/query/measure-push-operator.js +7 -0
- package/out/zql/src/query/measure-push-operator.js.map +1 -1
- package/package.json +1 -1
package/out/zql/src/ivm/take.js
CHANGED
|
@@ -24,6 +24,13 @@ var Take = class {
|
|
|
24
24
|
#partitionKeyComparator;
|
|
25
25
|
#rowHiddenFromFetch;
|
|
26
26
|
#takeGate;
|
|
27
|
+
/**
|
|
28
|
+
* Partitions that incurred removals in Phase 1 and require deficit refills in
|
|
29
|
+
* Phase 2 (reconcile). Keyed by `takeStateKey` (see `getTakeStateKey`), matching
|
|
30
|
+
* `#storage` keys: `'["take"]'` when unpartitioned, or `'["take", ...partitionValues]'`
|
|
31
|
+
* when partitioned.
|
|
32
|
+
*/
|
|
33
|
+
#dirtyPartitions = /* @__PURE__ */ new Map();
|
|
27
34
|
#output = throwOutput;
|
|
28
35
|
setTakeGate(gate) {
|
|
29
36
|
this.#takeGate = gate;
|
|
@@ -49,6 +56,8 @@ var Take = class {
|
|
|
49
56
|
getBound(constraint) {
|
|
50
57
|
if (this.#partitionKey && !constraintContainsPartitionKey(constraint, this.#partitionKey)) return;
|
|
51
58
|
const takeStateKey = getTakeStateKey(this.#partitionKey, constraint);
|
|
59
|
+
const dirty = this.#dirtyPartitions.get(takeStateKey);
|
|
60
|
+
if (dirty) return dirty.gateBound;
|
|
52
61
|
const takeState = this.#storage.get(takeStateKey);
|
|
53
62
|
if (!takeState || takeState.size < this.#limit) return;
|
|
54
63
|
return takeState.bound;
|
|
@@ -129,7 +138,12 @@ var Take = class {
|
|
|
129
138
|
const { compareRows } = this.getSchema();
|
|
130
139
|
if (change[0] === 0) {
|
|
131
140
|
if (takeState.size < this.#limit) {
|
|
132
|
-
this.#
|
|
141
|
+
if (this.#dirtyPartitions.has(takeStateKey)) {
|
|
142
|
+
if (takeState.bound === void 0 || compareRows(change[1].row, takeState.bound) >= 0) return;
|
|
143
|
+
}
|
|
144
|
+
const nextSize = takeState.size + 1;
|
|
145
|
+
if (nextSize === this.#limit) this.#dirtyPartitions.delete(takeStateKey);
|
|
146
|
+
this.#setTakeState(takeStateKey, nextSize, takeState.bound === void 0 || compareRows(takeState.bound, change[1].row) < 0 ? change[1].row : takeState.bound);
|
|
133
147
|
yield* this.#output.push(change, this);
|
|
134
148
|
return;
|
|
135
149
|
}
|
|
@@ -190,46 +204,11 @@ var Take = class {
|
|
|
190
204
|
beforeBoundNode = node;
|
|
191
205
|
break;
|
|
192
206
|
}
|
|
193
|
-
let newBound;
|
|
194
|
-
if (beforeBoundNode) {
|
|
195
|
-
const push = compareRows(beforeBoundNode.row, takeState.bound) > 0;
|
|
196
|
-
newBound = {
|
|
197
|
-
node: beforeBoundNode,
|
|
198
|
-
push
|
|
199
|
-
};
|
|
200
|
-
}
|
|
201
|
-
if (!newBound?.push) {
|
|
202
|
-
this.#takeGate?.open();
|
|
203
|
-
try {
|
|
204
|
-
for (const node of this.#input.fetch({
|
|
205
|
-
start: {
|
|
206
|
-
row: takeState.bound,
|
|
207
|
-
basis: "at"
|
|
208
|
-
},
|
|
209
|
-
constraint
|
|
210
|
-
})) {
|
|
211
|
-
if (node === "yield") {
|
|
212
|
-
yield node;
|
|
213
|
-
continue;
|
|
214
|
-
}
|
|
215
|
-
const push = compareRows(node.row, takeState.bound) > 0;
|
|
216
|
-
newBound = {
|
|
217
|
-
node,
|
|
218
|
-
push
|
|
219
|
-
};
|
|
220
|
-
if (push) break;
|
|
221
|
-
}
|
|
222
|
-
} finally {
|
|
223
|
-
this.#takeGate?.close();
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
if (newBound?.push) {
|
|
227
|
-
yield* this.#output.push(change, this);
|
|
228
|
-
this.#setTakeState(takeStateKey, takeState.size, newBound.node.row);
|
|
229
|
-
yield* this.#output.push(makeAddChange(newBound.node), this);
|
|
230
|
-
return;
|
|
231
|
-
}
|
|
232
207
|
const finalBound = takeState.size - 1 === 0 ? void 0 : compToBound < 0 ? takeState.bound : beforeBoundNode?.row;
|
|
208
|
+
if (!this.#dirtyPartitions.has(takeStateKey)) this.#dirtyPartitions.set(takeStateKey, {
|
|
209
|
+
constraint,
|
|
210
|
+
gateBound: takeState.bound
|
|
211
|
+
});
|
|
233
212
|
this.#setTakeState(takeStateKey, takeState.size - 1, finalBound);
|
|
234
213
|
yield* this.#output.push(change, this);
|
|
235
214
|
} else if (change[0] === 3) {
|
|
@@ -394,6 +373,46 @@ var Take = class {
|
|
|
394
373
|
destroy() {
|
|
395
374
|
this.#input.destroy();
|
|
396
375
|
}
|
|
376
|
+
*reconcile(_pusher) {
|
|
377
|
+
if (this.#dirtyPartitions.size > 0) {
|
|
378
|
+
const dirty = [...this.#dirtyPartitions.entries()];
|
|
379
|
+
this.#dirtyPartitions.clear();
|
|
380
|
+
for (const [takeStateKey, { constraint }] of dirty) {
|
|
381
|
+
const takeState = this.#storage.get(takeStateKey);
|
|
382
|
+
assert(takeState !== void 0, "Take: dirty partition must exist in storage");
|
|
383
|
+
const deficit = this.#limit - takeState.size;
|
|
384
|
+
assert(deficit > 0, "Take: dirty partition must have a deficit");
|
|
385
|
+
const toPush = [];
|
|
386
|
+
this.#takeGate?.open();
|
|
387
|
+
try {
|
|
388
|
+
const stream = this.#input.fetch({
|
|
389
|
+
start: takeState.bound ? {
|
|
390
|
+
row: takeState.bound,
|
|
391
|
+
basis: "after"
|
|
392
|
+
} : void 0,
|
|
393
|
+
constraint
|
|
394
|
+
});
|
|
395
|
+
for (const node of stream) {
|
|
396
|
+
if (node === "yield") {
|
|
397
|
+
yield "yield";
|
|
398
|
+
continue;
|
|
399
|
+
}
|
|
400
|
+
toPush.push(node);
|
|
401
|
+
if (toPush.length === deficit) break;
|
|
402
|
+
}
|
|
403
|
+
} finally {
|
|
404
|
+
this.#takeGate?.close();
|
|
405
|
+
}
|
|
406
|
+
let currentSize = takeState.size;
|
|
407
|
+
for (const node of toPush) {
|
|
408
|
+
currentSize++;
|
|
409
|
+
this.#setTakeState(takeStateKey, currentSize, node.row);
|
|
410
|
+
yield* this.#output.push(makeAddChange(node), this);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
if (this.#output.reconcile) yield* this.#output.reconcile(this);
|
|
415
|
+
}
|
|
397
416
|
};
|
|
398
417
|
function getTakeStateKey(partitionKey, rowOrConstraint) {
|
|
399
418
|
const partitionValues = [];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"take.js","names":["#input","#storage","#limit","#partitionKey","#partitionKeyComparator","#takeGate","#output","#initialFetch","#rowHiddenFromFetch","#setTakeState","#pushEditChange","#getStateAndConstraint","#pushWithRowHiddenFromFetch"],"sources":["../../../../../zql/src/ivm/take.ts"],"sourcesContent":["import {assert, unreachable} from '../../../shared/src/asserts.ts';\nimport {hasOwn} from '../../../shared/src/has-own.ts';\nimport type {Row, Value} from '../../../zero-protocol/src/data.ts';\nimport type {PrimaryKey} from '../../../zero-protocol/src/primary-key.ts';\nimport {assertOrderingIncludesPK} from '../query/complete-ordering.ts';\nimport {ChangeIndex} from './change-index.ts';\nimport {ChangeType} from './change-type.ts';\nimport {\n makeAddChange,\n makeRemoveChange,\n type Change,\n type EditChange,\n} from './change.ts';\nimport type {Constraint} from './constraint.ts';\nimport {compareValues, type Comparator, type Node} from './data.ts';\nimport {\n throwOutput,\n type FetchRequest,\n type Input,\n type Operator,\n type Output,\n type Storage,\n} from './operator.ts';\nimport type {SourceSchema} from './schema.ts';\nimport {type Stream} from './stream.ts';\nimport type {TakeBoundProvider, TakeGate} from './take-gate.ts';\n\ntype TakeState = {\n size: number;\n bound: Row | undefined;\n};\n\ninterface TakeStorage {\n get(key: string): TakeState | undefined;\n set(key: string, value: TakeState): void;\n del(key: string): void;\n}\n\nexport type PartitionKey = PrimaryKey;\n\n/**\n * The Take operator is for implementing limit queries. It takes the first n\n * nodes of its input as determined by the input’s comparator. It then keeps\n * a *bound* of the last item it has accepted so that it can evaluate whether\n * new incoming pushes should be accepted or rejected.\n *\n * Take can count rows globally or by unique value of some field.\n *\n * Maintains the invariant that its output size is always <= limit, even\n * mid processing of a push.\n */\nexport class Take implements Operator, TakeBoundProvider {\n readonly #input: Input;\n readonly #storage: TakeStorage;\n readonly #limit: number;\n readonly #partitionKey: PartitionKey | undefined;\n readonly #partitionKeyComparator: Comparator | undefined;\n // Fetch overlay needed for some split push cases.\n #rowHiddenFromFetch: Row | undefined;\n\n #takeGate: TakeGate | undefined;\n\n #output: Output = throwOutput;\n\n setTakeGate(gate: TakeGate): void {\n this.#takeGate = gate;\n }\n\n constructor(\n input: Input,\n storage: Storage,\n limit: number,\n partitionKey?: PartitionKey,\n ) {\n assert(limit >= 0, 'Limit must be non-negative');\n const {sort} = input.getSchema();\n assert(sort !== undefined, 'Take requires sorted input');\n assertOrderingIncludesPK(sort, input.getSchema().primaryKey);\n input.setOutput(this);\n this.#input = input;\n this.#storage = storage as TakeStorage;\n this.#limit = limit;\n this.#partitionKey = partitionKey;\n this.#partitionKeyComparator =\n partitionKey && makePartitionKeyComparator(partitionKey);\n }\n\n setOutput(output: Output): void {\n this.#output = output;\n }\n\n getSchema(): SourceSchema {\n return this.#input.getSchema();\n }\n\n getBound(constraint?: Constraint): Row | undefined {\n if (\n this.#partitionKey &&\n !constraintContainsPartitionKey(constraint, this.#partitionKey)\n ) {\n return undefined;\n }\n const takeStateKey = getTakeStateKey(this.#partitionKey, constraint);\n const takeState = this.#storage.get(takeStateKey);\n if (!takeState || takeState.size < this.#limit) {\n return undefined;\n }\n return takeState.bound;\n }\n\n *fetch(req: FetchRequest): Stream<Node | 'yield'> {\n assert(\n !this.#partitionKey ||\n (req.constraint !== undefined &&\n constraintContainsPartitionKey(req.constraint, this.#partitionKey)),\n 'Partitioned take does not allow unpartitioned fetches',\n );\n\n const takeStateKey = getTakeStateKey(this.#partitionKey, req.constraint);\n const takeState = this.#storage.get(takeStateKey);\n if (!takeState) {\n if (constraintMatchesPartitionKey(req.constraint, this.#partitionKey)) {\n yield* this.#initialFetch(req);\n }\n return;\n }\n if (takeState.bound === undefined) {\n return;\n }\n let count = 0;\n for (const inputNode of this.#input.fetch(req)) {\n if (inputNode === 'yield') {\n yield inputNode;\n continue;\n }\n if (this.getSchema().compareRows(takeState.bound, inputNode.row) < 0) {\n return;\n }\n if (\n this.#rowHiddenFromFetch &&\n this.getSchema().compareRows(\n this.#rowHiddenFromFetch,\n inputNode.row,\n ) === 0\n ) {\n continue;\n }\n yield inputNode;\n count++;\n if (count >= this.#limit) {\n return;\n }\n }\n }\n\n *#initialFetch(req: FetchRequest): Stream<Node | 'yield'> {\n assert(req.start === undefined, 'Start should be undefined');\n assert(!req.reverse, 'Reverse should be false');\n\n if (this.#limit === 0) {\n return;\n }\n\n assert(\n constraintMatchesPartitionKey(req.constraint, this.#partitionKey),\n 'Constraint should match partition key',\n );\n\n const takeStateKey = getTakeStateKey(this.#partitionKey, req.constraint);\n assert(\n this.#storage.get(takeStateKey) === undefined,\n 'Take state should be undefined',\n );\n\n let size = 0;\n let bound: Row | undefined;\n let downstreamEarlyReturn = true;\n let exceptionThrown = false;\n try {\n for (const inputNode of this.#input.fetch(req)) {\n if (inputNode === 'yield') {\n yield 'yield';\n continue;\n }\n yield inputNode;\n bound = inputNode.row;\n size++;\n if (size === this.#limit) {\n break;\n }\n }\n downstreamEarlyReturn = false;\n } catch (e) {\n exceptionThrown = true;\n throw e;\n } finally {\n if (!exceptionThrown) {\n this.#setTakeState(takeStateKey, size, bound);\n // If it becomes necessary to support downstream early return, this\n // assert should be removed, and replaced with code that consumes\n // the input stream until limit is reached or the input stream is\n // exhausted so that takeState is properly hydrated.\n assert(\n !downstreamEarlyReturn,\n 'Unexpected early return prevented full hydration',\n );\n }\n }\n }\n\n #getStateAndConstraint(row: Row) {\n const takeStateKey = getTakeStateKey(this.#partitionKey, row);\n const takeState = this.#storage.get(takeStateKey);\n let constraint: Constraint | undefined;\n if (takeState) {\n constraint =\n this.#partitionKey &&\n Object.fromEntries(\n this.#partitionKey.map(key => [key, row[key]] as const),\n );\n }\n\n return {takeState, takeStateKey, constraint} as\n | {\n takeState: undefined;\n takeStateKey: string;\n constraint: undefined;\n }\n | {\n takeState: TakeState;\n takeStateKey: string;\n constraint: Constraint | undefined;\n };\n }\n\n *push(change: Change): Stream<'yield'> {\n if (change[ChangeIndex.TYPE] === ChangeType.EDIT) {\n yield* this.#pushEditChange(change);\n return;\n }\n\n const {takeState, takeStateKey, constraint} = this.#getStateAndConstraint(\n change[ChangeIndex.NODE].row,\n );\n if (!takeState) {\n return;\n }\n\n const {compareRows} = this.getSchema();\n\n if (change[ChangeIndex.TYPE] === ChangeType.ADD) {\n if (takeState.size < this.#limit) {\n this.#setTakeState(\n takeStateKey,\n takeState.size + 1,\n takeState.bound === undefined ||\n compareRows(takeState.bound, change[ChangeIndex.NODE].row) < 0\n ? change[ChangeIndex.NODE].row\n : takeState.bound,\n );\n yield* this.#output.push(change, this);\n return;\n }\n // size === limit\n if (\n takeState.bound === undefined ||\n compareRows(change[ChangeIndex.NODE].row, takeState.bound) >= 0\n ) {\n return;\n }\n // added row < bound\n let beforeBoundNode: Node | undefined;\n let boundNode: Node | undefined;\n if (this.#limit === 1) {\n for (const node of this.#input.fetch({\n start: {\n row: takeState.bound,\n basis: 'at',\n },\n constraint,\n })) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n boundNode = node;\n break;\n }\n } else {\n for (const node of this.#input.fetch({\n start: {\n row: takeState.bound,\n basis: 'at',\n },\n constraint,\n reverse: true,\n })) {\n if (node === 'yield') {\n yield node;\n continue;\n } else if (boundNode === undefined) {\n boundNode = node;\n } else {\n beforeBoundNode = node;\n break;\n }\n }\n }\n assert(\n boundNode !== undefined,\n 'Take: boundNode must be found during fetch',\n );\n const removeChange = makeRemoveChange(boundNode);\n // Remove before add to maintain invariant that\n // output size <= limit.\n this.#setTakeState(\n takeStateKey,\n takeState.size,\n beforeBoundNode === undefined ||\n compareRows(change[ChangeIndex.NODE].row, beforeBoundNode.row) > 0\n ? change[ChangeIndex.NODE].row\n : beforeBoundNode.row,\n );\n yield* this.#pushWithRowHiddenFromFetch(\n change[ChangeIndex.NODE].row,\n removeChange,\n );\n yield* this.#output.push(change, this);\n } else if (change[ChangeIndex.TYPE] === ChangeType.REMOVE) {\n if (takeState.bound === undefined) {\n // change is after bound\n return;\n }\n const compToBound = compareRows(\n change[ChangeIndex.NODE].row,\n takeState.bound,\n );\n if (compToBound > 0 || (compToBound < 0 && this.#limit === 1)) {\n // change is not in window\n return;\n }\n let beforeBoundNode: Node | undefined;\n for (const node of this.#input.fetch({\n start: {\n row: takeState.bound,\n basis: 'after',\n },\n constraint,\n reverse: true,\n })) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n beforeBoundNode = node;\n break;\n }\n\n let newBound: {node: Node; push: boolean} | undefined;\n if (beforeBoundNode) {\n const push = compareRows(beforeBoundNode.row, takeState.bound) > 0;\n newBound = {\n node: beforeBoundNode,\n push,\n };\n }\n if (!newBound?.push) {\n this.#takeGate?.open();\n try {\n for (const node of this.#input.fetch({\n start: {\n row: takeState.bound,\n basis: 'at',\n },\n constraint,\n })) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n const push = compareRows(node.row, takeState.bound) > 0;\n newBound = {\n node,\n push,\n };\n if (push) {\n break;\n }\n }\n } finally {\n this.#takeGate?.close();\n }\n }\n\n if (newBound?.push) {\n yield* this.#output.push(change, this);\n this.#setTakeState(takeStateKey, takeState.size, newBound.node.row);\n yield* this.#output.push(makeAddChange(newBound.node), this);\n return;\n }\n const finalBound =\n takeState.size - 1 === 0\n ? undefined\n : compToBound < 0\n ? takeState.bound\n : beforeBoundNode?.row;\n this.#setTakeState(takeStateKey, takeState.size - 1, finalBound);\n yield* this.#output.push(change, this);\n } else if (change[ChangeIndex.TYPE] === ChangeType.CHILD) {\n // A 'child' change should be pushed to output if its row\n // is <= bound.\n if (\n takeState.bound &&\n compareRows(change[ChangeIndex.NODE].row, takeState.bound) <= 0\n ) {\n yield* this.#output.push(change, this);\n }\n }\n }\n\n *#pushEditChange(change: EditChange): Stream<'yield'> {\n assert(\n !this.#partitionKeyComparator ||\n this.#partitionKeyComparator(\n change[ChangeIndex.OLD_NODE].row,\n change[ChangeIndex.NODE].row,\n ) === 0,\n 'Unexpected change of partition key',\n );\n\n const {takeState, takeStateKey, constraint} = this.#getStateAndConstraint(\n change[ChangeIndex.OLD_NODE].row,\n );\n if (!takeState) {\n return;\n }\n\n assert(takeState.bound, 'Bound should be set');\n const {compareRows} = this.getSchema();\n const oldCmp = compareRows(\n change[ChangeIndex.OLD_NODE].row,\n takeState.bound,\n );\n const newCmp = compareRows(change[ChangeIndex.NODE].row, takeState.bound);\n\n const replaceBoundAndForwardChange = () => {\n this.#setTakeState(\n takeStateKey,\n takeState.size,\n change[ChangeIndex.NODE].row,\n );\n return this.#output.push(change, this);\n };\n\n // The bounds row was changed.\n if (oldCmp === 0) {\n // The new row is the new bound.\n if (newCmp === 0) {\n // no need to update the state since we are keeping the bounds\n yield* this.#output.push(change, this);\n return;\n }\n\n if (newCmp < 0) {\n if (this.#limit === 1) {\n yield* replaceBoundAndForwardChange();\n return;\n }\n\n // New row will be in the result but it might not be the bounds any\n // more. We need to find the row before the bounds to determine the new\n // bounds.\n\n let beforeBoundNode: Node | undefined;\n for (const node of this.#input.fetch({\n start: {\n row: takeState.bound,\n basis: 'after',\n },\n constraint,\n reverse: true,\n })) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n beforeBoundNode = node;\n break;\n }\n assert(\n beforeBoundNode !== undefined,\n 'Take: beforeBoundNode must be found during fetch',\n );\n\n this.#setTakeState(takeStateKey, takeState.size, beforeBoundNode.row);\n yield* this.#output.push(change, this);\n return;\n }\n\n assert(newCmp > 0, 'New comparison must be greater than 0');\n // Find the first item at the old bounds. This will be the new bounds.\n let newBoundNode: Node | undefined;\n this.#takeGate?.open();\n try {\n for (const node of this.#input.fetch({\n start: {\n row: takeState.bound,\n basis: 'at',\n },\n constraint,\n })) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n newBoundNode = node;\n break;\n }\n } finally {\n this.#takeGate?.close();\n }\n assert(\n newBoundNode !== undefined,\n 'Take: newBoundNode must be found during fetch',\n );\n\n // The next row is the new row. We can replace the bounds and keep the\n // edit change.\n if (compareRows(newBoundNode.row, change[ChangeIndex.NODE].row) === 0) {\n yield* replaceBoundAndForwardChange();\n return;\n }\n\n // The new row is now outside the bounds, so we need to remove the old\n // row and add the new bounds row.\n this.#setTakeState(takeStateKey, takeState.size, newBoundNode.row);\n yield* this.#pushWithRowHiddenFromFetch(\n newBoundNode.row,\n makeRemoveChange(change[ChangeIndex.OLD_NODE]),\n );\n yield* this.#output.push(makeAddChange(newBoundNode), this);\n return;\n }\n\n if (oldCmp > 0) {\n assert(newCmp !== 0, 'Invalid state. Row has duplicate primary key');\n\n // Both old and new outside of bounds\n if (newCmp > 0) {\n return;\n }\n\n // old was outside, new is inside. Pushing out the old bounds\n assert(newCmp < 0, 'New comparison must be less than 0');\n\n let oldBoundNode: Node | undefined;\n let newBoundNode: Node | undefined;\n for (const node of this.#input.fetch({\n start: {\n row: takeState.bound,\n basis: 'at',\n },\n constraint,\n reverse: true,\n })) {\n if (node === 'yield') {\n yield node;\n continue;\n } else if (oldBoundNode === undefined) {\n oldBoundNode = node;\n } else {\n newBoundNode = node;\n break;\n }\n }\n assert(\n oldBoundNode !== undefined,\n 'Take: oldBoundNode must be found during fetch',\n );\n assert(\n newBoundNode !== undefined,\n 'Take: newBoundNode must be found during fetch',\n );\n\n // Remove before add to maintain invariant that\n // output size <= limit.\n this.#setTakeState(takeStateKey, takeState.size, newBoundNode.row);\n yield* this.#pushWithRowHiddenFromFetch(\n change[ChangeIndex.NODE].row,\n makeRemoveChange(oldBoundNode),\n );\n yield* this.#output.push(makeAddChange(change[ChangeIndex.NODE]), this);\n\n return;\n }\n\n if (oldCmp < 0) {\n assert(newCmp !== 0, 'Invalid state. Row has duplicate primary key');\n\n // Both old and new inside of bounds\n if (newCmp < 0) {\n yield* this.#output.push(change, this);\n return;\n }\n\n // old was inside, new is larger than old bound\n\n assert(newCmp > 0, 'New comparison must be greater than 0');\n\n // at this point we need to find the row after the bound and use that or\n // the newRow as the new bound.\n let afterBoundNode: Node | undefined;\n this.#takeGate?.open();\n try {\n for (const node of this.#input.fetch({\n start: {\n row: takeState.bound,\n basis: 'after',\n },\n constraint,\n })) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n afterBoundNode = node;\n break;\n }\n } finally {\n this.#takeGate?.close();\n }\n assert(\n afterBoundNode !== undefined,\n 'Take: afterBoundNode must be found during fetch',\n );\n\n // The new row is the new bound. Use an edit change.\n if (compareRows(afterBoundNode.row, change[ChangeIndex.NODE].row) === 0) {\n yield* replaceBoundAndForwardChange();\n return;\n }\n\n yield* this.#output.push(\n makeRemoveChange(change[ChangeIndex.OLD_NODE]),\n this,\n );\n this.#setTakeState(takeStateKey, takeState.size, afterBoundNode.row);\n yield* this.#output.push(makeAddChange(afterBoundNode), this);\n return;\n }\n\n unreachable();\n }\n\n *#pushWithRowHiddenFromFetch(row: Row, change: Change) {\n this.#rowHiddenFromFetch = row;\n try {\n yield* this.#output.push(change, this);\n } finally {\n this.#rowHiddenFromFetch = undefined;\n }\n }\n\n #setTakeState(takeStateKey: string, size: number, bound: Row | undefined) {\n this.#storage.set(takeStateKey, {\n size,\n bound,\n });\n }\n\n destroy(): void {\n this.#input.destroy();\n }\n}\n\nfunction getTakeStateKey(\n partitionKey: PartitionKey | undefined,\n rowOrConstraint: Row | Constraint | undefined,\n): string {\n // The order must be consistent. We always use the order as defined by the\n // partition key.\n const partitionValues: Value[] = [];\n\n if (partitionKey && rowOrConstraint) {\n for (const key of partitionKey) {\n partitionValues.push(rowOrConstraint[key]);\n }\n }\n\n return JSON.stringify(['take', ...partitionValues]);\n}\n\nexport function constraintMatchesPartitionKey(\n constraint: Constraint | undefined,\n partitionKey: PartitionKey | undefined,\n): boolean {\n if (constraint === undefined || partitionKey === undefined) {\n return constraint === partitionKey;\n }\n if (partitionKey.length !== Object.keys(constraint).length) {\n return false;\n }\n for (const key of partitionKey) {\n if (!hasOwn(constraint, key)) {\n return false;\n }\n }\n return true;\n}\n\nexport function constraintContainsPartitionKey(\n constraint: Constraint | undefined,\n partitionKey: PartitionKey | undefined,\n): boolean {\n if (constraint === undefined || partitionKey === undefined) {\n return false;\n }\n for (const key of partitionKey) {\n if (!hasOwn(constraint, key)) {\n return false;\n }\n }\n return true;\n}\n\nexport function makePartitionKeyComparator(\n partitionKey: PartitionKey,\n): Comparator {\n return (a, b) => {\n for (const key of partitionKey) {\n const cmp = compareValues(a[key], b[key]);\n if (cmp !== 0) {\n return cmp;\n }\n }\n return 0;\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"take.js","names":["#input","#storage","#limit","#partitionKey","#partitionKeyComparator","#dirtyPartitions","#takeGate","#output","#initialFetch","#rowHiddenFromFetch","#setTakeState","#pushEditChange","#getStateAndConstraint","#pushWithRowHiddenFromFetch"],"sources":["../../../../../zql/src/ivm/take.ts"],"sourcesContent":["import {assert, unreachable} from '../../../shared/src/asserts.ts';\nimport {hasOwn} from '../../../shared/src/has-own.ts';\nimport type {Row, Value} from '../../../zero-protocol/src/data.ts';\nimport type {PrimaryKey} from '../../../zero-protocol/src/primary-key.ts';\nimport {assertOrderingIncludesPK} from '../query/complete-ordering.ts';\nimport {ChangeIndex} from './change-index.ts';\nimport {ChangeType} from './change-type.ts';\nimport {\n makeAddChange,\n makeRemoveChange,\n type Change,\n type EditChange,\n} from './change.ts';\nimport type {Constraint} from './constraint.ts';\nimport {compareValues, type Comparator, type Node} from './data.ts';\nimport {\n throwOutput,\n type FetchRequest,\n type Input,\n type InputBase,\n type Operator,\n type Output,\n type Storage,\n} from './operator.ts';\nimport type {SourceSchema} from './schema.ts';\nimport {type Stream} from './stream.ts';\nimport type {TakeBoundProvider, TakeGate} from './take-gate.ts';\n\ntype TakeState = {\n size: number;\n bound: Row | undefined;\n};\n\ninterface TakeStorage {\n get(key: string): TakeState | undefined;\n set(key: string, value: TakeState): void;\n del(key: string): void;\n}\n\nexport type PartitionKey = PrimaryKey;\n\ntype DirtyPartitionState = {\n constraint: Constraint | undefined;\n /**\n * The bound to report to upstream TakeGate via getBound() until Phase 2\n * reconciliation completes.\n *\n * When removals reduce partition size below limit in Phase 1, normal getBound()\n * would return undefined (unbounded), which would cause upstream TakeGate to open\n * and allow unbounded parent pushes through. Preserving gateBound keeps TakeGate\n * capped at the pre-removal boundary until Phase 2 refills the deficit.\n */\n gateBound: Row | undefined;\n};\n\n/**\n * The Take operator is for implementing limit queries. It takes the first n\n * nodes of its input as determined by the input’s comparator. It then keeps\n * a *bound* of the last item it has accepted so that it can evaluate whether\n * new incoming pushes should be accepted or rejected.\n *\n * Take can count rows globally or by unique value of some field.\n *\n * Maintains the invariant that its output size is always <= limit, even\n * mid processing of a push.\n */\nexport class Take implements Operator, TakeBoundProvider {\n readonly #input: Input;\n readonly #storage: TakeStorage;\n readonly #limit: number;\n readonly #partitionKey: PartitionKey | undefined;\n readonly #partitionKeyComparator: Comparator | undefined;\n // Fetch overlay needed for some split push cases.\n #rowHiddenFromFetch: Row | undefined;\n\n #takeGate: TakeGate | undefined;\n /**\n * Partitions that incurred removals in Phase 1 and require deficit refills in\n * Phase 2 (reconcile). Keyed by `takeStateKey` (see `getTakeStateKey`), matching\n * `#storage` keys: `'[\"take\"]'` when unpartitioned, or `'[\"take\", ...partitionValues]'`\n * when partitioned.\n */\n readonly #dirtyPartitions = new Map<string, DirtyPartitionState>();\n\n #output: Output = throwOutput;\n\n setTakeGate(gate: TakeGate): void {\n this.#takeGate = gate;\n }\n\n constructor(\n input: Input,\n storage: Storage,\n limit: number,\n partitionKey?: PartitionKey,\n ) {\n assert(limit >= 0, 'Limit must be non-negative');\n const {sort} = input.getSchema();\n assert(sort !== undefined, 'Take requires sorted input');\n assertOrderingIncludesPK(sort, input.getSchema().primaryKey);\n input.setOutput(this);\n this.#input = input;\n this.#storage = storage as TakeStorage;\n this.#limit = limit;\n this.#partitionKey = partitionKey;\n this.#partitionKeyComparator =\n partitionKey && makePartitionKeyComparator(partitionKey);\n }\n\n setOutput(output: Output): void {\n this.#output = output;\n }\n\n getSchema(): SourceSchema {\n return this.#input.getSchema();\n }\n\n getBound(constraint?: Constraint): Row | undefined {\n if (\n this.#partitionKey &&\n !constraintContainsPartitionKey(constraint, this.#partitionKey)\n ) {\n return undefined;\n }\n const takeStateKey = getTakeStateKey(this.#partitionKey, constraint);\n const dirty = this.#dirtyPartitions.get(takeStateKey);\n if (dirty) {\n // While dirty in Phase 1, return gateBound (the bound prior to removals) so\n // upstream operators (e.g. TakeGate) remain capped rather than unbounding during push.\n return dirty.gateBound;\n }\n const takeState = this.#storage.get(takeStateKey);\n if (!takeState || takeState.size < this.#limit) {\n return undefined;\n }\n return takeState.bound;\n }\n\n *fetch(req: FetchRequest): Stream<Node | 'yield'> {\n assert(\n !this.#partitionKey ||\n (req.constraint !== undefined &&\n constraintContainsPartitionKey(req.constraint, this.#partitionKey)),\n 'Partitioned take does not allow unpartitioned fetches',\n );\n\n const takeStateKey = getTakeStateKey(this.#partitionKey, req.constraint);\n const takeState = this.#storage.get(takeStateKey);\n if (!takeState) {\n if (constraintMatchesPartitionKey(req.constraint, this.#partitionKey)) {\n yield* this.#initialFetch(req);\n }\n return;\n }\n if (takeState.bound === undefined) {\n return;\n }\n let count = 0;\n for (const inputNode of this.#input.fetch(req)) {\n if (inputNode === 'yield') {\n yield inputNode;\n continue;\n }\n if (this.getSchema().compareRows(takeState.bound, inputNode.row) < 0) {\n return;\n }\n if (\n this.#rowHiddenFromFetch &&\n this.getSchema().compareRows(\n this.#rowHiddenFromFetch,\n inputNode.row,\n ) === 0\n ) {\n continue;\n }\n yield inputNode;\n count++;\n if (count >= this.#limit) {\n return;\n }\n }\n }\n\n *#initialFetch(req: FetchRequest): Stream<Node | 'yield'> {\n assert(req.start === undefined, 'Start should be undefined');\n assert(!req.reverse, 'Reverse should be false');\n\n if (this.#limit === 0) {\n return;\n }\n\n assert(\n constraintMatchesPartitionKey(req.constraint, this.#partitionKey),\n 'Constraint should match partition key',\n );\n\n const takeStateKey = getTakeStateKey(this.#partitionKey, req.constraint);\n assert(\n this.#storage.get(takeStateKey) === undefined,\n 'Take state should be undefined',\n );\n\n let size = 0;\n let bound: Row | undefined;\n let downstreamEarlyReturn = true;\n let exceptionThrown = false;\n try {\n for (const inputNode of this.#input.fetch(req)) {\n if (inputNode === 'yield') {\n yield 'yield';\n continue;\n }\n yield inputNode;\n bound = inputNode.row;\n size++;\n if (size === this.#limit) {\n break;\n }\n }\n downstreamEarlyReturn = false;\n } catch (e) {\n exceptionThrown = true;\n throw e;\n } finally {\n if (!exceptionThrown) {\n this.#setTakeState(takeStateKey, size, bound);\n // If it becomes necessary to support downstream early return, this\n // assert should be removed, and replaced with code that consumes\n // the input stream until limit is reached or the input stream is\n // exhausted so that takeState is properly hydrated.\n assert(\n !downstreamEarlyReturn,\n 'Unexpected early return prevented full hydration',\n );\n }\n }\n }\n\n #getStateAndConstraint(row: Row) {\n const takeStateKey = getTakeStateKey(this.#partitionKey, row);\n const takeState = this.#storage.get(takeStateKey);\n let constraint: Constraint | undefined;\n if (takeState) {\n constraint =\n this.#partitionKey &&\n Object.fromEntries(\n this.#partitionKey.map(key => [key, row[key]] as const),\n );\n }\n\n return {takeState, takeStateKey, constraint} as\n | {\n takeState: undefined;\n takeStateKey: string;\n constraint: undefined;\n }\n | {\n takeState: TakeState;\n takeStateKey: string;\n constraint: Constraint | undefined;\n };\n }\n\n *push(change: Change): Stream<'yield'> {\n if (change[ChangeIndex.TYPE] === ChangeType.EDIT) {\n yield* this.#pushEditChange(change);\n return;\n }\n\n const {takeState, takeStateKey, constraint} = this.#getStateAndConstraint(\n change[ChangeIndex.NODE].row,\n );\n if (!takeState) {\n return;\n }\n\n const {compareRows} = this.getSchema();\n\n if (change[ChangeIndex.TYPE] === ChangeType.ADD) {\n if (takeState.size < this.#limit) {\n if (this.#dirtyPartitions.has(takeStateKey)) {\n // While dirty with an unresolved deficit in Phase 1, only admit rows that\n // sort strictly before the current window boundary. We refuse to expand the\n // bound forward during Phase 1 so that Phase 2 can backfill earlier candidate\n // rows from storage in sort order.\n if (\n takeState.bound === undefined ||\n compareRows(change[ChangeIndex.NODE].row, takeState.bound) >= 0\n ) {\n return;\n }\n }\n const nextSize = takeState.size + 1;\n if (nextSize === this.#limit) {\n this.#dirtyPartitions.delete(takeStateKey);\n }\n this.#setTakeState(\n takeStateKey,\n nextSize,\n takeState.bound === undefined ||\n compareRows(takeState.bound, change[ChangeIndex.NODE].row) < 0\n ? change[ChangeIndex.NODE].row\n : takeState.bound,\n );\n yield* this.#output.push(change, this);\n return;\n }\n // size === limit\n if (\n takeState.bound === undefined ||\n compareRows(change[ChangeIndex.NODE].row, takeState.bound) >= 0\n ) {\n return;\n }\n // added row < bound\n let beforeBoundNode: Node | undefined;\n let boundNode: Node | undefined;\n if (this.#limit === 1) {\n for (const node of this.#input.fetch({\n start: {\n row: takeState.bound,\n basis: 'at',\n },\n constraint,\n })) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n boundNode = node;\n break;\n }\n } else {\n for (const node of this.#input.fetch({\n start: {\n row: takeState.bound,\n basis: 'at',\n },\n constraint,\n reverse: true,\n })) {\n if (node === 'yield') {\n yield node;\n continue;\n } else if (boundNode === undefined) {\n boundNode = node;\n } else {\n beforeBoundNode = node;\n break;\n }\n }\n }\n assert(\n boundNode !== undefined,\n 'Take: boundNode must be found during fetch',\n );\n const removeChange = makeRemoveChange(boundNode);\n // Remove before add to maintain invariant that\n // output size <= limit.\n this.#setTakeState(\n takeStateKey,\n takeState.size,\n beforeBoundNode === undefined ||\n compareRows(change[ChangeIndex.NODE].row, beforeBoundNode.row) > 0\n ? change[ChangeIndex.NODE].row\n : beforeBoundNode.row,\n );\n yield* this.#pushWithRowHiddenFromFetch(\n change[ChangeIndex.NODE].row,\n removeChange,\n );\n yield* this.#output.push(change, this);\n } else if (change[ChangeIndex.TYPE] === ChangeType.REMOVE) {\n if (takeState.bound === undefined) {\n // change is after bound\n return;\n }\n const compToBound = compareRows(\n change[ChangeIndex.NODE].row,\n takeState.bound,\n );\n if (compToBound > 0 || (compToBound < 0 && this.#limit === 1)) {\n // change is not in window\n return;\n }\n let beforeBoundNode: Node | undefined;\n for (const node of this.#input.fetch({\n start: {\n row: takeState.bound,\n basis: 'after',\n },\n constraint,\n reverse: true,\n })) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n beforeBoundNode = node;\n break;\n }\n\n const finalBound =\n takeState.size - 1 === 0\n ? undefined\n : compToBound < 0\n ? takeState.bound\n : beforeBoundNode?.row;\n if (!this.#dirtyPartitions.has(takeStateKey)) {\n // Snapshot the pre-removal bound as the gate bound on the first removal that\n // dirties this partition. Subsequent getBound() calls throughout Phase 1 will return\n // this bound to keep upstream TakeGate bounded until Phase 2 refills deficits.\n this.#dirtyPartitions.set(takeStateKey, {\n constraint,\n gateBound: takeState.bound,\n });\n }\n this.#setTakeState(takeStateKey, takeState.size - 1, finalBound);\n yield* this.#output.push(change, this);\n } else if (change[ChangeIndex.TYPE] === ChangeType.CHILD) {\n // A 'child' change should be pushed to output if its row\n // is <= bound.\n if (\n takeState.bound &&\n compareRows(change[ChangeIndex.NODE].row, takeState.bound) <= 0\n ) {\n yield* this.#output.push(change, this);\n }\n }\n }\n\n *#pushEditChange(change: EditChange): Stream<'yield'> {\n assert(\n !this.#partitionKeyComparator ||\n this.#partitionKeyComparator(\n change[ChangeIndex.OLD_NODE].row,\n change[ChangeIndex.NODE].row,\n ) === 0,\n 'Unexpected change of partition key',\n );\n\n const {takeState, takeStateKey, constraint} = this.#getStateAndConstraint(\n change[ChangeIndex.OLD_NODE].row,\n );\n if (!takeState) {\n return;\n }\n\n assert(takeState.bound, 'Bound should be set');\n const {compareRows} = this.getSchema();\n const oldCmp = compareRows(\n change[ChangeIndex.OLD_NODE].row,\n takeState.bound,\n );\n const newCmp = compareRows(change[ChangeIndex.NODE].row, takeState.bound);\n\n const replaceBoundAndForwardChange = () => {\n this.#setTakeState(\n takeStateKey,\n takeState.size,\n change[ChangeIndex.NODE].row,\n );\n return this.#output.push(change, this);\n };\n\n // The bounds row was changed.\n if (oldCmp === 0) {\n // The new row is the new bound.\n if (newCmp === 0) {\n // no need to update the state since we are keeping the bounds\n yield* this.#output.push(change, this);\n return;\n }\n\n if (newCmp < 0) {\n if (this.#limit === 1) {\n yield* replaceBoundAndForwardChange();\n return;\n }\n\n // New row will be in the result but it might not be the bounds any\n // more. We need to find the row before the bounds to determine the new\n // bounds.\n\n let beforeBoundNode: Node | undefined;\n for (const node of this.#input.fetch({\n start: {\n row: takeState.bound,\n basis: 'after',\n },\n constraint,\n reverse: true,\n })) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n beforeBoundNode = node;\n break;\n }\n assert(\n beforeBoundNode !== undefined,\n 'Take: beforeBoundNode must be found during fetch',\n );\n\n this.#setTakeState(takeStateKey, takeState.size, beforeBoundNode.row);\n yield* this.#output.push(change, this);\n return;\n }\n\n assert(newCmp > 0, 'New comparison must be greater than 0');\n // Find the first item at the old bounds. This will be the new bounds.\n let newBoundNode: Node | undefined;\n this.#takeGate?.open();\n try {\n for (const node of this.#input.fetch({\n start: {\n row: takeState.bound,\n basis: 'at',\n },\n constraint,\n })) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n newBoundNode = node;\n break;\n }\n } finally {\n this.#takeGate?.close();\n }\n assert(\n newBoundNode !== undefined,\n 'Take: newBoundNode must be found during fetch',\n );\n\n // The next row is the new row. We can replace the bounds and keep the\n // edit change.\n if (compareRows(newBoundNode.row, change[ChangeIndex.NODE].row) === 0) {\n yield* replaceBoundAndForwardChange();\n return;\n }\n\n // The new row is now outside the bounds, so we need to remove the old\n // row and add the new bounds row.\n this.#setTakeState(takeStateKey, takeState.size, newBoundNode.row);\n yield* this.#pushWithRowHiddenFromFetch(\n newBoundNode.row,\n makeRemoveChange(change[ChangeIndex.OLD_NODE]),\n );\n yield* this.#output.push(makeAddChange(newBoundNode), this);\n return;\n }\n\n if (oldCmp > 0) {\n assert(newCmp !== 0, 'Invalid state. Row has duplicate primary key');\n\n // Both old and new outside of bounds\n if (newCmp > 0) {\n return;\n }\n\n // old was outside, new is inside. Pushing out the old bounds\n assert(newCmp < 0, 'New comparison must be less than 0');\n\n let oldBoundNode: Node | undefined;\n let newBoundNode: Node | undefined;\n for (const node of this.#input.fetch({\n start: {\n row: takeState.bound,\n basis: 'at',\n },\n constraint,\n reverse: true,\n })) {\n if (node === 'yield') {\n yield node;\n continue;\n } else if (oldBoundNode === undefined) {\n oldBoundNode = node;\n } else {\n newBoundNode = node;\n break;\n }\n }\n assert(\n oldBoundNode !== undefined,\n 'Take: oldBoundNode must be found during fetch',\n );\n assert(\n newBoundNode !== undefined,\n 'Take: newBoundNode must be found during fetch',\n );\n\n // Remove before add to maintain invariant that\n // output size <= limit.\n this.#setTakeState(takeStateKey, takeState.size, newBoundNode.row);\n yield* this.#pushWithRowHiddenFromFetch(\n change[ChangeIndex.NODE].row,\n makeRemoveChange(oldBoundNode),\n );\n yield* this.#output.push(makeAddChange(change[ChangeIndex.NODE]), this);\n\n return;\n }\n\n if (oldCmp < 0) {\n assert(newCmp !== 0, 'Invalid state. Row has duplicate primary key');\n\n // Both old and new inside of bounds\n if (newCmp < 0) {\n yield* this.#output.push(change, this);\n return;\n }\n\n // old was inside, new is larger than old bound\n\n assert(newCmp > 0, 'New comparison must be greater than 0');\n\n // at this point we need to find the row after the bound and use that or\n // the newRow as the new bound.\n let afterBoundNode: Node | undefined;\n this.#takeGate?.open();\n try {\n for (const node of this.#input.fetch({\n start: {\n row: takeState.bound,\n basis: 'after',\n },\n constraint,\n })) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n afterBoundNode = node;\n break;\n }\n } finally {\n this.#takeGate?.close();\n }\n assert(\n afterBoundNode !== undefined,\n 'Take: afterBoundNode must be found during fetch',\n );\n\n // The new row is the new bound. Use an edit change.\n if (compareRows(afterBoundNode.row, change[ChangeIndex.NODE].row) === 0) {\n yield* replaceBoundAndForwardChange();\n return;\n }\n\n yield* this.#output.push(\n makeRemoveChange(change[ChangeIndex.OLD_NODE]),\n this,\n );\n this.#setTakeState(takeStateKey, takeState.size, afterBoundNode.row);\n yield* this.#output.push(makeAddChange(afterBoundNode), this);\n return;\n }\n\n unreachable();\n }\n\n *#pushWithRowHiddenFromFetch(row: Row, change: Change) {\n this.#rowHiddenFromFetch = row;\n try {\n yield* this.#output.push(change, this);\n } finally {\n this.#rowHiddenFromFetch = undefined;\n }\n }\n\n #setTakeState(takeStateKey: string, size: number, bound: Row | undefined) {\n this.#storage.set(takeStateKey, {\n size,\n bound,\n });\n }\n\n destroy(): void {\n this.#input.destroy();\n }\n\n *reconcile(_pusher?: InputBase): Stream<'yield'> {\n if (this.#dirtyPartitions.size > 0) {\n const dirty = [...this.#dirtyPartitions.entries()];\n this.#dirtyPartitions.clear();\n\n for (const [takeStateKey, {constraint}] of dirty) {\n const takeState = this.#storage.get(takeStateKey);\n assert(\n takeState !== undefined,\n 'Take: dirty partition must exist in storage',\n );\n const deficit = this.#limit - takeState.size;\n assert(deficit > 0, 'Take: dirty partition must have a deficit');\n\n const toPush: Node[] = [];\n this.#takeGate?.open();\n try {\n const stream = this.#input.fetch({\n start: takeState.bound\n ? {\n row: takeState.bound,\n basis: 'after',\n }\n : undefined,\n constraint,\n });\n\n for (const node of stream) {\n if (node === 'yield') {\n yield 'yield';\n continue;\n }\n toPush.push(node);\n if (toPush.length === deficit) {\n break;\n }\n }\n } finally {\n this.#takeGate?.close();\n }\n\n let currentSize = takeState.size;\n for (const node of toPush) {\n currentSize++;\n this.#setTakeState(takeStateKey, currentSize, node.row);\n yield* this.#output.push(makeAddChange(node), this);\n }\n }\n }\n\n if (this.#output.reconcile) {\n yield* this.#output.reconcile(this);\n }\n }\n}\n\nfunction getTakeStateKey(\n partitionKey: PartitionKey | undefined,\n rowOrConstraint: Row | Constraint | undefined,\n): string {\n // The order must be consistent. We always use the order as defined by the\n // partition key.\n const partitionValues: Value[] = [];\n\n if (partitionKey && rowOrConstraint) {\n for (const key of partitionKey) {\n partitionValues.push(rowOrConstraint[key]);\n }\n }\n\n return JSON.stringify(['take', ...partitionValues]);\n}\n\nexport function constraintMatchesPartitionKey(\n constraint: Constraint | undefined,\n partitionKey: PartitionKey | undefined,\n): boolean {\n if (constraint === undefined || partitionKey === undefined) {\n return constraint === partitionKey;\n }\n if (partitionKey.length !== Object.keys(constraint).length) {\n return false;\n }\n for (const key of partitionKey) {\n if (!hasOwn(constraint, key)) {\n return false;\n }\n }\n return true;\n}\n\nexport function constraintContainsPartitionKey(\n constraint: Constraint | undefined,\n partitionKey: PartitionKey | undefined,\n): boolean {\n if (constraint === undefined || partitionKey === undefined) {\n return false;\n }\n for (const key of partitionKey) {\n if (!hasOwn(constraint, key)) {\n return false;\n }\n }\n return true;\n}\n\nexport function makePartitionKeyComparator(\n partitionKey: PartitionKey,\n): Comparator {\n return (a, b) => {\n for (const key of partitionKey) {\n const cmp = compareValues(a[key], b[key]);\n if (cmp !== 0) {\n return cmp;\n }\n }\n return 0;\n };\n}\n"],"mappings":"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"}
|
|
@@ -14,7 +14,10 @@ export declare class UnionFanIn implements Operator {
|
|
|
14
14
|
push(change: Change, pusher: InputBase): Stream<'yield'>;
|
|
15
15
|
fanOutStartedPushing(): void;
|
|
16
16
|
fanOutDonePushing(fanOutChangeType: ChangeType): Stream<'yield'>;
|
|
17
|
+
fanOutStartedReconciling(): void;
|
|
18
|
+
fanOutDoneReconciling(): Stream<'yield'>;
|
|
17
19
|
setOutput(output: Output): void;
|
|
20
|
+
reconcile(_pusher: InputBase): Stream<'yield'>;
|
|
18
21
|
}
|
|
19
22
|
export declare function mergeFetches(fetches: Iterable<Node | 'yield'>[], comparator: (l: Node, r: Node) => number): IterableIterator<Node | 'yield'>;
|
|
20
23
|
//# sourceMappingURL=union-fan-in.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"union-fan-in.d.ts","sourceRoot":"","sources":["../../../../../zql/src/ivm/union-fan-in.ts"],"names":[],"mappings":"AAIA,OAAO,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAC5C,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,aAAa,CAAC;AAExC,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,WAAW,CAAC;AACpC,OAAO,EAEL,KAAK,YAAY,EACjB,KAAK,KAAK,EACV,KAAK,SAAS,EACd,KAAK,QAAQ,EACb,KAAK,MAAM,EACZ,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"union-fan-in.d.ts","sourceRoot":"","sources":["../../../../../zql/src/ivm/union-fan-in.ts"],"names":[],"mappings":"AAIA,OAAO,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAC5C,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,aAAa,CAAC;AAExC,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,WAAW,CAAC;AACpC,OAAO,EAEL,KAAK,YAAY,EACjB,KAAK,KAAK,EACV,KAAK,SAAS,EACd,KAAK,QAAQ,EACb,KAAK,MAAM,EACZ,MAAM,eAAe,CAAC;AAQvB,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,aAAa,CAAC;AAC9C,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,aAAa,CAAC;AACxC,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,oBAAoB,CAAC;AAEpD,qBAAa,UAAW,YAAW,QAAQ;;IASzC,YAAY,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,EAgE/C;IAED,OAAO,IAAI,IAAI,CAId;IAED,KAAK,CAAC,GAAG,EAAE,YAAY,GAAG,MAAM,CAAC,IAAI,GAAG,OAAO,CAAC,CAY/C;IAED,SAAS,IAAI,YAAY,CAExB;IAED,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,GAAG,MAAM,CAAC,OAAO,CAAC,CAMvD;IAwFD,oBAAoB,SAMnB;IAED,iBAAiB,CAAC,gBAAgB,EAAE,UAAU,GAAG,MAAM,CAAC,OAAO,CAAC,CAyB/D;IAED,wBAAwB,SAMvB;IAEA,qBAAqB,IAAI,MAAM,CAAC,OAAO,CAAC,CAYxC;IAED,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAE9B;IAEA,SAAS,CAAC,OAAO,EAAE,SAAS,GAAG,MAAM,CAAC,OAAO,CAAC,CAO9C;CACF;AAWD,wBAAiB,YAAY,CAC3B,OAAO,EAAE,QAAQ,CAAC,IAAI,GAAG,OAAO,CAAC,EAAE,EACnC,UAAU,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,KAAK,MAAM,GACvC,gBAAgB,CAAC,IAAI,GAAG,OAAO,CAAC,CA0FlC"}
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import { assert } from "../../../shared/src/asserts.js";
|
|
2
2
|
import { emptyArray } from "../../../shared/src/sentinels.js";
|
|
3
3
|
import { throwOutput } from "./operator.js";
|
|
4
|
-
import { makeAddEmptyRelationships, mergeRelationships, pushAccumulatedChanges } from "./push-accumulated.js";
|
|
4
|
+
import { makeAddEmptyRelationships, mergeEmpty, mergeRelationships, pushAccumulatedChanges, pushWitnessDeltas } from "./push-accumulated.js";
|
|
5
5
|
//#region ../zql/src/ivm/union-fan-in.ts
|
|
6
6
|
var UnionFanIn = class {
|
|
7
7
|
#inputs;
|
|
8
8
|
#schema;
|
|
9
9
|
#fanOutPushStarted = false;
|
|
10
|
+
#fanOutReconcileStarted = false;
|
|
10
11
|
#output = throwOutput;
|
|
11
12
|
#accumulatedPushes = [];
|
|
13
|
+
#branchRels;
|
|
12
14
|
constructor(fanOut, inputs) {
|
|
13
15
|
this.#inputs = inputs;
|
|
14
16
|
const fanOutSchema = fanOut.getSchema();
|
|
@@ -42,6 +44,7 @@ var UnionFanIn = class {
|
|
|
42
44
|
}
|
|
43
45
|
this.#schema = schema;
|
|
44
46
|
this.#inputs = inputs;
|
|
47
|
+
this.#branchRels = relationshipsFromBranches;
|
|
45
48
|
}
|
|
46
49
|
destroy() {
|
|
47
50
|
for (const input of this.#inputs) input.destroy();
|
|
@@ -49,7 +52,16 @@ var UnionFanIn = class {
|
|
|
49
52
|
fetch(req) {
|
|
50
53
|
const iterables = this.#inputs.map((input) => input.fetch(req));
|
|
51
54
|
const compareRows = this.#schema.compareRows;
|
|
52
|
-
|
|
55
|
+
const compare = req.reverse ? (l, r) => compareRows(r.row, l.row) : (l, r) => compareRows(l.row, r.row);
|
|
56
|
+
const relNames = Object.keys(this.#schema.relationships);
|
|
57
|
+
return mapNodes(mergeFetches(iterables, compare), (node) => {
|
|
58
|
+
const relationships = { ...node.relationships };
|
|
59
|
+
mergeEmpty(relationships, relNames);
|
|
60
|
+
return {
|
|
61
|
+
row: node.row,
|
|
62
|
+
relationships
|
|
63
|
+
};
|
|
64
|
+
});
|
|
53
65
|
}
|
|
54
66
|
getSchema() {
|
|
55
67
|
return this.#schema;
|
|
@@ -104,7 +116,10 @@ var UnionFanIn = class {
|
|
|
104
116
|
otherBranchHasRow = true;
|
|
105
117
|
break;
|
|
106
118
|
}
|
|
107
|
-
if (otherBranchHasRow)
|
|
119
|
+
if (otherBranchHasRow) {
|
|
120
|
+
yield* pushWitnessDeltas(change[1], change[0], (name) => this.#branchRels.has(name), this.#output, this);
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
108
123
|
}
|
|
109
124
|
assert(hadMatch, "Pusher was not one of the inputs to union-fan-in!");
|
|
110
125
|
yield* this.#output.push(change, this);
|
|
@@ -118,12 +133,29 @@ var UnionFanIn = class {
|
|
|
118
133
|
this.#fanOutPushStarted = false;
|
|
119
134
|
if (this.#inputs.length === 0) return emptyArray;
|
|
120
135
|
if (this.#accumulatedPushes.length === 0) return emptyArray;
|
|
121
|
-
return pushAccumulatedChanges(this.#accumulatedPushes, this.#output, this, fanOutChangeType, mergeRelationships, makeAddEmptyRelationships(this.#schema));
|
|
136
|
+
return pushAccumulatedChanges(this.#accumulatedPushes, this.#output, this, fanOutChangeType, mergeRelationships, makeAddEmptyRelationships(this.#schema), (name) => this.#branchRels.has(name));
|
|
137
|
+
}
|
|
138
|
+
fanOutStartedReconciling() {
|
|
139
|
+
assert(this.#fanOutReconcileStarted === false, "UnionFanIn: fanOutStartedReconciling called while already reconciling");
|
|
140
|
+
this.#fanOutReconcileStarted = true;
|
|
141
|
+
}
|
|
142
|
+
*fanOutDoneReconciling() {
|
|
143
|
+
assert(this.#fanOutReconcileStarted, "UnionFanIn: fanOutDoneReconciling called without fanOutStartedReconciling");
|
|
144
|
+
this.#fanOutReconcileStarted = false;
|
|
145
|
+
if (this.#inputs.length === 0) return;
|
|
146
|
+
if (this.#output.reconcile) yield* this.#output.reconcile(this);
|
|
122
147
|
}
|
|
123
148
|
setOutput(output) {
|
|
124
149
|
this.#output = output;
|
|
125
150
|
}
|
|
151
|
+
*reconcile(_pusher) {
|
|
152
|
+
if (this.#fanOutReconcileStarted) return;
|
|
153
|
+
if (this.#output.reconcile) yield* this.#output.reconcile(this);
|
|
154
|
+
}
|
|
126
155
|
};
|
|
156
|
+
function* mapNodes(nodes, f) {
|
|
157
|
+
for (const n of nodes) yield n === "yield" ? n : f(n);
|
|
158
|
+
}
|
|
127
159
|
function* mergeFetches(fetches, comparator) {
|
|
128
160
|
const iterators = fetches.map((i) => i[Symbol.iterator]());
|
|
129
161
|
let threw = false;
|
|
@@ -155,8 +187,27 @@ function* mergeFetches(fetches, comparator) {
|
|
|
155
187
|
}
|
|
156
188
|
current[minIndex] = result.done ? null : result.value;
|
|
157
189
|
if (lastNodeYielded !== void 0 && comparator(lastNodeYielded, minNode) === 0) continue;
|
|
190
|
+
let merged = minNode;
|
|
191
|
+
for (let j = 0; j < iterators.length; j++) {
|
|
192
|
+
let head = current[j];
|
|
193
|
+
while (head !== null && comparator(head, minNode) === 0) {
|
|
194
|
+
merged = {
|
|
195
|
+
row: merged.row,
|
|
196
|
+
relationships: {
|
|
197
|
+
...head.relationships,
|
|
198
|
+
...merged.relationships
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
let r = iterators[j].next();
|
|
202
|
+
while (!r.done && r.value === "yield") {
|
|
203
|
+
yield r.value;
|
|
204
|
+
r = iterators[j].next();
|
|
205
|
+
}
|
|
206
|
+
head = current[j] = r.done ? null : r.value;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
158
209
|
lastNodeYielded = minNode;
|
|
159
|
-
yield
|
|
210
|
+
yield merged;
|
|
160
211
|
}
|
|
161
212
|
} catch (e) {
|
|
162
213
|
threw = true;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"union-fan-in.js","names":["#inputs","#schema","#fanOutPushStarted","#pushInternalChange","#accumulatedPushes","#output"],"sources":["../../../../../zql/src/ivm/union-fan-in.ts"],"sourcesContent":["import {assert} from '../../../shared/src/asserts.ts';\nimport {emptyArray} from '../../../shared/src/sentinels.ts';\nimport type {Writable} from '../../../shared/src/writable.ts';\nimport {ChangeIndex} from './change-index.ts';\nimport {ChangeType} from './change-type.ts';\nimport type {Change} from './change.ts';\nimport type {Constraint} from './constraint.ts';\nimport type {Node} from './data.ts';\nimport {\n throwOutput,\n type FetchRequest,\n type Input,\n type InputBase,\n type Operator,\n type Output,\n} from './operator.ts';\nimport {\n makeAddEmptyRelationships,\n mergeRelationships,\n pushAccumulatedChanges,\n} from './push-accumulated.ts';\nimport type {SourceSchema} from './schema.ts';\nimport type {Stream} from './stream.ts';\nimport type {UnionFanOut} from './union-fan-out.ts';\n\nexport class UnionFanIn implements Operator {\n readonly #inputs: readonly Input[];\n readonly #schema: SourceSchema;\n #fanOutPushStarted: boolean = false;\n #output: Output = throwOutput;\n #accumulatedPushes: Change[] = [];\n\n constructor(fanOut: UnionFanOut, inputs: Input[]) {\n this.#inputs = inputs;\n const fanOutSchema = fanOut.getSchema();\n fanOut.setFanIn(this);\n assert(fanOutSchema.sort !== undefined, 'UnionFanIn requires sorted input');\n\n const schema: Writable<SourceSchema> = {\n tableName: fanOutSchema.tableName,\n columns: fanOutSchema.columns,\n primaryKey: fanOutSchema.primaryKey,\n relationships: {\n ...fanOutSchema.relationships,\n },\n isHidden: fanOutSchema.isHidden,\n system: fanOutSchema.system,\n compareRows: fanOutSchema.compareRows,\n sort: fanOutSchema.sort,\n };\n\n // now go through inputs and merge relationships\n const relationshipsFromBranches: Set<string> = new Set();\n for (const input of inputs) {\n const inputSchema = input.getSchema();\n assert(\n schema.tableName === inputSchema.tableName,\n `Table name mismatch in union fan-in: ${schema.tableName} !== ${inputSchema.tableName}`,\n );\n assert(\n schema.primaryKey === inputSchema.primaryKey,\n `Primary key mismatch in union fan-in`,\n );\n assert(\n schema.system === inputSchema.system,\n `System mismatch in union fan-in: ${schema.system} !== ${inputSchema.system}`,\n );\n assert(\n schema.compareRows === inputSchema.compareRows,\n `compareRows mismatch in union fan-in`,\n );\n assert(schema.sort === inputSchema.sort, `Sort mismatch in union fan-in`);\n\n for (const [relName, relSchema] of Object.entries(\n inputSchema.relationships,\n )) {\n if (relName in fanOutSchema.relationships) {\n continue;\n }\n\n // All branches will have unique relationship names except for relationships\n // that come in from `fanOut`.\n assert(\n !relationshipsFromBranches.has(relName),\n `Relationship ${relName} exists in multiple upstream inputs to union fan-in`,\n );\n schema.relationships[relName] = relSchema;\n relationshipsFromBranches.add(relName);\n }\n\n input.setOutput(this);\n }\n\n this.#schema = schema;\n this.#inputs = inputs;\n }\n\n destroy(): void {\n for (const input of this.#inputs) {\n input.destroy();\n }\n }\n\n fetch(req: FetchRequest): Stream<Node | 'yield'> {\n const iterables = this.#inputs.map(input => input.fetch(req));\n const compareRows = this.#schema.compareRows;\n const compare = req.reverse\n ? (l: Node, r: Node) => compareRows(r.row, l.row)\n : (l: Node, r: Node) => compareRows(l.row, r.row);\n return mergeFetches(iterables, compare);\n }\n\n getSchema(): SourceSchema {\n return this.#schema;\n }\n\n push(change: Change, pusher: InputBase): Stream<'yield'> {\n if (!this.#fanOutPushStarted) {\n return this.#pushInternalChange(change, pusher);\n }\n this.#accumulatedPushes.push(change);\n return emptyArray;\n }\n\n /**\n * An internal change means that a change was received inside the fan-out/fan-in sub-graph.\n *\n * These changes always come from children of a flip-join as no other push generating operators\n * currently exist between union-fan-in and union-fan-out. All other pushes\n * enter into union-fan-out before reaching union-fan-in.\n *\n * - normal joins for `exists` come before `union-fan-out`\n * - joins for `related` come after `union-fan-out`\n * - take comes after `union-fan-out`\n *\n * The algorithm for deciding whether or not to forward a push that came from inside the ufo/ufi sub-graph:\n * 1. If the change is a `child` change we can forward it. This is because all child branches in the ufo/ufi sub-graph are unique.\n * 2. If the change is `add` we can forward it iff no `fetches` for the row return any results.\n * If another branch has it, the add was already emitted in the past.\n * 3. If the change is `remove` we can forward it iff no `fetches` for the row return any results.\n * If no other branches have the change, the remove can be sent as the value is no longer present.\n * If other branches have it, the last branch the processes the remove will send the remove.\n * 4. Edits will always come through as child changes as flip join will flip them into children.\n * An edit that would result in a remove or add will have been split into an add/remove pair rather than being an edit.\n */\n *#pushInternalChange(change: Change, pusher: InputBase): Stream<'yield'> {\n if (change[ChangeIndex.TYPE] === ChangeType.CHILD) {\n yield* this.#output.push(change, this);\n return;\n }\n\n assert(\n change[ChangeIndex.TYPE] === ChangeType.ADD ||\n change[ChangeIndex.TYPE] === ChangeType.REMOVE,\n () =>\n `UnionFanIn: expected add or remove change type, got ${change[ChangeIndex.TYPE]}`,\n );\n\n let hadMatch = false;\n for (const input of this.#inputs) {\n if (input === pusher) {\n hadMatch = true;\n continue;\n }\n\n const constraint: Writable<Constraint> = {};\n for (const key of this.#schema.primaryKey) {\n constraint[key] = change[ChangeIndex.NODE].row[key];\n }\n const fetchResult = input.fetch({\n constraint,\n });\n\n // `fetch` interleaves 'yield' sentinels for cooperative multitasking.\n // They must be forwarded, not just skipped: this probe runs inside a\n // push, and the sentinel is the source offering the scheduler a breath.\n // They must also not be mistaken for rows -- reading one as a row is\n // what broke this before, since an empty branch that happened to yield\n // looked like a branch holding the row, silently dropping the\n // add/remove and desyncing a downstream `Take`'s push and fetch paths.\n let otherBranchHasRow = false;\n for (const node of fetchResult) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n otherBranchHasRow = true;\n break;\n }\n\n if (otherBranchHasRow) {\n // Another branch has the row, so the add/remove is not needed.\n return;\n }\n }\n\n assert(hadMatch, 'Pusher was not one of the inputs to union-fan-in!');\n\n // No other branches have the row, so we can push the change.\n yield* this.#output.push(change, this);\n }\n\n fanOutStartedPushing() {\n assert(\n this.#fanOutPushStarted === false,\n 'UnionFanIn: fanOutStartedPushing called while already pushing',\n );\n this.#fanOutPushStarted = true;\n }\n\n fanOutDonePushing(fanOutChangeType: ChangeType): Stream<'yield'> {\n assert(\n this.#fanOutPushStarted,\n 'UnionFanIn: fanOutDonePushing called without fanOutStartedPushing',\n );\n this.#fanOutPushStarted = false;\n if (this.#inputs.length === 0) {\n return emptyArray;\n }\n\n if (this.#accumulatedPushes.length === 0) {\n // It is possible for no forks to pass along the push.\n // E.g., if no filters match in any fork.\n return emptyArray;\n }\n\n return pushAccumulatedChanges(\n this.#accumulatedPushes,\n this.#output,\n this,\n fanOutChangeType,\n mergeRelationships,\n makeAddEmptyRelationships(this.#schema),\n );\n }\n\n setOutput(output: Output): void {\n this.#output = output;\n }\n}\n\nexport function* mergeFetches(\n fetches: Iterable<Node | 'yield'>[],\n comparator: (l: Node, r: Node) => number,\n): IterableIterator<Node | 'yield'> {\n const iterators = fetches.map(i => i[Symbol.iterator]());\n let threw = false;\n try {\n const current: (Node | null)[] = [];\n let lastNodeYielded: Node | undefined;\n for (let i = 0; i < iterators.length; i++) {\n const iter = iterators[i];\n let result = iter.next();\n // yield yields when initializing\n while (!result.done && result.value === 'yield') {\n yield result.value;\n result = iter.next();\n }\n current[i] = result.done ? null : (result.value as Node);\n }\n while (current.some(c => c !== null)) {\n const min = current.reduce(\n (acc: [Node, number] | undefined, c, i): [Node, number] | undefined => {\n if (c === null) {\n return acc;\n }\n if (acc === undefined || comparator(c, acc[0]) < 0) {\n return [c, i];\n }\n return acc;\n },\n undefined,\n );\n\n assert(min !== undefined, 'min is undefined');\n const [minNode, minIndex] = min;\n const iter = iterators[minIndex];\n let result = iter.next();\n while (!result.done && result.value === 'yield') {\n yield result.value;\n result = iter.next();\n }\n current[minIndex] = result.done ? null : (result.value as Node);\n if (\n lastNodeYielded !== undefined &&\n comparator(lastNodeYielded, minNode) === 0\n ) {\n continue;\n }\n lastNodeYielded = minNode;\n yield minNode;\n }\n } catch (e) {\n threw = true;\n for (const iter of iterators) {\n try {\n iter.throw?.(e);\n } catch (_cleanupError) {\n // error in the iter.throw cleanup,\n // catch so other iterators are cleaned up\n }\n }\n throw e;\n } finally {\n if (!threw) {\n for (const iter of iterators) {\n try {\n iter.return?.();\n } catch (_cleanupError) {\n // error in the iter.return cleanup,\n // catch so other iterators are cleaned up\n }\n }\n }\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"union-fan-in.js","names":["#inputs","#schema","#branchRels","#fanOutPushStarted","#pushInternalChange","#accumulatedPushes","#output","#fanOutReconcileStarted"],"sources":["../../../../../zql/src/ivm/union-fan-in.ts"],"sourcesContent":["import {assert} from '../../../shared/src/asserts.ts';\nimport {emptyArray} from '../../../shared/src/sentinels.ts';\nimport type {Writable} from '../../../shared/src/writable.ts';\nimport {ChangeIndex} from './change-index.ts';\nimport {ChangeType} from './change-type.ts';\nimport type {Change} from './change.ts';\nimport type {Constraint} from './constraint.ts';\nimport type {Node} from './data.ts';\nimport {\n throwOutput,\n type FetchRequest,\n type Input,\n type InputBase,\n type Operator,\n type Output,\n} from './operator.ts';\nimport {\n makeAddEmptyRelationships,\n mergeEmpty,\n mergeRelationships,\n pushAccumulatedChanges,\n pushWitnessDeltas,\n} from './push-accumulated.ts';\nimport type {SourceSchema} from './schema.ts';\nimport type {Stream} from './stream.ts';\nimport type {UnionFanOut} from './union-fan-out.ts';\n\nexport class UnionFanIn implements Operator {\n readonly #inputs: readonly Input[];\n readonly #schema: SourceSchema;\n #fanOutPushStarted: boolean = false;\n #fanOutReconcileStarted: boolean = false;\n #output: Output = throwOutput;\n #accumulatedPushes: Change[] = [];\n readonly #branchRels: ReadonlySet<string>;\n\n constructor(fanOut: UnionFanOut, inputs: Input[]) {\n this.#inputs = inputs;\n const fanOutSchema = fanOut.getSchema();\n fanOut.setFanIn(this);\n assert(fanOutSchema.sort !== undefined, 'UnionFanIn requires sorted input');\n\n const schema: Writable<SourceSchema> = {\n tableName: fanOutSchema.tableName,\n columns: fanOutSchema.columns,\n primaryKey: fanOutSchema.primaryKey,\n relationships: {\n ...fanOutSchema.relationships,\n },\n isHidden: fanOutSchema.isHidden,\n system: fanOutSchema.system,\n compareRows: fanOutSchema.compareRows,\n sort: fanOutSchema.sort,\n };\n\n // now go through inputs and merge relationships\n const relationshipsFromBranches: Set<string> = new Set();\n for (const input of inputs) {\n const inputSchema = input.getSchema();\n assert(\n schema.tableName === inputSchema.tableName,\n `Table name mismatch in union fan-in: ${schema.tableName} !== ${inputSchema.tableName}`,\n );\n assert(\n schema.primaryKey === inputSchema.primaryKey,\n `Primary key mismatch in union fan-in`,\n );\n assert(\n schema.system === inputSchema.system,\n `System mismatch in union fan-in: ${schema.system} !== ${inputSchema.system}`,\n );\n assert(\n schema.compareRows === inputSchema.compareRows,\n `compareRows mismatch in union fan-in`,\n );\n assert(schema.sort === inputSchema.sort, `Sort mismatch in union fan-in`);\n\n for (const [relName, relSchema] of Object.entries(\n inputSchema.relationships,\n )) {\n if (relName in fanOutSchema.relationships) {\n continue;\n }\n\n // All branches will have unique relationship names except for relationships\n // that come in from `fanOut`.\n assert(\n !relationshipsFromBranches.has(relName),\n `Relationship ${relName} exists in multiple upstream inputs to union fan-in`,\n );\n schema.relationships[relName] = relSchema;\n relationshipsFromBranches.add(relName);\n }\n\n input.setOutput(this);\n }\n\n this.#schema = schema;\n this.#inputs = inputs;\n this.#branchRels = relationshipsFromBranches;\n }\n\n destroy(): void {\n for (const input of this.#inputs) {\n input.destroy();\n }\n }\n\n fetch(req: FetchRequest): Stream<Node | 'yield'> {\n const iterables = this.#inputs.map(input => input.fetch(req));\n const compareRows = this.#schema.compareRows;\n const compare = req.reverse\n ? (l: Node, r: Node) => compareRows(r.row, l.row)\n : (l: Node, r: Node) => compareRows(l.row, r.row);\n const relNames = Object.keys(this.#schema.relationships);\n return mapNodes(mergeFetches(iterables, compare), node => {\n const relationships = {...node.relationships};\n mergeEmpty(relationships, relNames);\n return {row: node.row, relationships};\n });\n }\n\n getSchema(): SourceSchema {\n return this.#schema;\n }\n\n push(change: Change, pusher: InputBase): Stream<'yield'> {\n if (!this.#fanOutPushStarted) {\n return this.#pushInternalChange(change, pusher);\n }\n this.#accumulatedPushes.push(change);\n return emptyArray;\n }\n\n /**\n * An internal change means that a change was received inside the fan-out/fan-in sub-graph.\n *\n * These changes always come from children of a flip-join as no other push generating operators\n * currently exist between union-fan-in and union-fan-out. All other pushes\n * enter into union-fan-out before reaching union-fan-in.\n *\n * - normal joins for `exists` come before `union-fan-out`\n * - joins for `related` come after `union-fan-out`\n * - take comes after `union-fan-out`\n *\n * The algorithm for deciding whether or not to forward a push that came from inside the ufo/ufi sub-graph:\n * 1. If the change is a `child` change we can forward it. This is because all child branches in the ufo/ufi sub-graph are unique.\n * 2. If the change is `add` we can forward it iff no `fetches` for the row return any results.\n * If another branch has it, the add was already emitted in the past.\n * 3. If the change is `remove` we can forward it iff no `fetches` for the row return any results.\n * If no other branches have the change, the remove can be sent as the value is no longer present.\n * If other branches have it, the last branch the processes the remove will send the remove.\n * 4. Edits will always come through as child changes as flip join will flip them into children.\n * An edit that would result in a remove or add will have been split into an add/remove pair rather than being an edit.\n */\n *#pushInternalChange(change: Change, pusher: InputBase): Stream<'yield'> {\n if (change[ChangeIndex.TYPE] === ChangeType.CHILD) {\n yield* this.#output.push(change, this);\n return;\n }\n\n assert(\n change[ChangeIndex.TYPE] === ChangeType.ADD ||\n change[ChangeIndex.TYPE] === ChangeType.REMOVE,\n () =>\n `UnionFanIn: expected add or remove change type, got ${change[ChangeIndex.TYPE]}`,\n );\n\n let hadMatch = false;\n for (const input of this.#inputs) {\n if (input === pusher) {\n hadMatch = true;\n continue;\n }\n\n const constraint: Writable<Constraint> = {};\n for (const key of this.#schema.primaryKey) {\n constraint[key] = change[ChangeIndex.NODE].row[key];\n }\n const fetchResult = input.fetch({\n constraint,\n });\n\n // `fetch` interleaves 'yield' sentinels for cooperative multitasking.\n // They must be forwarded, not just skipped: this probe runs inside a\n // push, and the sentinel is the source offering the scheduler a breath.\n // They must also not be mistaken for rows -- reading one as a row is\n // what broke this before, since an empty branch that happened to yield\n // looked like a branch holding the row, silently dropping the\n // add/remove and desyncing a downstream `Take`'s push and fetch paths.\n let otherBranchHasRow = false;\n for (const node of fetchResult) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n otherBranchHasRow = true;\n break;\n }\n\n if (otherBranchHasRow) {\n // Another branch has the row, so it stays. What changed is that it\n // gained or lost the pusher's witnesses: say so as child changes.\n yield* pushWitnessDeltas(\n change[ChangeIndex.NODE],\n change[ChangeIndex.TYPE],\n name => this.#branchRels.has(name),\n this.#output,\n this,\n );\n return;\n }\n }\n\n assert(hadMatch, 'Pusher was not one of the inputs to union-fan-in!');\n\n // No other branches have the row, so we can push the change.\n yield* this.#output.push(change, this);\n }\n\n fanOutStartedPushing() {\n assert(\n this.#fanOutPushStarted === false,\n 'UnionFanIn: fanOutStartedPushing called while already pushing',\n );\n this.#fanOutPushStarted = true;\n }\n\n fanOutDonePushing(fanOutChangeType: ChangeType): Stream<'yield'> {\n assert(\n this.#fanOutPushStarted,\n 'UnionFanIn: fanOutDonePushing called without fanOutStartedPushing',\n );\n this.#fanOutPushStarted = false;\n if (this.#inputs.length === 0) {\n return emptyArray;\n }\n\n if (this.#accumulatedPushes.length === 0) {\n // It is possible for no forks to pass along the push.\n // E.g., if no filters match in any fork.\n return emptyArray;\n }\n\n return pushAccumulatedChanges(\n this.#accumulatedPushes,\n this.#output,\n this,\n fanOutChangeType,\n mergeRelationships,\n makeAddEmptyRelationships(this.#schema),\n name => this.#branchRels.has(name),\n );\n }\n\n fanOutStartedReconciling() {\n assert(\n this.#fanOutReconcileStarted === false,\n 'UnionFanIn: fanOutStartedReconciling called while already reconciling',\n );\n this.#fanOutReconcileStarted = true;\n }\n\n *fanOutDoneReconciling(): Stream<'yield'> {\n assert(\n this.#fanOutReconcileStarted,\n 'UnionFanIn: fanOutDoneReconciling called without fanOutStartedReconciling',\n );\n this.#fanOutReconcileStarted = false;\n if (this.#inputs.length === 0) {\n return;\n }\n if (this.#output.reconcile) {\n yield* this.#output.reconcile(this);\n }\n }\n\n setOutput(output: Output): void {\n this.#output = output;\n }\n\n *reconcile(_pusher: InputBase): Stream<'yield'> {\n if (this.#fanOutReconcileStarted) {\n return;\n }\n if (this.#output.reconcile) {\n yield* this.#output.reconcile(this);\n }\n }\n}\n\nfunction* mapNodes(\n nodes: Iterable<Node | 'yield'>,\n f: (n: Node) => Node,\n): Stream<Node | 'yield'> {\n for (const n of nodes) {\n yield n === 'yield' ? n : f(n);\n }\n}\n\nexport function* mergeFetches(\n fetches: Iterable<Node | 'yield'>[],\n comparator: (l: Node, r: Node) => number,\n): IterableIterator<Node | 'yield'> {\n const iterators = fetches.map(i => i[Symbol.iterator]());\n let threw = false;\n try {\n const current: (Node | null)[] = [];\n let lastNodeYielded: Node | undefined;\n for (let i = 0; i < iterators.length; i++) {\n const iter = iterators[i];\n let result = iter.next();\n // yield yields when initializing\n while (!result.done && result.value === 'yield') {\n yield result.value;\n result = iter.next();\n }\n current[i] = result.done ? null : (result.value as Node);\n }\n while (current.some(c => c !== null)) {\n const min = current.reduce(\n (acc: [Node, number] | undefined, c, i): [Node, number] | undefined => {\n if (c === null) {\n return acc;\n }\n if (acc === undefined || comparator(c, acc[0]) < 0) {\n return [c, i];\n }\n return acc;\n },\n undefined,\n );\n\n assert(min !== undefined, 'min is undefined');\n const [minNode, minIndex] = min;\n const iter = iterators[minIndex];\n let result = iter.next();\n while (!result.done && result.value === 'yield') {\n yield result.value;\n result = iter.next();\n }\n current[minIndex] = result.done ? null : (result.value as Node);\n if (\n lastNodeYielded !== undefined &&\n comparator(lastNodeYielded, minNode) === 0\n ) {\n continue;\n }\n // The same row from another branch is at the head of that branch's\n // stream. Union its relationships in (first branch wins a conflict)\n // instead of dropping it, so a fetched row carries what a pushed one does.\n let merged = minNode;\n for (let j = 0; j < iterators.length; j++) {\n let head = current[j];\n while (head !== null && comparator(head, minNode) === 0) {\n merged = {\n row: merged.row,\n relationships: {...head.relationships, ...merged.relationships},\n };\n let r = iterators[j].next();\n while (!r.done && r.value === 'yield') {\n yield r.value;\n r = iterators[j].next();\n }\n head = current[j] = r.done ? null : (r.value as Node);\n }\n }\n lastNodeYielded = minNode;\n yield merged;\n }\n } catch (e) {\n threw = true;\n for (const iter of iterators) {\n try {\n iter.throw?.(e);\n } catch (_cleanupError) {\n // error in the iter.throw cleanup,\n // catch so other iterators are cleaned up\n }\n }\n throw e;\n } finally {\n if (!threw) {\n for (const iter of iterators) {\n try {\n iter.return?.();\n } catch (_cleanupError) {\n // error in the iter.return cleanup,\n // catch so other iterators are cleaned up\n }\n }\n }\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Change } from './change.ts';
|
|
2
2
|
import type { Node } from './data.ts';
|
|
3
|
-
import type { FetchRequest, Input, Operator, Output } from './operator.ts';
|
|
3
|
+
import type { FetchRequest, Input, InputBase, Operator, Output } from './operator.ts';
|
|
4
4
|
import type { SourceSchema } from './schema.ts';
|
|
5
5
|
import type { Stream } from './stream.ts';
|
|
6
6
|
import type { UnionFanIn } from './union-fan-in.ts';
|
|
@@ -9,6 +9,7 @@ export declare class UnionFanOut implements Operator {
|
|
|
9
9
|
constructor(input: Input);
|
|
10
10
|
setFanIn(fanIn: UnionFanIn): void;
|
|
11
11
|
push(change: Change): Stream<'yield'>;
|
|
12
|
+
reconcile(_pusher: InputBase): Stream<'yield'>;
|
|
12
13
|
setOutput(output: Output): void;
|
|
13
14
|
getSchema(): SourceSchema;
|
|
14
15
|
fetch(req: FetchRequest): Stream<Node | 'yield'>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"union-fan-out.d.ts","sourceRoot":"","sources":["../../../../../zql/src/ivm/union-fan-out.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,aAAa,CAAC;AACxC,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,WAAW,CAAC;AACpC,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"union-fan-out.d.ts","sourceRoot":"","sources":["../../../../../zql/src/ivm/union-fan-out.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,aAAa,CAAC;AACxC,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,WAAW,CAAC;AACpC,OAAO,KAAK,EACV,YAAY,EACZ,KAAK,EACL,SAAS,EACT,QAAQ,EACR,MAAM,EACP,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,aAAa,CAAC;AAC9C,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,aAAa,CAAC;AACxC,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,mBAAmB,CAAC;AAElD,qBAAa,WAAY,YAAW,QAAQ;;IAM1C,YAAY,KAAK,EAAE,KAAK,EAGvB;IAED,QAAQ,CAAC,KAAK,EAAE,UAAU,QAGzB;IAEA,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,CAMrC;IAEA,SAAS,CAAC,OAAO,EAAE,SAAS,GAAG,MAAM,CAAC,OAAO,CAAC,CAQ9C;IAED,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAE9B;IAED,SAAS,IAAI,YAAY,CAExB;IAED,KAAK,CAAC,GAAG,EAAE,YAAY,GAAG,MAAM,CAAC,IAAI,GAAG,OAAO,CAAC,CAE/C;IAED,OAAO,IAAI,IAAI,CASd;CACF"}
|
|
@@ -19,6 +19,11 @@ var UnionFanOut = class {
|
|
|
19
19
|
for (const output of this.#outputs) yield* output.push(change, this);
|
|
20
20
|
yield* must(this.#unionFanIn).fanOutDonePushing(change[0]);
|
|
21
21
|
}
|
|
22
|
+
*reconcile(_pusher) {
|
|
23
|
+
must(this.#unionFanIn).fanOutStartedReconciling();
|
|
24
|
+
for (const output of this.#outputs) if (output.reconcile) yield* output.reconcile(this);
|
|
25
|
+
yield* must(this.#unionFanIn).fanOutDoneReconciling();
|
|
26
|
+
}
|
|
22
27
|
setOutput(output) {
|
|
23
28
|
this.#outputs.push(output);
|
|
24
29
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"union-fan-out.js","names":["#input","#outputs","#unionFanIn","#destroyCount"],"sources":["../../../../../zql/src/ivm/union-fan-out.ts"],"sourcesContent":["import {assert} from '../../../shared/src/asserts.ts';\nimport {must} from '../../../shared/src/must.ts';\nimport {ChangeIndex} from './change-index.ts';\nimport type {Change} from './change.ts';\nimport type {Node} from './data.ts';\nimport type {FetchRequest
|
|
1
|
+
{"version":3,"file":"union-fan-out.js","names":["#input","#outputs","#unionFanIn","#destroyCount"],"sources":["../../../../../zql/src/ivm/union-fan-out.ts"],"sourcesContent":["import {assert} from '../../../shared/src/asserts.ts';\nimport {must} from '../../../shared/src/must.ts';\nimport {ChangeIndex} from './change-index.ts';\nimport type {Change} from './change.ts';\nimport type {Node} from './data.ts';\nimport type {\n FetchRequest,\n Input,\n InputBase,\n Operator,\n Output,\n} from './operator.ts';\nimport type {SourceSchema} from './schema.ts';\nimport type {Stream} from './stream.ts';\nimport type {UnionFanIn} from './union-fan-in.ts';\n\nexport class UnionFanOut implements Operator {\n #destroyCount: number = 0;\n #unionFanIn?: UnionFanIn;\n readonly #input: Input;\n readonly #outputs: Output[] = [];\n\n constructor(input: Input) {\n this.#input = input;\n input.setOutput(this);\n }\n\n setFanIn(fanIn: UnionFanIn) {\n assert(!this.#unionFanIn, 'FanIn already set for this FanOut');\n this.#unionFanIn = fanIn;\n }\n\n *push(change: Change): Stream<'yield'> {\n must(this.#unionFanIn).fanOutStartedPushing();\n for (const output of this.#outputs) {\n yield* output.push(change, this);\n }\n yield* must(this.#unionFanIn).fanOutDonePushing(change[ChangeIndex.TYPE]);\n }\n\n *reconcile(_pusher: InputBase): Stream<'yield'> {\n must(this.#unionFanIn).fanOutStartedReconciling();\n for (const output of this.#outputs) {\n if (output.reconcile) {\n yield* output.reconcile(this);\n }\n }\n yield* must(this.#unionFanIn).fanOutDoneReconciling();\n }\n\n setOutput(output: Output): void {\n this.#outputs.push(output);\n }\n\n getSchema(): SourceSchema {\n return this.#input.getSchema();\n }\n\n fetch(req: FetchRequest): Stream<Node | 'yield'> {\n return this.#input.fetch(req);\n }\n\n destroy(): void {\n if (this.#destroyCount < this.#outputs.length) {\n ++this.#destroyCount;\n if (this.#destroyCount === this.#outputs.length) {\n this.#input.destroy();\n }\n } else {\n throw new Error('FanOut already destroyed once for each output');\n }\n }\n}\n"],"mappings":";;;AAgBA,IAAa,cAAb,MAA6C;CAC3C,gBAAwB;CACxB;CACA;CACA,WAA8B,CAAC;CAE/B,YAAY,OAAc;EACxB,KAAKA,SAAS;EACd,MAAM,UAAU,IAAI;CACtB;CAEA,SAAS,OAAmB;EAC1B,OAAO,CAAC,KAAKE,aAAa,mCAAmC;EAC7D,KAAKA,cAAc;CACrB;CAEA,CAAC,KAAK,QAAiC;EACrC,KAAK,KAAKA,WAAW,EAAE,qBAAqB;EAC5C,KAAK,MAAM,UAAU,KAAKD,UACxB,OAAO,OAAO,KAAK,QAAQ,IAAI;EAEjC,OAAO,KAAK,KAAKC,WAAW,EAAE,kBAAkB,OAAO,EAAiB;CAC1E;CAEA,CAAC,UAAU,SAAqC;EAC9C,KAAK,KAAKA,WAAW,EAAE,yBAAyB;EAChD,KAAK,MAAM,UAAU,KAAKD,UACxB,IAAI,OAAO,WACT,OAAO,OAAO,UAAU,IAAI;EAGhC,OAAO,KAAK,KAAKC,WAAW,EAAE,sBAAsB;CACtD;CAEA,UAAU,QAAsB;EAC9B,KAAKD,SAAS,KAAK,MAAM;CAC3B;CAEA,YAA0B;EACxB,OAAO,KAAKD,OAAO,UAAU;CAC/B;CAEA,MAAM,KAA2C;EAC/C,OAAO,KAAKA,OAAO,MAAM,GAAG;CAC9B;CAEA,UAAgB;EACd,IAAI,KAAKG,gBAAgB,KAAKF,SAAS,QAAQ;GAC7C,EAAE,KAAKE;GACP,IAAI,KAAKA,kBAAkB,KAAKF,SAAS,QACvC,KAAKD,OAAO,QAAQ;EAExB,OACE,MAAM,IAAI,MAAM,+CAA+C;CAEnE;AACF"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Change } from '../ivm/change.ts';
|
|
2
2
|
import type { Node } from '../ivm/data.ts';
|
|
3
|
-
import { type FetchRequest, type Input, type Operator, type Output } from '../ivm/operator.ts';
|
|
3
|
+
import { type FetchRequest, type Input, type InputBase, type Operator, type Output } from '../ivm/operator.ts';
|
|
4
4
|
import type { SourceSchema } from '../ivm/schema.ts';
|
|
5
5
|
import type { Stream } from '../ivm/stream.ts';
|
|
6
6
|
import type { MetricsDelegate } from './metrics-delegate.ts';
|
|
@@ -13,6 +13,7 @@ export declare class MeasurePushOperator implements Operator {
|
|
|
13
13
|
getSchema(): SourceSchema;
|
|
14
14
|
destroy(): void;
|
|
15
15
|
push(change: Change): Stream<'yield'>;
|
|
16
|
+
reconcile(_pusher: InputBase): Stream<'yield'>;
|
|
16
17
|
}
|
|
17
18
|
export {};
|
|
18
19
|
//# sourceMappingURL=measure-push-operator.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"measure-push-operator.d.ts","sourceRoot":"","sources":["../../../../../zql/src/query/measure-push-operator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,kBAAkB,CAAC;AAC7C,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,gBAAgB,CAAC;AACzC,OAAO,EAEL,KAAK,YAAY,EACjB,KAAK,KAAK,EACV,KAAK,QAAQ,EACb,KAAK,MAAM,EACZ,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,kBAAkB,CAAC;AACnD,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,kBAAkB,CAAC;AAC7C,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,uBAAuB,CAAC;AAE3D,KAAK,UAAU,GAAG,qBAAqB,GAAG,qBAAqB,CAAC;AAEhE,qBAAa,mBAAoB,YAAW,QAAQ;;IAQlD,YACE,KAAK,EAAE,KAAK,EACZ,OAAO,EAAE,MAAM,EACf,eAAe,EAAE,eAAe,EAChC,UAAU,EAAE,UAAU,EAOvB;IAED,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAE9B;IAED,KAAK,CAAC,GAAG,EAAE,YAAY,GAAG,MAAM,CAAC,IAAI,GAAG,OAAO,CAAC,CAE/C;IAED,SAAS,IAAI,YAAY,CAExB;IAED,OAAO,IAAI,IAAI,CAEd;IAEA,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,CAQrC;CACF"}
|
|
1
|
+
{"version":3,"file":"measure-push-operator.d.ts","sourceRoot":"","sources":["../../../../../zql/src/query/measure-push-operator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,kBAAkB,CAAC;AAC7C,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,gBAAgB,CAAC;AACzC,OAAO,EAEL,KAAK,YAAY,EACjB,KAAK,KAAK,EACV,KAAK,SAAS,EACd,KAAK,QAAQ,EACb,KAAK,MAAM,EACZ,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,kBAAkB,CAAC;AACnD,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,kBAAkB,CAAC;AAC7C,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,uBAAuB,CAAC;AAE3D,KAAK,UAAU,GAAG,qBAAqB,GAAG,qBAAqB,CAAC;AAEhE,qBAAa,mBAAoB,YAAW,QAAQ;;IAQlD,YACE,KAAK,EAAE,KAAK,EACZ,OAAO,EAAE,MAAM,EACf,eAAe,EAAE,eAAe,EAChC,UAAU,EAAE,UAAU,EAOvB;IAED,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAE9B;IAED,KAAK,CAAC,GAAG,EAAE,YAAY,GAAG,MAAM,CAAC,IAAI,GAAG,OAAO,CAAC,CAE/C;IAED,SAAS,IAAI,YAAY,CAExB;IAED,OAAO,IAAI,IAAI,CAEd;IAEA,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,CAQrC;IAEA,SAAS,CAAC,OAAO,EAAE,SAAS,GAAG,MAAM,CAAC,OAAO,CAAC,CAU9C;CACF"}
|
|
@@ -30,6 +30,13 @@ var MeasurePushOperator = class {
|
|
|
30
30
|
yield* this.#output.push(change, this);
|
|
31
31
|
this.#metricsDelegate.addMetric(this.#metricName, performance.now() - startTime, this.#queryID);
|
|
32
32
|
}
|
|
33
|
+
*reconcile(_pusher) {
|
|
34
|
+
if (this.#output.reconcile) {
|
|
35
|
+
const startTime = performance.now();
|
|
36
|
+
yield* this.#output.reconcile(this);
|
|
37
|
+
this.#metricsDelegate.addMetric(this.#metricName, performance.now() - startTime, this.#queryID);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
33
40
|
};
|
|
34
41
|
//#endregion
|
|
35
42
|
export { MeasurePushOperator };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"measure-push-operator.js","names":["#input","#queryID","#metricsDelegate","#metricName","#output"],"sources":["../../../../../zql/src/query/measure-push-operator.ts"],"sourcesContent":["import type {Change} from '../ivm/change.ts';\nimport type {Node} from '../ivm/data.ts';\nimport {\n throwOutput,\n type FetchRequest,\n type Input,\n type Operator,\n type Output,\n} from '../ivm/operator.ts';\nimport type {SourceSchema} from '../ivm/schema.ts';\nimport type {Stream} from '../ivm/stream.ts';\nimport type {MetricsDelegate} from './metrics-delegate.ts';\n\ntype MetricName = 'query-update-client' | 'query-update-server';\n\nexport class MeasurePushOperator implements Operator {\n readonly #input: Input;\n readonly #queryID: string;\n readonly #metricsDelegate: MetricsDelegate;\n\n #output: Output = throwOutput;\n readonly #metricName: MetricName;\n\n constructor(\n input: Input,\n queryID: string,\n metricsDelegate: MetricsDelegate,\n metricName: MetricName,\n ) {\n this.#input = input;\n this.#queryID = queryID;\n this.#metricsDelegate = metricsDelegate;\n this.#metricName = metricName;\n input.setOutput(this);\n }\n\n setOutput(output: Output): void {\n this.#output = output;\n }\n\n fetch(req: FetchRequest): Stream<Node | 'yield'> {\n return this.#input.fetch(req);\n }\n\n getSchema(): SourceSchema {\n return this.#input.getSchema();\n }\n\n destroy(): void {\n this.#input.destroy();\n }\n\n *push(change: Change): Stream<'yield'> {\n const startTime = performance.now();\n yield* this.#output.push(change, this);\n this.#metricsDelegate.addMetric(\n this.#metricName,\n performance.now() - startTime,\n this.#queryID,\n );\n }\n}\n"],"mappings":";;
|
|
1
|
+
{"version":3,"file":"measure-push-operator.js","names":["#input","#queryID","#metricsDelegate","#metricName","#output"],"sources":["../../../../../zql/src/query/measure-push-operator.ts"],"sourcesContent":["import type {Change} from '../ivm/change.ts';\nimport type {Node} from '../ivm/data.ts';\nimport {\n throwOutput,\n type FetchRequest,\n type Input,\n type InputBase,\n type Operator,\n type Output,\n} from '../ivm/operator.ts';\nimport type {SourceSchema} from '../ivm/schema.ts';\nimport type {Stream} from '../ivm/stream.ts';\nimport type {MetricsDelegate} from './metrics-delegate.ts';\n\ntype MetricName = 'query-update-client' | 'query-update-server';\n\nexport class MeasurePushOperator implements Operator {\n readonly #input: Input;\n readonly #queryID: string;\n readonly #metricsDelegate: MetricsDelegate;\n\n #output: Output = throwOutput;\n readonly #metricName: MetricName;\n\n constructor(\n input: Input,\n queryID: string,\n metricsDelegate: MetricsDelegate,\n metricName: MetricName,\n ) {\n this.#input = input;\n this.#queryID = queryID;\n this.#metricsDelegate = metricsDelegate;\n this.#metricName = metricName;\n input.setOutput(this);\n }\n\n setOutput(output: Output): void {\n this.#output = output;\n }\n\n fetch(req: FetchRequest): Stream<Node | 'yield'> {\n return this.#input.fetch(req);\n }\n\n getSchema(): SourceSchema {\n return this.#input.getSchema();\n }\n\n destroy(): void {\n this.#input.destroy();\n }\n\n *push(change: Change): Stream<'yield'> {\n const startTime = performance.now();\n yield* this.#output.push(change, this);\n this.#metricsDelegate.addMetric(\n this.#metricName,\n performance.now() - startTime,\n this.#queryID,\n );\n }\n\n *reconcile(_pusher: InputBase): Stream<'yield'> {\n if (this.#output.reconcile) {\n const startTime = performance.now();\n yield* this.#output.reconcile(this);\n this.#metricsDelegate.addMetric(\n this.#metricName,\n performance.now() - startTime,\n this.#queryID,\n );\n }\n }\n}\n"],"mappings":";;AAgBA,IAAa,sBAAb,MAAqD;CACnD;CACA;CACA;CAEA,UAAkB;CAClB;CAEA,YACE,OACA,SACA,iBACA,YACA;EACA,KAAKA,SAAS;EACd,KAAKC,WAAW;EAChB,KAAKC,mBAAmB;EACxB,KAAKC,cAAc;EACnB,MAAM,UAAU,IAAI;CACtB;CAEA,UAAU,QAAsB;EAC9B,KAAKC,UAAU;CACjB;CAEA,MAAM,KAA2C;EAC/C,OAAO,KAAKJ,OAAO,MAAM,GAAG;CAC9B;CAEA,YAA0B;EACxB,OAAO,KAAKA,OAAO,UAAU;CAC/B;CAEA,UAAgB;EACd,KAAKA,OAAO,QAAQ;CACtB;CAEA,CAAC,KAAK,QAAiC;EACrC,MAAM,YAAY,YAAY,IAAI;EAClC,OAAO,KAAKI,QAAQ,KAAK,QAAQ,IAAI;EACrC,KAAKF,iBAAiB,UACpB,KAAKC,aACL,YAAY,IAAI,IAAI,WACpB,KAAKF,QACP;CACF;CAEA,CAAC,UAAU,SAAqC;EAC9C,IAAI,KAAKG,QAAQ,WAAW;GAC1B,MAAM,YAAY,YAAY,IAAI;GAClC,OAAO,KAAKA,QAAQ,UAAU,IAAI;GAClC,KAAKF,iBAAiB,UACpB,KAAKC,aACL,YAAY,IAAI,IAAI,WACpB,KAAKF,QACP;EACF;CACF;AACF"}
|