@adobe/data 0.9.61 → 0.9.62
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ecs/archetype/archetype.d.ts +9 -2
- package/dist/ecs/archetype/archetype.js.map +1 -1
- package/dist/ecs/archetype/create-archetype.js +5 -2
- package/dist/ecs/archetype/create-archetype.js.map +1 -1
- package/dist/ecs/database/concurrency/concurrency-strategy.d.ts +86 -0
- package/dist/ecs/database/concurrency/concurrency-strategy.js +3 -0
- package/dist/ecs/database/concurrency/concurrency-strategy.js.map +1 -0
- package/dist/ecs/database/concurrency/immediate-concurrency.d.ts +22 -0
- package/dist/ecs/database/concurrency/immediate-concurrency.js +74 -0
- package/dist/ecs/database/concurrency/immediate-concurrency.js.map +1 -0
- package/dist/ecs/database/concurrency/index.d.ts +4 -0
- package/dist/ecs/database/concurrency/index.js +5 -0
- package/dist/ecs/database/concurrency/index.js.map +1 -0
- package/dist/ecs/database/concurrency/rebase-replay-concurrency.d.ts +20 -0
- package/dist/ecs/database/concurrency/rebase-replay-concurrency.js +34 -0
- package/dist/ecs/database/concurrency/rebase-replay-concurrency.js.map +1 -0
- package/dist/ecs/database/concurrency/roll-forward-concurrency.d.ts +33 -0
- package/dist/ecs/database/concurrency/roll-forward-concurrency.js +115 -0
- package/dist/ecs/database/concurrency/roll-forward-concurrency.js.map +1 -0
- package/dist/ecs/database/concurrency/roll-forward-concurrency.test.d.ts +1 -0
- package/dist/ecs/database/concurrency/roll-forward-concurrency.test.js +136 -0
- package/dist/ecs/database/concurrency/roll-forward-concurrency.test.js.map +1 -0
- package/dist/ecs/database/database.d.ts +29 -7
- package/dist/ecs/database/database.index.test.js +380 -0
- package/dist/ecs/database/database.index.test.js.map +1 -1
- package/dist/ecs/database/database.index.type-test.js.map +1 -1
- package/dist/ecs/database/database.js.map +1 -1
- package/dist/ecs/database/database.reset.test.js +2 -1
- package/dist/ecs/database/database.reset.test.js.map +1 -1
- package/dist/ecs/database/index.d.ts +1 -0
- package/dist/ecs/database/index.js +1 -0
- package/dist/ecs/database/index.js.map +1 -1
- package/dist/ecs/database/observe-index-entities.d.ts +26 -0
- package/dist/ecs/database/observe-index-entities.js +66 -0
- package/dist/ecs/database/observe-index-entities.js.map +1 -0
- package/dist/ecs/database/observed/create-observed-database.js +1 -1
- package/dist/ecs/database/observed/create-observed-database.js.map +1 -1
- package/dist/ecs/database/observed/observed-database.d.ts +1 -1
- package/dist/ecs/database/public/create-database.d.ts +12 -27
- package/dist/ecs/database/public/create-database.js +126 -30
- package/dist/ecs/database/public/create-database.js.map +1 -1
- package/dist/ecs/database/public/create-database.test.js +8 -7
- package/dist/ecs/database/public/create-database.test.js.map +1 -1
- package/dist/ecs/database/public/create-transaction-dispatcher.d.ts +4 -2
- package/dist/ecs/database/public/create-transaction-dispatcher.js +2 -8
- package/dist/ecs/database/public/create-transaction-dispatcher.js.map +1 -1
- package/dist/ecs/database/reconciling/create-rebase-replay-applier.d.ts +26 -0
- package/dist/ecs/database/reconciling/create-rebase-replay-applier.js +121 -0
- package/dist/ecs/database/reconciling/create-rebase-replay-applier.js.map +1 -0
- package/dist/ecs/database/reconciling/create-reconciling-database.d.ts +11 -0
- package/dist/ecs/database/reconciling/create-reconciling-database.js +28 -137
- package/dist/ecs/database/reconciling/create-reconciling-database.js.map +1 -1
- package/dist/ecs/database/transactional-store/create-transactional-store.js.map +1 -1
- package/dist/ecs/database/transactional-store/transactional-store.d.ts +1 -1
- package/dist/ecs/entity-location-table/create-entity-location-table.js +2 -2
- package/dist/ecs/entity-location-table/create-entity-location-table.js.map +1 -1
- package/dist/ecs/entity-location-table/entity-location-table.d.ts +7 -1
- package/dist/ecs/store/core/core.d.ts +7 -1
- package/dist/ecs/store/core/create-core.js +3 -3
- package/dist/ecs/store/core/create-core.js.map +1 -1
- package/dist/ecs/store/index-types.d.ts +10 -0
- package/dist/ecs/store/public/create-store.js +17 -1
- package/dist/ecs/store/public/create-store.js.map +1 -1
- package/dist/ecs/store/public/create-store.test.js +25 -0
- package/dist/ecs/store/public/create-store.test.js.map +1 -1
- package/dist/ecs/store/store.d.ts +8 -1
- package/dist/ecs/store/store.js.map +1 -1
- package/dist/test-setup.js +2 -2
- package/dist/test-setup.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -1
- package/references/data-lit/package.json +1 -1
- package/references/data-lit-tictactoe/package.json +1 -1
- package/references/data-react/package.json +1 -1
- package/references/data-react-hello/package.json +1 -1
- package/references/data-react-pixie/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"roll-forward-concurrency.test.js","sourceRoot":"","sources":["../../../../src/ecs/database/concurrency/roll-forward-concurrency.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -13,7 +13,8 @@ import { RequiredComponents } from "../required-components.js";
|
|
|
13
13
|
import { EntitySelectOptions } from "../store/entity-select-options.js";
|
|
14
14
|
import { Index as StoreIndex } from "../store/index-types.js";
|
|
15
15
|
import type { Service } from "../../service/index.js";
|
|
16
|
-
import { createDatabase
|
|
16
|
+
import { createDatabase } from "./public/create-database.js";
|
|
17
|
+
import type { ConcurrencyStrategy } from "./concurrency/concurrency-strategy.js";
|
|
17
18
|
import { ResourceSchemas } from "../resource-schemas.js";
|
|
18
19
|
import { ComponentSchemas } from "../component-schemas.js";
|
|
19
20
|
import { FromSchemas } from "../../schema/index.js";
|
|
@@ -160,13 +161,12 @@ export interface Database<C extends Components = {}, R extends ResourceComponent
|
|
|
160
161
|
*/
|
|
161
162
|
readonly cancel: (id: number, userId?: number | string) => void;
|
|
162
163
|
/**
|
|
163
|
-
* The
|
|
164
|
-
*
|
|
165
|
-
*
|
|
166
|
-
*
|
|
167
|
-
* separately.
|
|
164
|
+
* The concurrency strategy the database was created with. Sync services
|
|
165
|
+
* read `concurrency.userId` to recover the peer identifier and check
|
|
166
|
+
* `concurrency.deferredCommit` to confirm the database is in the
|
|
167
|
+
* appropriate mode for multi-peer operation.
|
|
168
168
|
*/
|
|
169
|
-
readonly
|
|
169
|
+
readonly concurrency: ConcurrencyStrategy;
|
|
170
170
|
readonly system: {
|
|
171
171
|
/** System create() return value, or null when create() returns void. Key is always present. */
|
|
172
172
|
readonly functions: {
|
|
@@ -175,6 +175,28 @@ export interface Database<C extends Components = {}, R extends ResourceComponent
|
|
|
175
175
|
/** Tier order for execution. Looser type allows extended dbs to be assignable to base. */
|
|
176
176
|
readonly order: ReadonlyArray<ReadonlyArray<string>>;
|
|
177
177
|
};
|
|
178
|
+
/**
|
|
179
|
+
* Serialize the database to a plain data snapshot.
|
|
180
|
+
*
|
|
181
|
+
* For a concurrency strategy that replays transients after serialization
|
|
182
|
+
* (`onAfterToData` — i.e. `createRebaseReplayConcurrency` /
|
|
183
|
+
* `createRollForwardConcurrency` / the legacy `createReconcilingDatabase`),
|
|
184
|
+
* this returns a snapshot **detached** from the live store: it rolls the
|
|
185
|
+
* transients back, serializes a copy of the committed state (`store.toData`
|
|
186
|
+
* with `copy: true` — columns and the entity table are cloned), then replays.
|
|
187
|
+
* So a database persisted with optimistic edits in flight contains only
|
|
188
|
+
* committed state, and the snapshot survives the subsequent replay.
|
|
189
|
+
*
|
|
190
|
+
* For strategies with no replay hook (the default `createImmediateConcurrency`),
|
|
191
|
+
* the faster live-reference snapshot is returned — it shares the store's
|
|
192
|
+
* buffers and is only valid until the next mutation, so callers must serialize
|
|
193
|
+
* it before mutating the database again.
|
|
194
|
+
*
|
|
195
|
+
* Historical note: the detach above fixes a bug where the live-reference
|
|
196
|
+
* snapshot was corrupted by the post-serialization replay, silently leaking
|
|
197
|
+
* in-flight transients into persisted state. The old reconciler tests missed
|
|
198
|
+
* it because they only asserted the snapshot was truthy, never round-tripped it.
|
|
199
|
+
*/
|
|
178
200
|
toData(): unknown;
|
|
179
201
|
fromData(data: unknown): void;
|
|
180
202
|
extend<P extends Database.Plugin>(plugin: P): Database<C & FromSchemas<RemoveIndex<P['components']>>, R & FromSchemas<RemoveIndex<P['resources']>>, A & RemoveIndex<P['archetypes']>, F & ToTransactionFunctions<RemoveIndex<P['transactions']>>, S | StringKeyof<P['systems']>, AF & ToActionFunctions<RemoveIndex<P['actions']>>, SV & FromServiceFactories<RemoveIndex<P['services']>>, CV & FromComputedFactories<RemoveIndex<P['computed']>>, IX & RemoveIndex<P['indexes']>>;
|
|
@@ -174,6 +174,257 @@ describe("Pattern 4 — sorted children (orderedChildrenOf)", () => {
|
|
|
174
174
|
expect(ext.indexes.orderedChildrenOf.find(5)).toEqual([a, b, c]);
|
|
175
175
|
});
|
|
176
176
|
});
|
|
177
|
+
describe("Pattern 4 — observe(arg): reactive sorted bucket view", () => {
|
|
178
|
+
const plugin = () => Database.Plugin.create({
|
|
179
|
+
components: {
|
|
180
|
+
parent: { type: "number" },
|
|
181
|
+
fractIndex: { type: "string" },
|
|
182
|
+
},
|
|
183
|
+
archetypes: { Child: ["parent", "fractIndex"] },
|
|
184
|
+
indexes: {
|
|
185
|
+
orderedChildrenOf: {
|
|
186
|
+
key: "parent",
|
|
187
|
+
order: { by: ["fractIndex"] },
|
|
188
|
+
},
|
|
189
|
+
},
|
|
190
|
+
transactions: {
|
|
191
|
+
add: (t, args) => t.archetypes.Child.insert(args),
|
|
192
|
+
move: (t, args) => t.update(args.entity, { fractIndex: args.fractIndex }),
|
|
193
|
+
reparent: (t, args) => t.update(args.entity, { parent: args.parent }),
|
|
194
|
+
delete: (t, e) => t.delete(e),
|
|
195
|
+
},
|
|
196
|
+
});
|
|
197
|
+
it("emits the initial sorted list synchronously on subscribe", () => {
|
|
198
|
+
const db = Database.create(plugin());
|
|
199
|
+
const c = db.transactions.add({ parent: 7, fractIndex: "c" });
|
|
200
|
+
const a = db.transactions.add({ parent: 7, fractIndex: "a" });
|
|
201
|
+
const b = db.transactions.add({ parent: 7, fractIndex: "b" });
|
|
202
|
+
const emissions = [];
|
|
203
|
+
const unsub = db.indexes.orderedChildrenOf.observe(7)((entities) => { emissions.push([...entities]); });
|
|
204
|
+
expect(emissions).toEqual([[a, b, c]]);
|
|
205
|
+
unsub();
|
|
206
|
+
});
|
|
207
|
+
it("emits the re-sorted list when an entity is inserted into the bucket", async () => {
|
|
208
|
+
const db = Database.create(plugin());
|
|
209
|
+
const a = db.transactions.add({ parent: 7, fractIndex: "a" });
|
|
210
|
+
const c = db.transactions.add({ parent: 7, fractIndex: "c" });
|
|
211
|
+
const emissions = [];
|
|
212
|
+
const unsub = db.indexes.orderedChildrenOf.observe(7)((entities) => { emissions.push([...entities]); });
|
|
213
|
+
const b = db.transactions.add({ parent: 7, fractIndex: "b" });
|
|
214
|
+
await Promise.resolve();
|
|
215
|
+
expect(emissions[0]).toEqual([a, c]);
|
|
216
|
+
expect(emissions[emissions.length - 1]).toEqual([a, b, c]);
|
|
217
|
+
unsub();
|
|
218
|
+
});
|
|
219
|
+
it("emits a reorder when only the sort key changes (regression case)", async () => {
|
|
220
|
+
const db = Database.create(plugin());
|
|
221
|
+
const a = db.transactions.add({ parent: 7, fractIndex: "a" });
|
|
222
|
+
const b = db.transactions.add({ parent: 7, fractIndex: "b" });
|
|
223
|
+
const c = db.transactions.add({ parent: 7, fractIndex: "c" });
|
|
224
|
+
const emissions = [];
|
|
225
|
+
const unsub = db.indexes.orderedChildrenOf.observe(7)((entities) => { emissions.push([...entities]); });
|
|
226
|
+
expect(emissions[0]).toEqual([a, b, c]);
|
|
227
|
+
// Reorder-only transaction: parent (the bucket key) is untouched, so a
|
|
228
|
+
// `where`-only observe.select would never see this. The index does.
|
|
229
|
+
db.transactions.move({ entity: b, fractIndex: "z" });
|
|
230
|
+
await Promise.resolve();
|
|
231
|
+
expect(emissions[emissions.length - 1]).toEqual([a, c, b]);
|
|
232
|
+
unsub();
|
|
233
|
+
});
|
|
234
|
+
it("emits the shrunken list when an entity is deleted from the bucket", async () => {
|
|
235
|
+
const db = Database.create(plugin());
|
|
236
|
+
const a = db.transactions.add({ parent: 7, fractIndex: "a" });
|
|
237
|
+
const b = db.transactions.add({ parent: 7, fractIndex: "b" });
|
|
238
|
+
const emissions = [];
|
|
239
|
+
const unsub = db.indexes.orderedChildrenOf.observe(7)((entities) => { emissions.push([...entities]); });
|
|
240
|
+
db.transactions.delete(b);
|
|
241
|
+
await Promise.resolve();
|
|
242
|
+
expect(emissions[emissions.length - 1]).toEqual([a]);
|
|
243
|
+
unsub();
|
|
244
|
+
});
|
|
245
|
+
it("does NOT emit when an unrelated bucket's entity changes", async () => {
|
|
246
|
+
const db = Database.create(plugin());
|
|
247
|
+
db.transactions.add({ parent: 7, fractIndex: "a" });
|
|
248
|
+
const other = db.transactions.add({ parent: 9, fractIndex: "a" });
|
|
249
|
+
const emissions = [];
|
|
250
|
+
const unsub = db.indexes.orderedChildrenOf.observe(7)((entities) => { emissions.push([...entities]); });
|
|
251
|
+
expect(emissions).toHaveLength(1);
|
|
252
|
+
// A child of a *different* parent moves. Same component (`fractIndex`)
|
|
253
|
+
// changes, so the observer is woken and recomputes — but its own
|
|
254
|
+
// bucket is unchanged, so no second emission.
|
|
255
|
+
db.transactions.move({ entity: other, fractIndex: "z" });
|
|
256
|
+
await Promise.resolve();
|
|
257
|
+
expect(emissions).toHaveLength(1);
|
|
258
|
+
unsub();
|
|
259
|
+
});
|
|
260
|
+
it("emits on both buckets when an entity reparents between them", async () => {
|
|
261
|
+
const db = Database.create(plugin());
|
|
262
|
+
const a = db.transactions.add({ parent: 7, fractIndex: "a" });
|
|
263
|
+
const m = db.transactions.add({ parent: 7, fractIndex: "b" });
|
|
264
|
+
const fromSeven = [];
|
|
265
|
+
const toNine = [];
|
|
266
|
+
const unsub7 = db.indexes.orderedChildrenOf.observe(7)((e) => fromSeven.push([...e]));
|
|
267
|
+
const unsub9 = db.indexes.orderedChildrenOf.observe(9)((e) => toNine.push([...e]));
|
|
268
|
+
db.transactions.reparent({ entity: m, parent: 9 });
|
|
269
|
+
await Promise.resolve();
|
|
270
|
+
expect(fromSeven[fromSeven.length - 1]).toEqual([a]);
|
|
271
|
+
expect(toNine[toNine.length - 1]).toEqual([m]);
|
|
272
|
+
unsub7();
|
|
273
|
+
unsub9();
|
|
274
|
+
});
|
|
275
|
+
it("unsubscribe stops further emissions", async () => {
|
|
276
|
+
const db = Database.create(plugin());
|
|
277
|
+
db.transactions.add({ parent: 7, fractIndex: "a" });
|
|
278
|
+
const emissions = [];
|
|
279
|
+
const unsub = db.indexes.orderedChildrenOf.observe(7)((entities) => { emissions.push([...entities]); });
|
|
280
|
+
expect(emissions).toHaveLength(1);
|
|
281
|
+
unsub();
|
|
282
|
+
db.transactions.add({ parent: 7, fractIndex: "b" });
|
|
283
|
+
await Promise.resolve();
|
|
284
|
+
expect(emissions).toHaveLength(1);
|
|
285
|
+
});
|
|
286
|
+
});
|
|
287
|
+
describe("observe(arg) — additional edge cases", () => {
|
|
288
|
+
const sortedPlugin = () => Database.Plugin.create({
|
|
289
|
+
components: {
|
|
290
|
+
parent: { type: "number" },
|
|
291
|
+
fractIndex: { type: "string" },
|
|
292
|
+
},
|
|
293
|
+
archetypes: { Child: ["parent", "fractIndex"] },
|
|
294
|
+
indexes: {
|
|
295
|
+
orderedChildrenOf: { key: "parent", order: { by: ["fractIndex"] } },
|
|
296
|
+
childrenOf: { key: "parent" },
|
|
297
|
+
},
|
|
298
|
+
transactions: {
|
|
299
|
+
add: (t, args) => t.archetypes.Child.insert(args),
|
|
300
|
+
move: (t, args) => t.update(args.entity, { fractIndex: args.fractIndex }),
|
|
301
|
+
delete: (t, e) => t.delete(e),
|
|
302
|
+
},
|
|
303
|
+
});
|
|
304
|
+
it("emits [] for an initially-empty bucket, then the entity once populated", async () => {
|
|
305
|
+
const db = Database.create(sortedPlugin());
|
|
306
|
+
const emissions = [];
|
|
307
|
+
const unsub = db.indexes.orderedChildrenOf.observe(42)((e) => emissions.push([...e]));
|
|
308
|
+
expect(emissions).toEqual([[]]);
|
|
309
|
+
const e = db.transactions.add({ parent: 42, fractIndex: "a" });
|
|
310
|
+
await Promise.resolve();
|
|
311
|
+
expect(emissions[emissions.length - 1]).toEqual([e]);
|
|
312
|
+
unsub();
|
|
313
|
+
});
|
|
314
|
+
it("emits [] when the last entity leaves the bucket", async () => {
|
|
315
|
+
const db = Database.create(sortedPlugin());
|
|
316
|
+
const only = db.transactions.add({ parent: 7, fractIndex: "a" });
|
|
317
|
+
const emissions = [];
|
|
318
|
+
const unsub = db.indexes.orderedChildrenOf.observe(7)((e) => emissions.push([...e]));
|
|
319
|
+
db.transactions.delete(only);
|
|
320
|
+
await Promise.resolve();
|
|
321
|
+
expect(emissions[emissions.length - 1]).toEqual([]);
|
|
322
|
+
unsub();
|
|
323
|
+
});
|
|
324
|
+
it("notifies every subscriber of the same bucket", async () => {
|
|
325
|
+
const db = Database.create(sortedPlugin());
|
|
326
|
+
const a = db.transactions.add({ parent: 7, fractIndex: "a" });
|
|
327
|
+
const first = [];
|
|
328
|
+
const second = [];
|
|
329
|
+
const unsub1 = db.indexes.orderedChildrenOf.observe(7)((e) => first.push([...e]));
|
|
330
|
+
const unsub2 = db.indexes.orderedChildrenOf.observe(7)((e) => second.push([...e]));
|
|
331
|
+
const b = db.transactions.add({ parent: 7, fractIndex: "b" });
|
|
332
|
+
await Promise.resolve();
|
|
333
|
+
expect(first[first.length - 1]).toEqual([a, b]);
|
|
334
|
+
expect(second[second.length - 1]).toEqual([a, b]);
|
|
335
|
+
unsub1();
|
|
336
|
+
unsub2();
|
|
337
|
+
});
|
|
338
|
+
it("coalesces several transactions before the microtask into one emission", async () => {
|
|
339
|
+
const db = Database.create(sortedPlugin());
|
|
340
|
+
const a = db.transactions.add({ parent: 7, fractIndex: "a" });
|
|
341
|
+
const emissions = [];
|
|
342
|
+
const unsub = db.indexes.orderedChildrenOf.observe(7)((e) => emissions.push([...e]));
|
|
343
|
+
// Three synchronous transactions, no awaits in between.
|
|
344
|
+
const c = db.transactions.add({ parent: 7, fractIndex: "c" });
|
|
345
|
+
const b = db.transactions.add({ parent: 7, fractIndex: "b" });
|
|
346
|
+
db.transactions.move({ entity: c, fractIndex: "d" });
|
|
347
|
+
await Promise.resolve();
|
|
348
|
+
// Initial emit + exactly one coalesced emit reflecting the final state.
|
|
349
|
+
expect(emissions).toHaveLength(2);
|
|
350
|
+
expect(emissions[1]).toEqual([a, b, c]);
|
|
351
|
+
unsub();
|
|
352
|
+
});
|
|
353
|
+
it("does not re-emit when a touched bucket ends a transaction unchanged", async () => {
|
|
354
|
+
const db = Database.create(sortedPlugin());
|
|
355
|
+
const a = db.transactions.add({ parent: 7, fractIndex: "a" });
|
|
356
|
+
const emissions = [];
|
|
357
|
+
const unsub = db.indexes.orderedChildrenOf.observe(7)((e) => emissions.push([...e]));
|
|
358
|
+
// Move the only child to a new sort key and back within the same
|
|
359
|
+
// microtask window: the bucket is woken but its final sequence is
|
|
360
|
+
// identical, so no second emission.
|
|
361
|
+
db.transactions.move({ entity: a, fractIndex: "z" });
|
|
362
|
+
db.transactions.move({ entity: a, fractIndex: "a" });
|
|
363
|
+
await Promise.resolve();
|
|
364
|
+
expect(emissions).toHaveLength(1);
|
|
365
|
+
unsub();
|
|
366
|
+
});
|
|
367
|
+
it("works on a non-sorted index — membership changes still emit", async () => {
|
|
368
|
+
const db = Database.create(sortedPlugin());
|
|
369
|
+
const a = db.transactions.add({ parent: 7, fractIndex: "a" });
|
|
370
|
+
const emissions = [];
|
|
371
|
+
const unsub = db.indexes.childrenOf.observe(7)((e) => emissions.push([...e].sort()));
|
|
372
|
+
expect(emissions[0]).toEqual([a]);
|
|
373
|
+
const b = db.transactions.add({ parent: 7, fractIndex: "b" });
|
|
374
|
+
await Promise.resolve();
|
|
375
|
+
expect(emissions[emissions.length - 1]).toEqual([a, b].sort());
|
|
376
|
+
unsub();
|
|
377
|
+
});
|
|
378
|
+
const priorityPlugin = () => Database.Plugin.create({
|
|
379
|
+
components: {
|
|
380
|
+
owner: { type: "number" },
|
|
381
|
+
priority: { type: "number" },
|
|
382
|
+
due: { type: "number" },
|
|
383
|
+
},
|
|
384
|
+
archetypes: { Task: ["owner", "priority", "due"] },
|
|
385
|
+
indexes: {
|
|
386
|
+
tasksByPriority: {
|
|
387
|
+
key: "owner",
|
|
388
|
+
order: {
|
|
389
|
+
by: ["priority", "due"],
|
|
390
|
+
compare: (a, b) => b.priority - a.priority || a.due - b.due,
|
|
391
|
+
},
|
|
392
|
+
},
|
|
393
|
+
},
|
|
394
|
+
transactions: {
|
|
395
|
+
add: (t, args) => t.archetypes.Task.insert(args),
|
|
396
|
+
setPriority: (t, args) => t.update(args.entity, { priority: args.priority }),
|
|
397
|
+
setDue: (t, args) => t.update(args.entity, { due: args.due }),
|
|
398
|
+
},
|
|
399
|
+
});
|
|
400
|
+
it("re-sorts under a custom comparator when the primary sort key changes", async () => {
|
|
401
|
+
const db = Database.create(priorityPlugin());
|
|
402
|
+
const low = db.transactions.add({ owner: 1, priority: 1, due: 100 });
|
|
403
|
+
const high = db.transactions.add({ owner: 1, priority: 3, due: 50 });
|
|
404
|
+
const emissions = [];
|
|
405
|
+
const unsub = db.indexes.tasksByPriority.observe(1)((e) => emissions.push([...e]));
|
|
406
|
+
// priority desc → [high, low].
|
|
407
|
+
expect(emissions[0]).toEqual([high, low]);
|
|
408
|
+
db.transactions.setPriority({ entity: low, priority: 5 });
|
|
409
|
+
await Promise.resolve();
|
|
410
|
+
expect(emissions[emissions.length - 1]).toEqual([low, high]);
|
|
411
|
+
unsub();
|
|
412
|
+
});
|
|
413
|
+
it("re-sorts when only the secondary (tie-break) sort component changes", async () => {
|
|
414
|
+
const db = Database.create(priorityPlugin());
|
|
415
|
+
// Equal priority → ordered by `due` asc.
|
|
416
|
+
const early = db.transactions.add({ owner: 1, priority: 2, due: 10 });
|
|
417
|
+
const late = db.transactions.add({ owner: 1, priority: 2, due: 20 });
|
|
418
|
+
const emissions = [];
|
|
419
|
+
const unsub = db.indexes.tasksByPriority.observe(1)((e) => emissions.push([...e]));
|
|
420
|
+
expect(emissions[0]).toEqual([early, late]);
|
|
421
|
+
// Push `early`'s due past `late`'s — flips the tie-break order.
|
|
422
|
+
db.transactions.setDue({ entity: early, due: 30 });
|
|
423
|
+
await Promise.resolve();
|
|
424
|
+
expect(emissions[emissions.length - 1]).toEqual([late, early]);
|
|
425
|
+
unsub();
|
|
426
|
+
});
|
|
427
|
+
});
|
|
177
428
|
describe("Pattern 5 — multi-value (array column → fan-out): tasksByAssignee", () => {
|
|
178
429
|
const plugin = () => Database.Plugin.create({
|
|
179
430
|
components: {
|
|
@@ -569,6 +820,135 @@ describe("auto-routing of db.observe.select", () => {
|
|
|
569
820
|
}
|
|
570
821
|
});
|
|
571
822
|
});
|
|
823
|
+
describe("auto-routing of ordered select through a sorted index", () => {
|
|
824
|
+
// Two indexes on the same key, distinct shapes: one unsorted, one sorted
|
|
825
|
+
// ascending by `priority` (default comparator). An ordered query must route
|
|
826
|
+
// to the *sorted* one (never the unsorted one), and a query whose order the
|
|
827
|
+
// index can't serve must fall back to the scan.
|
|
828
|
+
const plugin = () => Database.Plugin.create({
|
|
829
|
+
components: {
|
|
830
|
+
owner: { type: "number" },
|
|
831
|
+
priority: { type: "number" },
|
|
832
|
+
due: { type: "number" },
|
|
833
|
+
title: { type: "string" },
|
|
834
|
+
},
|
|
835
|
+
archetypes: { Task: ["owner", "priority", "due", "title"] },
|
|
836
|
+
indexes: {
|
|
837
|
+
byOwner: { key: "owner" },
|
|
838
|
+
byOwnerSorted: { key: "owner", order: { by: ["priority"] } },
|
|
839
|
+
},
|
|
840
|
+
transactions: {
|
|
841
|
+
add: (t, a) => t.archetypes.Task.insert(a),
|
|
842
|
+
},
|
|
843
|
+
});
|
|
844
|
+
const spyFind = (handle) => {
|
|
845
|
+
const original = handle.find;
|
|
846
|
+
const state = { calls: 0 };
|
|
847
|
+
handle.find = (v) => { state.calls += 1; return original(v); };
|
|
848
|
+
return { state, restore: () => { handle.find = original; } };
|
|
849
|
+
};
|
|
850
|
+
const seed = (db) => {
|
|
851
|
+
const mid = db.transactions.add({ owner: 1, priority: 2, due: 10, title: "mid" });
|
|
852
|
+
const low = db.transactions.add({ owner: 1, priority: 1, due: 20, title: "low" });
|
|
853
|
+
const high = db.transactions.add({ owner: 1, priority: 3, due: 30, title: "high" });
|
|
854
|
+
db.transactions.add({ owner: 2, priority: 1, due: 40, title: "other" });
|
|
855
|
+
return { low, mid, high };
|
|
856
|
+
};
|
|
857
|
+
it("routes an ascending ordered query to the sorted index (and returns it sorted)", () => {
|
|
858
|
+
const db = Database.create(plugin());
|
|
859
|
+
const { low, mid, high } = seed(db);
|
|
860
|
+
const sorted = spyFind(db.indexes.byOwnerSorted);
|
|
861
|
+
try {
|
|
862
|
+
const result = db.select(["owner", "priority"], { where: { owner: 1 }, order: { priority: true } });
|
|
863
|
+
// GREEN: the ordered query was served by the sorted index...
|
|
864
|
+
expect(sorted.state.calls).toBe(1);
|
|
865
|
+
// ...and the result is in the index's ascending priority order.
|
|
866
|
+
expect([...result]).toEqual([low, mid, high]);
|
|
867
|
+
}
|
|
868
|
+
finally {
|
|
869
|
+
sorted.restore();
|
|
870
|
+
}
|
|
871
|
+
});
|
|
872
|
+
it("routes the ordered query to the sorted index, never the unsorted one on the same key", () => {
|
|
873
|
+
const db = Database.create(plugin());
|
|
874
|
+
seed(db);
|
|
875
|
+
const unsorted = spyFind(db.indexes.byOwner);
|
|
876
|
+
const sorted = spyFind(db.indexes.byOwnerSorted);
|
|
877
|
+
try {
|
|
878
|
+
db.select(["owner", "priority"], { where: { owner: 1 }, order: { priority: true } });
|
|
879
|
+
expect(sorted.state.calls).toBe(1);
|
|
880
|
+
expect(unsorted.state.calls).toBe(0);
|
|
881
|
+
}
|
|
882
|
+
finally {
|
|
883
|
+
unsorted.restore();
|
|
884
|
+
sorted.restore();
|
|
885
|
+
}
|
|
886
|
+
});
|
|
887
|
+
it("falls back to scan for a descending order (sorted index only materializes ascending)", () => {
|
|
888
|
+
const db = Database.create(plugin());
|
|
889
|
+
const { low, mid, high } = seed(db);
|
|
890
|
+
const sorted = spyFind(db.indexes.byOwnerSorted);
|
|
891
|
+
try {
|
|
892
|
+
const result = db.select(["owner", "priority"], { where: { owner: 1 }, order: { priority: false } });
|
|
893
|
+
// Not routed — the index can't serve descending without re-sorting.
|
|
894
|
+
expect(sorted.state.calls).toBe(0);
|
|
895
|
+
// The scan still produces a correct descending result.
|
|
896
|
+
expect([...result]).toEqual([high, mid, low]);
|
|
897
|
+
}
|
|
898
|
+
finally {
|
|
899
|
+
sorted.restore();
|
|
900
|
+
}
|
|
901
|
+
});
|
|
902
|
+
it("falls back to scan when the requested order column is not the index's sort column", () => {
|
|
903
|
+
const db = Database.create(plugin());
|
|
904
|
+
seed(db);
|
|
905
|
+
const sorted = spyFind(db.indexes.byOwnerSorted);
|
|
906
|
+
try {
|
|
907
|
+
// Index sorts by `priority`; this asks for `due`.
|
|
908
|
+
const result = db.select(["owner", "due"], { where: { owner: 1 }, order: { due: true } });
|
|
909
|
+
expect(sorted.state.calls).toBe(0);
|
|
910
|
+
expect(result).toHaveLength(3);
|
|
911
|
+
}
|
|
912
|
+
finally {
|
|
913
|
+
sorted.restore();
|
|
914
|
+
}
|
|
915
|
+
});
|
|
916
|
+
it("still routes a plain (no-order) equality query — unaffected by the order support", () => {
|
|
917
|
+
const db = Database.create(plugin());
|
|
918
|
+
seed(db);
|
|
919
|
+
// No order: the first matching key index (byOwner) serves it.
|
|
920
|
+
const unsorted = spyFind(db.indexes.byOwner);
|
|
921
|
+
try {
|
|
922
|
+
const result = db.select(["owner"], { where: { owner: 1 } });
|
|
923
|
+
expect(unsorted.state.calls).toBe(1);
|
|
924
|
+
expect(result).toHaveLength(3);
|
|
925
|
+
}
|
|
926
|
+
finally {
|
|
927
|
+
unsorted.restore();
|
|
928
|
+
}
|
|
929
|
+
});
|
|
930
|
+
it("routes the ordered query through db.observe.select too (initial snapshot + requery)", async () => {
|
|
931
|
+
const db = Database.create(plugin());
|
|
932
|
+
const { low, mid, high } = seed(db);
|
|
933
|
+
const sorted = spyFind(db.indexes.byOwnerSorted);
|
|
934
|
+
try {
|
|
935
|
+
const emissions = [];
|
|
936
|
+
const unsub = db.observe.select(["owner", "priority"], { where: { owner: 1 }, order: { priority: true } })((entities) => { emissions.push([...entities]); });
|
|
937
|
+
// Initial snapshot routed and sorted.
|
|
938
|
+
expect(sorted.state.calls).toBe(1);
|
|
939
|
+
expect(emissions[0]).toEqual([low, mid, high]);
|
|
940
|
+
// A new task for owner 1 with the lowest priority sorts to the front.
|
|
941
|
+
const lowest = db.transactions.add({ owner: 1, priority: 0, due: 5, title: "lowest" });
|
|
942
|
+
await Promise.resolve();
|
|
943
|
+
expect(sorted.state.calls).toBe(2);
|
|
944
|
+
expect(emissions[emissions.length - 1]).toEqual([lowest, low, mid, high]);
|
|
945
|
+
unsub();
|
|
946
|
+
}
|
|
947
|
+
finally {
|
|
948
|
+
sorted.restore();
|
|
949
|
+
}
|
|
950
|
+
});
|
|
951
|
+
});
|
|
572
952
|
describe("t.indexes — eager maintenance inside transactions", () => {
|
|
573
953
|
const plugin = () => Database.Plugin.create({
|
|
574
954
|
components: {
|