@fjader/svelte 0.2.4 → 0.2.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/README.md +1 -1
- package/dist/animation/EntranceGate.d.ts +52 -0
- package/dist/animation/EntranceGate.js +93 -0
- package/dist/animation/TransitionClock.d.ts +61 -30
- package/dist/animation/TransitionClock.js +67 -29
- package/dist/animation/transition-log.d.ts +41 -0
- package/dist/animation/transition-log.js +57 -0
- package/dist/components/axes/AxisBottom.svelte +4 -2
- package/dist/components/axes/AxisCenter.svelte +4 -2
- package/dist/components/axes/AxisLeft.svelte +4 -2
- package/dist/components/axes/BandAxisTicks.svelte +3 -2
- package/dist/components/charts/ChartCell.svelte +32 -5
- package/dist/components/charts/ChartCell.svelte.d.ts +9 -0
- package/dist/components/charts/ChartContainer.svelte +45 -3
- package/dist/components/charts/ChartContainer.svelte.d.ts +7 -1
- package/dist/components/charts/ChartHover.svelte.d.ts +14 -0
- package/dist/components/charts/ChartHover.svelte.js +22 -0
- package/dist/components/charts/ChartVisibility.svelte.d.ts +43 -0
- package/dist/components/charts/ChartVisibility.svelte.js +67 -0
- package/dist/components/guides/Grid.svelte +12 -1
- package/dist/components/marks/AreaMarkX.svelte +16 -4
- package/dist/components/marks/AreaMarkY.svelte +16 -4
- package/dist/components/marks/BarMarkX.svelte +7 -3
- package/dist/components/marks/BarMarkY.svelte +7 -3
- package/dist/components/marks/DotsMark.svelte +6 -3
- package/dist/components/marks/LineMark.svelte +11 -8
- package/dist/components/marks/ParallelCoordinatesMark.svelte +7 -2
- package/dist/components/marks/ParallelCoordinatesMark.svelte.d.ts +2 -0
- package/dist/components/marks/RectMark.svelte +6 -3
- package/dist/components/marks/RectXMark.svelte +7 -3
- package/dist/components/marks/RectYMark.svelte +7 -3
- package/dist/components/marks/RuleMarkX.svelte +30 -26
- package/dist/components/marks/RuleMarkY.svelte +30 -26
- package/dist/components/marks/TextMark.svelte +44 -5
- package/dist/components/marks/lifecycle/MarkLifecycleV2.svelte.d.ts +16 -0
- package/dist/components/marks/lifecycle/MarkLifecycleV2.svelte.js +398 -54
- package/dist/components/marks/lifecycle/domain-queries.d.ts +11 -2
- package/dist/components/marks/lifecycle/domain-queries.js +33 -6
- package/dist/components/marks/lifecycle/domain-registration.d.ts +6 -1
- package/dist/components/marks/lifecycle/domain-registration.js +15 -14
- package/dist/components/marks/lifecycle/other-rows.svelte.d.ts +9 -1
- package/dist/components/marks/lifecycle/other-rows.svelte.js +2 -2
- package/dist/components/marks/lifecycle/query-builder.d.ts +27 -2
- package/dist/components/marks/lifecycle/query-builder.js +23 -20
- package/dist/components/marks/utils/MarkColor.svelte.d.ts +3 -1
- package/dist/components/marks/utils/MarkColor.svelte.js +33 -3
- package/dist/components/marks/utils/MarkSeries.svelte.d.ts +14 -1
- package/dist/components/marks/utils/MarkSeries.svelte.js +25 -1
- package/dist/components/marks/utils/MarkSize.svelte.js +4 -0
- package/dist/components/marks/utils/reactive-config.d.ts +1 -1
- package/dist/components/marks/utils/reactive-config.js +3 -0
- package/dist/components/rendering/Areas.svelte +57 -19
- package/dist/components/rendering/Areas.svelte.d.ts +12 -0
- package/dist/components/rendering/BaseGPURenderer.svelte.d.ts +1 -1
- package/dist/components/rendering/Dots.svelte +23 -3
- package/dist/components/rendering/Dots.svelte.d.ts +6 -0
- package/dist/components/rendering/GridLine.svelte +13 -1
- package/dist/components/rendering/GridLine.svelte.d.ts +5 -0
- package/dist/components/rendering/Lines.svelte +48 -13
- package/dist/components/rendering/Lines.svelte.d.ts +12 -0
- package/dist/components/rendering/ParallelLines.svelte +5 -5
- package/dist/components/rendering/Rects.svelte +51 -11
- package/dist/components/rendering/Rects.svelte.d.ts +16 -2
- package/dist/components/rendering/RuleX.svelte +1 -1
- package/dist/components/rendering/RuleY.svelte +1 -1
- package/dist/components/rendering/WebGPUCanvas.svelte +4 -2
- package/dist/components/rendering/activity-reads.d.ts +41 -24
- package/dist/components/rendering/activity-reads.js +54 -31
- package/dist/components/rendering/uniform-sizes.d.ts +1 -1
- package/dist/components/rendering/uniform-sizes.js +1 -1
- package/dist/config/ChartConfig.svelte.d.ts +7 -0
- package/dist/config/ChartConfig.svelte.js +17 -0
- package/dist/gpu/GPUCoordinator.svelte.d.ts +31 -1
- package/dist/gpu/GPUCoordinator.svelte.js +164 -7
- package/dist/gpu/MorphedChannels.d.ts +11 -2
- package/dist/gpu/MorphedChannels.js +33 -11
- package/dist/gpu/ScaleTemplateSystem.d.ts +20 -2
- package/dist/gpu/ScaleTemplateSystem.js +58 -2
- package/dist/index.d.ts +17 -4
- package/dist/index.js +19 -4
- package/dist/interactors/TooltipManager.svelte.js +14 -5
- package/dist/query-engine/CrossfilterSelection.svelte.d.ts +4 -2
- package/dist/query-engine/CrossfilterSelection.svelte.js +2 -0
- package/dist/query-engine/FieldActivity.svelte.d.ts +2 -3
- package/dist/query-engine/FieldActivity.svelte.js +1 -2
- package/dist/query-engine/MarkHandle.svelte.d.ts +28 -1
- package/dist/query-engine/MarkHandle.svelte.js +33 -1
- package/dist/query-engine/QueryEngine.d.ts +225 -5
- package/dist/query-engine/QueryEngine.js +671 -41
- package/dist/query-engine/categorical-identity.d.ts +34 -8
- package/dist/query-engine/categorical-identity.js +138 -73
- package/dist/query-engine/debug/query-log.d.ts +39 -0
- package/dist/query-engine/debug/query-log.js +90 -0
- package/dist/query-engine/duckdb.d.ts +55 -8
- package/dist/query-engine/duckdb.js +82 -30
- package/dist/query-engine/index.d.ts +7 -4
- package/dist/query-engine/index.js +3 -3
- package/dist/query-engine/preagg/BuildQueue.d.ts +33 -13
- package/dist/query-engine/preagg/BuildQueue.js +51 -15
- package/dist/query-engine/preagg/Preagg.d.ts +46 -3
- package/dist/query-engine/preagg/Preagg.js +159 -31
- package/dist/query-engine/preagg/plan.d.ts +28 -1
- package/dist/query-engine/preagg/plan.js +128 -8
- package/dist/query-engine/sliced-lane.d.ts +108 -0
- package/dist/query-engine/sliced-lane.js +299 -0
- package/dist/query-engine/source-routing.d.ts +106 -0
- package/dist/query-engine/source-routing.js +189 -0
- package/dist/scales/BandScaleState.svelte.d.ts +9 -1
- package/dist/scales/BandScaleState.svelte.js +16 -5
- package/dist/scales/ChartScales.svelte.d.ts +3 -0
- package/dist/scales/ChartScales.svelte.js +26 -8
- package/dist/scales/ColorScaleState.svelte.d.ts +18 -2
- package/dist/scales/ColorScaleState.svelte.js +138 -26
- package/dist/scales/LinearScaleState.svelte.d.ts +7 -10
- package/dist/scales/LinearScaleState.svelte.js +21 -39
- package/dist/scales/ScaleGeneration.svelte.d.ts +48 -0
- package/dist/scales/ScaleGeneration.svelte.js +42 -0
- package/dist/scales/ScaleRegistry.svelte.js +3 -1
- package/dist/scales/SlotTransition.d.ts +15 -1
- package/dist/scales/SlotTransition.js +33 -4
- package/dist/scales/TimeScaleState.svelte.d.ts +7 -3
- package/dist/scales/TimeScaleState.svelte.js +7 -7
- package/dist/scales/color-utils.d.ts +0 -3
- package/dist/scales/color-utils.js +0 -3
- package/dist/scales/entered.d.ts +8 -0
- package/dist/scales/entered.js +10 -0
- package/dist/spec/LazyQueryEngine.js +2 -19
- package/dist/spec/MarkRegistry.d.ts +3 -1
- package/dist/spec/MarkRegistry.js +13 -1
- package/dist/spec/PlotFromSpec.svelte +1 -0
- package/dist/spec/export.js +11 -2
- package/dist/spec/types.d.ts +41 -13
- package/dist/sql/bin.d.ts +25 -0
- package/dist/sql/bin.js +108 -5
- package/dist/transforms/TransformCompiler.d.ts +10 -0
- package/dist/transforms/TransformCompiler.js +26 -0
- package/dist/transforms/index.js +144 -24
- package/dist/types.d.ts +136 -2
- package/package.json +2 -2
|
@@ -1,43 +1,47 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* DuckDB-WASM initialization — replaces mosaic-core's wasmConnector.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* initDuckDB() returns a Promise<DuckDBConnection> suitable for passing to
|
|
5
|
+
* setQueryContext(). It loads the jsDelivr bundles unless the app passes its own.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* zero-copy typed arrays that go straight to GPU buffers.
|
|
7
|
+
* connectionFrom() adapts a duckdb-wasm connection the app created itself, for
|
|
8
|
+
* apps that need the AsyncDuckDB instance (registerFileBuffer, extensions, …).
|
|
10
9
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
10
|
+
* Both use the fast IPC query path: bypasses duckdb-wasm's Arrow JS
|
|
11
|
+
* deserialization and returns raw Arrow IPC bytes. QueryEngine decodes them with
|
|
12
|
+
* flechette into typed arrays for the GPU buffers. These are views on the IPC
|
|
13
|
+
* bytes only up to one record batch: DuckDB-WASM writes a batch per DuckDB vector
|
|
14
|
+
* (~2k rows), and flechette concatenates a column that spans several.
|
|
13
15
|
*/
|
|
16
|
+
import { PendingQueryLane } from './sliced-lane';
|
|
14
17
|
/**
|
|
15
|
-
*
|
|
18
|
+
* How long the worker runs a sliced statement before serving other messages:
|
|
19
|
+
* a quick query on another connection waits at most about this long behind a
|
|
20
|
+
* slow one. Measured in Phase 0: 1 ms at 5 ms, ~50 ms at the default (~100 ms).
|
|
21
|
+
*/
|
|
22
|
+
export const QUERY_POLLING_INTERVAL_MS = 5;
|
|
23
|
+
/**
|
|
24
|
+
* Adapt a duckdb-wasm connection to the DuckDBConnection QueryEngine expects,
|
|
25
|
+
* with the fast IPC query path, and sliced lanes: further connections on the
|
|
26
|
+
* same database (`openSlicedLane`).
|
|
16
27
|
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
28
|
+
* const db = new duckdb.AsyncDuckDB(logger, worker);
|
|
29
|
+
* await db.instantiate(…);
|
|
30
|
+
* await db.open({ query: { queryPollingInterval: 5 } }); // short slices, see below
|
|
31
|
+
* await db.registerFileBuffer('cached.parquet', bytes);
|
|
32
|
+
* initFjader(connectionFrom(await db.connect(), { pollingIntervalMs: 5 }));
|
|
33
|
+
*
|
|
34
|
+
* Sliced statements run in slices of the database's `queryPollingInterval`.
|
|
35
|
+
* At DuckDB's default (~100 ms) a quick query can wait ~50 ms behind a slow
|
|
36
|
+
* one; open the database with ~5 ms, as `initDuckDB` does. It is a setting of
|
|
37
|
+
* the database, which this function cannot change or read: say what you set
|
|
38
|
+
* as `pollingIntervalMs`. Without it, lanes measure their slices and warn if
|
|
39
|
+
* they run long.
|
|
19
40
|
*/
|
|
20
|
-
export function
|
|
21
|
-
if (typeof window === 'undefined') {
|
|
22
|
-
// SSR: return pending promise. QueryEngine defers until resolved.
|
|
23
|
-
// Component re-runs on client where the real init happens.
|
|
24
|
-
return new Promise(() => { });
|
|
25
|
-
}
|
|
26
|
-
return doInit();
|
|
27
|
-
}
|
|
28
|
-
async function doInit() {
|
|
29
|
-
const duckdb = await import('@duckdb/duckdb-wasm');
|
|
30
|
-
const bundles = duckdb.getJsDelivrBundles();
|
|
31
|
-
const bundle = await duckdb.selectBundle(bundles);
|
|
32
|
-
const workerUrl = URL.createObjectURL(new Blob([`importScripts("${bundle.mainWorker}");`], { type: 'text/javascript' }));
|
|
33
|
-
const worker = new Worker(workerUrl);
|
|
34
|
-
const db = new duckdb.AsyncDuckDB(new duckdb.VoidLogger(), worker);
|
|
35
|
-
await db.instantiate(bundle.mainModule, bundle.pthreadWorker);
|
|
36
|
-
URL.revokeObjectURL(workerUrl);
|
|
37
|
-
const conn = await db.connect();
|
|
38
|
-
// Wrap the duckdb-wasm connection to expose the fast IPC query path.
|
|
41
|
+
export function connectionFrom(conn, options = {}) {
|
|
39
42
|
// useUnsafe + bindings.runQuery bypasses Arrow JS deserialization entirely,
|
|
40
|
-
// returning raw IPC bytes that flechette decodes into
|
|
43
|
+
// returning raw IPC bytes that flechette decodes into typed arrays (one copy per
|
|
44
|
+
// column once a result spans more than one ~2k-row record batch).
|
|
41
45
|
return {
|
|
42
46
|
queryIPC(sql) {
|
|
43
47
|
return conn.useUnsafe((bindings, connId) => bindings.runQuery(connId, sql));
|
|
@@ -56,6 +60,54 @@ async function doInit() {
|
|
|
56
60
|
},
|
|
57
61
|
insertArrowFromIPCStream(data, options) {
|
|
58
62
|
return conn.insertArrowFromIPCStream(data, options);
|
|
63
|
+
},
|
|
64
|
+
async openSlicedLane(laneOptions = {}) {
|
|
65
|
+
// Another connection on the same AsyncDuckDB: one catalog, its own
|
|
66
|
+
// pending query.
|
|
67
|
+
const lane = await conn.bindings.connect();
|
|
68
|
+
return lane.useUnsafe((bindings, connId) => new PendingQueryLane(bindings, connId, () => lane.close(), {
|
|
69
|
+
pollingIntervalMs: options.pollingIntervalMs,
|
|
70
|
+
hold: laneOptions.hold
|
|
71
|
+
}));
|
|
59
72
|
}
|
|
60
73
|
};
|
|
61
74
|
}
|
|
75
|
+
/**
|
|
76
|
+
* Initialize DuckDB-WASM and return a connection.
|
|
77
|
+
*
|
|
78
|
+
* Safe to call during SSR — returns a permanently pending promise on the
|
|
79
|
+
* server. The component re-runs on the client where Worker is available.
|
|
80
|
+
*
|
|
81
|
+
* Usage:
|
|
82
|
+
* const { queryEngine } = setQueryContext(initDuckDB());
|
|
83
|
+
*/
|
|
84
|
+
export function initDuckDB(options = {}) {
|
|
85
|
+
if (typeof window === 'undefined') {
|
|
86
|
+
// SSR: return pending promise. QueryEngine defers until resolved.
|
|
87
|
+
// Component re-runs on client where the real init happens.
|
|
88
|
+
return new Promise(() => { });
|
|
89
|
+
}
|
|
90
|
+
return doInit(options);
|
|
91
|
+
}
|
|
92
|
+
async function doInit({ bundles }) {
|
|
93
|
+
const duckdb = await import('@duckdb/duckdb-wasm');
|
|
94
|
+
const bundle = await duckdb.selectBundle(bundles ?? duckdb.getJsDelivrBundles());
|
|
95
|
+
// The worker starts from a blob: URL so a cross-origin (CDN) worker script can
|
|
96
|
+
// load. importScripts there resolves against the blob: URL, so an app's
|
|
97
|
+
// relative bundle URL has to be made absolute first.
|
|
98
|
+
const mainWorker = new URL(bundle.mainWorker, location.href).href;
|
|
99
|
+
const workerUrl = URL.createObjectURL(new Blob([`importScripts(${JSON.stringify(mainWorker)});`], { type: 'text/javascript' }));
|
|
100
|
+
const worker = new Worker(workerUrl);
|
|
101
|
+
const db = new duckdb.AsyncDuckDB(new duckdb.VoidLogger(), worker);
|
|
102
|
+
try {
|
|
103
|
+
await db.instantiate(new URL(bundle.mainModule, location.href).href, bundle.pthreadWorker && new URL(bundle.pthreadWorker, location.href).href);
|
|
104
|
+
}
|
|
105
|
+
finally {
|
|
106
|
+
URL.revokeObjectURL(workerUrl);
|
|
107
|
+
}
|
|
108
|
+
// Short slices for sliced lanes. Only the polling interval: the query
|
|
109
|
+
// config's other fields decide how big integers, decimals, timestamps and
|
|
110
|
+
// durations come back, and stay at their defaults.
|
|
111
|
+
await db.open({ query: { queryPollingInterval: QUERY_POLLING_INTERVAL_MS } });
|
|
112
|
+
return connectionFrom(await db.connect(), { pollingIntervalMs: QUERY_POLLING_INTERVAL_MS });
|
|
113
|
+
}
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* context.set('filter-groups', filterGroups);
|
|
12
12
|
*/
|
|
13
13
|
import { QueryEngine } from './QueryEngine';
|
|
14
|
-
import type { DuckDBConnection } from './QueryEngine';
|
|
14
|
+
import type { DuckDBConnection, QueryEngineOptions } from './QueryEngine';
|
|
15
15
|
import { FilterGroupRegistry } from './FilterGroupRegistry.svelte';
|
|
16
16
|
/**
|
|
17
17
|
* Create the FilterGroupRegistry for an engine and wire both to the schema
|
|
@@ -27,16 +27,19 @@ export declare function createQueryServices(queryEngine: QueryEngine): {
|
|
|
27
27
|
* Call this synchronously in a component's <script> block.
|
|
28
28
|
* Accepts a Promise<DuckDBConnection> — queries defer until it resolves.
|
|
29
29
|
*/
|
|
30
|
-
export declare function setQueryContext(connection: DuckDBConnection | Promise<DuckDBConnection
|
|
30
|
+
export declare function setQueryContext(connection: DuckDBConnection | Promise<DuckDBConnection>, options?: QueryEngineOptions): {
|
|
31
31
|
queryEngine: QueryEngine;
|
|
32
32
|
filterGroups: FilterGroupRegistry;
|
|
33
33
|
};
|
|
34
34
|
/** Get the FilterGroupRegistry from context. */
|
|
35
35
|
export declare function getFilterGroups(): FilterGroupRegistry;
|
|
36
|
-
export { initDuckDB } from './duckdb';
|
|
36
|
+
export { initDuckDB, connectionFrom } from './duckdb';
|
|
37
|
+
export type { SlicedLane } from './sliced-lane';
|
|
38
|
+
export type { InitDuckDBOptions } from './duckdb';
|
|
37
39
|
export { QueryEngine } from './QueryEngine';
|
|
38
|
-
export type { DuckDBConnection, QueryOpts, ColumnInfo, ColumnSchema, JsColumnType, QueryDescriptor, ActiveDim, ActivationSource, QueryEngineOptions } from './QueryEngine';
|
|
40
|
+
export type { DuckDBConnection, QueryOpts, ColumnInfo, ColumnSchema, JsColumnType, QueryDescriptor, ActiveDim, ActivationSource, QueryEngineOptions, StatementClass, StatementRecord } from './QueryEngine';
|
|
39
41
|
export { mapSqlTypeToJs, buildSchemaMap } from './QueryEngine';
|
|
42
|
+
export type { BuildEvent } from './preagg/Preagg';
|
|
40
43
|
export { MarkHandle } from './MarkHandle.svelte';
|
|
41
44
|
export type { QueryParams } from './MarkHandle.svelte';
|
|
42
45
|
export { FilterGroupRegistry } from './FilterGroupRegistry.svelte';
|
|
@@ -30,8 +30,8 @@ export function createQueryServices(queryEngine) {
|
|
|
30
30
|
* Call this synchronously in a component's <script> block.
|
|
31
31
|
* Accepts a Promise<DuckDBConnection> — queries defer until it resolves.
|
|
32
32
|
*/
|
|
33
|
-
export function setQueryContext(connection) {
|
|
34
|
-
const { queryEngine, filterGroups } = createQueryServices(new QueryEngine(connection));
|
|
33
|
+
export function setQueryContext(connection, options) {
|
|
34
|
+
const { queryEngine, filterGroups } = createQueryServices(new QueryEngine(connection, options));
|
|
35
35
|
setContext('query-engine', queryEngine);
|
|
36
36
|
setContext('filter-groups', filterGroups);
|
|
37
37
|
return { queryEngine, filterGroups };
|
|
@@ -41,7 +41,7 @@ export function getFilterGroups() {
|
|
|
41
41
|
return getContext('filter-groups');
|
|
42
42
|
}
|
|
43
43
|
// Re-exports
|
|
44
|
-
export { initDuckDB } from './duckdb';
|
|
44
|
+
export { initDuckDB, connectionFrom } from './duckdb';
|
|
45
45
|
export { QueryEngine } from './QueryEngine';
|
|
46
46
|
export { mapSqlTypeToJs, buildSchemaMap } from './QueryEngine';
|
|
47
47
|
export { MarkHandle } from './MarkHandle.svelte';
|
|
@@ -29,16 +29,27 @@ export interface QueuedBuild {
|
|
|
29
29
|
}
|
|
30
30
|
/** What the queue needs from its owner. */
|
|
31
31
|
export interface BuildQueueHost {
|
|
32
|
-
exec(sql: string): Promise<void>;
|
|
33
|
-
/**
|
|
32
|
+
exec(sql: string, signal: AbortSignal): Promise<void>;
|
|
33
|
+
/**
|
|
34
|
+
* Whether an interaction statement is on the connection, or a mark query is
|
|
35
|
+
* waiting to be sent. Background statements (other builds, tooltip writes)
|
|
36
|
+
* do not count.
|
|
37
|
+
*/
|
|
34
38
|
busy(): boolean;
|
|
35
39
|
/**
|
|
36
|
-
*
|
|
37
|
-
* re-registered, the view moved or a baked filter changed while it
|
|
40
|
+
* Why a build is no longer wanted, checked when it would be sent (a mark
|
|
41
|
+
* re-registered, the view moved or a baked filter changed while it
|
|
42
|
+
* waited), or null when it still is.
|
|
43
|
+
*/
|
|
44
|
+
unwanted(item: QueuedBuild): string | null;
|
|
45
|
+
/** A build is being sent. */
|
|
46
|
+
started?(item: QueuedBuild): void;
|
|
47
|
+
/**
|
|
48
|
+
* A build was dropped before it ran, or cancelled while running, so the
|
|
49
|
+
* next activation plans it again. `reason` says which: a check that
|
|
50
|
+
* failed (`unwanted`), 'newer batch', 'cleared' or 'cancelled'.
|
|
38
51
|
*/
|
|
39
|
-
|
|
40
|
-
/** A queued build was dropped before it ran, so the next activation plans it again. */
|
|
41
|
-
dropped(item: QueuedBuild): void;
|
|
52
|
+
dropped(item: QueuedBuild, reason: string): void;
|
|
42
53
|
built(item: QueuedBuild): void;
|
|
43
54
|
failed(item: QueuedBuild, error: unknown): void;
|
|
44
55
|
}
|
|
@@ -52,6 +63,8 @@ export declare class BuildQueue {
|
|
|
52
63
|
* behind the build instead.
|
|
53
64
|
*/
|
|
54
65
|
constructor(host: BuildQueueHost, maxWaitMs: number);
|
|
66
|
+
/** The current hover batch's number, for build records. */
|
|
67
|
+
get batch(): number;
|
|
55
68
|
/** Run a hover's activations as one batch. Nested calls join the outer batch. */
|
|
56
69
|
runBatch(activations: () => void): void;
|
|
57
70
|
/** Start a batch unless one is running: an activation outside runBatch() is a batch of one. */
|
|
@@ -59,11 +72,12 @@ export declare class BuildQueue {
|
|
|
59
72
|
/** Queue a build in the current batch. Settles when it has run or been dropped. */
|
|
60
73
|
push(build: Omit<QueuedBuild, 'batch' | 'queuedAt' | 'done'>): Promise<void>;
|
|
61
74
|
/**
|
|
62
|
-
* Send the next queued build if nothing
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
* a
|
|
66
|
-
*
|
|
75
|
+
* Send the next queued build if nothing someone waits for is on the
|
|
76
|
+
* connection: no build running, no interaction statement in flight, and no
|
|
77
|
+
* mark query waiting. Called when a build is queued, when one finishes, when
|
|
78
|
+
* a mark query settles, and when the last interaction statement settles.
|
|
79
|
+
* Background statements (tooltip-table writes, cleanup) do not hold builds
|
|
80
|
+
* back.
|
|
67
81
|
*
|
|
68
82
|
* Mark queries hold a build back for at most the wait limit. A drag that
|
|
69
83
|
* starts before its source's tables are built reads the base table, each
|
|
@@ -71,7 +85,13 @@ export declare class BuildQueue {
|
|
|
71
85
|
* never run; past the limit it is sent anyway, and one frame waits for it.
|
|
72
86
|
*/
|
|
73
87
|
pump(): void;
|
|
74
|
-
/**
|
|
88
|
+
/**
|
|
89
|
+
* Cancel the running build if `unwanted` says so. On a lane it is
|
|
90
|
+
* interrupted and rolls back (no table); on a shared connection it cannot
|
|
91
|
+
* be, and finishes, but is not registered.
|
|
92
|
+
*/
|
|
93
|
+
abortRunning(unwanted: (item: QueuedBuild) => boolean): void;
|
|
94
|
+
/** Drop every queued build and cancel the running one (a data reload). */
|
|
75
95
|
clear(): void;
|
|
76
96
|
/** Stop for good: settle every waiting activation and send nothing more. */
|
|
77
97
|
destroy(): void;
|
|
@@ -19,6 +19,8 @@ export class BuildQueue {
|
|
|
19
19
|
/** Waiting builds, oldest first. */
|
|
20
20
|
#queue = [];
|
|
21
21
|
#running = false;
|
|
22
|
+
/** The build being sent, and how to cancel it. */
|
|
23
|
+
#current = null;
|
|
22
24
|
#destroyed = false;
|
|
23
25
|
/** Pumps the queue when the oldest build's wait runs out, if no query does first. */
|
|
24
26
|
#waitTimer = null;
|
|
@@ -36,6 +38,10 @@ export class BuildQueue {
|
|
|
36
38
|
this.#host = host;
|
|
37
39
|
this.#maxWaitMs = maxWaitMs;
|
|
38
40
|
}
|
|
41
|
+
/** The current hover batch's number, for build records. */
|
|
42
|
+
get batch() {
|
|
43
|
+
return this.#batch;
|
|
44
|
+
}
|
|
39
45
|
/** Run a hover's activations as one batch. Nested calls join the outer batch. */
|
|
40
46
|
runBatch(activations) {
|
|
41
47
|
if (this.#inBatch) {
|
|
@@ -63,11 +69,12 @@ export class BuildQueue {
|
|
|
63
69
|
});
|
|
64
70
|
}
|
|
65
71
|
/**
|
|
66
|
-
* Send the next queued build if nothing
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
* a
|
|
70
|
-
*
|
|
72
|
+
* Send the next queued build if nothing someone waits for is on the
|
|
73
|
+
* connection: no build running, no interaction statement in flight, and no
|
|
74
|
+
* mark query waiting. Called when a build is queued, when one finishes, when
|
|
75
|
+
* a mark query settles, and when the last interaction statement settles.
|
|
76
|
+
* Background statements (tooltip-table writes, cleanup) do not hold builds
|
|
77
|
+
* back.
|
|
71
78
|
*
|
|
72
79
|
* Mark queries hold a build back for at most the wait limit. A drag that
|
|
73
80
|
* starts before its source's tables are built reads the base table, each
|
|
@@ -90,32 +97,61 @@ export class BuildQueue {
|
|
|
90
97
|
}
|
|
91
98
|
}
|
|
92
99
|
const item = this.#queue.shift();
|
|
93
|
-
|
|
94
|
-
|
|
100
|
+
const reason = this.#host.unwanted(item);
|
|
101
|
+
if (reason !== null) {
|
|
102
|
+
this.#drop(item, reason);
|
|
95
103
|
continue;
|
|
96
104
|
}
|
|
97
105
|
this.#running = true;
|
|
106
|
+
this.#host.started?.(item);
|
|
107
|
+
const abort = new AbortController();
|
|
108
|
+
this.#current = { item, abort };
|
|
98
109
|
this.#host
|
|
99
|
-
.exec(item.createSQL)
|
|
100
|
-
.then(() =>
|
|
101
|
-
|
|
110
|
+
.exec(item.createSQL, abort.signal)
|
|
111
|
+
.then(() => {
|
|
112
|
+
// Cancelled but finished anyway (the cancel lost the race): the
|
|
113
|
+
// caller gave it up, so it does not land.
|
|
114
|
+
if (abort.signal.aborted)
|
|
115
|
+
this.#host.dropped(item, 'cancelled');
|
|
116
|
+
else
|
|
117
|
+
this.#host.built(item);
|
|
118
|
+
})
|
|
119
|
+
.catch((e) => {
|
|
120
|
+
if (abort.signal.aborted)
|
|
121
|
+
this.#host.dropped(item, 'cancelled');
|
|
122
|
+
else
|
|
123
|
+
this.#host.failed(item, e);
|
|
124
|
+
})
|
|
102
125
|
.finally(() => {
|
|
103
126
|
this.#running = false;
|
|
127
|
+
this.#current = null;
|
|
104
128
|
item.done();
|
|
105
129
|
this.pump();
|
|
106
130
|
});
|
|
107
131
|
return;
|
|
108
132
|
}
|
|
109
133
|
}
|
|
110
|
-
/**
|
|
134
|
+
/**
|
|
135
|
+
* Cancel the running build if `unwanted` says so. On a lane it is
|
|
136
|
+
* interrupted and rolls back (no table); on a shared connection it cannot
|
|
137
|
+
* be, and finishes, but is not registered.
|
|
138
|
+
*/
|
|
139
|
+
abortRunning(unwanted) {
|
|
140
|
+
const current = this.#current;
|
|
141
|
+
if (current && unwanted(current.item))
|
|
142
|
+
current.abort.abort();
|
|
143
|
+
}
|
|
144
|
+
/** Drop every queued build and cancel the running one (a data reload). */
|
|
111
145
|
clear() {
|
|
112
146
|
for (const item of this.#queue)
|
|
113
|
-
this.#drop(item);
|
|
147
|
+
this.#drop(item, 'cleared');
|
|
114
148
|
this.#queue = [];
|
|
149
|
+
this.abortRunning(() => true);
|
|
115
150
|
}
|
|
116
151
|
/** Stop for good: settle every waiting activation and send nothing more. */
|
|
117
152
|
destroy() {
|
|
118
153
|
this.#destroyed = true;
|
|
154
|
+
this.abortRunning(() => true);
|
|
119
155
|
if (this.#waitTimer)
|
|
120
156
|
clearTimeout(this.#waitTimer);
|
|
121
157
|
this.#waitTimer = null;
|
|
@@ -130,12 +166,12 @@ export class BuildQueue {
|
|
|
130
166
|
if (item.batch === this.#batch)
|
|
131
167
|
keep.push(item);
|
|
132
168
|
else
|
|
133
|
-
this.#drop(item);
|
|
169
|
+
this.#drop(item, 'newer batch');
|
|
134
170
|
}
|
|
135
171
|
this.#queue = keep;
|
|
136
172
|
}
|
|
137
|
-
#drop(item) {
|
|
138
|
-
this.#host.dropped(item);
|
|
173
|
+
#drop(item, reason) {
|
|
174
|
+
this.#host.dropped(item, reason);
|
|
139
175
|
item.done();
|
|
140
176
|
}
|
|
141
177
|
}
|
|
@@ -25,18 +25,56 @@ export interface PreaggMark {
|
|
|
25
25
|
handle: MarkHandle;
|
|
26
26
|
filterGroup: FilterGroup;
|
|
27
27
|
}
|
|
28
|
+
/**
|
|
29
|
+
* One step of a preagg build, for `QueryEngine.observeBuilds` (debugging and
|
|
30
|
+
* measurement). In order for one build: planned, queued, started, then built,
|
|
31
|
+
* failed or dropped. A planning that builds nothing is `skipped`.
|
|
32
|
+
*/
|
|
33
|
+
export interface BuildEvent {
|
|
34
|
+
kind: 'skipped' | 'planned' | 'queued' | 'started' | 'built' | 'failed' | 'dropped';
|
|
35
|
+
markId: string;
|
|
36
|
+
sourceId: string;
|
|
37
|
+
/** The preagg table, once planned. */
|
|
38
|
+
table: string | null;
|
|
39
|
+
/** performance.now(). */
|
|
40
|
+
time: number;
|
|
41
|
+
/** The hover batch the build was queued in. */
|
|
42
|
+
batch?: number;
|
|
43
|
+
/**
|
|
44
|
+
* skipped: 'unplannable' or 'too large'. dropped: the check that failed
|
|
45
|
+
* ('unregistered', 'inactive', 'superseded', 'parts changed', 'bins do not
|
|
46
|
+
* fit the view', 'baked clauses changed'), or 'newer batch', 'cleared',
|
|
47
|
+
* 'cancelled'.
|
|
48
|
+
*/
|
|
49
|
+
reason?: string;
|
|
50
|
+
/** The values the failed check compared (see #dropDetail), or the size guard's. */
|
|
51
|
+
detail?: Record<string, unknown>;
|
|
52
|
+
/** failed: the error. */
|
|
53
|
+
error?: unknown;
|
|
54
|
+
}
|
|
28
55
|
/** What Preagg needs from the engine. */
|
|
29
56
|
export interface PreaggHost {
|
|
30
|
-
exec(sql: string): Promise<void>;
|
|
57
|
+
exec(sql: string, signal?: AbortSignal): Promise<void>;
|
|
31
58
|
query(sql: string, opts: {
|
|
32
59
|
cache: boolean;
|
|
33
60
|
}): Promise<Table>;
|
|
34
61
|
/** Whether a mark query is running or waiting. */
|
|
35
62
|
busy(): boolean;
|
|
63
|
+
/**
|
|
64
|
+
* Whether background statements run sliced on their own connection (a
|
|
65
|
+
* lane), where a build no longer holds up interaction.
|
|
66
|
+
*/
|
|
67
|
+
sliced(): boolean;
|
|
36
68
|
identity: PreaggIdentity;
|
|
37
69
|
/** The mark's current registration, which a re-setup replaces. */
|
|
38
70
|
mark(markId: string): PreaggMark | undefined;
|
|
39
71
|
marks(): Iterable<[string, PreaggMark]>;
|
|
72
|
+
/**
|
|
73
|
+
* Whether anything listens to onBuild. Checked first, so with no
|
|
74
|
+
* listener no record is built.
|
|
75
|
+
*/
|
|
76
|
+
observingBuilds?(): boolean;
|
|
77
|
+
onBuild?(event: BuildEvent): void;
|
|
40
78
|
}
|
|
41
79
|
export declare class Preagg {
|
|
42
80
|
#private;
|
|
@@ -97,13 +135,18 @@ export declare class Preagg {
|
|
|
97
135
|
* A data reload: drop every queued build, every mark's tables and the tables
|
|
98
136
|
* themselves (CREATE TABLE IF NOT EXISTS would otherwise keep serving
|
|
99
137
|
* pre-reload aggregates), and the row counts.
|
|
138
|
+
*
|
|
139
|
+
* The running build is cancelled too (`clear`). On a lane it runs beside the
|
|
140
|
+
* reload, reading the old rows through its snapshot; should it finish anyway
|
|
141
|
+
* (the cancel lost the race), `built` finds its mark gone from `#marks` and
|
|
142
|
+
* does not register it, and the DROP below follows it on the same lane.
|
|
100
143
|
*/
|
|
101
144
|
reset(): void;
|
|
102
145
|
/**
|
|
103
146
|
* The engine is going: settle waiting activations and drop this session's
|
|
104
147
|
* tables. Resolves once the drops have run; call it while the engine can
|
|
105
|
-
* still exec. A build already running is
|
|
106
|
-
* connection, so its table is dropped too.
|
|
148
|
+
* still exec. A build already running is cancelled, and is ahead of the
|
|
149
|
+
* drops on its connection either way, so its table is dropped too.
|
|
107
150
|
*/
|
|
108
151
|
destroy(): Promise<void>;
|
|
109
152
|
}
|