@rocicorp/zero 1.2.0-canary.4 → 1.2.0-canary.5
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/ast-to-zql/src/format.d.ts.map +1 -1
- package/out/ast-to-zql/src/format.js +6 -6
- package/out/ast-to-zql/src/format.js.map +1 -1
- package/out/shared/src/btree-set.d.ts.map +1 -1
- package/out/shared/src/btree-set.js +73 -41
- package/out/shared/src/btree-set.js.map +1 -1
- package/out/zero/package.js +4 -5
- package/out/zero/package.js.map +1 -1
- package/out/zero-cache/src/db/transaction-pool.d.ts.map +1 -1
- package/out/zero-cache/src/db/transaction-pool.js.map +1 -1
- package/out/zero-cache/src/observability/metrics.d.ts +1 -1
- package/out/zero-cache/src/observability/metrics.d.ts.map +1 -1
- package/out/zero-cache/src/observability/metrics.js.map +1 -1
- package/out/zero-cache/src/server/anonymous-otel-start.d.ts.map +1 -1
- package/out/zero-cache/src/server/anonymous-otel-start.js +6 -1
- package/out/zero-cache/src/server/anonymous-otel-start.js.map +1 -1
- package/out/zero-cache/src/server/change-streamer.d.ts.map +1 -1
- package/out/zero-cache/src/server/change-streamer.js +3 -1
- package/out/zero-cache/src/server/change-streamer.js.map +1 -1
- package/out/zero-cache/src/server/replicator.d.ts.map +1 -1
- package/out/zero-cache/src/server/replicator.js +28 -1
- package/out/zero-cache/src/server/replicator.js.map +1 -1
- package/out/zero-cache/src/services/change-source/common/replica-schema.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/common/replica-schema.js +13 -1
- package/out/zero-cache/src/services/change-source/common/replica-schema.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/pg/change-source.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/change-source.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/logical-replication/stream.js +1 -1
- package/out/zero-cache/src/services/change-streamer/backup-monitor.d.ts +1 -1
- package/out/zero-cache/src/services/change-streamer/backup-monitor.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/backup-monitor.js +31 -1
- package/out/zero-cache/src/services/change-streamer/backup-monitor.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer-http.js +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer-service.js +1 -1
- package/out/zero-cache/src/services/change-streamer/storer.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/storer.js +1 -1
- package/out/zero-cache/src/services/change-streamer/storer.js.map +1 -1
- package/out/zero-cache/src/services/replicator/schema/replication-state.d.ts.map +1 -1
- package/out/zero-cache/src/services/replicator/schema/replication-state.js +6 -3
- package/out/zero-cache/src/services/replicator/schema/replication-state.js.map +1 -1
- package/out/zero-cache/src/types/pg.d.ts.map +1 -1
- package/out/zero-cache/src/types/pg.js +16 -8
- package/out/zero-cache/src/types/pg.js.map +1 -1
- package/out/zero-cache/src/workers/replicator.d.ts +5 -2
- package/out/zero-cache/src/workers/replicator.d.ts.map +1 -1
- package/out/zero-cache/src/workers/replicator.js +10 -6
- package/out/zero-cache/src/workers/replicator.js.map +1 -1
- package/out/zero-client/src/client/version.js +1 -1
- package/out/zql/src/ivm/view-apply-change.d.ts.map +1 -1
- package/out/zql/src/ivm/view-apply-change.js +34 -26
- package/out/zql/src/ivm/view-apply-change.js.map +1 -1
- package/out/zql/src/query/query.d.ts +1 -2
- package/out/zql/src/query/query.d.ts.map +1 -1
- package/package.json +4 -5
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../../../ast-to-zql/src/format.ts"],"names":[],"mappings":"AAEA,wBAAsB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,
|
|
1
|
+
{"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../../../ast-to-zql/src/format.ts"],"names":[],"mappings":"AAEA,wBAAsB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAYnE"}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { format } from "
|
|
1
|
+
import { format } from "oxfmt";
|
|
2
2
|
//#region ../ast-to-zql/src/format.ts
|
|
3
3
|
async function formatOutput(content) {
|
|
4
4
|
try {
|
|
5
|
-
return await format(content, {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
});
|
|
5
|
+
return (await format("output.ts", content, {
|
|
6
|
+
semi: false,
|
|
7
|
+
printWidth: 80
|
|
8
|
+
})).code;
|
|
9
9
|
} catch (error) {
|
|
10
|
-
console.warn("Warning: Unable to format output with
|
|
10
|
+
console.warn("Warning: Unable to format output with oxfmt:", error);
|
|
11
11
|
return content;
|
|
12
12
|
}
|
|
13
13
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"format.js","names":[],"sources":["../../../../ast-to-zql/src/format.ts"],"sourcesContent":["import {format} from '
|
|
1
|
+
{"version":3,"file":"format.js","names":[],"sources":["../../../../ast-to-zql/src/format.ts"],"sourcesContent":["import {format} from 'oxfmt';\n\nexport async function formatOutput(content: string): Promise<string> {\n try {\n const result = await format('output.ts', content, {\n semi: false,\n printWidth: 80,\n });\n return result.code;\n } catch (error) {\n // oxlint-disable-next-line no-console\n console.warn('Warning: Unable to format output with oxfmt:', error);\n return content;\n }\n}\n"],"mappings":";;AAEA,eAAsB,aAAa,SAAkC;AACnE,KAAI;AAKF,UAJe,MAAM,OAAO,aAAa,SAAS;GAChD,MAAM;GACN,YAAY;GACb,CAAC,EACY;UACP,OAAO;AAEd,UAAQ,KAAK,gDAAgD,MAAM;AACnE,SAAO"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"btree-set.d.ts","sourceRoot":"","sources":["../../../../shared/src/btree-set.ts"],"names":[],"mappings":"AAIA,KAAK,UAAU,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"btree-set.d.ts","sourceRoot":"","sources":["../../../../shared/src/btree-set.ts"],"names":[],"mappings":"AAIA,KAAK,UAAU,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,MAAM,CAAC;AAE5C,qBAAa,QAAQ,CAAC,CAAC;;IAErB,IAAI,EAAE,MAAM,CAAK;IAEjB,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC;gBAEvB,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,gBAAgB,CAAC,CAAC,CAAC;IASpE,+CAA+C;IAC/C,KAAK;IAKL,KAAK;IAQL,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,GAAG,SAAS;IAI1B,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,IAAI;IASjB;;;;;;OAMG;IACH,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO;IAIpB;;;;;OAKG;IACH,MAAM,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO;IAyBvB,IAAI,IAAI,gBAAgB,CAAC,CAAC,CAAC;IAI3B,MAAM,IAAI,gBAAgB,CAAC,CAAC,CAAC;IAI7B,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC,EAAE,SAAS,GAAE,OAAc,GAAG,gBAAgB,CAAC,CAAC,CAAC;IAIzE,cAAc,IAAI,gBAAgB,CAAC,CAAC,CAAC;IAUrC,kBAAkB,CAChB,UAAU,CAAC,EAAE,CAAC,EACd,SAAS,GAAE,OAAc,GACxB,gBAAgB,CAAC,CAAC,CAAC;IAetB,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,gBAAgB,CAAC,CAAC,CAAC;CAGzC"}
|
|
@@ -87,72 +87,104 @@ var BTreeSet = class BTreeSet {
|
|
|
87
87
|
return this.keys();
|
|
88
88
|
}
|
|
89
89
|
};
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
90
|
+
var BTreeForwardIterator = class {
|
|
91
|
+
#nodeQueue;
|
|
92
|
+
#nodeIndex;
|
|
93
|
+
#leaf;
|
|
94
|
+
#i;
|
|
95
|
+
constructor(nodeQueue, nodeIndex, leaf, startI) {
|
|
96
|
+
this.#nodeQueue = nodeQueue;
|
|
97
|
+
this.#nodeIndex = nodeIndex;
|
|
98
|
+
this.#leaf = leaf;
|
|
99
|
+
this.#i = startI;
|
|
100
|
+
}
|
|
101
|
+
next() {
|
|
100
102
|
for (;;) {
|
|
101
|
-
if (++i < leaf.keys.length) return {
|
|
103
|
+
if (++this.#i < this.#leaf.keys.length) return {
|
|
102
104
|
done: false,
|
|
103
|
-
value: leaf.keys[i]
|
|
105
|
+
value: this.#leaf.keys[this.#i]
|
|
104
106
|
};
|
|
105
107
|
let level = -1;
|
|
106
108
|
for (;;) {
|
|
107
|
-
if (++level >= nodeQueue.length) return {
|
|
109
|
+
if (++level >= this.#nodeQueue.length) return {
|
|
108
110
|
done: true,
|
|
109
111
|
value: void 0
|
|
110
112
|
};
|
|
111
|
-
if (++nodeIndex[level] < nodeQueue[level].length) break;
|
|
113
|
+
if (++this.#nodeIndex[level] < this.#nodeQueue[level].length) break;
|
|
112
114
|
}
|
|
113
115
|
for (; level > 0; level--) {
|
|
114
|
-
nodeQueue[level - 1] = nodeQueue[level][nodeIndex[level]].children;
|
|
115
|
-
nodeIndex[level - 1] = 0;
|
|
116
|
+
this.#nodeQueue[level - 1] = this.#nodeQueue[level][this.#nodeIndex[level]].children;
|
|
117
|
+
this.#nodeIndex[level - 1] = 0;
|
|
116
118
|
}
|
|
117
|
-
leaf = nodeQueue[0][nodeIndex[0]];
|
|
118
|
-
i = -1;
|
|
119
|
+
this.#leaf = this.#nodeQueue[0][this.#nodeIndex[0]];
|
|
120
|
+
this.#i = -1;
|
|
119
121
|
}
|
|
120
|
-
});
|
|
121
|
-
}
|
|
122
|
-
function valuesFromReversed(maxKey, root, comparator, highestKey, inclusive) {
|
|
123
|
-
if (highestKey === void 0) {
|
|
124
|
-
highestKey = maxKey;
|
|
125
|
-
if (highestKey === void 0) return iterator(() => ({
|
|
126
|
-
done: true,
|
|
127
|
-
value: void 0
|
|
128
|
-
}));
|
|
129
122
|
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
123
|
+
[Symbol.iterator]() {
|
|
124
|
+
return this;
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
var BTreeReverseIterator = class {
|
|
128
|
+
#nodeQueue;
|
|
129
|
+
#nodeIndex;
|
|
130
|
+
#leaf;
|
|
131
|
+
#i;
|
|
132
|
+
constructor(nodeQueue, nodeIndex, leaf, startI) {
|
|
133
|
+
this.#nodeQueue = nodeQueue;
|
|
134
|
+
this.#nodeIndex = nodeIndex;
|
|
135
|
+
this.#leaf = leaf;
|
|
136
|
+
this.#i = startI;
|
|
137
|
+
}
|
|
138
|
+
next() {
|
|
135
139
|
for (;;) {
|
|
136
|
-
if (--i >= 0) return {
|
|
140
|
+
if (--this.#i >= 0) return {
|
|
137
141
|
done: false,
|
|
138
|
-
value: leaf.keys[i]
|
|
142
|
+
value: this.#leaf.keys[this.#i]
|
|
139
143
|
};
|
|
140
144
|
let level;
|
|
141
145
|
for (level = -1;;) {
|
|
142
|
-
if (++level >= nodeQueue.length) return {
|
|
146
|
+
if (++level >= this.#nodeQueue.length) return {
|
|
143
147
|
done: true,
|
|
144
148
|
value: void 0
|
|
145
149
|
};
|
|
146
|
-
if (--nodeIndex[level] >= 0) break;
|
|
150
|
+
if (--this.#nodeIndex[level] >= 0) break;
|
|
147
151
|
}
|
|
148
152
|
for (; level > 0; level--) {
|
|
149
|
-
nodeQueue[level - 1] = nodeQueue[level][nodeIndex[level]].children;
|
|
150
|
-
nodeIndex[level - 1] = nodeQueue[level - 1].length - 1;
|
|
153
|
+
this.#nodeQueue[level - 1] = this.#nodeQueue[level][this.#nodeIndex[level]].children;
|
|
154
|
+
this.#nodeIndex[level - 1] = this.#nodeQueue[level - 1].length - 1;
|
|
151
155
|
}
|
|
152
|
-
leaf = nodeQueue[0][nodeIndex[0]];
|
|
153
|
-
i = leaf.keys.length;
|
|
156
|
+
this.#leaf = this.#nodeQueue[0][this.#nodeIndex[0]];
|
|
157
|
+
this.#i = this.#leaf.keys.length;
|
|
154
158
|
}
|
|
155
|
-
}
|
|
159
|
+
}
|
|
160
|
+
[Symbol.iterator]() {
|
|
161
|
+
return this;
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
function valuesFrom(root, comparator, lowestKey, inclusive) {
|
|
165
|
+
const info = findPath(lowestKey, root, comparator);
|
|
166
|
+
if (info === void 0) return iterator(() => ({
|
|
167
|
+
done: true,
|
|
168
|
+
value: void 0
|
|
169
|
+
}));
|
|
170
|
+
let [nodeQueue, nodeIndex, leaf] = info;
|
|
171
|
+
let i = lowestKey === void 0 ? -1 : indexOf(lowestKey, leaf.keys, 0, comparator) - 1;
|
|
172
|
+
if (!inclusive && lowestKey !== void 0 && i + 1 < leaf.keys.length && comparator(leaf.keys[i + 1], lowestKey) === 0) i++;
|
|
173
|
+
return new BTreeForwardIterator(nodeQueue, nodeIndex, leaf, i);
|
|
174
|
+
}
|
|
175
|
+
function valuesFromReversed(maxKey, root, comparator, highestKey, inclusive) {
|
|
176
|
+
if (highestKey === void 0) {
|
|
177
|
+
highestKey = maxKey;
|
|
178
|
+
if (highestKey === void 0) return iterator(() => ({
|
|
179
|
+
done: true,
|
|
180
|
+
value: void 0
|
|
181
|
+
}));
|
|
182
|
+
}
|
|
183
|
+
let [nodeQueue, nodeIndex, leaf] = findPath(highestKey, root, comparator) || findPath(maxKey, root, comparator);
|
|
184
|
+
assert(!nodeQueue[0] || leaf === nodeQueue[0][nodeIndex[0]], "BTreeSet: leaf node mismatch in iteration");
|
|
185
|
+
let i = indexOf(highestKey, leaf.keys, 0, comparator);
|
|
186
|
+
if (inclusive && i < leaf.keys.length && comparator(leaf.keys[i], highestKey) <= 0) i++;
|
|
187
|
+
return new BTreeReverseIterator(nodeQueue, nodeIndex, leaf, i);
|
|
156
188
|
}
|
|
157
189
|
function findPath(key, root, comparator) {
|
|
158
190
|
let nextNode = root;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"btree-set.js","names":["#root","#delete","#maxKey"],"sources":["../../../../shared/src/btree-set.ts"],"sourcesContent":["import {assert} from './asserts.ts';\n\nconst MAX_NODE_SIZE = 32;\n\ntype Comparator<K> = (a: K, b: K) => number;\nexport class BTreeSet<K> {\n #root: BNode<K> = emptyLeaf as BNode<K>;\n size: number = 0;\n\n readonly comparator: Comparator<K>;\n\n constructor(comparator: Comparator<K>, entries?: IterableIterator<K>) {\n this.comparator = comparator;\n if (entries) {\n for (const key of entries) {\n this.add(key);\n }\n }\n }\n\n /** Releases the tree so that its size is 0. */\n clear() {\n this.#root = emptyLeaf as BNode<K>;\n this.size = 0;\n }\n\n clone() {\n this.#root.isShared = true;\n const ret = new BTreeSet<K>(this.comparator);\n ret.#root = this.#root;\n ret.size = this.size;\n return ret;\n }\n\n get(key: K): K | undefined {\n return this.#root.get(key, this);\n }\n\n add(key: K): this {\n if (this.#root.isShared) this.#root = this.#root.clone();\n const result = this.#root.set(key, this);\n if (result === null) return this;\n // Root node has split, so create a new root node.\n this.#root = new BNodeInternal<K>([this.#root, result]);\n return this;\n }\n\n /**\n * Returns true if the key exists in the B+ tree, false if not.\n * Use get() for best performance; use has() if you need to\n * distinguish between \"undefined value\" and \"key not present\".\n * @param key Key to detect\n * @description Computational complexity: O(log size)\n */\n has(key: K): boolean {\n return this.#root.has(key, this);\n }\n\n /**\n * Removes a single key-value pair from the B+ tree.\n * @param key Key to find\n * @returns true if a pair was found and removed, false otherwise.\n * @description Computational complexity: O(log size)\n */\n delete(key: K): boolean {\n return this.#delete(key);\n }\n\n #delete(key: K): boolean {\n let root = this.#root;\n if (root.isShared) {\n this.#root = root = root.clone();\n }\n try {\n return root.delete(key, this);\n } finally {\n let isShared;\n while (root.keys.length <= 1 && root.isInternal()) {\n isShared ||= root.isShared;\n this.#root = root =\n root.keys.length === 0 ? emptyLeaf : root.children[0];\n }\n // If any ancestor of the new root was shared, the new root must also be shared\n if (isShared) {\n root.isShared = true;\n }\n }\n }\n\n keys(): IterableIterator<K> {\n return valuesFrom(this.#root, this.comparator, undefined, true);\n }\n\n values(): IterableIterator<K> {\n return valuesFrom(this.#root, this.comparator, undefined, true);\n }\n\n valuesFrom(lowestKey?: K, inclusive: boolean = true): IterableIterator<K> {\n return valuesFrom(this.#root, this.comparator, lowestKey, inclusive);\n }\n\n valuesReversed(): IterableIterator<K> {\n return valuesFromReversed(\n this.#maxKey(),\n this.#root,\n this.comparator,\n undefined,\n true,\n );\n }\n\n valuesFromReversed(\n highestKey?: K,\n inclusive: boolean = true,\n ): IterableIterator<K> {\n return valuesFromReversed(\n this.#maxKey(),\n this.#root,\n this.comparator,\n highestKey,\n inclusive,\n );\n }\n\n /** Gets the highest key in the tree. Complexity: O(1) */\n #maxKey(): K | undefined {\n return this.#root.maxKey();\n }\n\n [Symbol.iterator](): IterableIterator<K> {\n return this.keys();\n }\n}\n\nfunction valuesFrom<K>(\n root: BNode<K>,\n comparator: Comparator<K>,\n lowestKey: K | undefined,\n inclusive: boolean,\n): IterableIterator<K> {\n const info = findPath(lowestKey, root, comparator);\n if (info === undefined) {\n return iterator<K>(() => ({done: true, value: undefined}));\n }\n\n let [nodeQueue, nodeIndex, leaf] = info;\n let i =\n lowestKey === undefined\n ? -1\n : indexOf(lowestKey, leaf.keys, 0, comparator) - 1;\n\n if (\n !inclusive &&\n i < leaf.keys.length &&\n // +1 because we did -1 above.\n comparator(leaf.keys[i + 1], lowestKey!) === 0\n ) {\n i++;\n }\n\n return iterator<K>(() => {\n for (;;) {\n if (++i < leaf.keys.length) {\n return {done: false, value: leaf.keys[i]};\n }\n\n let level = -1;\n for (;;) {\n if (++level >= nodeQueue.length) {\n return {done: true, value: undefined};\n }\n if (++nodeIndex[level] < nodeQueue[level].length) {\n break;\n }\n }\n for (; level > 0; level--) {\n nodeQueue[level - 1] = (\n nodeQueue[level][nodeIndex[level]] as BNodeInternal<K>\n ).children;\n nodeIndex[level - 1] = 0;\n }\n leaf = nodeQueue[0][nodeIndex[0]];\n i = -1;\n }\n });\n}\n\nfunction valuesFromReversed<K>(\n maxKey: K | undefined,\n root: BNode<K>,\n comparator: Comparator<K>,\n highestKey: K | undefined,\n inclusive: boolean,\n): IterableIterator<K> {\n if (highestKey === undefined) {\n highestKey = maxKey;\n if (highestKey === undefined) {\n return iterator<K>(() => ({done: true, value: undefined}));\n } // collection is empty\n }\n let [nodeQueue, nodeIndex, leaf] =\n findPath(highestKey, root, comparator) ||\n findPath(maxKey, root, comparator)!;\n assert(\n !nodeQueue[0] || leaf === nodeQueue[0][nodeIndex[0]],\n 'BTreeSet: leaf node mismatch in iteration',\n );\n let i = indexOf(highestKey, leaf.keys, 0, comparator);\n if (\n inclusive &&\n i < leaf.keys.length &&\n comparator(leaf.keys[i], highestKey) <= 0\n ) {\n i++;\n }\n\n return iterator<K>(() => {\n for (;;) {\n if (--i >= 0) {\n return {done: false, value: leaf.keys[i]};\n }\n\n let level;\n // Advance to the next leaf node\n for (level = -1; ; ) {\n if (++level >= nodeQueue.length) {\n return {done: true, value: undefined};\n }\n if (--nodeIndex[level] >= 0) {\n break;\n }\n }\n for (; level > 0; level--) {\n nodeQueue[level - 1] = (\n nodeQueue[level][nodeIndex[level]] as BNodeInternal<K>\n ).children;\n nodeIndex[level - 1] = nodeQueue[level - 1].length - 1;\n }\n leaf = nodeQueue[0][nodeIndex[0]];\n i = leaf.keys.length;\n }\n });\n}\n\nfunction findPath<K>(\n key: K | undefined,\n root: BNode<K>,\n comparator: Comparator<K>,\n): [nodeQueue: BNode<K>[][], nodeIndex: number[], leaf: BNode<K>] | undefined {\n let nextNode = root;\n const nodeQueue: BNode<K>[][] = [];\n const nodeIndex: number[] = [];\n\n if (nextNode.isInternal()) {\n for (let d = 0; nextNode.isInternal(); d++) {\n nodeQueue[d] = nextNode.children;\n nodeIndex[d] =\n key === undefined ? 0 : indexOf(key, nextNode.keys, 0, comparator);\n if (nodeIndex[d] >= nodeQueue[d].length) return; // first key > maxKey()\n nextNode = nodeQueue[d][nodeIndex[d]];\n }\n nodeQueue.reverse();\n nodeIndex.reverse();\n }\n return [nodeQueue, nodeIndex, nextNode];\n}\n\nfunction iterator<T>(next: () => IteratorResult<T>): IterableIterator<T> {\n return {\n next,\n [Symbol.iterator]() {\n return this;\n },\n };\n}\n\n/** Leaf node / base class. **************************************************/\nclass BNode<K> {\n // If this is an internal node, _keys[i] is the highest key in children[i].\n keys: K[];\n // True if this node might be within multiple `BTree`s (or have multiple parents).\n // If so, it must be cloned before being mutated to avoid changing an unrelated tree.\n // This is transitive: if it's true, children are also shared even if `isShared!=true`\n // in those children. (Certain operations will propagate isShared=true to children.)\n isShared: true | undefined;\n\n constructor(keys: K[]) {\n this.keys = keys;\n this.isShared = undefined;\n }\n\n isInternal(): this is BNodeInternal<K> {\n return false;\n }\n\n maxKey() {\n return this.keys[this.keys.length - 1];\n }\n\n minKey(): K | undefined {\n return this.keys[0];\n }\n\n clone(): BNode<K> {\n return new BNode<K>(this.keys.slice(0));\n }\n\n get(key: K, tree: BTreeSet<K>): K | undefined {\n const i = indexOf(key, this.keys, -1, tree.comparator);\n return i < 0 ? undefined : this.keys[i];\n }\n\n has(key: K, tree: BTreeSet<K>): boolean {\n const i = indexOf(key, this.keys, -1, tree.comparator);\n return i >= 0 && i < this.keys.length;\n }\n\n set(key: K, tree: BTreeSet<K>): null | BNode<K> {\n let i = indexOf(key, this.keys, -1, tree.comparator);\n if (i < 0) {\n // key does not exist yet\n i = ~i;\n tree.size++;\n\n if (this.keys.length < MAX_NODE_SIZE) {\n this.keys.splice(i, 0, key);\n return null;\n }\n // This leaf node is full and must split\n const newRightSibling = this.splitOffRightSide();\n // oxlint-disable-next-line @typescript-eslint/no-this-alias\n let target: BNode<K> = this;\n if (i > this.keys.length) {\n i -= this.keys.length;\n target = newRightSibling;\n }\n // target.#insertInLeaf(i, key);\n target.keys.splice(i, 0, key);\n\n return newRightSibling;\n }\n\n // usually this is a no-op, but some users may wish to edit the key\n this.keys[i] = key;\n return null;\n }\n\n takeFromRight(rhs: BNode<K>) {\n this.keys.push(rhs.keys.shift()!);\n }\n\n takeFromLeft(lhs: BNode<K>) {\n this.keys.unshift(lhs.keys.pop()!);\n }\n\n splitOffRightSide(): BNode<K> {\n const half = this.keys.length >> 1;\n const keys = this.keys.splice(half);\n return new BNode<K>(keys);\n }\n\n delete(key: K, tree: BTreeSet<K>): boolean {\n const cmp = tree.comparator;\n const iLow = indexOf(key, this.keys, -1, cmp);\n const iHigh = iLow + 1;\n\n if (iLow < 0) {\n return false;\n }\n\n const {keys} = this;\n for (let i = iLow; i < iHigh; i++) {\n const key = keys[i];\n\n if (key !== keys[i] || this.isShared === true) {\n throw new Error('BTree illegally changed or cloned in delete');\n }\n\n this.keys.splice(i, 1);\n tree.size--;\n return true;\n }\n\n return false;\n }\n\n mergeSibling(rhs: BNode<K>, _: number) {\n this.keys.push(...rhs.keys);\n }\n}\n\n/** Internal node (non-leaf node) ********************************************/\nclass BNodeInternal<K> extends BNode<K> {\n // Note: conventionally B+ trees have one fewer key than the number of\n // children, but I find it easier to keep the array lengths equal: each\n // keys[i] caches the value of children[i].maxKey().\n children: BNode<K>[];\n\n /**\n * This does not mark `children` as shared, so it is the responsibility of the caller\n * to ensure children are either marked shared, or aren't included in another tree.\n */\n constructor(children: BNode<K>[], keys?: K[]) {\n if (!keys) {\n keys = [];\n for (let i = 0; i < children.length; i++) {\n keys[i] = children[i].maxKey();\n }\n }\n super(keys);\n this.children = children;\n }\n\n isInternal(): this is BNodeInternal<K> {\n return true;\n }\n\n clone(): BNode<K> {\n const children = this.children.slice(0);\n for (let i = 0; i < children.length; i++) {\n children[i].isShared = true;\n }\n return new BNodeInternal<K>(children, this.keys.slice(0));\n }\n\n minKey() {\n return this.children[0].minKey();\n }\n\n get(key: K, tree: BTreeSet<K>): K | undefined {\n const i = indexOf(key, this.keys, 0, tree.comparator);\n const {children} = this;\n return i < children.length ? children[i].get(key, tree) : undefined;\n }\n\n has(key: K, tree: BTreeSet<K>): boolean {\n const i = indexOf(key, this.keys, 0, tree.comparator);\n const {children} = this;\n return i < children.length ? children[i].has(key, tree) : false;\n }\n\n set(key: K, tree: BTreeSet<K>): null | BNode<K> {\n const c = this.children;\n const cmp = tree.comparator;\n let i = Math.min(indexOf(key, this.keys, 0, cmp), c.length - 1);\n let child = c[i];\n\n if (child.isShared) {\n c[i] = child = child.clone();\n }\n if (child.keys.length >= MAX_NODE_SIZE) {\n // child is full; inserting anything else will cause a split.\n // Shifting an item to the left or right sibling may avoid a split.\n // We can do a shift if the adjacent node is not full and if the\n // current key can still be placed in the same node after the shift.\n let other: BNode<K>;\n if (\n i > 0 &&\n (other = c[i - 1]).keys.length < MAX_NODE_SIZE &&\n cmp(child.keys[0], key) < 0\n ) {\n if (other.isShared) {\n c[i - 1] = other = other.clone();\n }\n other.takeFromRight(child);\n this.keys[i - 1] = other.maxKey();\n } else if (\n (other = c[i + 1]) !== undefined &&\n other.keys.length < MAX_NODE_SIZE &&\n cmp(child.maxKey(), key) < 0\n ) {\n if (other.isShared) c[i + 1] = other = other.clone();\n other.takeFromLeft(child);\n this.keys[i] = c[i].maxKey();\n }\n }\n\n const result = child.set(key, tree);\n this.keys[i] = child.maxKey();\n if (result === null) return null;\n\n // The child has split and `result` is a new right child... does it fit?\n if (this.keys.length < MAX_NODE_SIZE) {\n // yes\n this.insert(i + 1, result);\n return null;\n }\n // no, we must split also\n const newRightSibling = this.splitOffRightSide();\n // oxlint-disable-next-line @typescript-eslint/no-this-alias\n let target: BNodeInternal<K> = this;\n if (cmp(result.maxKey(), this.maxKey()) > 0) {\n target = newRightSibling;\n i -= this.keys.length;\n }\n target.insert(i + 1, result);\n return newRightSibling;\n }\n\n /**\n * Inserts `child` at index `i`.\n * This does not mark `child` as shared, so it is the responsibility of the caller\n * to ensure that either child is marked shared, or it is not included in another tree.\n */\n insert(i: number, child: BNode<K>) {\n this.children.splice(i, 0, child);\n this.keys.splice(i, 0, child.maxKey());\n }\n\n /**\n * Split this node.\n * Modifies this to remove the second half of the items, returning a separate node containing them.\n */\n splitOffRightSide() {\n const half = this.children.length >> 1;\n return new BNodeInternal<K>(\n this.children.splice(half),\n this.keys.splice(half),\n );\n }\n\n takeFromRight(rhs: BNode<K>) {\n this.keys.push(rhs.keys.shift()!);\n this.children.push((rhs as BNodeInternal<K>).children.shift()!);\n }\n\n takeFromLeft(lhs: BNode<K>) {\n this.keys.unshift(lhs.keys.pop()!);\n this.children.unshift((lhs as BNodeInternal<K>).children.pop()!);\n }\n\n delete(key: K, tree: BTreeSet<K>): boolean {\n const cmp = tree.comparator;\n const {keys} = this;\n const {children} = this;\n let iLow = indexOf(key, this.keys, 0, cmp);\n let i = iLow;\n const iHigh = Math.min(iLow, keys.length - 1);\n if (i <= iHigh) {\n try {\n if (children[i].isShared) {\n children[i] = children[i].clone();\n }\n const result = children[i].delete(key, tree);\n // Note: if children[i] is empty then keys[i]=undefined.\n // This is an invalid state, but it is fixed below.\n keys[i] = children[i].maxKey();\n return result;\n } finally {\n // Deletions may have occurred, so look for opportunities to merge nodes.\n const half = MAX_NODE_SIZE >> 1;\n if (iLow > 0) iLow--;\n for (i = iHigh; i >= iLow; i--) {\n if (children[i].keys.length <= half) {\n if (children[i].keys.length !== 0) {\n this.tryMerge(i, MAX_NODE_SIZE);\n } else {\n // child is empty! delete it!\n keys.splice(i, 1);\n children.splice(i, 1);\n }\n }\n }\n }\n }\n return false;\n }\n\n /** Merges child i with child i+1 if their combined size is not too large */\n tryMerge(i: number, maxSize: number): boolean {\n const {children} = this;\n if (i >= 0 && i + 1 < children.length) {\n if (children[i].keys.length + children[i + 1].keys.length <= maxSize) {\n if (\n children[i].isShared\n ) // cloned already UNLESS i is outside scan range\n {\n children[i] = children[i].clone();\n }\n children[i].mergeSibling(children[i + 1], maxSize);\n children.splice(i + 1, 1);\n this.keys.splice(i + 1, 1);\n this.keys[i] = children[i].maxKey();\n return true;\n }\n }\n return false;\n }\n\n /**\n * Move children from `rhs` into this.\n * `rhs` must be part of this tree, and be removed from it after this call\n * (otherwise isShared for its children could be incorrect).\n */\n mergeSibling(rhs: BNode<K>, maxNodeSize: number) {\n // assert !this.isShared;\n const oldLength = this.keys.length;\n this.keys.push(...rhs.keys);\n const rhsChildren = (rhs as unknown as BNodeInternal<K>).children;\n this.children.push(...rhsChildren);\n\n if (rhs.isShared && !this.isShared) {\n // All children of a shared node are implicitly shared, and since their new\n // parent is not shared, they must now be explicitly marked as shared.\n for (let i = 0; i < rhsChildren.length; i++) {\n rhsChildren[i].isShared = true;\n }\n }\n\n // If our children are themselves almost empty due to a mass-delete,\n // they may need to be merged too (but only the oldLength-1 and its\n // right sibling should need this).\n this.tryMerge(oldLength - 1, maxNodeSize);\n }\n}\n\n// If key not found, returns i^failXor where i is the insertion index.\n// Callers that don't care whether there was a match will set failXor=0.\nfunction indexOf<K>(\n key: K,\n keys: K[],\n failXor: number,\n comparator: Comparator<K>,\n): number {\n let lo = 0;\n let hi = keys.length;\n let mid = hi >> 1;\n while (lo < hi) {\n const c = comparator(keys[mid], key);\n if (c < 0) {\n lo = mid + 1;\n } else if (c > 0) {\n // key < keys[mid]\n hi = mid;\n } else if (c === 0) {\n return mid;\n } else {\n // c is NaN or otherwise invalid\n if (key === key) {\n // at least the search key is not NaN\n return keys.length;\n }\n throw new Error('NaN was used as a key');\n }\n mid = (lo + hi) >> 1;\n }\n return mid ^ failXor;\n}\n\n// oxlint-disable-next-line @typescript-eslint/no-explicit-any\nconst emptyLeaf = new BNode<any>([]);\nemptyLeaf.isShared = true;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"btree-set.js","names":["#root","#delete","#maxKey","#nodeQueue","#nodeIndex","#leaf","#i"],"sources":["../../../../shared/src/btree-set.ts"],"sourcesContent":["import {assert} from './asserts.ts';\n\nconst MAX_NODE_SIZE = 32;\n\ntype Comparator<K> = (a: K, b: K) => number;\n\nexport class BTreeSet<K> {\n #root: BNode<K> = emptyLeaf as BNode<K>;\n size: number = 0;\n\n readonly comparator: Comparator<K>;\n\n constructor(comparator: Comparator<K>, entries?: IterableIterator<K>) {\n this.comparator = comparator;\n if (entries) {\n for (const key of entries) {\n this.add(key);\n }\n }\n }\n\n /** Releases the tree so that its size is 0. */\n clear() {\n this.#root = emptyLeaf as BNode<K>;\n this.size = 0;\n }\n\n clone() {\n this.#root.isShared = true;\n const ret = new BTreeSet<K>(this.comparator);\n ret.#root = this.#root;\n ret.size = this.size;\n return ret;\n }\n\n get(key: K): K | undefined {\n return this.#root.get(key, this);\n }\n\n add(key: K): this {\n if (this.#root.isShared) this.#root = this.#root.clone();\n const result = this.#root.set(key, this);\n if (result === null) return this;\n // Root node has split, so create a new root node.\n this.#root = new BNodeInternal<K>([this.#root, result]);\n return this;\n }\n\n /**\n * Returns true if the key exists in the B+ tree, false if not.\n * Use get() for best performance; use has() if you need to\n * distinguish between \"undefined value\" and \"key not present\".\n * @param key Key to detect\n * @description Computational complexity: O(log size)\n */\n has(key: K): boolean {\n return this.#root.has(key, this);\n }\n\n /**\n * Removes a single key-value pair from the B+ tree.\n * @param key Key to find\n * @returns true if a pair was found and removed, false otherwise.\n * @description Computational complexity: O(log size)\n */\n delete(key: K): boolean {\n return this.#delete(key);\n }\n\n #delete(key: K): boolean {\n let root = this.#root;\n if (root.isShared) {\n this.#root = root = root.clone();\n }\n try {\n return root.delete(key, this);\n } finally {\n let isShared;\n while (root.keys.length <= 1 && root.isInternal()) {\n isShared ||= root.isShared;\n this.#root = root =\n root.keys.length === 0 ? emptyLeaf : root.children[0];\n }\n // If any ancestor of the new root was shared, the new root must also be shared\n if (isShared) {\n root.isShared = true;\n }\n }\n }\n\n keys(): IterableIterator<K> {\n return valuesFrom(this.#root, this.comparator, undefined, true);\n }\n\n values(): IterableIterator<K> {\n return valuesFrom(this.#root, this.comparator, undefined, true);\n }\n\n valuesFrom(lowestKey?: K, inclusive: boolean = true): IterableIterator<K> {\n return valuesFrom(this.#root, this.comparator, lowestKey, inclusive);\n }\n\n valuesReversed(): IterableIterator<K> {\n return valuesFromReversed(\n this.#maxKey(),\n this.#root,\n this.comparator,\n undefined,\n true,\n );\n }\n\n valuesFromReversed(\n highestKey?: K,\n inclusive: boolean = true,\n ): IterableIterator<K> {\n return valuesFromReversed(\n this.#maxKey(),\n this.#root,\n this.comparator,\n highestKey,\n inclusive,\n );\n }\n\n /** Gets the highest key in the tree. Complexity: O(1) */\n #maxKey(): K | undefined {\n return this.#root.maxKey();\n }\n\n [Symbol.iterator](): IterableIterator<K> {\n return this.keys();\n }\n}\n\nclass BTreeForwardIterator<K> implements IterableIterator<K> {\n readonly #nodeQueue: BNode<K>[][];\n readonly #nodeIndex: number[];\n #leaf: BNode<K>;\n #i: number;\n\n constructor(\n nodeQueue: BNode<K>[][],\n nodeIndex: number[],\n leaf: BNode<K>,\n startI: number,\n ) {\n this.#nodeQueue = nodeQueue;\n this.#nodeIndex = nodeIndex;\n this.#leaf = leaf;\n this.#i = startI;\n }\n\n next(): IteratorResult<K> {\n for (;;) {\n if (++this.#i < this.#leaf.keys.length) {\n return {done: false, value: this.#leaf.keys[this.#i]};\n }\n\n let level = -1;\n for (;;) {\n if (++level >= this.#nodeQueue.length) {\n return {done: true, value: undefined as unknown as K};\n }\n if (++this.#nodeIndex[level] < this.#nodeQueue[level].length) {\n break;\n }\n }\n for (; level > 0; level--) {\n this.#nodeQueue[level - 1] = (\n this.#nodeQueue[level][this.#nodeIndex[level]] as BNodeInternal<K>\n ).children;\n this.#nodeIndex[level - 1] = 0;\n }\n this.#leaf = this.#nodeQueue[0][this.#nodeIndex[0]];\n this.#i = -1;\n }\n }\n\n [Symbol.iterator]() {\n return this;\n }\n}\n\nclass BTreeReverseIterator<K> implements IterableIterator<K> {\n readonly #nodeQueue: BNode<K>[][];\n readonly #nodeIndex: number[];\n #leaf: BNode<K>;\n #i: number;\n\n constructor(\n nodeQueue: BNode<K>[][],\n nodeIndex: number[],\n leaf: BNode<K>,\n startI: number,\n ) {\n this.#nodeQueue = nodeQueue;\n this.#nodeIndex = nodeIndex;\n this.#leaf = leaf;\n this.#i = startI;\n }\n\n next(): IteratorResult<K> {\n for (;;) {\n if (--this.#i >= 0) {\n return {done: false, value: this.#leaf.keys[this.#i]};\n }\n\n let level;\n // Advance to the next leaf node\n for (level = -1; ; ) {\n if (++level >= this.#nodeQueue.length) {\n return {done: true, value: undefined as unknown as K};\n }\n if (--this.#nodeIndex[level] >= 0) {\n break;\n }\n }\n for (; level > 0; level--) {\n this.#nodeQueue[level - 1] = (\n this.#nodeQueue[level][this.#nodeIndex[level]] as BNodeInternal<K>\n ).children;\n this.#nodeIndex[level - 1] = this.#nodeQueue[level - 1].length - 1;\n }\n this.#leaf = this.#nodeQueue[0][this.#nodeIndex[0]];\n this.#i = this.#leaf.keys.length;\n }\n }\n\n [Symbol.iterator]() {\n return this;\n }\n}\n\nfunction valuesFrom<K>(\n root: BNode<K>,\n comparator: Comparator<K>,\n lowestKey: K | undefined,\n inclusive: boolean,\n): IterableIterator<K> {\n const info = findPath(lowestKey, root, comparator);\n if (info === undefined) {\n return iterator<K>(() => ({done: true, value: undefined}));\n }\n\n let [nodeQueue, nodeIndex, leaf] = info;\n let i =\n lowestKey === undefined\n ? -1\n : indexOf(lowestKey, leaf.keys, 0, comparator) - 1;\n\n if (\n !inclusive &&\n lowestKey !== undefined &&\n // +1 because we did -1 above.\n i + 1 < leaf.keys.length &&\n comparator(leaf.keys[i + 1], lowestKey) === 0\n ) {\n i++;\n }\n\n return new BTreeForwardIterator(nodeQueue, nodeIndex, leaf, i);\n}\n\nfunction valuesFromReversed<K>(\n maxKey: K | undefined,\n root: BNode<K>,\n comparator: Comparator<K>,\n highestKey: K | undefined,\n inclusive: boolean,\n): IterableIterator<K> {\n if (highestKey === undefined) {\n highestKey = maxKey;\n if (highestKey === undefined) {\n return iterator<K>(() => ({done: true, value: undefined}));\n } // collection is empty\n }\n let [nodeQueue, nodeIndex, leaf] =\n findPath(highestKey, root, comparator) ||\n findPath(maxKey, root, comparator)!;\n assert(\n !nodeQueue[0] || leaf === nodeQueue[0][nodeIndex[0]],\n 'BTreeSet: leaf node mismatch in iteration',\n );\n let i = indexOf(highestKey, leaf.keys, 0, comparator);\n if (\n inclusive &&\n i < leaf.keys.length &&\n comparator(leaf.keys[i], highestKey) <= 0\n ) {\n i++;\n }\n\n return new BTreeReverseIterator(nodeQueue, nodeIndex, leaf, i);\n}\n\nfunction findPath<K>(\n key: K | undefined,\n root: BNode<K>,\n comparator: Comparator<K>,\n): [nodeQueue: BNode<K>[][], nodeIndex: number[], leaf: BNode<K>] | undefined {\n let nextNode = root;\n const nodeQueue: BNode<K>[][] = [];\n const nodeIndex: number[] = [];\n\n if (nextNode.isInternal()) {\n for (let d = 0; nextNode.isInternal(); d++) {\n nodeQueue[d] = nextNode.children;\n nodeIndex[d] =\n key === undefined ? 0 : indexOf(key, nextNode.keys, 0, comparator);\n if (nodeIndex[d] >= nodeQueue[d].length) return; // first key > maxKey()\n nextNode = nodeQueue[d][nodeIndex[d]];\n }\n nodeQueue.reverse();\n nodeIndex.reverse();\n }\n return [nodeQueue, nodeIndex, nextNode];\n}\n\nfunction iterator<T>(next: () => IteratorResult<T>): IterableIterator<T> {\n return {\n next,\n [Symbol.iterator]() {\n return this;\n },\n };\n}\n\n/** Leaf node / base class. **************************************************/\nclass BNode<K> {\n // If this is an internal node, _keys[i] is the highest key in children[i].\n keys: K[];\n // True if this node might be within multiple `BTree`s (or have multiple parents).\n // If so, it must be cloned before being mutated to avoid changing an unrelated tree.\n // This is transitive: if it's true, children are also shared even if `isShared!=true`\n // in those children. (Certain operations will propagate isShared=true to children.)\n isShared: true | undefined;\n\n constructor(keys: K[]) {\n this.keys = keys;\n this.isShared = undefined;\n }\n\n isInternal(): this is BNodeInternal<K> {\n return false;\n }\n\n maxKey() {\n return this.keys[this.keys.length - 1];\n }\n\n minKey(): K | undefined {\n return this.keys[0];\n }\n\n clone(): BNode<K> {\n return new BNode<K>(this.keys.slice(0));\n }\n\n get(key: K, tree: BTreeSet<K>): K | undefined {\n const i = indexOf(key, this.keys, -1, tree.comparator);\n return i < 0 ? undefined : this.keys[i];\n }\n\n has(key: K, tree: BTreeSet<K>): boolean {\n const i = indexOf(key, this.keys, -1, tree.comparator);\n return i >= 0 && i < this.keys.length;\n }\n\n set(key: K, tree: BTreeSet<K>): null | BNode<K> {\n let i = indexOf(key, this.keys, -1, tree.comparator);\n if (i < 0) {\n // key does not exist yet\n i = ~i;\n tree.size++;\n\n if (this.keys.length < MAX_NODE_SIZE) {\n this.keys.splice(i, 0, key);\n return null;\n }\n // This leaf node is full and must split\n const newRightSibling = this.splitOffRightSide();\n // oxlint-disable-next-line @typescript-eslint/no-this-alias\n let target: BNode<K> = this;\n if (i > this.keys.length) {\n i -= this.keys.length;\n target = newRightSibling;\n }\n // target.#insertInLeaf(i, key);\n target.keys.splice(i, 0, key);\n\n return newRightSibling;\n }\n\n // usually this is a no-op, but some users may wish to edit the key\n this.keys[i] = key;\n return null;\n }\n\n takeFromRight(rhs: BNode<K>) {\n this.keys.push(rhs.keys.shift()!);\n }\n\n takeFromLeft(lhs: BNode<K>) {\n this.keys.unshift(lhs.keys.pop()!);\n }\n\n splitOffRightSide(): BNode<K> {\n const half = this.keys.length >> 1;\n const keys = this.keys.splice(half);\n return new BNode<K>(keys);\n }\n\n delete(key: K, tree: BTreeSet<K>): boolean {\n const cmp = tree.comparator;\n const iLow = indexOf(key, this.keys, -1, cmp);\n const iHigh = iLow + 1;\n\n if (iLow < 0) {\n return false;\n }\n\n const {keys} = this;\n for (let i = iLow; i < iHigh; i++) {\n const key = keys[i];\n\n if (key !== keys[i] || this.isShared === true) {\n throw new Error('BTree illegally changed or cloned in delete');\n }\n\n this.keys.splice(i, 1);\n tree.size--;\n return true;\n }\n\n return false;\n }\n\n mergeSibling(rhs: BNode<K>, _: number) {\n this.keys.push(...rhs.keys);\n }\n}\n\n/** Internal node (non-leaf node) ********************************************/\nclass BNodeInternal<K> extends BNode<K> {\n // Note: conventionally B+ trees have one fewer key than the number of\n // children, but I find it easier to keep the array lengths equal: each\n // keys[i] caches the value of children[i].maxKey().\n children: BNode<K>[];\n\n /**\n * This does not mark `children` as shared, so it is the responsibility of the caller\n * to ensure children are either marked shared, or aren't included in another tree.\n */\n constructor(children: BNode<K>[], keys?: K[]) {\n if (!keys) {\n keys = [];\n for (let i = 0; i < children.length; i++) {\n keys[i] = children[i].maxKey();\n }\n }\n super(keys);\n this.children = children;\n }\n\n isInternal(): this is BNodeInternal<K> {\n return true;\n }\n\n clone(): BNode<K> {\n const children = this.children.slice(0);\n for (let i = 0; i < children.length; i++) {\n children[i].isShared = true;\n }\n return new BNodeInternal<K>(children, this.keys.slice(0));\n }\n\n minKey() {\n return this.children[0].minKey();\n }\n\n get(key: K, tree: BTreeSet<K>): K | undefined {\n const i = indexOf(key, this.keys, 0, tree.comparator);\n const {children} = this;\n return i < children.length ? children[i].get(key, tree) : undefined;\n }\n\n has(key: K, tree: BTreeSet<K>): boolean {\n const i = indexOf(key, this.keys, 0, tree.comparator);\n const {children} = this;\n return i < children.length ? children[i].has(key, tree) : false;\n }\n\n set(key: K, tree: BTreeSet<K>): null | BNode<K> {\n const c = this.children;\n const cmp = tree.comparator;\n let i = Math.min(indexOf(key, this.keys, 0, cmp), c.length - 1);\n let child = c[i];\n\n if (child.isShared) {\n c[i] = child = child.clone();\n }\n if (child.keys.length >= MAX_NODE_SIZE) {\n // child is full; inserting anything else will cause a split.\n // Shifting an item to the left or right sibling may avoid a split.\n // We can do a shift if the adjacent node is not full and if the\n // current key can still be placed in the same node after the shift.\n let other: BNode<K>;\n if (\n i > 0 &&\n (other = c[i - 1]).keys.length < MAX_NODE_SIZE &&\n cmp(child.keys[0], key) < 0\n ) {\n if (other.isShared) {\n c[i - 1] = other = other.clone();\n }\n other.takeFromRight(child);\n this.keys[i - 1] = other.maxKey();\n } else if (\n (other = c[i + 1]) !== undefined &&\n other.keys.length < MAX_NODE_SIZE &&\n cmp(child.maxKey(), key) < 0\n ) {\n if (other.isShared) c[i + 1] = other = other.clone();\n other.takeFromLeft(child);\n this.keys[i] = c[i].maxKey();\n }\n }\n\n const result = child.set(key, tree);\n this.keys[i] = child.maxKey();\n if (result === null) return null;\n\n // The child has split and `result` is a new right child... does it fit?\n if (this.keys.length < MAX_NODE_SIZE) {\n // yes\n this.insert(i + 1, result);\n return null;\n }\n // no, we must split also\n const newRightSibling = this.splitOffRightSide();\n // oxlint-disable-next-line @typescript-eslint/no-this-alias\n let target: BNodeInternal<K> = this;\n if (cmp(result.maxKey(), this.maxKey()) > 0) {\n target = newRightSibling;\n i -= this.keys.length;\n }\n target.insert(i + 1, result);\n return newRightSibling;\n }\n\n /**\n * Inserts `child` at index `i`.\n * This does not mark `child` as shared, so it is the responsibility of the caller\n * to ensure that either child is marked shared, or it is not included in another tree.\n */\n insert(i: number, child: BNode<K>) {\n this.children.splice(i, 0, child);\n this.keys.splice(i, 0, child.maxKey());\n }\n\n /**\n * Split this node.\n * Modifies this to remove the second half of the items, returning a separate node containing them.\n */\n splitOffRightSide() {\n const half = this.children.length >> 1;\n return new BNodeInternal<K>(\n this.children.splice(half),\n this.keys.splice(half),\n );\n }\n\n takeFromRight(rhs: BNode<K>) {\n this.keys.push(rhs.keys.shift()!);\n this.children.push((rhs as BNodeInternal<K>).children.shift()!);\n }\n\n takeFromLeft(lhs: BNode<K>) {\n this.keys.unshift(lhs.keys.pop()!);\n this.children.unshift((lhs as BNodeInternal<K>).children.pop()!);\n }\n\n delete(key: K, tree: BTreeSet<K>): boolean {\n const cmp = tree.comparator;\n const {keys} = this;\n const {children} = this;\n let iLow = indexOf(key, this.keys, 0, cmp);\n let i = iLow;\n const iHigh = Math.min(iLow, keys.length - 1);\n if (i <= iHigh) {\n try {\n if (children[i].isShared) {\n children[i] = children[i].clone();\n }\n const result = children[i].delete(key, tree);\n // Note: if children[i] is empty then keys[i]=undefined.\n // This is an invalid state, but it is fixed below.\n keys[i] = children[i].maxKey();\n return result;\n } finally {\n // Deletions may have occurred, so look for opportunities to merge nodes.\n const half = MAX_NODE_SIZE >> 1;\n if (iLow > 0) iLow--;\n for (i = iHigh; i >= iLow; i--) {\n if (children[i].keys.length <= half) {\n if (children[i].keys.length !== 0) {\n this.tryMerge(i, MAX_NODE_SIZE);\n } else {\n // child is empty! delete it!\n keys.splice(i, 1);\n children.splice(i, 1);\n }\n }\n }\n }\n }\n return false;\n }\n\n /** Merges child i with child i+1 if their combined size is not too large */\n tryMerge(i: number, maxSize: number): boolean {\n const {children} = this;\n if (i >= 0 && i + 1 < children.length) {\n if (children[i].keys.length + children[i + 1].keys.length <= maxSize) {\n if (\n children[i].isShared\n ) // cloned already UNLESS i is outside scan range\n {\n children[i] = children[i].clone();\n }\n children[i].mergeSibling(children[i + 1], maxSize);\n children.splice(i + 1, 1);\n this.keys.splice(i + 1, 1);\n this.keys[i] = children[i].maxKey();\n return true;\n }\n }\n return false;\n }\n\n /**\n * Move children from `rhs` into this.\n * `rhs` must be part of this tree, and be removed from it after this call\n * (otherwise isShared for its children could be incorrect).\n */\n mergeSibling(rhs: BNode<K>, maxNodeSize: number) {\n // assert !this.isShared;\n const oldLength = this.keys.length;\n this.keys.push(...rhs.keys);\n const rhsChildren = (rhs as unknown as BNodeInternal<K>).children;\n this.children.push(...rhsChildren);\n\n if (rhs.isShared && !this.isShared) {\n // All children of a shared node are implicitly shared, and since their new\n // parent is not shared, they must now be explicitly marked as shared.\n for (let i = 0; i < rhsChildren.length; i++) {\n rhsChildren[i].isShared = true;\n }\n }\n\n // If our children are themselves almost empty due to a mass-delete,\n // they may need to be merged too (but only the oldLength-1 and its\n // right sibling should need this).\n this.tryMerge(oldLength - 1, maxNodeSize);\n }\n}\n\n// If key not found, returns i^failXor where i is the insertion index.\n// Callers that don't care whether there was a match will set failXor=0.\nfunction indexOf<K>(\n key: K,\n keys: K[],\n failXor: number,\n comparator: Comparator<K>,\n): number {\n let lo = 0;\n let hi = keys.length;\n let mid = hi >> 1;\n while (lo < hi) {\n const c = comparator(keys[mid], key);\n if (c < 0) {\n lo = mid + 1;\n } else if (c > 0) {\n // key < keys[mid]\n hi = mid;\n } else if (c === 0) {\n return mid;\n } else {\n // c is NaN or otherwise invalid\n if (key === key) {\n // at least the search key is not NaN\n return keys.length;\n }\n throw new Error('NaN was used as a key');\n }\n mid = (lo + hi) >> 1;\n }\n return mid ^ failXor;\n}\n\n// oxlint-disable-next-line @typescript-eslint/no-explicit-any\nconst emptyLeaf = new BNode<any>([]);\nemptyLeaf.isShared = true;\n"],"mappings":"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"}
|
package/out/zero/package.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
var package_default = {
|
|
2
2
|
name: "@rocicorp/zero",
|
|
3
|
-
version: "1.2.0-canary.
|
|
3
|
+
version: "1.2.0-canary.5",
|
|
4
4
|
description: "Zero is a web framework for serverless web development.",
|
|
5
5
|
author: "Rocicorp, Inc.",
|
|
6
6
|
repository: {
|
|
@@ -17,8 +17,8 @@ var package_default = {
|
|
|
17
17
|
"check-types": "tsc -p tsconfig.client.json && tsc -p tsconfig.server.json",
|
|
18
18
|
"check-types:client:watch": "tsc -p tsconfig.client.json --watch",
|
|
19
19
|
"check-types:server:watch": "tsc -p tsconfig.server.json --watch",
|
|
20
|
-
"format": "
|
|
21
|
-
"check-format": "
|
|
20
|
+
"format": "oxfmt .",
|
|
21
|
+
"check-format": "oxfmt --check .",
|
|
22
22
|
"lint": "oxlint --type-aware src/",
|
|
23
23
|
"docs": "node --experimental-strip-types --no-warnings tool/generate-docs.ts",
|
|
24
24
|
"docs:server": "node --watch --experimental-strip-types --no-warnings tool/generate-docs.ts --server"
|
|
@@ -55,7 +55,7 @@ var package_default = {
|
|
|
55
55
|
"cloudevents": "^10.0.0",
|
|
56
56
|
"command-line-args": "^6.0.1",
|
|
57
57
|
"command-line-usage": "^7.0.3",
|
|
58
|
-
"compare-utf8": "^0.
|
|
58
|
+
"compare-utf8": "^0.2.0",
|
|
59
59
|
"defu": "^6.1.4",
|
|
60
60
|
"eventemitter3": "^5.0.1",
|
|
61
61
|
"fastify": "^5.0.0",
|
|
@@ -68,7 +68,6 @@ var package_default = {
|
|
|
68
68
|
"parse-prometheus-text-format": "^1.1.1",
|
|
69
69
|
"pg-format": "npm:pg-format-fix@^1.0.5",
|
|
70
70
|
"postgres": "3.4.7",
|
|
71
|
-
"prettier": "^3.8.1",
|
|
72
71
|
"semver": "^7.5.4",
|
|
73
72
|
"tsx": "^4.21.0",
|
|
74
73
|
"url-pattern": "^1.0.3",
|
package/out/zero/package.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"package.js","names":[],"sources":["../../package.json"],"sourcesContent":["{\n \"name\": \"@rocicorp/zero\",\n \"version\": \"1.2.0-canary.
|
|
1
|
+
{"version":3,"file":"package.js","names":[],"sources":["../../package.json"],"sourcesContent":["{\n \"name\": \"@rocicorp/zero\",\n \"version\": \"1.2.0-canary.5\",\n \"description\": \"Zero is a web framework for serverless web development.\",\n \"author\": \"Rocicorp, Inc.\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/rocicorp/mono.git\",\n \"directory\": \"packages/zero\"\n },\n \"license\": \"Apache-2.0\",\n \"homepage\": \"https://zero.rocicorp.dev\",\n \"bugs\": {\n \"url\": \"https://bugs.rocicorp.dev\"\n },\n \"scripts\": {\n \"build\": \"node --experimental-strip-types --no-warnings tool/build.ts\",\n \"build:watch\": \"node --experimental-strip-types --no-warnings tool/build.ts --watch\",\n \"check-types\": \"tsc -p tsconfig.client.json && tsc -p tsconfig.server.json\",\n \"check-types:client:watch\": \"tsc -p tsconfig.client.json --watch\",\n \"check-types:server:watch\": \"tsc -p tsconfig.server.json --watch\",\n \"format\": \"oxfmt .\",\n \"check-format\": \"oxfmt --check .\",\n \"lint\": \"oxlint --type-aware src/\",\n \"docs\": \"node --experimental-strip-types --no-warnings tool/generate-docs.ts\",\n \"docs:server\": \"node --watch --experimental-strip-types --no-warnings tool/generate-docs.ts --server\"\n },\n \"dependencies\": {\n \"@badrap/valita\": \"0.3.11\",\n \"@databases/escape-identifier\": \"^1.0.3\",\n \"@databases/sql\": \"^3.3.0\",\n \"@dotenvx/dotenvx\": \"^1.39.0\",\n \"@drdgvhbh/postgres-error-codes\": \"^0.0.6\",\n \"@fastify/cors\": \"^10.0.0\",\n \"@fastify/websocket\": \"^11.0.0\",\n \"@google-cloud/precise-date\": \"^4.0.0\",\n \"@opentelemetry/api\": \"^1.9.0\",\n \"@opentelemetry/api-logs\": \"^0.203.0\",\n \"@opentelemetry/auto-instrumentations-node\": \"^0.62.0\",\n \"@opentelemetry/exporter-metrics-otlp-http\": \"^0.203.0\",\n \"@opentelemetry/resources\": \"^2.0.1\",\n \"@opentelemetry/sdk-metrics\": \"^2.0.1\",\n \"@opentelemetry/sdk-node\": \"^0.203.0\",\n \"@opentelemetry/sdk-trace-node\": \"^2.0.1\",\n \"@postgresql-typed/oids\": \"^0.2.0\",\n \"@rocicorp/lock\": \"^1.0.4\",\n \"@rocicorp/logger\": \"^5.4.0\",\n \"@rocicorp/resolver\": \"^1.0.2\",\n \"@rocicorp/zero-sqlite3\": \"^1.0.15\",\n \"@standard-schema/spec\": \"^1.0.0\",\n \"@types/basic-auth\": \"^1.1.8\",\n \"@types/ws\": \"^8.5.12\",\n \"basic-auth\": \"^2.0.1\",\n \"chalk\": \"^5.3.0\",\n \"chalk-template\": \"^1.1.0\",\n \"chokidar\": \"^4.0.1\",\n \"cloudevents\": \"^10.0.0\",\n \"command-line-args\": \"^6.0.1\",\n \"command-line-usage\": \"^7.0.3\",\n \"compare-utf8\": \"^0.2.0\",\n \"defu\": \"^6.1.4\",\n \"eventemitter3\": \"^5.0.1\",\n \"fastify\": \"^5.0.0\",\n \"is-in-subnet\": \"^4.0.1\",\n \"jose\": \"^5.9.3\",\n \"js-xxhash\": \"^4.0.0\",\n \"json-custom-numbers\": \"^3.1.1\",\n \"kasi\": \"^1.1.0\",\n \"nanoid\": \"^5.1.2\",\n \"parse-prometheus-text-format\": \"^1.1.1\",\n \"pg-format\": \"npm:pg-format-fix@^1.0.5\",\n \"postgres\": \"3.4.7\",\n \"semver\": \"^7.5.4\",\n \"tsx\": \"^4.21.0\",\n \"url-pattern\": \"^1.0.3\",\n \"urlpattern-polyfill\": \"^10.1.0\",\n \"ws\": \"^8.18.1\"\n },\n \"devDependencies\": {\n \"@op-engineering/op-sqlite\": \">=15\",\n \"@vitest/runner\": \"4.1.2\",\n \"analyze-query\": \"0.0.0\",\n \"ast-to-zql\": \"0.0.0\",\n \"expo-sqlite\": \">=15\",\n \"replicache\": \"15.2.1\",\n \"shared\": \"0.0.0\",\n \"syncpack\": \"^14.2.1\",\n \"typedoc\": \"^0.28.17\",\n \"typedoc-plugin-markdown\": \"^4.10.0\",\n \"typescript\": \"~5.9.3\",\n \"vite\": \"8.0.3\",\n \"vitest\": \"4.1.2\",\n \"zero-cache\": \"0.0.0\",\n \"zero-client\": \"0.0.0\",\n \"zero-pg\": \"0.0.0\",\n \"zero-protocol\": \"0.0.0\",\n \"zero-react\": \"0.0.0\",\n \"zero-server\": \"0.0.0\",\n \"zero-solid\": \"0.0.0\",\n \"zqlite\": \"0.0.0\"\n },\n \"peerDependencies\": {\n \"@op-engineering/op-sqlite\": \">=15\",\n \"expo-sqlite\": \">=15\"\n },\n \"peerDependenciesMeta\": {\n \"expo-sqlite\": {\n \"optional\": true\n },\n \"@op-engineering/op-sqlite\": {\n \"optional\": true\n }\n },\n \"type\": \"module\",\n \"main\": \"out/zero/src/zero.js\",\n \"module\": \"out/zero/src/zero.js\",\n \"types\": \"out/zero/src/zero.d.ts\",\n \"exports\": {\n \".\": {\n \"types\": \"./out/zero/src/zero.d.ts\",\n \"default\": \"./out/zero/src/zero.js\"\n },\n \"./bindings\": {\n \"types\": \"./out/zero/src/bindings.d.ts\",\n \"default\": \"./out/zero/src/bindings.js\"\n },\n \"./change-protocol/v0\": {\n \"types\": \"./out/zero/src/change-protocol/v0.d.ts\",\n \"default\": \"./out/zero/src/change-protocol/v0.js\"\n },\n \"./expo-sqlite\": {\n \"types\": \"./out/zero/src/expo-sqlite.d.ts\",\n \"default\": \"./out/zero/src/expo-sqlite.js\"\n },\n \"./op-sqlite\": {\n \"types\": \"./out/zero/src/op-sqlite.d.ts\",\n \"default\": \"./out/zero/src/op-sqlite.js\"\n },\n \"./pg\": {\n \"types\": \"./out/zero/src/pg.d.ts\",\n \"default\": \"./out/zero/src/pg.js\"\n },\n \"./react\": {\n \"types\": \"./out/zero/src/react.d.ts\",\n \"default\": \"./out/zero/src/react.js\"\n },\n \"./react-native\": {\n \"types\": \"./out/zero/src/react-native.d.ts\",\n \"default\": \"./out/zero/src/react-native.js\"\n },\n \"./server\": {\n \"types\": \"./out/zero/src/server.d.ts\",\n \"default\": \"./out/zero/src/server.js\"\n },\n \"./server/adapters/drizzle\": {\n \"types\": \"./out/zero/src/adapters/drizzle.d.ts\",\n \"default\": \"./out/zero/src/adapters/drizzle.js\"\n },\n \"./server/adapters/prisma\": {\n \"types\": \"./out/zero/src/adapters/prisma.d.ts\",\n \"default\": \"./out/zero/src/adapters/prisma.js\"\n },\n \"./server/adapters/pg\": {\n \"types\": \"./out/zero/src/adapters/pg.d.ts\",\n \"default\": \"./out/zero/src/adapters/pg.js\"\n },\n \"./server/adapters/postgresjs\": {\n \"types\": \"./out/zero/src/adapters/postgresjs.d.ts\",\n \"default\": \"./out/zero/src/adapters/postgresjs.js\"\n },\n \"./solid\": {\n \"types\": \"./out/zero/src/solid.d.ts\",\n \"default\": \"./out/zero/src/solid.js\"\n },\n \"./sqlite\": {\n \"types\": \"./out/zero/src/sqlite.d.ts\",\n \"default\": \"./out/zero/src/sqlite.js\"\n },\n \"./zqlite\": {\n \"types\": \"./out/zero/src/zqlite.d.ts\",\n \"default\": \"./out/zero/src/zqlite.js\"\n }\n },\n \"bin\": {\n \"zero-build-schema\": \"./out/zero/src/build-schema.js\",\n \"zero-cache\": \"./out/zero/src/cli.js\",\n \"zero-cache-dev\": \"./out/zero/src/zero-cache-dev.js\",\n \"zero-deploy-permissions\": \"./out/zero/src/deploy-permissions.js\",\n \"zero-out\": \"./out/zero/src/zero-out.js\",\n \"ast-to-zql\": \"./out/zero/src/ast-to-zql.js\",\n \"analyze-query\": \"./out/zero/src/analyze-query.js\",\n \"transform-query\": \"./out/zero/src/transform-query.js\"\n },\n \"engines\": {\n \"node\": \">=22\"\n },\n \"files\": [\n \"out\",\n \"!*.tsbuildinfo\"\n ]\n}"],"mappings":""}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transaction-pool.d.ts","sourceRoot":"","sources":["../../../../../zero-cache/src/db/transaction-pool.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAEjD,OAAO,KAAK,QAAQ,MAAM,UAAU,CAAC;AAErC,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,6BAA6B,CAAC;AAGtD,OAAO,EAAC,KAAK,UAAU,EAAE,KAAK,mBAAmB,EAAC,MAAM,gBAAgB,CAAC;AACzE,OAAO,KAAK,KAAK,IAAI,MAAM,gBAAgB,CAAC;AAG5C,KAAK,IAAI,GAAG,IAAI,CAAC,OAAO,IAAI,CAAC,CAAC;AAE9B,KAAK,YAAY,CAAC,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;AAEtC,MAAM,MAAM,SAAS,GACjB,QAAQ,CAAC,YAAY,CAAC,CAAC,QAAQ,CAAC,GAAG,GAAG,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,GAChE,QAAQ,CAAC,YAAY,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,CAAC;AAE1C;;;;;GAKG;AACH,MAAM,MAAM,IAAI,GAAG,CACjB,EAAE,EAAE,mBAAmB,EACvB,EAAE,EAAE,UAAU,KACX,YAAY,CAAC,SAAS,EAAE,CAAC,CAAC;AAE/B;;;;GAIG;AACH,MAAM,MAAM,QAAQ,CAAC,CAAC,IAAI,CACxB,EAAE,EAAE,mBAAmB,EACvB,EAAE,EAAE,UAAU,KACX,YAAY,CAAC,CAAC,CAAC,CAAC;AAErB;;;;;;;GAOG;AACH,qBAAa,eAAe;;IAkB1B;;;;;;;;;;;;;;;OAeG;gBAED,EAAE,EAAE,UAAU,EACd,IAAI,EAAE,IAAI,EACV,IAAI,CAAC,EAAE,IAAI,EACX,OAAO,CAAC,EAAE,IAAI,EACd,cAAc,SAAI,EAClB,UAAU,SAAiB,EAC3B,YAAY,eAAgB;IAkB9B;;;OAGG;IACH,GAAG,CAAC,EAAE,EAAE,UAAU,GAAG,IAAI;IASzB;;;;;;OAMG;IACH,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;iCAIf,MAAM,SAAS,MAAM;;IAKlD;;;;;;;;;;;;;;;;;;;OAmBG;IACG,IAAI;IA0GV;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IA+DlC;;;;;OAKG;IACH,eAAe,CAAC,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAsDrD;;;OAGG;IACH,KAAK;IAIL;;;OAGG;IACH,OAAO;IASP;;;;;;;;;;;;;;;;;OAiBG;IAEH,GAAG,CAAC,KAAK,SAAI;IAQb;;OAEG;IACH,KAAK,CAAC,KAAK,SAAI;IAYf,SAAS,IAAI,OAAO;IAIpB;;OAEG;IACH,IAAI,CAAC,GAAG,EAAE,OAAO;CAelB;AAED,KAAK,wBAAwB,GAAG;IAC9B;;;;;OAKG;IACH,cAAc,EAAE,IAAI,CAAC;IAErB;;;;;OAKG;IACH,aAAa,EAAE,IAAI,CAAC;IAEpB;;;;OAIG;IACH,WAAW,EAAE,IAAI,CAAC;IAElB,qCAAqC;IACrC,UAAU,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;CAC7B,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,qBAAqB,IAAI,wBAAwB,CAoDhE;AAED;;;;GAIG;AACH,wBAAgB,cAAc,IAAI;IAChC,IAAI,EAAE,IAAI,CAAC;IACX,OAAO,EAAE,IAAI,CAAC;IACd,UAAU,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;CAC7B,
|
|
1
|
+
{"version":3,"file":"transaction-pool.d.ts","sourceRoot":"","sources":["../../../../../zero-cache/src/db/transaction-pool.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transaction-pool.js","names":["#mode","#init","#cleanup","#tasks","#workers","#initialWorkers","#maxWorkers","#timeoutTask","#lc","#stmtRunner","#numWorkers","#db","#addWorker","#numWorking","#failure","#done","#process","#start","#stmts","#readRunner","#refCount"],"sources":["../../../../../zero-cache/src/db/transaction-pool.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {type Resolver, resolver} from '@rocicorp/resolver';\nimport type postgres from 'postgres';\nimport {assert} from '../../../shared/src/asserts.ts';\nimport type {Enum} from '../../../shared/src/enum.ts';\nimport {Queue} from '../../../shared/src/queue.ts';\nimport {promiseVoid} from '../../../shared/src/resolved-promises.ts';\nimport {type PostgresDB, type PostgresTransaction} from '../types/pg.ts';\nimport type * as Mode from './mode-enum.ts';\nimport {runTx} from './run-transaction.ts';\n\ntype Mode = Enum<typeof Mode>;\n\ntype MaybePromise<T> = Promise<T> | T;\n\nexport type Statement =\n | postgres.PendingQuery<(postgres.Row & Iterable<postgres.Row>)[]>\n | postgres.PendingQuery<postgres.Row[]>;\n\n/**\n * A {@link Task} is logic run from within a transaction in a {@link TransactionPool}.\n * It returns a list of `Statements` that the transaction executes asynchronously and\n * awaits when it receives the 'done' signal.\n *\n */\nexport type Task = (\n tx: PostgresTransaction,\n lc: LogContext,\n) => MaybePromise<Statement[]>;\n\n/**\n * A {@link ReadTask} is run from within a transaction, but unlike a {@link Task},\n * the results of a ReadTask are opaque to the TransactionPool and returned to the\n * caller of {@link TransactionPool.processReadTask}.\n */\nexport type ReadTask<T> = (\n tx: PostgresTransaction,\n lc: LogContext,\n) => MaybePromise<T>;\n\n/**\n * A TransactionPool is a pool of one or more {@link postgres.TransactionSql}\n * objects that participate in processing a dynamic queue of tasks.\n *\n * This can be used for serializing a set of tasks that arrive asynchronously\n * to a single transaction (for writing) or performing parallel reads across\n * multiple connections at the same snapshot (e.g. read only snapshot transactions).\n */\nexport class TransactionPool {\n #lc: LogContext;\n readonly #mode: Mode;\n readonly #init: TaskRunner | undefined;\n readonly #cleanup: TaskRunner | undefined;\n readonly #tasks = new Queue<TaskRunner | Error | 'done'>();\n readonly #workers: Promise<unknown>[] = [];\n readonly #initialWorkers: number;\n readonly #maxWorkers: number;\n readonly #timeoutTask: TimeoutTasks;\n #numWorkers: number;\n #numWorking = 0;\n #db: PostgresDB | undefined; // set when running. stored to allow adaptive pool sizing.\n\n #refCount = 1;\n #done = false;\n #failure: Error | undefined;\n\n /**\n * @param init A {@link Task} that is run in each Transaction before it begins\n * processing general tasks. This can be used to to set the transaction\n * mode, export/set snapshots, etc. This will be run even if\n * {@link fail} has been called on the pool.\n * @param cleanup A {@link Task} that is run in each Transaction before it closes.\n * This will be run even if {@link fail} has been called, or if a\n * preceding Task threw an Error.\n * @param initialWorkers The initial number of transaction workers to process tasks.\n * This is the steady state number of workers that will be kept\n * alive if the TransactionPool is long lived.\n * This must be greater than 0. Defaults to 1.\n * @param maxWorkers When specified, allows the pool to grow to `maxWorkers`. This\n * must be greater than or equal to `initialWorkers`. On-demand\n * workers will be shut down after an idle timeout of 5 seconds.\n */\n constructor(\n lc: LogContext,\n mode: Mode,\n init?: Task,\n cleanup?: Task,\n initialWorkers = 1,\n maxWorkers = initialWorkers,\n timeoutTasks = TIMEOUT_TASKS, // Overridden for tests.\n ) {\n assert(initialWorkers > 0, 'initialWorkers must be positive');\n assert(\n maxWorkers >= initialWorkers,\n 'maxWorkers must be >= initialWorkers',\n );\n\n this.#lc = lc;\n this.#mode = mode;\n this.#init = init ? this.#stmtRunner(init) : undefined;\n this.#cleanup = cleanup ? this.#stmtRunner(cleanup) : undefined;\n this.#initialWorkers = initialWorkers;\n this.#numWorkers = initialWorkers;\n this.#maxWorkers = maxWorkers;\n this.#timeoutTask = timeoutTasks;\n }\n\n /**\n * Starts the pool of workers to process Tasks with transactions opened from the\n * specified {@link db}.\n */\n run(db: PostgresDB): this {\n assert(!this.#db, 'already running');\n this.#db = db;\n for (let i = 0; i < this.#numWorkers; i++) {\n this.#addWorker(db);\n }\n return this;\n }\n\n /**\n * Adds context parameters to internal LogContext. This is useful for context values that\n * are not known when the TransactionPool is constructed (e.g. determined after a database\n * call when the pool is running).\n *\n * Returns an object that can be used to add more parameters.\n */\n addLoggingContext(key: string, value: string) {\n this.#lc = this.#lc.withContext(key, value);\n\n return {\n addLoggingContext: (key: string, value: string) =>\n this.addLoggingContext(key, value),\n };\n }\n\n /**\n * Returns a promise that:\n *\n * * resolves after {@link setDone} has been called (or the the pool as been {@link unref}ed\n * to a 0 ref count), once all added tasks have been processed and all transactions have been\n * committed or closed.\n *\n * * rejects if processing was aborted with {@link fail} or if processing any of\n * the tasks resulted in an error. All uncommitted transactions will have been\n * rolled back.\n *\n * Note that partial failures are possible if processing writes with multiple workers\n * (e.g. `setDone` is called, allowing some workers to commit, after which other\n * workers encounter errors). Using a TransactionPool in this manner does not make\n * sense in terms of transactional semantics, and is thus not recommended.\n *\n * For reads, however, multiple workers is useful for performing parallel reads\n * at the same snapshot. See {@link synchronizedSnapshots} for an example.\n * Resolves or rejects when all workers are done or failed.\n */\n async done() {\n const numWorkers = this.#workers.length;\n await Promise.all(this.#workers);\n\n if (numWorkers < this.#workers.length) {\n // If workers were added after the initial set, they must be awaited to ensure\n // that the results (i.e. rejections) of all workers are accounted for. This only\n // needs to be re-done once, because the fact that the first `await` completed\n // guarantees that the pool is in a terminal state and no new workers can be added.\n await Promise.all(this.#workers);\n }\n this.#lc.debug?.('transaction pool done');\n }\n\n #addWorker(db: PostgresDB) {\n const id = this.#workers.length + 1;\n const lc = this.#lc.withContext('tx', id);\n\n const tt: TimeoutTask =\n this.#workers.length < this.#initialWorkers\n ? this.#timeoutTask.forInitialWorkers\n : this.#timeoutTask.forExtraWorkers;\n const {timeoutMs} = tt;\n const timeoutTask = tt.task === 'done' ? 'done' : this.#stmtRunner(tt.task);\n\n const worker = async (tx: PostgresTransaction) => {\n const start = performance.now();\n try {\n lc.debug?.('started transaction');\n\n let last: Promise<void> = promiseVoid;\n\n const executeTask = async (runner: TaskRunner) => {\n runner !== this.#init && this.#numWorking++;\n const {pending} = await runner.run(tx, lc, () => {\n runner !== this.#init && this.#numWorking--;\n });\n last = pending ?? last;\n };\n\n let task: TaskRunner | Error | 'done' =\n this.#init ?? (await this.#tasks.dequeue(timeoutTask, timeoutMs));\n\n try {\n while (task !== 'done') {\n if (\n task instanceof Error ||\n (task !== this.#init && this.#failure)\n ) {\n throw this.#failure ?? task;\n }\n await executeTask(task);\n\n // await the next task.\n task = await this.#tasks.dequeue(timeoutTask, timeoutMs);\n }\n } finally {\n // Execute the cleanup task even on failure.\n if (this.#cleanup) {\n await executeTask(this.#cleanup);\n }\n }\n\n const elapsed = performance.now() - start;\n lc.debug?.(`closing transaction (${elapsed.toFixed(3)} ms)`);\n // Given the semantics of a Postgres transaction, the last statement\n // will only succeed if all of the preceding statements succeeded.\n return last;\n } catch (e) {\n if (e !== this.#failure) {\n this.fail(e); // A failure in any worker should fail the pool.\n }\n throw e;\n }\n };\n\n const workerTx = runTx(db, worker, {mode: this.#mode})\n .catch(e => {\n if (e instanceof RollbackSignal) {\n // A RollbackSignal is used to gracefully rollback the postgres.js\n // transaction block. It should not be thrown up to the application.\n lc.debug?.('aborted transaction');\n } else {\n throw e;\n }\n })\n .finally(() => this.#numWorkers--);\n\n // Attach a rejection handler immediately to prevent unhandledRejections.\n // The application will handle errors when it awaits processReadTask()\n // or done().\n workerTx.catch(() => {});\n\n this.#workers.push(workerTx);\n\n // After adding the worker, enqueue a terminal signal if we are in either of the\n // terminal states (both of which prevent more tasks from being enqueued), to ensure\n // that the added worker eventually exits.\n if (this.#done) {\n this.#tasks.enqueue('done');\n }\n if (this.#failure) {\n this.#tasks.enqueue(this.#failure);\n }\n }\n\n /**\n * Processes the statements produced by the specified {@link Task},\n * returning a Promise that resolves when the statements are either processed\n * by the database or rejected.\n *\n * Note that statement failures will result in failing the entire\n * TransactionPool (per transaction semantics). However, the returned Promise\n * itself will resolve rather than reject. As such, it is fine to ignore\n * returned Promises in order to pipeline requests to the database. It is\n * recommended to occasionally await them (e.g. after some threshold) in\n * order to avoid memory blowup in the case of database slowness.\n */\n process(task: Task): Promise<void> {\n const r = resolver<void>();\n this.#process(this.#stmtRunner(task, r));\n return r.promise;\n }\n\n readonly #start = performance.now();\n #stmts = 0;\n\n /**\n * Implements the semantics specified in {@link process()}.\n *\n * Specifically:\n * * `freeWorker()` is called as soon as the statements are produced,\n * allowing them to be pipelined to the database.\n * * Statement errors result in failing the transaction pool.\n * * The client-supplied Resolver resolves on success or failure;\n * it is never rejected.\n */\n #stmtRunner(task: Task, r: {resolve: () => void} = resolver()): TaskRunner {\n return {\n run: async (tx, lc, freeWorker) => {\n let stmts: Statement[];\n try {\n stmts = await task(tx, lc);\n } catch (e) {\n r.resolve();\n throw e;\n } finally {\n freeWorker();\n }\n\n if (stmts.length === 0) {\n r.resolve();\n return {pending: null};\n }\n\n // Execute the statements (i.e. send to the db) immediately.\n // The last result is returned for the worker to await before\n // closing the transaction.\n const last = stmts.reduce(\n (_, stmt) =>\n stmt\n .execute()\n .then(() => {\n if (++this.#stmts % 1000 === 0) {\n const log = this.#stmts % 10000 === 0 ? 'info' : 'debug';\n const q = stmt as unknown as Query;\n lc[log]?.(\n `executed ${this.#stmts}th statement (${(performance.now() - this.#start).toFixed(3)} ms)`,\n {statement: q.string},\n );\n }\n })\n .catch(e => this.fail(e)),\n promiseVoid,\n );\n return {pending: last.then(r.resolve)};\n },\n rejected: r.resolve,\n };\n }\n\n /**\n * Processes and returns the result of executing the {@link ReadTask} from\n * within the transaction. An error thrown by the task will result in\n * rejecting the returned Promise, but will not affect the transaction pool\n * itself.\n */\n processReadTask<T>(readTask: ReadTask<T>): Promise<T> {\n const r = resolver<T>();\n this.#process(this.#readRunner(readTask, r));\n return r.promise;\n }\n\n /**\n * Implements the semantics specified in {@link processReadTask()}.\n *\n * Specifically:\n * * `freeWorker()` is called as soon as the result is produced,\n * before resolving the client-supplied Resolver.\n * * Errors result in rejecting the client-supplied Resolver but\n * do not affect transaction pool.\n */\n #readRunner<T>(readTask: ReadTask<T>, r: Resolver<T>): TaskRunner {\n return {\n run: async (tx, lc, freeWorker) => {\n let result: T;\n try {\n result = await readTask(tx, lc);\n freeWorker();\n r.resolve(result);\n } catch (e) {\n freeWorker();\n r.reject(e);\n }\n return {pending: null};\n },\n rejected: r.reject,\n };\n }\n\n #process(runner: TaskRunner): void {\n assert(!this.#done, 'already set done');\n if (this.#failure) {\n runner.rejected(this.#failure);\n return;\n }\n\n this.#tasks.enqueue(runner);\n\n // Check if the pool size can and should be increased.\n if (this.#numWorkers < this.#maxWorkers) {\n const outstanding = this.#tasks.size();\n\n if (outstanding > this.#numWorkers - this.#numWorking) {\n this.#db && this.#addWorker(this.#db);\n this.#numWorkers++;\n this.#lc.debug?.(`Increased pool size to ${this.#numWorkers}`);\n }\n }\n }\n\n /**\n * Ends all workers with a ROLLBACK. Throws if the pool is already done\n * or aborted.\n */\n abort() {\n this.fail(new RollbackSignal());\n }\n\n /**\n * Signals to all workers to end their transaction once all pending tasks have\n * been completed. Throws if the pool is already done or aborted.\n */\n setDone() {\n assert(!this.#done, 'already set done');\n this.#done = true;\n\n for (let i = 0; i < this.#numWorkers; i++) {\n this.#tasks.enqueue('done');\n }\n }\n\n /**\n * An alternative to explicitly calling {@link setDone}, `ref()` increments an internal reference\n * count, and {@link unref} decrements it. When the reference count reaches 0, {@link setDone} is\n * automatically called. A TransactionPool is initialized with a reference count of 1.\n *\n * `ref()` should be called before sharing the pool with another component, and only after the\n * pool has been started with {@link run()}. It must not be called on a TransactionPool that is\n * already done (either via {@link unref()} or {@link setDone()}. (Doing so indicates a logical\n * error in the code.)\n *\n * It follows that:\n * * The creator of the TransactionPool is responsible for running it.\n * * The TransactionPool should be ref'ed before being sharing.\n * * The receiver of the TransactionPool is only responsible for unref'ing it.\n *\n * On the other hand, a transaction pool that fails with a runtime error can still be ref'ed;\n * attempts to use the pool will result in the runtime error as expected.\n */\n // TODO: Get rid of the ref-counting stuff. It's no longer needed.\n ref(count = 1) {\n assert(\n this.#db !== undefined && !this.#done,\n `Cannot ref() a TransactionPool that is not running`,\n );\n this.#refCount += count;\n }\n\n /**\n * Decrements the internal reference count, automatically invoking {@link setDone} when it reaches 0.\n */\n unref(count = 1) {\n assert(\n count <= this.#refCount,\n () => `Cannot unref ${count} when refCount is ${this.#refCount}`,\n );\n\n this.#refCount -= count;\n if (this.#refCount === 0) {\n this.setDone();\n }\n }\n\n isRunning(): boolean {\n return this.#db !== undefined && !this.#done && this.#failure === undefined;\n }\n\n /**\n * Signals all workers to fail their transactions with the given {@link err}.\n */\n fail(err: unknown) {\n if (!this.#failure) {\n this.#failure = ensureError(err); // Fail fast: this is checked in the worker loop.\n // Logged for informational purposes. It is the responsibility of\n // higher level logic to classify and handle the exception.\n const level =\n this.#failure instanceof ControlFlowError ? 'debug' : 'info';\n this.#lc[level]?.(this.#failure);\n\n for (let i = 0; i < this.#numWorkers; i++) {\n // Enqueue the Error to terminate any workers waiting for tasks.\n this.#tasks.enqueue(this.#failure);\n }\n }\n }\n}\n\ntype SynchronizeSnapshotTasks = {\n /**\n * The `init` Task for the TransactionPool from which the snapshot originates.\n * The pool must have Mode.SERIALIZABLE, and will be set to READ ONLY by the\n * `exportSnapshot` init task. If the TransactionPool has multiple workers, the\n * first worker will export a snapshot that the others set.\n */\n exportSnapshot: Task;\n\n /**\n * The `cleanup` Task for the TransactionPool from which the snapshot\n * originates. This Task will wait for the follower pool to `setSnapshot`\n * to ensure that the snapshot is successfully shared before the originating\n * transaction is closed.\n */\n cleanupExport: Task;\n\n /**\n * The `init` Task for the TransactionPool in which workers will\n * consequently see the same snapshot as that of the first pool. The pool\n * must have Mode.SERIALIZABLE, and will have the ability to perform writes.\n */\n setSnapshot: Task;\n\n /** The ID of the shared snapshot. */\n snapshotID: Promise<string>;\n};\n\n/**\n * Init Tasks for Postgres snapshot synchronization across transactions.\n *\n * https://www.postgresql.org/docs/9.3/functions-admin.html#:~:text=Snapshot%20Synchronization%20Functions,identical%20content%20in%20the%20database.\n */\nexport function synchronizedSnapshots(): SynchronizeSnapshotTasks {\n const {\n promise: snapshotExported,\n resolve: exportSnapshot,\n reject: failExport,\n } = resolver<string>();\n\n const {\n promise: snapshotCaptured,\n resolve: captureSnapshot,\n reject: failCapture,\n } = resolver<unknown>();\n\n // Set by the first worker to run its initTask, who becomes responsible for\n // exporting the snapshot. TODO: Plumb the workerNum and use that instead.\n let firstWorkerRun = false;\n\n // Note: Neither init task should `await`, as processing in each pool can proceed\n // as soon as the statements have been sent to the db. However, the `cleanupExport`\n // task must `await` the result of `setSnapshot` to ensure that exporting transaction\n // does not close before the snapshot has been captured.\n return {\n exportSnapshot: tx => {\n if (!firstWorkerRun) {\n firstWorkerRun = true;\n const stmt =\n tx`SELECT pg_export_snapshot() AS snapshot; SET TRANSACTION READ ONLY;`.simple();\n // Intercept the promise to propagate the information to `snapshotExported`.\n stmt.then(result => exportSnapshot(result[0].snapshot), failExport);\n return [stmt]; // Also return the stmt so that it gets awaited (and errors handled).\n }\n return snapshotExported.then(snapshotID => [\n tx.unsafe(`SET TRANSACTION SNAPSHOT '${snapshotID}'`),\n tx`SET TRANSACTION READ ONLY`.simple(),\n ]);\n },\n\n setSnapshot: tx =>\n snapshotExported.then(snapshotID => {\n const stmt = tx.unsafe(`SET TRANSACTION SNAPSHOT '${snapshotID}'`);\n // Intercept the promise to propagate the information to `cleanupExport`.\n stmt.then(captureSnapshot, failCapture);\n return [stmt];\n }),\n\n cleanupExport: async () => {\n await snapshotCaptured;\n return [];\n },\n\n snapshotID: snapshotExported,\n };\n}\n\n/**\n * Returns `init` and `cleanup` {@link Task}s for a TransactionPool that ensure its workers\n * share a single view of the database. This is used for View Notifier and View Syncer logic\n * that allows multiple entities to perform parallel reads on the same snapshot of the database.\n */\nexport function sharedSnapshot(): {\n init: Task;\n cleanup: Task;\n snapshotID: Promise<string>;\n} {\n const {\n promise: snapshotExported,\n resolve: exportSnapshot,\n reject: failExport,\n } = resolver<string>();\n\n // Set by the first worker to run its initTask, who becomes responsible for\n // exporting the snapshot.\n let firstWorkerRun = false;\n\n // Set when any worker is done, signalling that all non-sentinel Tasks have been\n // dequeued, and thus any subsequently spawned workers should skip their initTask\n // since the snapshot is no longer needed (and soon to become invalid).\n let firstWorkerDone = false;\n\n return {\n init: (tx, lc) => {\n if (!firstWorkerRun) {\n firstWorkerRun = true;\n const stmt = tx`SELECT pg_export_snapshot() AS snapshot;`.simple();\n // Intercept the promise to propagate the information to `snapshotExported`.\n stmt.then(result => exportSnapshot(result[0].snapshot), failExport);\n return [stmt]; // Also return the stmt so that it gets awaited (and errors handled).\n }\n if (!firstWorkerDone) {\n return snapshotExported.then(snapshotID => [\n tx.unsafe(`SET TRANSACTION SNAPSHOT '${snapshotID}'`),\n ]);\n }\n lc.debug?.('All work is done. No need to set snapshot');\n return [];\n },\n\n cleanup: () => {\n firstWorkerDone = true;\n return [];\n },\n\n snapshotID: snapshotExported,\n };\n}\n\n/**\n * @returns An `init` Task for importing a snapshot from another transaction.\n */\nexport function importSnapshot(snapshotID: string): {\n init: Task;\n imported: Promise<void>;\n} {\n const {promise: imported, resolve, reject} = resolver<void>();\n\n return {\n init: tx => {\n const stmt = tx.unsafe(`SET TRANSACTION SNAPSHOT '${snapshotID}'`);\n stmt.then(() => resolve(), reject);\n return [stmt];\n },\n\n imported,\n };\n}\n\n/**\n * A superclass of Errors used for control flow that is needed to handle\n * another Error but does not constitute an error condition itself (e.g.\n * aborting transactions after a previous one fails). Subclassing this Error\n * will result in lowering the log level from `error` to `debug`.\n */\nexport class ControlFlowError extends Error {\n constructor(cause?: unknown) {\n super();\n this.cause = cause;\n }\n}\n\n/**\n * Internal error used to rollback the worker transaction. This is used\n * instead of executing a `ROLLBACK` statement because the postgres.js\n * library will otherwise try to execute an extraneous `COMMIT`, which\n * results in outputting a \"no transaction in progress\" warning to the\n * database logs.\n *\n * Throwing an exception, on the other hand, executes the postgres.js\n * codepath that calls `ROLLBACK` instead.\n */\nclass RollbackSignal extends ControlFlowError {\n readonly name = 'RollbackSignal';\n readonly message = 'rolling back transaction';\n}\n\nfunction ensureError(err: unknown): Error {\n if (err instanceof Error) {\n return err;\n }\n const error = new Error();\n error.cause = err;\n return error;\n}\n\ninterface TaskRunner {\n /**\n * Manages the running of a Task or ReadTask in two phases:\n *\n * - If the task involves blocking, this is done in the worker. Once the\n * blocking is done, `freeWorker()` is invoked to signal that the worker\n * is available to run another task. Note that this should be invoked\n * *before* resolving the result to the calling thread so that a\n * subsequent task can reuse the same worker.\n *\n * - Task statements are executed on the database asynchronously. The final\n * result of this processing is encapsulated in the returned `pending`\n * Promise. The worker will await the last pending Promise before closing\n * the transaction.\n *\n * @param freeWorker should be called as soon as all blocking operations are\n * completed in order to return the transaction to the pool.\n * @returns A `pending` Promise indicating when the statements have been\n * processed by the database, allowing the transaction to be closed.\n * This should be `null` if there are no transaction-dependent\n * statements to await.\n */\n run(\n tx: PostgresTransaction,\n lc: LogContext,\n freeWorker: () => void,\n ): Promise<{pending: Promise<void> | null}>;\n\n /**\n * Invoked if the TransactionPool is already in a failed state when the task\n * is requested.\n */\n rejected(reason: unknown): void;\n}\n\nconst IDLE_TIMEOUT_MS = 5_000;\n\n// The keepalive interval is settable by ZERO_TRANSACTION_POOL_KEEPALIVE_MS\n// as an emergency measure and is explicitly not made available as a server\n// option. This value is function of how the zero-cache uses transactions, and\n// should never need to be \"tuned\" or adjusted for different environments.\n//\n// Note that it must be shorter than IDLE_IN_TRANSACTION_SESSION_TIMEOUT_MS\n// with sufficient buffering to account for when the process is blocked by\n// synchronous calls (e.g. to the replica).\nconst KEEPALIVE_TIMEOUT_MS = parseInt(\n process.env.ZERO_TRANSACTION_POOL_KEEPALIVE_MS ?? '5000',\n);\n\nconst KEEPALIVE_TASK: Task = (tx, lc) => {\n lc.debug?.(`sending tx keepalive`);\n return [tx`SELECT 1`.simple()];\n};\n\ntype TimeoutTask = {\n timeoutMs: number;\n task: Task | 'done';\n};\n\ntype TimeoutTasks = {\n forInitialWorkers: TimeoutTask;\n forExtraWorkers: TimeoutTask;\n};\n\n// Production timeout tasks. Overridden in tests.\nexport const TIMEOUT_TASKS: TimeoutTasks = {\n forInitialWorkers: {\n timeoutMs: KEEPALIVE_TIMEOUT_MS,\n task: KEEPALIVE_TASK,\n },\n forExtraWorkers: {\n timeoutMs: IDLE_TIMEOUT_MS,\n task: 'done',\n },\n};\n\n// The slice of information from the Query object in Postgres.js that gets logged for debugging.\n// https://github.com/porsager/postgres/blob/f58cd4f3affd3e8ce8f53e42799672d86cd2c70b/src/connection.js#L219\ntype Query = {string: string; parameters: object[]};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"transaction-pool.js","names":["#mode","#init","#cleanup","#tasks","#workers","#initialWorkers","#maxWorkers","#timeoutTask","#lc","#stmtRunner","#numWorkers","#db","#addWorker","#numWorking","#failure","#done","#process","#start","#stmts","#readRunner","#refCount"],"sources":["../../../../../zero-cache/src/db/transaction-pool.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {type Resolver, resolver} from '@rocicorp/resolver';\nimport type postgres from 'postgres';\nimport {assert} from '../../../shared/src/asserts.ts';\nimport type {Enum} from '../../../shared/src/enum.ts';\nimport {Queue} from '../../../shared/src/queue.ts';\nimport {promiseVoid} from '../../../shared/src/resolved-promises.ts';\nimport {type PostgresDB, type PostgresTransaction} from '../types/pg.ts';\nimport type * as Mode from './mode-enum.ts';\nimport {runTx} from './run-transaction.ts';\n\ntype Mode = Enum<typeof Mode>;\n\ntype MaybePromise<T> = Promise<T> | T;\n\nexport type Statement =\n | postgres.PendingQuery<(postgres.Row & Iterable<postgres.Row>)[]>\n | postgres.PendingQuery<postgres.Row[]>;\n\n/**\n * A {@link Task} is logic run from within a transaction in a {@link TransactionPool}.\n * It returns a list of `Statements` that the transaction executes asynchronously and\n * awaits when it receives the 'done' signal.\n *\n */\nexport type Task = (\n tx: PostgresTransaction,\n lc: LogContext,\n) => MaybePromise<Statement[]>;\n\n/**\n * A {@link ReadTask} is run from within a transaction, but unlike a {@link Task},\n * the results of a ReadTask are opaque to the TransactionPool and returned to the\n * caller of {@link TransactionPool.processReadTask}.\n */\nexport type ReadTask<T> = (\n tx: PostgresTransaction,\n lc: LogContext,\n) => MaybePromise<T>;\n\n/**\n * A TransactionPool is a pool of one or more {@link postgres.TransactionSql}\n * objects that participate in processing a dynamic queue of tasks.\n *\n * This can be used for serializing a set of tasks that arrive asynchronously\n * to a single transaction (for writing) or performing parallel reads across\n * multiple connections at the same snapshot (e.g. read only snapshot transactions).\n */\nexport class TransactionPool {\n #lc: LogContext;\n readonly #mode: Mode;\n readonly #init: TaskRunner | undefined;\n readonly #cleanup: TaskRunner | undefined;\n readonly #tasks = new Queue<TaskRunner | Error | 'done'>();\n readonly #workers: Promise<unknown>[] = [];\n readonly #initialWorkers: number;\n readonly #maxWorkers: number;\n readonly #timeoutTask: TimeoutTasks;\n #numWorkers: number;\n #numWorking = 0;\n #db: PostgresDB | undefined; // set when running. stored to allow adaptive pool sizing.\n\n #refCount = 1;\n #done = false;\n #failure: Error | undefined;\n\n /**\n * @param init A {@link Task} that is run in each Transaction before it begins\n * processing general tasks. This can be used to to set the transaction\n * mode, export/set snapshots, etc. This will be run even if\n * {@link fail} has been called on the pool.\n * @param cleanup A {@link Task} that is run in each Transaction before it closes.\n * This will be run even if {@link fail} has been called, or if a\n * preceding Task threw an Error.\n * @param initialWorkers The initial number of transaction workers to process tasks.\n * This is the steady state number of workers that will be kept\n * alive if the TransactionPool is long lived.\n * This must be greater than 0. Defaults to 1.\n * @param maxWorkers When specified, allows the pool to grow to `maxWorkers`. This\n * must be greater than or equal to `initialWorkers`. On-demand\n * workers will be shut down after an idle timeout of 5 seconds.\n */\n constructor(\n lc: LogContext,\n mode: Mode,\n init?: Task,\n cleanup?: Task,\n initialWorkers = 1,\n maxWorkers = initialWorkers,\n timeoutTasks = TIMEOUT_TASKS, // Overridden for tests.\n ) {\n assert(initialWorkers > 0, 'initialWorkers must be positive');\n assert(\n maxWorkers >= initialWorkers,\n 'maxWorkers must be >= initialWorkers',\n );\n\n this.#lc = lc;\n this.#mode = mode;\n this.#init = init ? this.#stmtRunner(init) : undefined;\n this.#cleanup = cleanup ? this.#stmtRunner(cleanup) : undefined;\n this.#initialWorkers = initialWorkers;\n this.#numWorkers = initialWorkers;\n this.#maxWorkers = maxWorkers;\n this.#timeoutTask = timeoutTasks;\n }\n\n /**\n * Starts the pool of workers to process Tasks with transactions opened from the\n * specified {@link db}.\n */\n run(db: PostgresDB): this {\n assert(!this.#db, 'already running');\n this.#db = db;\n for (let i = 0; i < this.#numWorkers; i++) {\n this.#addWorker(db);\n }\n return this;\n }\n\n /**\n * Adds context parameters to internal LogContext. This is useful for context values that\n * are not known when the TransactionPool is constructed (e.g. determined after a database\n * call when the pool is running).\n *\n * Returns an object that can be used to add more parameters.\n */\n addLoggingContext(key: string, value: string) {\n this.#lc = this.#lc.withContext(key, value);\n\n return {\n addLoggingContext: (key: string, value: string) =>\n this.addLoggingContext(key, value),\n };\n }\n\n /**\n * Returns a promise that:\n *\n * * resolves after {@link setDone} has been called (or the the pool as been {@link unref}ed\n * to a 0 ref count), once all added tasks have been processed and all transactions have been\n * committed or closed.\n *\n * * rejects if processing was aborted with {@link fail} or if processing any of\n * the tasks resulted in an error. All uncommitted transactions will have been\n * rolled back.\n *\n * Note that partial failures are possible if processing writes with multiple workers\n * (e.g. `setDone` is called, allowing some workers to commit, after which other\n * workers encounter errors). Using a TransactionPool in this manner does not make\n * sense in terms of transactional semantics, and is thus not recommended.\n *\n * For reads, however, multiple workers is useful for performing parallel reads\n * at the same snapshot. See {@link synchronizedSnapshots} for an example.\n * Resolves or rejects when all workers are done or failed.\n */\n async done() {\n const numWorkers = this.#workers.length;\n await Promise.all(this.#workers);\n\n if (numWorkers < this.#workers.length) {\n // If workers were added after the initial set, they must be awaited to ensure\n // that the results (i.e. rejections) of all workers are accounted for. This only\n // needs to be re-done once, because the fact that the first `await` completed\n // guarantees that the pool is in a terminal state and no new workers can be added.\n await Promise.all(this.#workers);\n }\n this.#lc.debug?.('transaction pool done');\n }\n\n #addWorker(db: PostgresDB) {\n const id = this.#workers.length + 1;\n const lc = this.#lc.withContext('tx', id);\n\n const tt: TimeoutTask =\n this.#workers.length < this.#initialWorkers\n ? this.#timeoutTask.forInitialWorkers\n : this.#timeoutTask.forExtraWorkers;\n const {timeoutMs} = tt;\n const timeoutTask = tt.task === 'done' ? 'done' : this.#stmtRunner(tt.task);\n\n const worker = async (tx: PostgresTransaction) => {\n const start = performance.now();\n try {\n lc.debug?.('started transaction');\n\n let last: Promise<void> = promiseVoid;\n\n const executeTask = async (runner: TaskRunner) => {\n runner !== this.#init && this.#numWorking++;\n const {pending} = await runner.run(tx, lc, () => {\n runner !== this.#init && this.#numWorking--;\n });\n last = pending ?? last;\n };\n\n let task: TaskRunner | Error | 'done' =\n this.#init ?? (await this.#tasks.dequeue(timeoutTask, timeoutMs));\n\n try {\n while (task !== 'done') {\n if (\n task instanceof Error ||\n (task !== this.#init && this.#failure)\n ) {\n throw this.#failure ?? task;\n }\n await executeTask(task);\n\n // await the next task.\n task = await this.#tasks.dequeue(timeoutTask, timeoutMs);\n }\n } finally {\n // Execute the cleanup task even on failure.\n if (this.#cleanup) {\n await executeTask(this.#cleanup);\n }\n }\n\n const elapsed = performance.now() - start;\n lc.debug?.(`closing transaction (${elapsed.toFixed(3)} ms)`);\n // Given the semantics of a Postgres transaction, the last statement\n // will only succeed if all of the preceding statements succeeded.\n return last;\n } catch (e) {\n if (e !== this.#failure) {\n this.fail(e); // A failure in any worker should fail the pool.\n }\n throw e;\n }\n };\n\n const workerTx = runTx(db, worker, {mode: this.#mode})\n .catch(e => {\n if (e instanceof RollbackSignal) {\n // A RollbackSignal is used to gracefully rollback the postgres.js\n // transaction block. It should not be thrown up to the application.\n lc.debug?.('aborted transaction');\n } else {\n throw e;\n }\n })\n .finally(() => this.#numWorkers--);\n\n // Attach a rejection handler immediately to prevent unhandledRejections.\n // The application will handle errors when it awaits processReadTask()\n // or done().\n workerTx.catch(() => {});\n\n this.#workers.push(workerTx);\n\n // After adding the worker, enqueue a terminal signal if we are in either of the\n // terminal states (both of which prevent more tasks from being enqueued), to ensure\n // that the added worker eventually exits.\n if (this.#done) {\n this.#tasks.enqueue('done');\n }\n if (this.#failure) {\n this.#tasks.enqueue(this.#failure);\n }\n }\n\n /**\n * Processes the statements produced by the specified {@link Task},\n * returning a Promise that resolves when the statements are either processed\n * by the database or rejected.\n *\n * Note that statement failures will result in failing the entire\n * TransactionPool (per transaction semantics). However, the returned Promise\n * itself will resolve rather than reject. As such, it is fine to ignore\n * returned Promises in order to pipeline requests to the database. It is\n * recommended to occasionally await them (e.g. after some threshold) in\n * order to avoid memory blowup in the case of database slowness.\n */\n process(task: Task): Promise<void> {\n const r = resolver<void>();\n this.#process(this.#stmtRunner(task, r));\n return r.promise;\n }\n\n readonly #start = performance.now();\n #stmts = 0;\n\n /**\n * Implements the semantics specified in {@link process()}.\n *\n * Specifically:\n * * `freeWorker()` is called as soon as the statements are produced,\n * allowing them to be pipelined to the database.\n * * Statement errors result in failing the transaction pool.\n * * The client-supplied Resolver resolves on success or failure;\n * it is never rejected.\n */\n #stmtRunner(task: Task, r: {resolve: () => void} = resolver()): TaskRunner {\n return {\n run: async (tx, lc, freeWorker) => {\n let stmts: Statement[];\n try {\n stmts = await task(tx, lc);\n } catch (e) {\n r.resolve();\n throw e;\n } finally {\n freeWorker();\n }\n\n if (stmts.length === 0) {\n r.resolve();\n return {pending: null};\n }\n\n // Execute the statements (i.e. send to the db) immediately.\n // The last result is returned for the worker to await before\n // closing the transaction.\n const last = stmts.reduce(\n (_, stmt) =>\n stmt\n .execute()\n .then(() => {\n if (++this.#stmts % 1000 === 0) {\n const log = this.#stmts % 10000 === 0 ? 'info' : 'debug';\n const q = stmt as unknown as Query;\n lc[log]?.(\n `executed ${this.#stmts}th statement (${(performance.now() - this.#start).toFixed(3)} ms)`,\n {statement: q.string},\n );\n }\n })\n .catch(e => this.fail(e)),\n promiseVoid,\n );\n return {pending: last.then(r.resolve)};\n },\n rejected: r.resolve,\n };\n }\n\n /**\n * Processes and returns the result of executing the {@link ReadTask} from\n * within the transaction. An error thrown by the task will result in\n * rejecting the returned Promise, but will not affect the transaction pool\n * itself.\n */\n processReadTask<T>(readTask: ReadTask<T>): Promise<T> {\n const r = resolver<T>();\n this.#process(this.#readRunner(readTask, r));\n return r.promise;\n }\n\n /**\n * Implements the semantics specified in {@link processReadTask()}.\n *\n * Specifically:\n * * `freeWorker()` is called as soon as the result is produced,\n * before resolving the client-supplied Resolver.\n * * Errors result in rejecting the client-supplied Resolver but\n * do not affect transaction pool.\n */\n #readRunner<T>(readTask: ReadTask<T>, r: Resolver<T>): TaskRunner {\n return {\n run: async (tx, lc, freeWorker) => {\n let result: T;\n try {\n result = await readTask(tx, lc);\n freeWorker();\n r.resolve(result);\n } catch (e) {\n freeWorker();\n r.reject(e);\n }\n return {pending: null};\n },\n rejected: r.reject,\n };\n }\n\n #process(runner: TaskRunner): void {\n assert(!this.#done, 'already set done');\n if (this.#failure) {\n runner.rejected(this.#failure);\n return;\n }\n\n this.#tasks.enqueue(runner);\n\n // Check if the pool size can and should be increased.\n if (this.#numWorkers < this.#maxWorkers) {\n const outstanding = this.#tasks.size();\n\n if (outstanding > this.#numWorkers - this.#numWorking) {\n this.#db && this.#addWorker(this.#db);\n this.#numWorkers++;\n this.#lc.debug?.(`Increased pool size to ${this.#numWorkers}`);\n }\n }\n }\n\n /**\n * Ends all workers with a ROLLBACK. Throws if the pool is already done\n * or aborted.\n */\n abort() {\n this.fail(new RollbackSignal());\n }\n\n /**\n * Signals to all workers to end their transaction once all pending tasks have\n * been completed. Throws if the pool is already done or aborted.\n */\n setDone() {\n assert(!this.#done, 'already set done');\n this.#done = true;\n\n for (let i = 0; i < this.#numWorkers; i++) {\n this.#tasks.enqueue('done');\n }\n }\n\n /**\n * An alternative to explicitly calling {@link setDone}, `ref()` increments an internal reference\n * count, and {@link unref} decrements it. When the reference count reaches 0, {@link setDone} is\n * automatically called. A TransactionPool is initialized with a reference count of 1.\n *\n * `ref()` should be called before sharing the pool with another component, and only after the\n * pool has been started with {@link run()}. It must not be called on a TransactionPool that is\n * already done (either via {@link unref()} or {@link setDone()}. (Doing so indicates a logical\n * error in the code.)\n *\n * It follows that:\n * * The creator of the TransactionPool is responsible for running it.\n * * The TransactionPool should be ref'ed before being sharing.\n * * The receiver of the TransactionPool is only responsible for unref'ing it.\n *\n * On the other hand, a transaction pool that fails with a runtime error can still be ref'ed;\n * attempts to use the pool will result in the runtime error as expected.\n */\n // TODO: Get rid of the ref-counting stuff. It's no longer needed.\n ref(count = 1) {\n assert(\n this.#db !== undefined && !this.#done,\n `Cannot ref() a TransactionPool that is not running`,\n );\n this.#refCount += count;\n }\n\n /**\n * Decrements the internal reference count, automatically invoking {@link setDone} when it reaches 0.\n */\n unref(count = 1) {\n assert(\n count <= this.#refCount,\n () => `Cannot unref ${count} when refCount is ${this.#refCount}`,\n );\n\n this.#refCount -= count;\n if (this.#refCount === 0) {\n this.setDone();\n }\n }\n\n isRunning(): boolean {\n return this.#db !== undefined && !this.#done && this.#failure === undefined;\n }\n\n /**\n * Signals all workers to fail their transactions with the given {@link err}.\n */\n fail(err: unknown) {\n if (!this.#failure) {\n this.#failure = ensureError(err); // Fail fast: this is checked in the worker loop.\n // Logged for informational purposes. It is the responsibility of\n // higher level logic to classify and handle the exception.\n const level =\n this.#failure instanceof ControlFlowError ? 'debug' : 'info';\n this.#lc[level]?.(this.#failure);\n\n for (let i = 0; i < this.#numWorkers; i++) {\n // Enqueue the Error to terminate any workers waiting for tasks.\n this.#tasks.enqueue(this.#failure);\n }\n }\n }\n}\n\ntype SynchronizeSnapshotTasks = {\n /**\n * The `init` Task for the TransactionPool from which the snapshot originates.\n * The pool must have Mode.SERIALIZABLE, and will be set to READ ONLY by the\n * `exportSnapshot` init task. If the TransactionPool has multiple workers, the\n * first worker will export a snapshot that the others set.\n */\n exportSnapshot: Task;\n\n /**\n * The `cleanup` Task for the TransactionPool from which the snapshot\n * originates. This Task will wait for the follower pool to `setSnapshot`\n * to ensure that the snapshot is successfully shared before the originating\n * transaction is closed.\n */\n cleanupExport: Task;\n\n /**\n * The `init` Task for the TransactionPool in which workers will\n * consequently see the same snapshot as that of the first pool. The pool\n * must have Mode.SERIALIZABLE, and will have the ability to perform writes.\n */\n setSnapshot: Task;\n\n /** The ID of the shared snapshot. */\n snapshotID: Promise<string>;\n};\n\n/**\n * Init Tasks for Postgres snapshot synchronization across transactions.\n *\n * https://www.postgresql.org/docs/9.3/functions-admin.html#:~:text=Snapshot%20Synchronization%20Functions,identical%20content%20in%20the%20database.\n */\nexport function synchronizedSnapshots(): SynchronizeSnapshotTasks {\n const {\n promise: snapshotExported,\n resolve: exportSnapshot,\n reject: failExport,\n } = resolver<string>();\n\n const {\n promise: snapshotCaptured,\n resolve: captureSnapshot,\n reject: failCapture,\n } = resolver<unknown>();\n\n // Set by the first worker to run its initTask, who becomes responsible for\n // exporting the snapshot. TODO: Plumb the workerNum and use that instead.\n let firstWorkerRun = false;\n\n // Note: Neither init task should `await`, as processing in each pool can proceed\n // as soon as the statements have been sent to the db. However, the `cleanupExport`\n // task must `await` the result of `setSnapshot` to ensure that exporting transaction\n // does not close before the snapshot has been captured.\n return {\n exportSnapshot: tx => {\n if (!firstWorkerRun) {\n firstWorkerRun = true;\n const stmt =\n tx`SELECT pg_export_snapshot() AS snapshot; SET TRANSACTION READ ONLY;`.simple();\n // Intercept the promise to propagate the information to `snapshotExported`.\n stmt.then(result => exportSnapshot(result[0].snapshot), failExport);\n return [stmt]; // Also return the stmt so that it gets awaited (and errors handled).\n }\n return snapshotExported.then(snapshotID => [\n tx.unsafe(`SET TRANSACTION SNAPSHOT '${snapshotID}'`),\n tx`SET TRANSACTION READ ONLY`.simple(),\n ]);\n },\n\n setSnapshot: tx =>\n snapshotExported.then(snapshotID => {\n const stmt = tx.unsafe(`SET TRANSACTION SNAPSHOT '${snapshotID}'`);\n // Intercept the promise to propagate the information to `cleanupExport`.\n stmt.then(captureSnapshot, failCapture);\n return [stmt];\n }),\n\n cleanupExport: async () => {\n await snapshotCaptured;\n return [];\n },\n\n snapshotID: snapshotExported,\n };\n}\n\n/**\n * Returns `init` and `cleanup` {@link Task}s for a TransactionPool that ensure its workers\n * share a single view of the database. This is used for View Notifier and View Syncer logic\n * that allows multiple entities to perform parallel reads on the same snapshot of the database.\n */\nexport function sharedSnapshot(): {\n init: Task;\n cleanup: Task;\n snapshotID: Promise<string>;\n} {\n const {\n promise: snapshotExported,\n resolve: exportSnapshot,\n reject: failExport,\n } = resolver<string>();\n\n // Set by the first worker to run its initTask, who becomes responsible for\n // exporting the snapshot.\n let firstWorkerRun = false;\n\n // The LogContext of the exporting worker, used to identify its cleanup call.\n // Each worker receives a unique lc instance (via withContext('tx', id)), so\n // reference equality reliably identifies the exporting worker.\n let exporterLc: LogContext | undefined;\n\n // Set when the exporting worker's cleanup runs, signalling that the snapshot\n // is no longer needed and any subsequently spawned workers should skip their\n // initTask.\n let firstWorkerDone = false;\n\n return {\n init: (tx, lc) => {\n if (!firstWorkerRun) {\n firstWorkerRun = true;\n exporterLc = lc; // Remember which worker is the exporter.\n const stmt = tx`SELECT pg_export_snapshot() AS snapshot;`.simple();\n // Intercept the promise to propagate the information to `snapshotExported`.\n stmt.then(result => exportSnapshot(result[0].snapshot), failExport);\n return [stmt]; // Also return the stmt so that it gets awaited (and errors handled).\n }\n if (!firstWorkerDone) {\n return snapshotExported.then(snapshotID => [\n tx.unsafe(`SET TRANSACTION SNAPSHOT '${snapshotID}'`),\n ]);\n }\n lc.debug?.('All work is done. No need to set snapshot');\n return [];\n },\n\n cleanup: (_tx, lc) => {\n // Only the exporting worker's cleanup should disable snapshot-setting.\n // Non-exporter workers may finish early; letting them flip this flag\n // would cause subsequently spawned workers to skip SET TRANSACTION SNAPSHOT\n // and read a newer database view, violating snapshot isolation.\n if (lc === exporterLc) {\n firstWorkerDone = true;\n }\n return [];\n },\n\n snapshotID: snapshotExported,\n };\n}\n\n/**\n * @returns An `init` Task for importing a snapshot from another transaction.\n */\nexport function importSnapshot(snapshotID: string): {\n init: Task;\n imported: Promise<void>;\n} {\n const {promise: imported, resolve, reject} = resolver<void>();\n\n return {\n init: tx => {\n const stmt = tx.unsafe(`SET TRANSACTION SNAPSHOT '${snapshotID}'`);\n stmt.then(() => resolve(), reject);\n return [stmt];\n },\n\n imported,\n };\n}\n\n/**\n * A superclass of Errors used for control flow that is needed to handle\n * another Error but does not constitute an error condition itself (e.g.\n * aborting transactions after a previous one fails). Subclassing this Error\n * will result in lowering the log level from `error` to `debug`.\n */\nexport class ControlFlowError extends Error {\n constructor(cause?: unknown) {\n super();\n this.cause = cause;\n }\n}\n\n/**\n * Internal error used to rollback the worker transaction. This is used\n * instead of executing a `ROLLBACK` statement because the postgres.js\n * library will otherwise try to execute an extraneous `COMMIT`, which\n * results in outputting a \"no transaction in progress\" warning to the\n * database logs.\n *\n * Throwing an exception, on the other hand, executes the postgres.js\n * codepath that calls `ROLLBACK` instead.\n */\nclass RollbackSignal extends ControlFlowError {\n readonly name = 'RollbackSignal';\n readonly message = 'rolling back transaction';\n}\n\nfunction ensureError(err: unknown): Error {\n if (err instanceof Error) {\n return err;\n }\n const error = new Error();\n error.cause = err;\n return error;\n}\n\ninterface TaskRunner {\n /**\n * Manages the running of a Task or ReadTask in two phases:\n *\n * - If the task involves blocking, this is done in the worker. Once the\n * blocking is done, `freeWorker()` is invoked to signal that the worker\n * is available to run another task. Note that this should be invoked\n * *before* resolving the result to the calling thread so that a\n * subsequent task can reuse the same worker.\n *\n * - Task statements are executed on the database asynchronously. The final\n * result of this processing is encapsulated in the returned `pending`\n * Promise. The worker will await the last pending Promise before closing\n * the transaction.\n *\n * @param freeWorker should be called as soon as all blocking operations are\n * completed in order to return the transaction to the pool.\n * @returns A `pending` Promise indicating when the statements have been\n * processed by the database, allowing the transaction to be closed.\n * This should be `null` if there are no transaction-dependent\n * statements to await.\n */\n run(\n tx: PostgresTransaction,\n lc: LogContext,\n freeWorker: () => void,\n ): Promise<{pending: Promise<void> | null}>;\n\n /**\n * Invoked if the TransactionPool is already in a failed state when the task\n * is requested.\n */\n rejected(reason: unknown): void;\n}\n\nconst IDLE_TIMEOUT_MS = 5_000;\n\n// The keepalive interval is settable by ZERO_TRANSACTION_POOL_KEEPALIVE_MS\n// as an emergency measure and is explicitly not made available as a server\n// option. This value is function of how the zero-cache uses transactions, and\n// should never need to be \"tuned\" or adjusted for different environments.\n//\n// Note that it must be shorter than IDLE_IN_TRANSACTION_SESSION_TIMEOUT_MS\n// with sufficient buffering to account for when the process is blocked by\n// synchronous calls (e.g. to the replica).\nconst KEEPALIVE_TIMEOUT_MS = parseInt(\n process.env.ZERO_TRANSACTION_POOL_KEEPALIVE_MS ?? '5000',\n);\n\nconst KEEPALIVE_TASK: Task = (tx, lc) => {\n lc.debug?.(`sending tx keepalive`);\n return [tx`SELECT 1`.simple()];\n};\n\ntype TimeoutTask = {\n timeoutMs: number;\n task: Task | 'done';\n};\n\ntype TimeoutTasks = {\n forInitialWorkers: TimeoutTask;\n forExtraWorkers: TimeoutTask;\n};\n\n// Production timeout tasks. Overridden in tests.\nexport const TIMEOUT_TASKS: TimeoutTasks = {\n forInitialWorkers: {\n timeoutMs: KEEPALIVE_TIMEOUT_MS,\n task: KEEPALIVE_TASK,\n },\n forExtraWorkers: {\n timeoutMs: IDLE_TIMEOUT_MS,\n task: 'done',\n },\n};\n\n// The slice of information from the Query object in Postgres.js that gets logged for debugging.\n// https://github.com/porsager/postgres/blob/f58cd4f3affd3e8ce8f53e42799672d86cd2c70b/src/connection.js#L219\ntype Query = {string: string; parameters: object[]};\n"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Counter, Histogram, MetricOptions, ObservableGauge, UpDownCounter } from '@opentelemetry/api';
|
|
2
|
-
export type Category = 'replication' | 'sync' | 'mutation' | 'server';
|
|
2
|
+
export type Category = 'replication' | 'replica' | 'sync' | 'mutation' | 'server';
|
|
3
3
|
type Options = MetricOptions & {
|
|
4
4
|
description: string;
|
|
5
5
|
};
|