@dxos/echo-pipeline 0.8.3 → 0.8.4-main.03d5cd7b56
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/lib/{browser/chunk-35I6ERLG.mjs → neutral/chunk-VIHYDCCC.mjs} +313 -709
- package/dist/lib/neutral/chunk-VIHYDCCC.mjs.map +7 -0
- package/dist/lib/neutral/chunk-YBC6ONBZ.mjs +247 -0
- package/dist/lib/neutral/chunk-YBC6ONBZ.mjs.map +7 -0
- package/dist/lib/{browser → neutral}/filter/index.mjs +3 -2
- package/dist/lib/neutral/index.mjs +7065 -0
- package/dist/lib/neutral/index.mjs.map +7 -0
- package/dist/lib/neutral/meta.json +1 -0
- package/dist/lib/{browser → neutral}/testing/index.mjs +90 -311
- package/dist/lib/neutral/testing/index.mjs.map +7 -0
- package/dist/types/src/automerge/automerge-host-subduction.test.d.ts +2 -0
- package/dist/types/src/automerge/automerge-host-subduction.test.d.ts.map +1 -0
- package/dist/types/src/automerge/automerge-host.d.ts +99 -54
- package/dist/types/src/automerge/automerge-host.d.ts.map +1 -1
- package/dist/types/src/automerge/automerge-repo-subduction.test.d.ts +2 -0
- package/dist/types/src/automerge/automerge-repo-subduction.test.d.ts.map +1 -0
- package/dist/types/src/automerge/automerge-repo.test.d.ts +1 -1
- package/dist/types/src/automerge/automerge-repo.test.d.ts.map +1 -1
- package/dist/types/src/automerge/automerge-subduction.test.d.ts +2 -0
- package/dist/types/src/automerge/automerge-subduction.test.d.ts.map +1 -0
- package/dist/types/src/automerge/collection-synchronizer.d.ts +25 -4
- package/dist/types/src/automerge/collection-synchronizer.d.ts.map +1 -1
- package/dist/types/src/automerge/echo-data-monitor.d.ts.map +1 -1
- package/dist/types/src/automerge/echo-network-adapter.d.ts +23 -10
- package/dist/types/src/automerge/echo-network-adapter.d.ts.map +1 -1
- package/dist/types/src/automerge/echo-replicator.d.ts +38 -15
- package/dist/types/src/automerge/echo-replicator.d.ts.map +1 -1
- package/dist/types/src/automerge/handle-state.d.ts +52 -0
- package/dist/types/src/automerge/handle-state.d.ts.map +1 -0
- package/dist/types/src/automerge/heads-store.d.ts +9 -2
- package/dist/types/src/automerge/heads-store.d.ts.map +1 -1
- package/dist/types/src/automerge/index.d.ts +2 -1
- package/dist/types/src/automerge/index.d.ts.map +1 -1
- package/dist/types/src/automerge/leveldb-storage-adapter.d.ts +9 -5
- package/dist/types/src/automerge/leveldb-storage-adapter.d.ts.map +1 -1
- package/dist/types/src/automerge/mesh-echo-replicator-connection.d.ts +9 -8
- package/dist/types/src/automerge/mesh-echo-replicator-connection.d.ts.map +1 -1
- package/dist/types/src/automerge/mesh-echo-replicator.d.ts +6 -5
- package/dist/types/src/automerge/mesh-echo-replicator.d.ts.map +1 -1
- package/dist/types/src/automerge/network-protocol.d.ts.map +1 -1
- package/dist/types/src/automerge/space-collection.d.ts.map +1 -1
- package/dist/types/src/common/codec.d.ts +1 -1
- package/dist/types/src/common/codec.d.ts.map +1 -1
- package/dist/types/src/common/feeds.d.ts.map +1 -1
- package/dist/types/src/common/space-id.d.ts.map +1 -1
- package/dist/types/src/db-host/automerge-data-source.d.ts +35 -0
- package/dist/types/src/db-host/automerge-data-source.d.ts.map +1 -0
- package/dist/types/src/db-host/automerge-data-source.test.d.ts +2 -0
- package/dist/types/src/db-host/automerge-data-source.test.d.ts.map +1 -0
- package/dist/types/src/db-host/automerge-metrics.d.ts.map +1 -1
- package/dist/types/src/db-host/data-service.d.ts +8 -7
- package/dist/types/src/db-host/data-service.d.ts.map +1 -1
- package/dist/types/src/db-host/database-root.d.ts +9 -3
- package/dist/types/src/db-host/database-root.d.ts.map +1 -1
- package/dist/types/src/db-host/documents-synchronizer.d.ts +9 -8
- package/dist/types/src/db-host/documents-synchronizer.d.ts.map +1 -1
- package/dist/types/src/db-host/echo-host.d.ts +62 -27
- package/dist/types/src/db-host/echo-host.d.ts.map +1 -1
- package/dist/types/src/db-host/index.d.ts +2 -0
- package/dist/types/src/db-host/index.d.ts.map +1 -1
- package/dist/types/src/db-host/invalidation-hint.d.ts +19 -0
- package/dist/types/src/db-host/invalidation-hint.d.ts.map +1 -0
- package/dist/types/src/db-host/local-queue-service.d.ts +19 -0
- package/dist/types/src/db-host/local-queue-service.d.ts.map +1 -0
- package/dist/types/src/db-host/query-invalidation.test.d.ts +2 -0
- package/dist/types/src/db-host/query-invalidation.test.d.ts.map +1 -0
- package/dist/types/src/db-host/query-service.d.ts +19 -11
- package/dist/types/src/db-host/query-service.d.ts.map +1 -1
- package/dist/types/src/db-host/queue-data-source.d.ts +38 -0
- package/dist/types/src/db-host/queue-data-source.d.ts.map +1 -0
- package/dist/types/src/db-host/queue-service.test.d.ts +2 -0
- package/dist/types/src/db-host/queue-service.test.d.ts.map +1 -0
- package/dist/types/src/db-host/space-state-manager.d.ts +4 -3
- package/dist/types/src/db-host/space-state-manager.d.ts.map +1 -1
- package/dist/types/src/db-host/stub.d.ts +11 -0
- package/dist/types/src/db-host/stub.d.ts.map +1 -0
- package/dist/types/src/edge/echo-edge-replicator.d.ts +10 -7
- package/dist/types/src/edge/echo-edge-replicator.d.ts.map +1 -1
- package/dist/types/src/edge/echo-edge-subduction-replicator.d.ts +39 -0
- package/dist/types/src/edge/echo-edge-subduction-replicator.d.ts.map +1 -0
- package/dist/types/src/edge/echo-edge-subduction-replicator.test.d.ts +2 -0
- package/dist/types/src/edge/echo-edge-subduction-replicator.test.d.ts.map +1 -0
- package/dist/types/src/edge/index.d.ts +1 -0
- package/dist/types/src/edge/index.d.ts.map +1 -1
- package/dist/types/src/edge/inflight-request-limiter.d.ts.map +1 -1
- package/dist/types/src/filter/filter-match.d.ts +4 -1
- package/dist/types/src/filter/filter-match.d.ts.map +1 -1
- package/dist/types/src/metadata/metadata-store.d.ts +1 -1
- package/dist/types/src/metadata/metadata-store.d.ts.map +1 -1
- package/dist/types/src/pipeline/message-selector.d.ts.map +1 -1
- package/dist/types/src/pipeline/pipeline.d.ts +3 -3
- package/dist/types/src/pipeline/pipeline.d.ts.map +1 -1
- package/dist/types/src/pipeline/timeframe-clock.d.ts.map +1 -1
- package/dist/types/src/query/errors.d.ts +22 -10
- package/dist/types/src/query/errors.d.ts.map +1 -1
- package/dist/types/src/query/plan.d.ts +66 -8
- package/dist/types/src/query/plan.d.ts.map +1 -1
- package/dist/types/src/query/query-executor.d.ts +66 -11
- package/dist/types/src/query/query-executor.d.ts.map +1 -1
- package/dist/types/src/query/query-planner.d.ts +12 -1
- package/dist/types/src/query/query-planner.d.ts.map +1 -1
- package/dist/types/src/space/admission-discovery-extension.d.ts.map +1 -1
- package/dist/types/src/space/auth.d.ts +3 -3
- package/dist/types/src/space/auth.d.ts.map +1 -1
- package/dist/types/src/space/control-pipeline.d.ts +5 -4
- package/dist/types/src/space/control-pipeline.d.ts.map +1 -1
- package/dist/types/src/space/space-manager.d.ts +8 -8
- package/dist/types/src/space/space-manager.d.ts.map +1 -1
- package/dist/types/src/space/space-protocol.d.ts +10 -9
- package/dist/types/src/space/space-protocol.d.ts.map +1 -1
- package/dist/types/src/space/space.d.ts +6 -6
- package/dist/types/src/space/space.d.ts.map +1 -1
- package/dist/types/src/testing/change-metadata.d.ts.map +1 -1
- package/dist/types/src/testing/index.d.ts +0 -2
- package/dist/types/src/testing/index.d.ts.map +1 -1
- package/dist/types/src/testing/test-agent-builder.d.ts +2 -2
- package/dist/types/src/testing/test-agent-builder.d.ts.map +1 -1
- package/dist/types/src/testing/test-feed-builder.d.ts.map +1 -1
- package/dist/types/src/testing/test-network-adapter.d.ts +1 -1
- package/dist/types/src/testing/test-network-adapter.d.ts.map +1 -1
- package/dist/types/src/testing/test-replicator.d.ts +14 -13
- package/dist/types/src/testing/test-replicator.d.ts.map +1 -1
- package/dist/types/src/util.d.ts +1 -1
- package/dist/types/src/util.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +56 -54
- package/src/automerge/automerge-host-subduction.test.ts +247 -0
- package/src/automerge/automerge-host.test.ts +65 -39
- package/src/automerge/automerge-host.ts +589 -206
- package/src/automerge/automerge-repo-subduction.test.ts +1049 -0
- package/src/automerge/automerge-repo.test.ts +349 -91
- package/src/automerge/automerge-subduction.test.ts +330 -0
- package/src/automerge/collection-synchronizer.test.ts +53 -17
- package/src/automerge/collection-synchronizer.ts +119 -36
- package/src/automerge/echo-data-monitor.ts +1 -1
- package/src/automerge/echo-network-adapter.test.ts +5 -4
- package/src/automerge/echo-network-adapter.ts +74 -30
- package/src/automerge/echo-replicator.ts +41 -15
- package/src/automerge/handle-state.ts +62 -0
- package/src/automerge/heads-store.ts +42 -3
- package/src/automerge/index.ts +2 -1
- package/src/automerge/leveldb-storage-adapter.ts +40 -11
- package/src/automerge/mesh-echo-replicator-connection.ts +12 -8
- package/src/automerge/mesh-echo-replicator.ts +15 -11
- package/src/automerge/storage-adapter.test.ts +1 -1
- package/src/common/space-id.ts +1 -1
- package/src/db-host/automerge-data-source.test.ts +299 -0
- package/src/db-host/automerge-data-source.ts +187 -0
- package/src/db-host/data-service.ts +25 -26
- package/src/db-host/database-root.ts +20 -8
- package/src/db-host/documents-synchronizer.test.ts +143 -14
- package/src/db-host/documents-synchronizer.ts +72 -55
- package/src/db-host/echo-host.ts +348 -96
- package/src/db-host/index.ts +2 -0
- package/src/db-host/invalidation-hint.ts +50 -0
- package/src/db-host/local-queue-service.ts +127 -0
- package/src/db-host/query-invalidation.test.ts +270 -0
- package/src/db-host/query-service.ts +111 -106
- package/src/db-host/queue-data-source.ts +160 -0
- package/src/db-host/queue-service.test.ts +145 -0
- package/src/db-host/space-state-manager.ts +12 -10
- package/src/db-host/stub.ts +33 -0
- package/src/edge/echo-edge-replicator.test.ts +12 -10
- package/src/edge/echo-edge-replicator.ts +144 -58
- package/src/edge/echo-edge-subduction-replicator.test.ts +158 -0
- package/src/edge/echo-edge-subduction-replicator.ts +377 -0
- package/src/edge/index.ts +1 -0
- package/src/filter/filter-match.test.ts +68 -32
- package/src/filter/filter-match.ts +184 -20
- package/src/metadata/metadata-store.ts +11 -8
- package/src/pipeline/pipeline-stress.test.ts +1 -1
- package/src/pipeline/pipeline.test.ts +2 -2
- package/src/pipeline/pipeline.ts +9 -7
- package/src/query/errors.ts +3 -1
- package/src/query/plan.ts +82 -8
- package/src/query/query-executor.ts +1199 -164
- package/src/query/query-planner.test.ts +993 -81
- package/src/query/query-planner.ts +467 -27
- package/src/space/admission-discovery-extension.ts +3 -3
- package/src/space/auth.ts +6 -6
- package/src/space/control-pipeline.test.ts +5 -4
- package/src/space/control-pipeline.ts +16 -13
- package/src/space/space-manager.browser.test.ts +1 -1
- package/src/space/space-manager.ts +16 -20
- package/src/space/space-protocol.browser.test.ts +15 -14
- package/src/space/space-protocol.test.ts +15 -14
- package/src/space/space-protocol.ts +22 -18
- package/src/space/space.test.ts +3 -3
- package/src/space/space.ts +11 -11
- package/src/testing/index.ts +0 -2
- package/src/testing/test-agent-builder.ts +3 -3
- package/src/testing/test-network-adapter.ts +24 -3
- package/src/testing/test-replicator.ts +19 -20
- package/src/util.ts +7 -3
- package/dist/lib/browser/chunk-35I6ERLG.mjs.map +0 -7
- package/dist/lib/browser/chunk-CGS2ULMK.mjs +0 -11
- package/dist/lib/browser/chunk-CGS2ULMK.mjs.map +0 -7
- package/dist/lib/browser/chunk-TQJTKNMS.mjs +0 -126
- package/dist/lib/browser/chunk-TQJTKNMS.mjs.map +0 -7
- package/dist/lib/browser/index.mjs +0 -4743
- package/dist/lib/browser/index.mjs.map +0 -7
- package/dist/lib/browser/meta.json +0 -1
- package/dist/lib/browser/testing/index.mjs.map +0 -7
- package/dist/lib/node/chunk-HOPOFWAL.cjs +0 -147
- package/dist/lib/node/chunk-HOPOFWAL.cjs.map +0 -7
- package/dist/lib/node/chunk-JXX6LF5U.cjs +0 -2084
- package/dist/lib/node/chunk-JXX6LF5U.cjs.map +0 -7
- package/dist/lib/node/chunk-Q7SFCCGT.cjs +0 -33
- package/dist/lib/node/chunk-Q7SFCCGT.cjs.map +0 -7
- package/dist/lib/node/filter/index.cjs +0 -32
- package/dist/lib/node/filter/index.cjs.map +0 -7
- package/dist/lib/node/index.cjs +0 -4699
- package/dist/lib/node/index.cjs.map +0 -7
- package/dist/lib/node/meta.json +0 -1
- package/dist/lib/node/testing/index.cjs +0 -753
- package/dist/lib/node/testing/index.cjs.map +0 -7
- package/dist/lib/node-esm/chunk-5BHLPT24.mjs +0 -2067
- package/dist/lib/node-esm/chunk-5BHLPT24.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-HSLMI22Q.mjs +0 -11
- package/dist/lib/node-esm/chunk-HSLMI22Q.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-RVK35BS7.mjs +0 -126
- package/dist/lib/node-esm/chunk-RVK35BS7.mjs.map +0 -7
- package/dist/lib/node-esm/filter/index.mjs +0 -11
- package/dist/lib/node-esm/filter/index.mjs.map +0 -7
- package/dist/lib/node-esm/index.mjs +0 -4743
- package/dist/lib/node-esm/index.mjs.map +0 -7
- package/dist/lib/node-esm/meta.json +0 -1
- package/dist/lib/node-esm/testing/index.mjs +0 -742
- package/dist/lib/node-esm/testing/index.mjs.map +0 -7
- package/dist/types/src/db-host/documents-iterator.d.ts +0 -7
- package/dist/types/src/db-host/documents-iterator.d.ts.map +0 -1
- package/dist/types/src/testing/test-data.d.ts +0 -18
- package/dist/types/src/testing/test-data.d.ts.map +0 -1
- package/dist/types/src/testing/test-schema.d.ts +0 -37
- package/dist/types/src/testing/test-schema.d.ts.map +0 -1
- package/src/db-host/documents-iterator.ts +0 -74
- package/src/testing/test-data.ts +0 -127
- package/src/testing/test-schema.ts +0 -53
- /package/dist/lib/{browser → neutral}/filter/index.mjs.map +0 -0
package/src/db-host/echo-host.ts
CHANGED
|
@@ -2,66 +2,77 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
} from '@automerge/automerge-repo';
|
|
12
|
-
|
|
13
|
-
import { LifecycleState, Resource, type Context } from '@dxos/context';
|
|
5
|
+
import { type AnyDocumentId, type AutomergeUrl, type DocHandle, type DocumentId } from '@automerge/automerge-repo';
|
|
6
|
+
import * as SqlClient from '@effect/sql/SqlClient';
|
|
7
|
+
import * as Effect from 'effect/Effect';
|
|
8
|
+
|
|
9
|
+
import { asyncTimeout, DeferredTask, sleep } from '@dxos/async';
|
|
10
|
+
import { Context, LifecycleState, Resource } from '@dxos/context';
|
|
14
11
|
import { todo } from '@dxos/debug';
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
12
|
+
import { type DatabaseDirectory, SpaceDocVersion, createIdFromSpaceKey } from '@dxos/echo-protocol';
|
|
13
|
+
import { RuntimeProvider } from '@dxos/effect';
|
|
14
|
+
import { FeedStore } from '@dxos/feed';
|
|
15
|
+
import { IndexEngine, type IndexingResult } from '@dxos/index-core';
|
|
17
16
|
import { invariant } from '@dxos/invariant';
|
|
18
|
-
import { type PublicKey, type SpaceId } from '@dxos/keys';
|
|
17
|
+
import { type ObjectId, type PublicKey, type SpaceId } from '@dxos/keys';
|
|
19
18
|
import { type LevelDB } from '@dxos/kv-store';
|
|
20
|
-
import {
|
|
19
|
+
import { log } from '@dxos/log';
|
|
20
|
+
import { type FeedProtocol } from '@dxos/protocols';
|
|
21
|
+
import type { SyncQueueRequest } from '@dxos/protocols/proto/dxos/client/services';
|
|
22
|
+
import type * as SqlTransaction from '@dxos/sql-sqlite/SqlTransaction';
|
|
21
23
|
import { trace } from '@dxos/tracing';
|
|
22
24
|
|
|
23
|
-
import { DataServiceImpl } from './data-service';
|
|
24
|
-
import { type DatabaseRoot } from './database-root';
|
|
25
|
-
import { createSelectedDocumentsIterator } from './documents-iterator';
|
|
26
|
-
import { QueryServiceImpl } from './query-service';
|
|
27
|
-
import { SpaceStateManager } from './space-state-manager';
|
|
28
25
|
import {
|
|
29
26
|
AutomergeHost,
|
|
30
|
-
|
|
31
|
-
EchoDataMonitor,
|
|
32
|
-
deriveCollectionIdFromSpaceId,
|
|
33
|
-
type LoadDocOptions,
|
|
27
|
+
type AutomergeReplicator,
|
|
34
28
|
type CreateDocOptions,
|
|
35
|
-
|
|
29
|
+
EchoDataMonitor,
|
|
36
30
|
type EchoDataStats,
|
|
31
|
+
type LoadDocOptions,
|
|
37
32
|
type PeerIdProvider,
|
|
38
33
|
type RootDocumentSpaceKeyProvider,
|
|
34
|
+
deriveCollectionIdFromSpaceId,
|
|
39
35
|
} from '../automerge';
|
|
36
|
+
import { AutomergeDataSource } from './automerge-data-source';
|
|
37
|
+
import { DataServiceImpl } from './data-service';
|
|
38
|
+
import { type DatabaseRoot } from './database-root';
|
|
39
|
+
import { hintFromIndexingResult } from './invalidation-hint';
|
|
40
|
+
import { LocalQueueServiceImpl } from './local-queue-service';
|
|
41
|
+
import { QueryServiceImpl } from './query-service';
|
|
42
|
+
import { QueueDataSource } from './queue-data-source';
|
|
43
|
+
import { SpaceStateManager } from './space-state-manager';
|
|
40
44
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
45
|
+
/**
|
|
46
|
+
* Executes "PRAGMA quick_check;" on SQLite database on startup.
|
|
47
|
+
*
|
|
48
|
+
* NOTE: Keep DISABLED in production, "quick" check is O(dbSize) which took 6 seconds for my relatively-small profile.
|
|
49
|
+
*
|
|
50
|
+
*/
|
|
51
|
+
const RUN_SQLITE_QUICK_CHECK_ON_STARTUP = false;
|
|
52
|
+
|
|
53
|
+
export type EchoHostProps = {
|
|
54
|
+
kv: LevelDB;
|
|
55
|
+
peerIdProvider?: PeerIdProvider;
|
|
56
|
+
getSpaceKeyByRootDocumentId?: RootDocumentSpaceKeyProvider;
|
|
57
|
+
|
|
58
|
+
runtime: RuntimeProvider.RuntimeProvider<SqlClient.SqlClient | SqlTransaction.SqlTransaction>;
|
|
46
59
|
|
|
47
60
|
/**
|
|
61
|
+
* This peer is allowed to assign positions (global-order) to items appended to the queue.
|
|
48
62
|
* @default false
|
|
49
63
|
*/
|
|
50
|
-
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const DEFAULT_INDEXING_CONFIG: EchoHostIndexingConfig = {
|
|
54
|
-
// TODO(dmaretskyi): Disabled by default since embedding generation is expensive.
|
|
55
|
-
fullText: false,
|
|
56
|
-
vector: false,
|
|
57
|
-
};
|
|
64
|
+
assignQueuePositions?: boolean;
|
|
58
65
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
66
|
+
/**
|
|
67
|
+
* Callback to run blocking queue sync.
|
|
68
|
+
*/
|
|
69
|
+
syncQueue?: (ctx: Context, request: SyncQueueRequest) => Promise<void>;
|
|
63
70
|
|
|
64
|
-
|
|
71
|
+
/**
|
|
72
|
+
* Enable Subduction sedimentree transport for Automerge document replication.
|
|
73
|
+
* @default false
|
|
74
|
+
*/
|
|
75
|
+
useSubduction?: boolean;
|
|
65
76
|
};
|
|
66
77
|
|
|
67
78
|
/**
|
|
@@ -71,51 +82,62 @@ export type EchoHostParams = {
|
|
|
71
82
|
* Can sync with pluggable data replicators.
|
|
72
83
|
*/
|
|
73
84
|
export class EchoHost extends Resource {
|
|
74
|
-
private readonly _indexMetadataStore: IndexMetadataStore;
|
|
75
|
-
private readonly _indexer: Indexer;
|
|
76
85
|
private readonly _automergeHost: AutomergeHost;
|
|
77
86
|
private readonly _queryService: QueryServiceImpl;
|
|
78
87
|
private readonly _dataService: DataServiceImpl;
|
|
79
88
|
private readonly _spaceStateManager = new SpaceStateManager();
|
|
80
89
|
private readonly _echoDataMonitor: EchoDataMonitor;
|
|
81
90
|
|
|
82
|
-
|
|
83
|
-
|
|
91
|
+
private readonly _automergeDataSource: AutomergeDataSource;
|
|
92
|
+
private readonly _indexEngine: IndexEngine;
|
|
93
|
+
private readonly _runtime: RuntimeProvider.RuntimeProvider<SqlClient.SqlClient | SqlTransaction.SqlTransaction>;
|
|
94
|
+
private readonly _feedStore: FeedStore;
|
|
95
|
+
private readonly _queueDataSource: QueueDataSource;
|
|
96
|
+
|
|
97
|
+
private _updateIndexes!: DeferredTask;
|
|
98
|
+
|
|
99
|
+
private _queuesService: FeedProtocol.QueueService;
|
|
84
100
|
|
|
85
|
-
|
|
101
|
+
private _indexesUpToDate = false;
|
|
102
|
+
|
|
103
|
+
constructor({
|
|
104
|
+
kv,
|
|
105
|
+
peerIdProvider,
|
|
106
|
+
getSpaceKeyByRootDocumentId,
|
|
107
|
+
runtime,
|
|
108
|
+
assignQueuePositions = false,
|
|
109
|
+
syncQueue,
|
|
110
|
+
useSubduction,
|
|
111
|
+
}: EchoHostProps) {
|
|
112
|
+
super();
|
|
86
113
|
|
|
87
|
-
this._indexMetadataStore = new IndexMetadataStore({ db: kv.sublevel('index-metadata') });
|
|
88
114
|
this._echoDataMonitor = new EchoDataMonitor();
|
|
89
115
|
this._automergeHost = new AutomergeHost({
|
|
90
116
|
db: kv,
|
|
91
117
|
dataMonitor: this._echoDataMonitor,
|
|
92
|
-
indexMetadataStore: this._indexMetadataStore,
|
|
93
118
|
peerIdProvider,
|
|
94
119
|
getSpaceKeyByRootDocumentId,
|
|
120
|
+
useSubduction,
|
|
95
121
|
});
|
|
96
122
|
|
|
97
|
-
this.
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
enabled: true,
|
|
106
|
-
indexes: [
|
|
107
|
-
//
|
|
108
|
-
{ kind: IndexKind.Kind.SCHEMA_MATCH },
|
|
109
|
-
{ kind: IndexKind.Kind.GRAPH },
|
|
110
|
-
|
|
111
|
-
...(indexingConfig.fullText ? [{ kind: IndexKind.Kind.FULL_TEXT }] : []),
|
|
112
|
-
...(indexingConfig.vector ? [{ kind: IndexKind.Kind.VECTOR }] : []),
|
|
113
|
-
],
|
|
123
|
+
this._runtime = runtime;
|
|
124
|
+
this._automergeDataSource = new AutomergeDataSource(this._automergeHost);
|
|
125
|
+
|
|
126
|
+
this._feedStore = new FeedStore({ assignPositions: assignQueuePositions, localActorId: crypto.randomUUID() });
|
|
127
|
+
this._queueDataSource = new QueueDataSource({
|
|
128
|
+
feedStore: this._feedStore,
|
|
129
|
+
runtime: this._runtime,
|
|
130
|
+
getSpaceIds: () => this._spaceStateManager.spaceIds,
|
|
114
131
|
});
|
|
132
|
+
this._queuesService = new LocalQueueServiceImpl(runtime, this._feedStore, syncQueue);
|
|
133
|
+
|
|
134
|
+
// SQLite-based index engine for all queries.
|
|
135
|
+
this._indexEngine = new IndexEngine();
|
|
115
136
|
|
|
116
137
|
this._queryService = new QueryServiceImpl({
|
|
117
138
|
automergeHost: this._automergeHost,
|
|
118
|
-
|
|
139
|
+
indexEngine: this._indexEngine,
|
|
140
|
+
runtime: this._runtime,
|
|
119
141
|
spaceStateManager: this._spaceStateManager,
|
|
120
142
|
});
|
|
121
143
|
|
|
@@ -123,7 +145,9 @@ export class EchoHost extends Resource {
|
|
|
123
145
|
automergeHost: this._automergeHost,
|
|
124
146
|
spaceStateManager: this._spaceStateManager,
|
|
125
147
|
updateIndexes: async () => {
|
|
126
|
-
|
|
148
|
+
do {
|
|
149
|
+
await this._updateIndexes.runBlocking();
|
|
150
|
+
} while (!this._indexesUpToDate);
|
|
127
151
|
},
|
|
128
152
|
});
|
|
129
153
|
|
|
@@ -168,6 +192,10 @@ export class EchoHost extends Resource {
|
|
|
168
192
|
});
|
|
169
193
|
}
|
|
170
194
|
|
|
195
|
+
get spaceIds(): SpaceId[] {
|
|
196
|
+
return this._spaceStateManager.spaceIds;
|
|
197
|
+
}
|
|
198
|
+
|
|
171
199
|
get queryService(): QueryServiceImpl {
|
|
172
200
|
return this._queryService;
|
|
173
201
|
}
|
|
@@ -176,58 +204,93 @@ export class EchoHost extends Resource {
|
|
|
176
204
|
return this._dataService;
|
|
177
205
|
}
|
|
178
206
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
*/
|
|
182
|
-
get automergeRepo(): Repo {
|
|
183
|
-
return this._automergeHost.repo;
|
|
207
|
+
get queuesService(): FeedProtocol.QueueService {
|
|
208
|
+
return this._queuesService;
|
|
184
209
|
}
|
|
185
210
|
|
|
186
211
|
get roots(): ReadonlyMap<DocumentId, DatabaseRoot> {
|
|
187
212
|
return this._spaceStateManager.roots;
|
|
188
213
|
}
|
|
189
214
|
|
|
215
|
+
get feedStore(): FeedStore {
|
|
216
|
+
return this._feedStore;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Index engine for queries.
|
|
221
|
+
*/
|
|
222
|
+
get indexEngine(): IndexEngine {
|
|
223
|
+
return this._indexEngine;
|
|
224
|
+
}
|
|
225
|
+
|
|
190
226
|
protected override async _open(ctx: Context): Promise<void> {
|
|
191
|
-
|
|
192
|
-
await this.
|
|
227
|
+
log('echo-host: opening automerge host...');
|
|
228
|
+
await this._automergeHost.open(ctx);
|
|
229
|
+
log('echo-host: automerge host opened');
|
|
230
|
+
|
|
231
|
+
log('echo-host: opening query service...');
|
|
193
232
|
await this._queryService.open(ctx);
|
|
233
|
+
log('echo-host: query service opened');
|
|
234
|
+
|
|
235
|
+
log('echo-host: opening space state manager...');
|
|
194
236
|
await this._spaceStateManager.open(ctx);
|
|
237
|
+
log('echo-host: space state manager opened');
|
|
238
|
+
|
|
239
|
+
// Timeout needs to be outside effect runtime to catch the layer hanging on startup.
|
|
240
|
+
await asyncTimeout(
|
|
241
|
+
RuntimeProvider.runPromise(this._runtime)(testSqlite()),
|
|
242
|
+
15_000,
|
|
243
|
+
new Error('SQLite quick check timed out'),
|
|
244
|
+
);
|
|
245
|
+
|
|
246
|
+
log('echo-host: running index engine migration...');
|
|
247
|
+
await RuntimeProvider.runPromise(this._runtime)(this._indexEngine.migrate());
|
|
248
|
+
log('echo-host: index engine migration done');
|
|
249
|
+
this._updateIndexes = new DeferredTask(this._ctx, this._runUpdateIndexes);
|
|
250
|
+
|
|
251
|
+
log('echo-host: running feed store migration...');
|
|
252
|
+
await RuntimeProvider.runPromise(this._runtime)(this._feedStore.migrate());
|
|
253
|
+
log('echo-host: feed store migration done');
|
|
254
|
+
this._feedStore.onNewBlocks.on(this._ctx, () => {
|
|
255
|
+
this._updateIndexes.schedule();
|
|
256
|
+
});
|
|
195
257
|
|
|
196
258
|
this._spaceStateManager.spaceDocumentListUpdated.on(this._ctx, (e) => {
|
|
197
259
|
if (e.previousRootId) {
|
|
198
260
|
void this._automergeHost.clearLocalCollectionState(deriveCollectionIdFromSpaceId(e.spaceId, e.previousRootId));
|
|
199
261
|
}
|
|
200
|
-
// TODO(yaroslav): remove collection without spaceRootId after release (production<->staging interop)
|
|
201
|
-
void this._automergeHost.updateLocalCollectionState(deriveCollectionIdFromSpaceId(e.spaceId), e.documentIds);
|
|
202
262
|
void this._automergeHost.updateLocalCollectionState(
|
|
203
263
|
deriveCollectionIdFromSpaceId(e.spaceId, e.spaceRootId),
|
|
204
264
|
e.documentIds,
|
|
205
265
|
);
|
|
206
266
|
});
|
|
207
267
|
this._automergeHost.documentsSaved.on(this._ctx, () => {
|
|
208
|
-
this.
|
|
268
|
+
this._updateIndexes.schedule();
|
|
209
269
|
});
|
|
270
|
+
this._updateIndexes.schedule();
|
|
271
|
+
log('echo-host: open complete');
|
|
210
272
|
}
|
|
211
273
|
|
|
212
274
|
protected override async _close(ctx: Context): Promise<void> {
|
|
213
275
|
await this._queryService.close(ctx);
|
|
214
276
|
await this._spaceStateManager.close(ctx);
|
|
215
|
-
await this._indexer.close(ctx);
|
|
216
277
|
await this._automergeHost.close();
|
|
217
278
|
}
|
|
218
279
|
|
|
219
280
|
/**
|
|
220
281
|
* Flush all pending writes to the underlying storage.
|
|
221
282
|
*/
|
|
222
|
-
async flush(): Promise<void> {
|
|
223
|
-
await this._automergeHost.
|
|
283
|
+
async flush(ctx: Context): Promise<void> {
|
|
284
|
+
await this._automergeHost.flush(ctx);
|
|
224
285
|
}
|
|
225
286
|
|
|
226
287
|
/**
|
|
227
288
|
* Perform any pending index updates.
|
|
228
289
|
*/
|
|
229
290
|
async updateIndexes(): Promise<void> {
|
|
230
|
-
|
|
291
|
+
do {
|
|
292
|
+
await this._updateIndexes.runBlocking();
|
|
293
|
+
} while (!this._indexesUpToDate);
|
|
231
294
|
}
|
|
232
295
|
|
|
233
296
|
/**
|
|
@@ -237,25 +300,25 @@ export class EchoHost extends Resource {
|
|
|
237
300
|
return await this._automergeHost.loadDoc(ctx, documentId, opts);
|
|
238
301
|
}
|
|
239
302
|
|
|
240
|
-
async exportDoc(
|
|
241
|
-
return await this._automergeHost.exportDoc(
|
|
303
|
+
async exportDoc(id: AnyDocumentId): Promise<Uint8Array> {
|
|
304
|
+
return await this._automergeHost.exportDoc(id);
|
|
242
305
|
}
|
|
243
306
|
|
|
244
307
|
/**
|
|
245
308
|
* Create new persisted document.
|
|
246
309
|
*/
|
|
247
|
-
createDoc<T>(initialValue?: T, opts?: CreateDocOptions): DocHandle<T
|
|
310
|
+
async createDoc<T>(initialValue?: T, opts?: CreateDocOptions): Promise<DocHandle<T>> {
|
|
248
311
|
return this._automergeHost.createDoc(initialValue, opts);
|
|
249
312
|
}
|
|
250
313
|
|
|
251
314
|
/**
|
|
252
315
|
* Create new space root.
|
|
253
316
|
*/
|
|
254
|
-
async createSpaceRoot(spaceKey: PublicKey): Promise<DatabaseRoot> {
|
|
317
|
+
async createSpaceRoot(ctx: Context, spaceKey: PublicKey): Promise<DatabaseRoot> {
|
|
255
318
|
invariant(this._lifecycleState === LifecycleState.OPEN);
|
|
256
319
|
const spaceId = await createIdFromSpaceKey(spaceKey);
|
|
257
320
|
|
|
258
|
-
const automergeRoot = this._automergeHost.createDoc<DatabaseDirectory>({
|
|
321
|
+
const automergeRoot = await this._automergeHost.createDoc<DatabaseDirectory>({
|
|
259
322
|
version: SpaceDocVersion.CURRENT,
|
|
260
323
|
access: { spaceKey: spaceKey.toHex() },
|
|
261
324
|
|
|
@@ -264,43 +327,232 @@ export class EchoHost extends Resource {
|
|
|
264
327
|
links: {},
|
|
265
328
|
});
|
|
266
329
|
|
|
267
|
-
await this._automergeHost.flush({ documentIds: [automergeRoot.documentId] });
|
|
330
|
+
await this._automergeHost.flush(ctx, { documentIds: [automergeRoot.documentId] });
|
|
268
331
|
|
|
269
|
-
return await this.openSpaceRoot(spaceId, automergeRoot.url);
|
|
332
|
+
return await this.openSpaceRoot(ctx, spaceId, automergeRoot.url);
|
|
270
333
|
}
|
|
271
334
|
|
|
272
335
|
// TODO(dmaretskyi): Change to document id.
|
|
273
|
-
async openSpaceRoot(spaceId: SpaceId, automergeUrl: AutomergeUrl): Promise<DatabaseRoot> {
|
|
336
|
+
async openSpaceRoot(ctx: Context, spaceId: SpaceId, automergeUrl: AutomergeUrl): Promise<DatabaseRoot> {
|
|
274
337
|
invariant(this._lifecycleState === LifecycleState.OPEN);
|
|
275
|
-
const handle = await this._automergeHost.
|
|
276
|
-
|
|
338
|
+
const handle = await this._automergeHost.loadDoc<DatabaseDirectory>(ctx, automergeUrl, {
|
|
339
|
+
fetchFromNetwork: true,
|
|
340
|
+
});
|
|
341
|
+
const query = this._automergeHost.findWithProgress<DatabaseDirectory>(handle.documentId);
|
|
277
342
|
|
|
278
|
-
return this._spaceStateManager.assignRootToSpace(spaceId,
|
|
343
|
+
return this._spaceStateManager.assignRootToSpace(spaceId, query);
|
|
279
344
|
}
|
|
280
345
|
|
|
281
346
|
// TODO(dmaretskyi): Change to document id.
|
|
282
|
-
async closeSpaceRoot(
|
|
347
|
+
async closeSpaceRoot(_automergeUrl: AutomergeUrl): Promise<void> {
|
|
283
348
|
todo();
|
|
284
349
|
}
|
|
285
350
|
|
|
286
351
|
/**
|
|
287
352
|
* Install data replicator.
|
|
288
353
|
*/
|
|
289
|
-
async addReplicator(replicator:
|
|
290
|
-
await this._automergeHost.addReplicator(replicator);
|
|
354
|
+
async addReplicator(ctx: Context, replicator: AutomergeReplicator): Promise<void> {
|
|
355
|
+
await this._automergeHost.addReplicator(ctx, replicator);
|
|
291
356
|
}
|
|
292
357
|
|
|
293
358
|
/**
|
|
294
359
|
* Remove data replicator.
|
|
295
360
|
*/
|
|
296
|
-
async removeReplicator(replicator:
|
|
361
|
+
async removeReplicator(replicator: AutomergeReplicator): Promise<void> {
|
|
297
362
|
await this._automergeHost.removeReplicator(replicator);
|
|
298
363
|
}
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* Run collection sync for the given space.
|
|
367
|
+
* Does not wait for the sync to complete.
|
|
368
|
+
*/
|
|
369
|
+
async runCollectionSync(spaceId: SpaceId) {
|
|
370
|
+
const root = this._spaceStateManager.getRootBySpaceId(spaceId);
|
|
371
|
+
if (!root) {
|
|
372
|
+
throw new Error(`Space not found: ${spaceId}`);
|
|
373
|
+
}
|
|
374
|
+
this._automergeHost.refreshCollection(deriveCollectionIdFromSpaceId(spaceId, root.documentId));
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* Get all feeds and their blocks for a space.
|
|
379
|
+
* Used for space archive export.
|
|
380
|
+
*/
|
|
381
|
+
async getAllFeedsForSpace(
|
|
382
|
+
spaceId: SpaceId,
|
|
383
|
+
): Promise<Array<{ feedId: string; feedNamespace: string; blocks: FeedProtocol.Block[] }>> {
|
|
384
|
+
return RuntimeProvider.runPromise(this._runtime)(this._feedStore.getAllFeedsForSpace({ spaceId }));
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
private _runUpdateIndexes = async (): Promise<void> => {
|
|
388
|
+
if (this._ctx.disposed || !this.isOpen) {
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
try {
|
|
393
|
+
const combinedResult = _makeEmptyMergedResult();
|
|
394
|
+
|
|
395
|
+
{
|
|
396
|
+
performance.mark('indexEngine.update.automerge:start');
|
|
397
|
+
const result = await this._indexEngine
|
|
398
|
+
.update(this._ctx, this._automergeDataSource, { spaceId: null, limit: 50 })
|
|
399
|
+
.pipe(RuntimeProvider.runPromise(this._runtime));
|
|
400
|
+
_mergeInto(combinedResult, result);
|
|
401
|
+
performance.measure('Index Automerge', {
|
|
402
|
+
start: 'indexEngine.update.automerge:start',
|
|
403
|
+
detail: {
|
|
404
|
+
devtools: {
|
|
405
|
+
dataType: 'track-entry',
|
|
406
|
+
track: 'Indexing',
|
|
407
|
+
trackGroup: 'ECHO', // Group related tracks together
|
|
408
|
+
color: 'tertiary-dark',
|
|
409
|
+
properties: [['count', result.updated]],
|
|
410
|
+
},
|
|
411
|
+
},
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
if (this._ctx.disposed || !this.isOpen) {
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
{
|
|
419
|
+
performance.mark('indexEngine.update.queue:start');
|
|
420
|
+
const result = await this._indexEngine
|
|
421
|
+
.update(this._ctx, this._queueDataSource, { spaceId: null, limit: 50 })
|
|
422
|
+
.pipe(RuntimeProvider.runPromise(this._runtime));
|
|
423
|
+
_mergeInto(combinedResult, result);
|
|
424
|
+
performance.measure('Index Queues', {
|
|
425
|
+
start: 'indexEngine.update.queue:start',
|
|
426
|
+
detail: {
|
|
427
|
+
devtools: {
|
|
428
|
+
dataType: 'track-entry',
|
|
429
|
+
track: 'Indexing',
|
|
430
|
+
trackGroup: 'ECHO',
|
|
431
|
+
color: 'tertiary-dark',
|
|
432
|
+
properties: [['count', result.updated]],
|
|
433
|
+
},
|
|
434
|
+
},
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
log.verbose('indexEngine update completed', {
|
|
439
|
+
updated: combinedResult.updated,
|
|
440
|
+
done: combinedResult.done,
|
|
441
|
+
spaces: combinedResult.spaces.size,
|
|
442
|
+
queues: combinedResult.queues.size,
|
|
443
|
+
documents: combinedResult.documents.size,
|
|
444
|
+
types: combinedResult.types.size,
|
|
445
|
+
objects: combinedResult.objects.size,
|
|
446
|
+
});
|
|
447
|
+
await sleep(1);
|
|
448
|
+
if (!combinedResult.done) {
|
|
449
|
+
this._indexesUpToDate = false;
|
|
450
|
+
this._updateIndexes!.schedule();
|
|
451
|
+
} else {
|
|
452
|
+
this._indexesUpToDate = true;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
// Invalidate queries after index update — the indexer is the sole invalidation source.
|
|
456
|
+
const hint = hintFromIndexingResult(combinedResult);
|
|
457
|
+
if (hint) {
|
|
458
|
+
this._queryService.invalidateQueries(hint);
|
|
459
|
+
}
|
|
460
|
+
} catch (err) {
|
|
461
|
+
if (this._ctx.disposed || !this.isOpen) {
|
|
462
|
+
return;
|
|
463
|
+
}
|
|
464
|
+
log.catch(err);
|
|
465
|
+
// Failsafe: prevent queries from freezing if the indexer faults.
|
|
466
|
+
this._queryService.invalidateQueries();
|
|
467
|
+
throw err;
|
|
468
|
+
}
|
|
469
|
+
};
|
|
299
470
|
}
|
|
300
471
|
|
|
301
472
|
export type { EchoDataStats };
|
|
302
473
|
|
|
474
|
+
type MutableIndexingAccumulator = {
|
|
475
|
+
updated: number;
|
|
476
|
+
done: boolean;
|
|
477
|
+
spaces: Set<SpaceId>;
|
|
478
|
+
queues: Set<ObjectId>;
|
|
479
|
+
documents: Set<string>;
|
|
480
|
+
types: Set<string>;
|
|
481
|
+
objects: Set<ObjectId>;
|
|
482
|
+
};
|
|
483
|
+
|
|
484
|
+
const _makeEmptyMergedResult = (): MutableIndexingAccumulator => ({
|
|
485
|
+
updated: 0,
|
|
486
|
+
done: true,
|
|
487
|
+
spaces: new Set(),
|
|
488
|
+
queues: new Set(),
|
|
489
|
+
documents: new Set(),
|
|
490
|
+
types: new Set(),
|
|
491
|
+
objects: new Set(),
|
|
492
|
+
});
|
|
493
|
+
|
|
494
|
+
const _mergeInto = (acc: MutableIndexingAccumulator, r: IndexingResult): void => {
|
|
495
|
+
acc.updated += r.updated;
|
|
496
|
+
acc.done = acc.done && r.done;
|
|
497
|
+
for (const s of r.spaces) {
|
|
498
|
+
acc.spaces.add(s);
|
|
499
|
+
}
|
|
500
|
+
for (const q of r.queues) {
|
|
501
|
+
acc.queues.add(q);
|
|
502
|
+
}
|
|
503
|
+
for (const d of r.documents) {
|
|
504
|
+
acc.documents.add(d);
|
|
505
|
+
}
|
|
506
|
+
for (const t of r.types) {
|
|
507
|
+
acc.types.add(t);
|
|
508
|
+
}
|
|
509
|
+
for (const o of r.objects) {
|
|
510
|
+
acc.objects.add(o);
|
|
511
|
+
}
|
|
512
|
+
};
|
|
513
|
+
|
|
303
514
|
export type EchoStatsDiagnostic = {
|
|
304
515
|
loadedDocsCount: number;
|
|
305
516
|
dataStats: EchoDataStats;
|
|
306
517
|
};
|
|
518
|
+
|
|
519
|
+
/**
|
|
520
|
+
* Sqlite health check on startup.
|
|
521
|
+
*
|
|
522
|
+
* Always runs a cheap schema-level probe (`PRAGMA schema_version` + a read from
|
|
523
|
+
* `sqlite_schema`). These do not walk user data, but they fail fast if the DB
|
|
524
|
+
* file header or schema root page is unreadable.
|
|
525
|
+
*
|
|
526
|
+
* Additionally runs `PRAGMA quick_check` when `RUN_SQLITE_QUICK_CHECK_ON_STARTUP`
|
|
527
|
+
* is enabled. That walks every page and is O(dbSize) — slow on OPFS.
|
|
528
|
+
*/
|
|
529
|
+
const testSqlite = () =>
|
|
530
|
+
Effect.gen(function* () {
|
|
531
|
+
log('begin sqlite health check');
|
|
532
|
+
const sql = yield* SqlClient.SqlClient;
|
|
533
|
+
const databases = yield* sql<{ seq: number; name: string; file: string }>`PRAGMA database_list`;
|
|
534
|
+
log('SQLite databases', { databases });
|
|
535
|
+
|
|
536
|
+
// Cheap header/schema probe: forces SQLite to read the file header and
|
|
537
|
+
// schema root page. Catches catastrophic corruption (bad header,
|
|
538
|
+
// unreadable schema) without walking user data.
|
|
539
|
+
const [schemaVersion] = yield* sql<{ schema_version: number }>`PRAGMA schema_version`;
|
|
540
|
+
const [schemaCount] = yield* sql<{ n: number }>`SELECT count(*) AS n FROM sqlite_schema`;
|
|
541
|
+
log('sqlite schema probe passed', {
|
|
542
|
+
schemaVersion: schemaVersion.schema_version,
|
|
543
|
+
objectCount: schemaCount.n,
|
|
544
|
+
});
|
|
545
|
+
|
|
546
|
+
if (RUN_SQLITE_QUICK_CHECK_ON_STARTUP) {
|
|
547
|
+
// NOTE: This is slow on non-trivial databases: O(dbSize).
|
|
548
|
+
log('starting sqlite quick_check');
|
|
549
|
+
const [result] = yield* sql<{ quick_check: string }>`PRAGMA quick_check`;
|
|
550
|
+
if (result.quick_check !== 'ok') {
|
|
551
|
+
throw new Error('SQLite quick check failed');
|
|
552
|
+
}
|
|
553
|
+
log('sqlite quick_check passed');
|
|
554
|
+
} else {
|
|
555
|
+
log('sqlite quick_check skipped');
|
|
556
|
+
}
|
|
557
|
+
log('sqlite health check complete');
|
|
558
|
+
});
|
package/src/db-host/index.ts
CHANGED
|
@@ -2,9 +2,11 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
+
export * from './automerge-data-source';
|
|
5
6
|
export * from './data-service';
|
|
6
7
|
export * from './documents-synchronizer';
|
|
7
8
|
export * from './echo-host';
|
|
8
9
|
export * from './database-root';
|
|
10
|
+
export * from './invalidation-hint';
|
|
9
11
|
export * from './query-service';
|
|
10
12
|
export * from './space-state-manager';
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2026 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import type { IndexingResult } from '@dxos/index-core';
|
|
6
|
+
import type { ObjectId, SpaceId } from '@dxos/keys';
|
|
7
|
+
|
|
8
|
+
export type InvalidationHint = {
|
|
9
|
+
spaceIds?: ReadonlySet<SpaceId>;
|
|
10
|
+
queueIds?: ReadonlySet<ObjectId>;
|
|
11
|
+
typenames?: ReadonlySet<string>;
|
|
12
|
+
objectIds?: ReadonlySet<ObjectId>;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Converts an IndexingResult to an InvalidationHint.
|
|
17
|
+
* Returns undefined when nothing was indexed (no need to invalidate).
|
|
18
|
+
*/
|
|
19
|
+
export const hintFromIndexingResult = (r: IndexingResult): InvalidationHint | undefined => {
|
|
20
|
+
if (r.updated === 0) {
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
return {
|
|
24
|
+
spaceIds: r.spaces.size > 0 ? r.spaces : undefined,
|
|
25
|
+
queueIds: r.queues.size > 0 ? r.queues : undefined,
|
|
26
|
+
typenames: r.types.size > 0 ? r.types : undefined,
|
|
27
|
+
objectIds: r.objects.size > 0 ? r.objects : undefined,
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Merges two hints for the same tick.
|
|
33
|
+
* If either side leaves a dimension unconstrained (undefined), the merged result is also unconstrained on that dimension.
|
|
34
|
+
*/
|
|
35
|
+
export const mergeHints = (a: InvalidationHint, b: InvalidationHint): InvalidationHint => ({
|
|
36
|
+
spaceIds: unionOrUndefined(a.spaceIds, b.spaceIds),
|
|
37
|
+
queueIds: unionOrUndefined(a.queueIds, b.queueIds),
|
|
38
|
+
typenames: unionOrUndefined(a.typenames, b.typenames),
|
|
39
|
+
objectIds: unionOrUndefined(a.objectIds, b.objectIds),
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
const unionOrUndefined = <T>(
|
|
43
|
+
a: ReadonlySet<T> | undefined,
|
|
44
|
+
b: ReadonlySet<T> | undefined,
|
|
45
|
+
): ReadonlySet<T> | undefined => {
|
|
46
|
+
if (a === undefined || b === undefined) {
|
|
47
|
+
return undefined;
|
|
48
|
+
}
|
|
49
|
+
return new Set<T>([...a, ...b]);
|
|
50
|
+
};
|