@xnetjs/data 0.1.0 → 0.1.1
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.
|
@@ -3413,6 +3413,7 @@ var DEFAULT_QUERY_VERIFICATION = {
|
|
|
3413
3413
|
logFailures: true
|
|
3414
3414
|
};
|
|
3415
3415
|
var QUERY_TELEMETRY_FLUSH_THRESHOLD = 50;
|
|
3416
|
+
var COMPILED_QUERY_DIAGNOSTICS_MEMO_LIMIT = 512;
|
|
3416
3417
|
var SQLITE_BIND_PARAMETER_BATCH_SIZE = 900;
|
|
3417
3418
|
var SQLITE_HYDRATE_NODE_BATCH_SIZE = Math.floor(SQLITE_BIND_PARAMETER_BATCH_SIZE / 2);
|
|
3418
3419
|
var SQL_HYDRATE_ARITY_BUCKETS = [1, 10, 50, 150, SQLITE_HYDRATE_NODE_BATCH_SIZE];
|
|
@@ -3474,6 +3475,14 @@ var SQLiteNodeStorageAdapter = class {
|
|
|
3474
3475
|
pendingQueryTelemetryHits = 0;
|
|
3475
3476
|
adaptiveIndexBudgetColumnsReady = false;
|
|
3476
3477
|
storageCapabilitiesPromise;
|
|
3478
|
+
/**
|
|
3479
|
+
* Plan diagnostics memoized per compiled SQL shape (the string EXPLAINed),
|
|
3480
|
+
* shared across executions AND concurrent callers. Cleared when this adapter
|
|
3481
|
+
* creates/drops an adaptive index, since that changes plans. Debug mode must
|
|
3482
|
+
* not distort what it measures: per-execution EXPLAIN + index-inventory
|
|
3483
|
+
* round-trips convoyed the single serial worker at boot.
|
|
3484
|
+
*/
|
|
3485
|
+
compiledQueryDiagnosticsMemo = /* @__PURE__ */ new Map();
|
|
3477
3486
|
spatialTablesState = "unknown";
|
|
3478
3487
|
fullTextSearchTablesState = "unknown";
|
|
3479
3488
|
writeQueue = Promise.resolve();
|
|
@@ -5741,7 +5750,34 @@ CREATE INDEX IF NOT EXISTS idx_node_spatial_ids_schema
|
|
|
5741
5750
|
isQueryDiagnosticsEnabled() {
|
|
5742
5751
|
return this.queryDiagnostics || this.isQueryDebugEnabled();
|
|
5743
5752
|
}
|
|
5744
|
-
|
|
5753
|
+
/**
|
|
5754
|
+
* Throttled: one collection per unique compiled SQL shape per session.
|
|
5755
|
+
* EXPLAIN plans depend on the statement, not the bound values, so keyset
|
|
5756
|
+
* pages of the same query share one entry. Concurrent callers share the
|
|
5757
|
+
* in-flight promise; failed collections are not memoized.
|
|
5758
|
+
*/
|
|
5759
|
+
collectCompiledQueryDiagnostics(compiled) {
|
|
5760
|
+
const memoKey = compiled.sql;
|
|
5761
|
+
const memoized = this.compiledQueryDiagnosticsMemo.get(memoKey);
|
|
5762
|
+
if (memoized) {
|
|
5763
|
+
return memoized;
|
|
5764
|
+
}
|
|
5765
|
+
const pending = this.computeCompiledQueryDiagnostics(compiled).then((diagnostics) => {
|
|
5766
|
+
if (diagnostics.diagnosticsError) {
|
|
5767
|
+
this.compiledQueryDiagnosticsMemo.delete(memoKey);
|
|
5768
|
+
}
|
|
5769
|
+
return diagnostics;
|
|
5770
|
+
});
|
|
5771
|
+
if (this.compiledQueryDiagnosticsMemo.size >= COMPILED_QUERY_DIAGNOSTICS_MEMO_LIMIT) {
|
|
5772
|
+
const oldest = this.compiledQueryDiagnosticsMemo.keys().next().value;
|
|
5773
|
+
if (oldest !== void 0) {
|
|
5774
|
+
this.compiledQueryDiagnosticsMemo.delete(oldest);
|
|
5775
|
+
}
|
|
5776
|
+
}
|
|
5777
|
+
this.compiledQueryDiagnosticsMemo.set(memoKey, pending);
|
|
5778
|
+
return pending;
|
|
5779
|
+
}
|
|
5780
|
+
async computeCompiledQueryDiagnostics(compiled) {
|
|
5745
5781
|
try {
|
|
5746
5782
|
const [analysis, indexes, storageCapabilities] = await Promise.all([
|
|
5747
5783
|
analyzeQuery(this.db, compiled.sql, compiled.params),
|
|
@@ -5965,6 +6001,7 @@ CREATE INDEX IF NOT EXISTS idx_node_spatial_ids_schema
|
|
|
5965
6001
|
if (createdIndex) {
|
|
5966
6002
|
await runAnalyze(this.db, "node_property_scalars");
|
|
5967
6003
|
await this.db.exec("PRAGMA optimize");
|
|
6004
|
+
this.compiledQueryDiagnosticsMemo.clear();
|
|
5968
6005
|
}
|
|
5969
6006
|
return touchedIndexNames;
|
|
5970
6007
|
}
|
|
@@ -6113,6 +6150,7 @@ CREATE INDEX IF NOT EXISTS idx_node_spatial_ids_schema
|
|
|
6113
6150
|
async dropAdaptiveIndex(indexName, reason) {
|
|
6114
6151
|
await this.db.exec(`DROP INDEX IF EXISTS ${this.quoteSqlIdentifier(indexName)}`);
|
|
6115
6152
|
await this.db.run("DELETE FROM query_index_candidates WHERE index_name = ?", [indexName]);
|
|
6153
|
+
this.compiledQueryDiagnosticsMemo.clear();
|
|
6116
6154
|
this.debugAdaptiveIndex("drop", { indexName, reason });
|
|
6117
6155
|
}
|
|
6118
6156
|
debugAdaptiveIndex(action, details) {
|
package/dist/index.js
CHANGED
package/dist/store/index.d.ts
CHANGED
|
@@ -120,9 +120,13 @@ interface SQLiteNodeStorageAdapterOptions {
|
|
|
120
120
|
adaptiveIndexing?: SQLiteAdaptiveIndexingOptions;
|
|
121
121
|
queryVerification?: SQLiteQueryVerificationOptions;
|
|
122
122
|
/**
|
|
123
|
-
* Collect EXPLAIN QUERY PLAN + index inventory
|
|
124
|
-
* round trips
|
|
125
|
-
* `xnet:query:debug` localStorage flag is set.
|
|
123
|
+
* Collect EXPLAIN QUERY PLAN + index inventory for queries. Costs extra
|
|
124
|
+
* round trips, so it is off unless explicitly enabled or the
|
|
125
|
+
* `xnet:query:debug` localStorage flag is set. Diagnostics are collected
|
|
126
|
+
* once per unique compiled SQL shape per session (invalidated when the
|
|
127
|
+
* adapter itself runs DDL) — per-execution collection convoyed the serial
|
|
128
|
+
* worker and delayed the very queries being measured by 18-20s at boot
|
|
129
|
+
* (2026-07-05 capture).
|
|
126
130
|
*/
|
|
127
131
|
queryDiagnostics?: boolean;
|
|
128
132
|
/**
|
|
@@ -175,6 +179,14 @@ declare class SQLiteNodeStorageAdapter implements NodeStorageAdapter {
|
|
|
175
179
|
private pendingQueryTelemetryHits;
|
|
176
180
|
private adaptiveIndexBudgetColumnsReady;
|
|
177
181
|
private storageCapabilitiesPromise?;
|
|
182
|
+
/**
|
|
183
|
+
* Plan diagnostics memoized per compiled SQL shape (the string EXPLAINed),
|
|
184
|
+
* shared across executions AND concurrent callers. Cleared when this adapter
|
|
185
|
+
* creates/drops an adaptive index, since that changes plans. Debug mode must
|
|
186
|
+
* not distort what it measures: per-execution EXPLAIN + index-inventory
|
|
187
|
+
* round-trips convoyed the single serial worker at boot.
|
|
188
|
+
*/
|
|
189
|
+
private compiledQueryDiagnosticsMemo;
|
|
178
190
|
private spatialTablesState;
|
|
179
191
|
private fullTextSearchTablesState;
|
|
180
192
|
private writeQueue;
|
|
@@ -449,7 +461,14 @@ declare class SQLiteNodeStorageAdapter implements NodeStorageAdapter {
|
|
|
449
461
|
flushQueryTelemetry(): Promise<void>;
|
|
450
462
|
private writeQueryTelemetryRow;
|
|
451
463
|
private isQueryDiagnosticsEnabled;
|
|
464
|
+
/**
|
|
465
|
+
* Throttled: one collection per unique compiled SQL shape per session.
|
|
466
|
+
* EXPLAIN plans depend on the statement, not the bound values, so keyset
|
|
467
|
+
* pages of the same query share one entry. Concurrent callers share the
|
|
468
|
+
* in-flight promise; failed collections are not memoized.
|
|
469
|
+
*/
|
|
452
470
|
private collectCompiledQueryDiagnostics;
|
|
471
|
+
private computeCompiledQueryDiagnostics;
|
|
453
472
|
private getStorageCapabilities;
|
|
454
473
|
private debugQueryPlan;
|
|
455
474
|
private isQueryDebugEnabled;
|
package/dist/store/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xnetjs/data",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -57,12 +57,12 @@
|
|
|
57
57
|
"nanoid": "^5.1.6",
|
|
58
58
|
"y-protocols": "^1.0.6",
|
|
59
59
|
"yjs": "^13.6.24",
|
|
60
|
-
"@xnetjs/core": "0.1.
|
|
61
|
-
"@xnetjs/crypto": "0.1.
|
|
62
|
-
"@xnetjs/identity": "0.1.
|
|
63
|
-
"@xnetjs/sqlite": "0.1.
|
|
64
|
-
"@xnetjs/storage": "0.1.
|
|
65
|
-
"@xnetjs/sync": "0.1.
|
|
60
|
+
"@xnetjs/core": "0.1.1",
|
|
61
|
+
"@xnetjs/crypto": "0.1.1",
|
|
62
|
+
"@xnetjs/identity": "0.1.1",
|
|
63
|
+
"@xnetjs/sqlite": "0.1.1",
|
|
64
|
+
"@xnetjs/storage": "0.1.1",
|
|
65
|
+
"@xnetjs/sync": "0.1.1"
|
|
66
66
|
},
|
|
67
67
|
"devDependencies": {
|
|
68
68
|
"tsup": "^8.0.0",
|