@dxos/echo-pipeline 0.8.3 → 0.8.4-main.05e74ebcff
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/LICENSE +102 -5
- package/README.md +1 -1
- package/dist/lib/neutral/chunk-46DCRDA7.mjs +271 -0
- package/dist/lib/neutral/chunk-46DCRDA7.mjs.map +7 -0
- package/dist/lib/{browser/chunk-35I6ERLG.mjs → neutral/chunk-LR7UFVQX.mjs} +313 -709
- package/dist/lib/neutral/chunk-LR7UFVQX.mjs.map +7 -0
- package/dist/lib/{browser → neutral}/filter/index.mjs +3 -2
- package/dist/lib/neutral/index.mjs +7332 -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 +119 -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 +46 -15
- package/dist/types/src/automerge/echo-replicator.d.ts.map +1 -1
- package/dist/types/src/automerge/errors.d.ts +35 -0
- package/dist/types/src/automerge/errors.d.ts.map +1 -0
- 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 +3 -1
- package/dist/types/src/automerge/index.d.ts.map +1 -1
- package/dist/types/src/automerge/leveldb-storage-adapter.d.ts +11 -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 +13 -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 +30 -8
- package/dist/types/src/db-host/documents-synchronizer.d.ts.map +1 -1
- package/dist/types/src/db-host/echo-host.d.ts +74 -28
- 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 +59 -55
- package/src/automerge/automerge-host-subduction.test.ts +263 -0
- package/src/automerge/automerge-host.test.ts +162 -39
- package/src/automerge/automerge-host.ts +679 -201
- package/src/automerge/automerge-repo-subduction.test.ts +1054 -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 +98 -17
- package/src/automerge/collection-synchronizer.ts +141 -37
- 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 +76 -31
- package/src/automerge/echo-replicator.ts +50 -15
- package/src/automerge/errors.ts +10 -0
- package/src/automerge/handle-state.ts +62 -0
- package/src/automerge/heads-store.ts +42 -3
- package/src/automerge/index.ts +3 -1
- package/src/automerge/leveldb-storage-adapter.ts +77 -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 +35 -26
- package/src/db-host/database-root.ts +20 -8
- package/src/db-host/documents-synchronizer.test.ts +145 -14
- package/src/db-host/documents-synchronizer.ts +163 -51
- package/src/db-host/echo-host.ts +384 -99
- 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 +230 -21
- 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 +84 -9
- package/src/query/query-executor.ts +1197 -165
- package/src/query/query-planner.test.ts +993 -81
- package/src/query/query-planner.ts +503 -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,60 +82,71 @@ 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;
|
|
84
96
|
|
|
85
|
-
|
|
97
|
+
private _updateIndexes!: DeferredTask;
|
|
98
|
+
|
|
99
|
+
private _queuesService: FeedProtocol.QueueService;
|
|
100
|
+
|
|
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
|
|
|
122
144
|
this._dataService = new DataServiceImpl({
|
|
123
145
|
automergeHost: this._automergeHost,
|
|
124
146
|
spaceStateManager: this._spaceStateManager,
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
147
|
+
// Delegate to the public method so the closed-host early-out and
|
|
148
|
+
// cooperative loop apply uniformly to the RPC handler path.
|
|
149
|
+
updateIndexes: () => this.updateIndexes(),
|
|
128
150
|
});
|
|
129
151
|
|
|
130
152
|
trace.diagnostic<EchoStatsDiagnostic>({
|
|
@@ -168,6 +190,10 @@ export class EchoHost extends Resource {
|
|
|
168
190
|
});
|
|
169
191
|
}
|
|
170
192
|
|
|
193
|
+
get spaceIds(): SpaceId[] {
|
|
194
|
+
return this._spaceStateManager.spaceIds;
|
|
195
|
+
}
|
|
196
|
+
|
|
171
197
|
get queryService(): QueryServiceImpl {
|
|
172
198
|
return this._queryService;
|
|
173
199
|
}
|
|
@@ -176,86 +202,149 @@ export class EchoHost extends Resource {
|
|
|
176
202
|
return this._dataService;
|
|
177
203
|
}
|
|
178
204
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
*/
|
|
182
|
-
get automergeRepo(): Repo {
|
|
183
|
-
return this._automergeHost.repo;
|
|
205
|
+
get queuesService(): FeedProtocol.QueueService {
|
|
206
|
+
return this._queuesService;
|
|
184
207
|
}
|
|
185
208
|
|
|
186
209
|
get roots(): ReadonlyMap<DocumentId, DatabaseRoot> {
|
|
187
210
|
return this._spaceStateManager.roots;
|
|
188
211
|
}
|
|
189
212
|
|
|
213
|
+
get feedStore(): FeedStore {
|
|
214
|
+
return this._feedStore;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Index engine for queries.
|
|
219
|
+
*/
|
|
220
|
+
get indexEngine(): IndexEngine {
|
|
221
|
+
return this._indexEngine;
|
|
222
|
+
}
|
|
223
|
+
|
|
190
224
|
protected override async _open(ctx: Context): Promise<void> {
|
|
191
|
-
|
|
192
|
-
await this.
|
|
225
|
+
log('echo-host: opening automerge host...');
|
|
226
|
+
await this._automergeHost.open(ctx);
|
|
227
|
+
log('echo-host: automerge host opened');
|
|
228
|
+
|
|
229
|
+
log('echo-host: opening query service...');
|
|
193
230
|
await this._queryService.open(ctx);
|
|
231
|
+
log('echo-host: query service opened');
|
|
232
|
+
|
|
233
|
+
log('echo-host: opening space state manager...');
|
|
194
234
|
await this._spaceStateManager.open(ctx);
|
|
235
|
+
log('echo-host: space state manager opened');
|
|
236
|
+
|
|
237
|
+
// Timeout needs to be outside effect runtime to catch the layer hanging on startup.
|
|
238
|
+
await asyncTimeout(
|
|
239
|
+
RuntimeProvider.runPromise(this._runtime)(testSqlite()),
|
|
240
|
+
15_000,
|
|
241
|
+
new Error('SQLite quick check timed out'),
|
|
242
|
+
);
|
|
243
|
+
|
|
244
|
+
log('echo-host: running index engine migration...');
|
|
245
|
+
await RuntimeProvider.runPromise(this._runtime)(this._indexEngine.migrate());
|
|
246
|
+
log('echo-host: index engine migration done');
|
|
247
|
+
this._updateIndexes = new DeferredTask(this._ctx, this._runUpdateIndexes);
|
|
248
|
+
|
|
249
|
+
log('echo-host: running feed store migration...');
|
|
250
|
+
await RuntimeProvider.runPromise(this._runtime)(this._feedStore.migrate());
|
|
251
|
+
log('echo-host: feed store migration done');
|
|
252
|
+
this._feedStore.onNewBlocks.on(this._ctx, () => {
|
|
253
|
+
this._updateIndexes.schedule();
|
|
254
|
+
});
|
|
195
255
|
|
|
196
256
|
this._spaceStateManager.spaceDocumentListUpdated.on(this._ctx, (e) => {
|
|
197
257
|
if (e.previousRootId) {
|
|
198
258
|
void this._automergeHost.clearLocalCollectionState(deriveCollectionIdFromSpaceId(e.spaceId, e.previousRootId));
|
|
199
259
|
}
|
|
200
|
-
// TODO(yaroslav): remove collection without spaceRootId after release (production<->staging interop)
|
|
201
|
-
void this._automergeHost.updateLocalCollectionState(deriveCollectionIdFromSpaceId(e.spaceId), e.documentIds);
|
|
202
260
|
void this._automergeHost.updateLocalCollectionState(
|
|
203
261
|
deriveCollectionIdFromSpaceId(e.spaceId, e.spaceRootId),
|
|
204
262
|
e.documentIds,
|
|
205
263
|
);
|
|
206
264
|
});
|
|
207
265
|
this._automergeHost.documentsSaved.on(this._ctx, () => {
|
|
208
|
-
this.
|
|
266
|
+
this._updateIndexes.schedule();
|
|
209
267
|
});
|
|
268
|
+
this._updateIndexes.schedule();
|
|
269
|
+
log('echo-host: open complete');
|
|
210
270
|
}
|
|
211
271
|
|
|
212
272
|
protected override async _close(ctx: Context): Promise<void> {
|
|
273
|
+
// Drain any in-flight indexer task before the Resource base disposes
|
|
274
|
+
// `this._ctx`. Without this, an in-flight `DataServiceImpl.updateIndexes`
|
|
275
|
+
// RPC handler's `do { await runBlocking() } while (!_indexesUpToDate)`
|
|
276
|
+
// loop can hit a disposed ctx on its next iteration and throw
|
|
277
|
+
// `ContextDisposedError` — which escapes as an unhandled rejection
|
|
278
|
+
// because the originating client `flush()` is fire-and-forget at the
|
|
279
|
+
// test layer. The cooperative `_indexesUpToDate = true` set inside
|
|
280
|
+
// `_runUpdateIndexes` lets the loop exit cleanly once the current
|
|
281
|
+
// iteration finishes.
|
|
282
|
+
await this._updateIndexes?.join();
|
|
283
|
+
|
|
213
284
|
await this._queryService.close(ctx);
|
|
214
285
|
await this._spaceStateManager.close(ctx);
|
|
215
|
-
await this._indexer.close(ctx);
|
|
216
286
|
await this._automergeHost.close();
|
|
217
287
|
}
|
|
218
288
|
|
|
219
289
|
/**
|
|
220
290
|
* Flush all pending writes to the underlying storage.
|
|
221
291
|
*/
|
|
222
|
-
async flush(): Promise<void> {
|
|
223
|
-
await this._automergeHost.
|
|
292
|
+
async flush(ctx: Context): Promise<void> {
|
|
293
|
+
await this._automergeHost.flush(ctx);
|
|
224
294
|
}
|
|
225
295
|
|
|
226
296
|
/**
|
|
227
297
|
* Perform any pending index updates.
|
|
298
|
+
*
|
|
299
|
+
* Bails as a no-op when the host has been closed: a late `db.flush()` RPC
|
|
300
|
+
* (client still has an open service ref while the host is in/post-teardown)
|
|
301
|
+
* has nothing to update against. The pre-loop and post-iteration
|
|
302
|
+
* `_ctx.disposed` checks prevent `runBlocking` from being entered against a
|
|
303
|
+
* disposed context — which would throw `ContextDisposedError` and escape as
|
|
304
|
+
* an unhandled rejection at the fire-and-forget originating caller. Other
|
|
305
|
+
* `Resource` methods in this codebase (e.g. `LevelDBStorageAdapter.load`)
|
|
306
|
+
* follow the same closed-host early-out pattern.
|
|
228
307
|
*/
|
|
229
308
|
async updateIndexes(): Promise<void> {
|
|
230
|
-
|
|
309
|
+
if (this._ctx.disposed) {
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
do {
|
|
313
|
+
await this._updateIndexes.runBlocking();
|
|
314
|
+
if (this._ctx.disposed) {
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
} while (!this._indexesUpToDate);
|
|
231
318
|
}
|
|
232
319
|
|
|
233
320
|
/**
|
|
234
321
|
* Loads the document handle from the repo and waits for it to be ready.
|
|
322
|
+
*
|
|
323
|
+
* @returns `null` when the document is not available yet (e.g. storage-only load with no local chunks).
|
|
235
324
|
*/
|
|
236
|
-
async loadDoc<T>(ctx: Context, documentId: AnyDocumentId, opts?: LoadDocOptions): Promise<DocHandle<T
|
|
325
|
+
async loadDoc<T>(ctx: Context, documentId: AnyDocumentId, opts?: LoadDocOptions): Promise<DocHandle<T> | null> {
|
|
237
326
|
return await this._automergeHost.loadDoc(ctx, documentId, opts);
|
|
238
327
|
}
|
|
239
328
|
|
|
240
|
-
async exportDoc(
|
|
241
|
-
return await this._automergeHost.exportDoc(
|
|
329
|
+
async exportDoc(id: AnyDocumentId): Promise<Uint8Array> {
|
|
330
|
+
return await this._automergeHost.exportDoc(id);
|
|
242
331
|
}
|
|
243
332
|
|
|
244
333
|
/**
|
|
245
334
|
* Create new persisted document.
|
|
246
335
|
*/
|
|
247
|
-
createDoc<T>(initialValue?: T, opts?: CreateDocOptions): DocHandle<T
|
|
336
|
+
async createDoc<T>(initialValue?: T, opts?: CreateDocOptions): Promise<DocHandle<T>> {
|
|
248
337
|
return this._automergeHost.createDoc(initialValue, opts);
|
|
249
338
|
}
|
|
250
339
|
|
|
251
340
|
/**
|
|
252
341
|
* Create new space root.
|
|
253
342
|
*/
|
|
254
|
-
async createSpaceRoot(spaceKey: PublicKey): Promise<DatabaseRoot> {
|
|
343
|
+
async createSpaceRoot(ctx: Context, spaceKey: PublicKey): Promise<DatabaseRoot> {
|
|
255
344
|
invariant(this._lifecycleState === LifecycleState.OPEN);
|
|
256
345
|
const spaceId = await createIdFromSpaceKey(spaceKey);
|
|
257
346
|
|
|
258
|
-
const automergeRoot = this._automergeHost.createDoc<DatabaseDirectory>({
|
|
347
|
+
const automergeRoot = await this._automergeHost.createDoc<DatabaseDirectory>({
|
|
259
348
|
version: SpaceDocVersion.CURRENT,
|
|
260
349
|
access: { spaceKey: spaceKey.toHex() },
|
|
261
350
|
|
|
@@ -264,43 +353,239 @@ export class EchoHost extends Resource {
|
|
|
264
353
|
links: {},
|
|
265
354
|
});
|
|
266
355
|
|
|
267
|
-
await this._automergeHost.flush({ documentIds: [automergeRoot.documentId] });
|
|
356
|
+
await this._automergeHost.flush(ctx, { documentIds: [automergeRoot.documentId] });
|
|
268
357
|
|
|
269
|
-
return await this.openSpaceRoot(spaceId, automergeRoot.url);
|
|
358
|
+
return await this.openSpaceRoot(ctx, spaceId, automergeRoot.url);
|
|
270
359
|
}
|
|
271
360
|
|
|
272
361
|
// TODO(dmaretskyi): Change to document id.
|
|
273
|
-
async openSpaceRoot(spaceId: SpaceId, automergeUrl: AutomergeUrl): Promise<DatabaseRoot> {
|
|
362
|
+
async openSpaceRoot(ctx: Context, spaceId: SpaceId, automergeUrl: AutomergeUrl): Promise<DatabaseRoot> {
|
|
274
363
|
invariant(this._lifecycleState === LifecycleState.OPEN);
|
|
275
|
-
const handle = await this._automergeHost.
|
|
276
|
-
|
|
364
|
+
const handle = await this._automergeHost.loadDoc<DatabaseDirectory>(ctx, automergeUrl, {
|
|
365
|
+
fetchFromNetwork: true,
|
|
366
|
+
});
|
|
367
|
+
invariant(handle, 'Space root document must load before assignment.');
|
|
368
|
+
const query = this._automergeHost.findWithProgress<DatabaseDirectory>(handle.documentId);
|
|
277
369
|
|
|
278
|
-
return this._spaceStateManager.assignRootToSpace(spaceId,
|
|
370
|
+
return this._spaceStateManager.assignRootToSpace(spaceId, query);
|
|
279
371
|
}
|
|
280
372
|
|
|
281
373
|
// TODO(dmaretskyi): Change to document id.
|
|
282
|
-
async closeSpaceRoot(
|
|
374
|
+
async closeSpaceRoot(_automergeUrl: AutomergeUrl): Promise<void> {
|
|
283
375
|
todo();
|
|
284
376
|
}
|
|
285
377
|
|
|
286
378
|
/**
|
|
287
379
|
* Install data replicator.
|
|
288
380
|
*/
|
|
289
|
-
async addReplicator(replicator:
|
|
290
|
-
await this._automergeHost.addReplicator(replicator);
|
|
381
|
+
async addReplicator(ctx: Context, replicator: AutomergeReplicator): Promise<void> {
|
|
382
|
+
await this._automergeHost.addReplicator(ctx, replicator);
|
|
291
383
|
}
|
|
292
384
|
|
|
293
385
|
/**
|
|
294
386
|
* Remove data replicator.
|
|
295
387
|
*/
|
|
296
|
-
async removeReplicator(replicator:
|
|
388
|
+
async removeReplicator(replicator: AutomergeReplicator): Promise<void> {
|
|
297
389
|
await this._automergeHost.removeReplicator(replicator);
|
|
298
390
|
}
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
* Run collection sync for the given space.
|
|
394
|
+
* Does not wait for the sync to complete.
|
|
395
|
+
*/
|
|
396
|
+
async runCollectionSync(spaceId: SpaceId) {
|
|
397
|
+
const root = this._spaceStateManager.getRootBySpaceId(spaceId);
|
|
398
|
+
if (!root) {
|
|
399
|
+
throw new Error(`Space not found: ${spaceId}`);
|
|
400
|
+
}
|
|
401
|
+
this._automergeHost.refreshCollection(deriveCollectionIdFromSpaceId(spaceId, root.documentId));
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
* Get all feeds and their blocks for a space.
|
|
406
|
+
* Used for space archive export.
|
|
407
|
+
*/
|
|
408
|
+
async getAllFeedsForSpace(
|
|
409
|
+
spaceId: SpaceId,
|
|
410
|
+
): Promise<Array<{ feedId: string; feedNamespace: string; blocks: FeedProtocol.Block[] }>> {
|
|
411
|
+
return RuntimeProvider.runPromise(this._runtime)(this._feedStore.getAllFeedsForSpace({ spaceId }));
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
private _runUpdateIndexes = async (): Promise<void> => {
|
|
415
|
+
if (this._ctx.disposed || !this.isOpen) {
|
|
416
|
+
// Signal the `updateIndexes` RPC handler's `do-while` loop to exit
|
|
417
|
+
// cooperatively. Without this, the loop sees `_indexesUpToDate === false`
|
|
418
|
+
// and calls `runBlocking` again, which throws on the disposed context.
|
|
419
|
+
this._indexesUpToDate = true;
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
try {
|
|
424
|
+
const combinedResult = _makeEmptyMergedResult();
|
|
425
|
+
|
|
426
|
+
{
|
|
427
|
+
performance.mark('indexEngine.update.automerge:start');
|
|
428
|
+
const result = await this._indexEngine
|
|
429
|
+
.update(this._ctx, this._automergeDataSource, { spaceId: null, limit: 50 })
|
|
430
|
+
.pipe(RuntimeProvider.runPromise(this._runtime));
|
|
431
|
+
_mergeInto(combinedResult, result);
|
|
432
|
+
performance.measure('Index Automerge', {
|
|
433
|
+
start: 'indexEngine.update.automerge:start',
|
|
434
|
+
detail: {
|
|
435
|
+
devtools: {
|
|
436
|
+
dataType: 'track-entry',
|
|
437
|
+
track: 'Indexing',
|
|
438
|
+
trackGroup: 'ECHO', // Group related tracks together
|
|
439
|
+
color: 'tertiary-dark',
|
|
440
|
+
properties: [['count', result.updated]],
|
|
441
|
+
},
|
|
442
|
+
},
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
if (this._ctx.disposed || !this.isOpen) {
|
|
446
|
+
this._indexesUpToDate = true;
|
|
447
|
+
return;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
{
|
|
451
|
+
performance.mark('indexEngine.update.queue:start');
|
|
452
|
+
const result = await this._indexEngine
|
|
453
|
+
.update(this._ctx, this._queueDataSource, { spaceId: null, limit: 50 })
|
|
454
|
+
.pipe(RuntimeProvider.runPromise(this._runtime));
|
|
455
|
+
_mergeInto(combinedResult, result);
|
|
456
|
+
performance.measure('Index Queues', {
|
|
457
|
+
start: 'indexEngine.update.queue:start',
|
|
458
|
+
detail: {
|
|
459
|
+
devtools: {
|
|
460
|
+
dataType: 'track-entry',
|
|
461
|
+
track: 'Indexing',
|
|
462
|
+
trackGroup: 'ECHO',
|
|
463
|
+
color: 'tertiary-dark',
|
|
464
|
+
properties: [['count', result.updated]],
|
|
465
|
+
},
|
|
466
|
+
},
|
|
467
|
+
});
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
log.verbose('indexEngine update completed', {
|
|
471
|
+
updated: combinedResult.updated,
|
|
472
|
+
done: combinedResult.done,
|
|
473
|
+
spaces: combinedResult.spaces.size,
|
|
474
|
+
queues: combinedResult.queues.size,
|
|
475
|
+
documents: combinedResult.documents.size,
|
|
476
|
+
types: combinedResult.types.size,
|
|
477
|
+
objects: combinedResult.objects.size,
|
|
478
|
+
});
|
|
479
|
+
await sleep(1);
|
|
480
|
+
if (!combinedResult.done) {
|
|
481
|
+
this._indexesUpToDate = false;
|
|
482
|
+
this._updateIndexes!.schedule();
|
|
483
|
+
} else {
|
|
484
|
+
this._indexesUpToDate = true;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
// Invalidate queries after index update — the indexer is the sole invalidation source.
|
|
488
|
+
const hint = hintFromIndexingResult(combinedResult);
|
|
489
|
+
if (hint) {
|
|
490
|
+
this._queryService.invalidateQueries(hint);
|
|
491
|
+
}
|
|
492
|
+
} catch (err) {
|
|
493
|
+
if (this._ctx.disposed || !this.isOpen) {
|
|
494
|
+
this._indexesUpToDate = true;
|
|
495
|
+
return;
|
|
496
|
+
}
|
|
497
|
+
log.catch(err);
|
|
498
|
+
// Failsafe: prevent queries from freezing if the indexer faults.
|
|
499
|
+
this._queryService.invalidateQueries();
|
|
500
|
+
throw err;
|
|
501
|
+
}
|
|
502
|
+
};
|
|
299
503
|
}
|
|
300
504
|
|
|
301
505
|
export type { EchoDataStats };
|
|
302
506
|
|
|
507
|
+
type MutableIndexingAccumulator = {
|
|
508
|
+
updated: number;
|
|
509
|
+
done: boolean;
|
|
510
|
+
spaces: Set<SpaceId>;
|
|
511
|
+
queues: Set<ObjectId>;
|
|
512
|
+
documents: Set<string>;
|
|
513
|
+
types: Set<string>;
|
|
514
|
+
objects: Set<ObjectId>;
|
|
515
|
+
};
|
|
516
|
+
|
|
517
|
+
const _makeEmptyMergedResult = (): MutableIndexingAccumulator => ({
|
|
518
|
+
updated: 0,
|
|
519
|
+
done: true,
|
|
520
|
+
spaces: new Set(),
|
|
521
|
+
queues: new Set(),
|
|
522
|
+
documents: new Set(),
|
|
523
|
+
types: new Set(),
|
|
524
|
+
objects: new Set(),
|
|
525
|
+
});
|
|
526
|
+
|
|
527
|
+
const _mergeInto = (acc: MutableIndexingAccumulator, r: IndexingResult): void => {
|
|
528
|
+
acc.updated += r.updated;
|
|
529
|
+
acc.done = acc.done && r.done;
|
|
530
|
+
for (const s of r.spaces) {
|
|
531
|
+
acc.spaces.add(s);
|
|
532
|
+
}
|
|
533
|
+
for (const q of r.queues) {
|
|
534
|
+
acc.queues.add(q);
|
|
535
|
+
}
|
|
536
|
+
for (const d of r.documents) {
|
|
537
|
+
acc.documents.add(d);
|
|
538
|
+
}
|
|
539
|
+
for (const t of r.types) {
|
|
540
|
+
acc.types.add(t);
|
|
541
|
+
}
|
|
542
|
+
for (const o of r.objects) {
|
|
543
|
+
acc.objects.add(o);
|
|
544
|
+
}
|
|
545
|
+
};
|
|
546
|
+
|
|
303
547
|
export type EchoStatsDiagnostic = {
|
|
304
548
|
loadedDocsCount: number;
|
|
305
549
|
dataStats: EchoDataStats;
|
|
306
550
|
};
|
|
551
|
+
|
|
552
|
+
/**
|
|
553
|
+
* Sqlite health check on startup.
|
|
554
|
+
*
|
|
555
|
+
* Always runs a cheap schema-level probe (`PRAGMA schema_version` + a read from
|
|
556
|
+
* `sqlite_schema`). These do not walk user data, but they fail fast if the DB
|
|
557
|
+
* file header or schema root page is unreadable.
|
|
558
|
+
*
|
|
559
|
+
* Additionally runs `PRAGMA quick_check` when `RUN_SQLITE_QUICK_CHECK_ON_STARTUP`
|
|
560
|
+
* is enabled. That walks every page and is O(dbSize) — slow on OPFS.
|
|
561
|
+
*/
|
|
562
|
+
const testSqlite = () =>
|
|
563
|
+
Effect.gen(function* () {
|
|
564
|
+
log('begin sqlite health check');
|
|
565
|
+
const sql = yield* SqlClient.SqlClient;
|
|
566
|
+
const databases = yield* sql<{ seq: number; name: string; file: string }>`PRAGMA database_list`;
|
|
567
|
+
log('SQLite databases', { databases });
|
|
568
|
+
|
|
569
|
+
// Cheap header/schema probe: forces SQLite to read the file header and
|
|
570
|
+
// schema root page. Catches catastrophic corruption (bad header,
|
|
571
|
+
// unreadable schema) without walking user data.
|
|
572
|
+
const [schemaVersion] = yield* sql<{ schema_version: number }>`PRAGMA schema_version`;
|
|
573
|
+
const [schemaCount] = yield* sql<{ n: number }>`SELECT count(*) AS n FROM sqlite_schema`;
|
|
574
|
+
log('sqlite schema probe passed', {
|
|
575
|
+
schemaVersion: schemaVersion.schema_version,
|
|
576
|
+
objectCount: schemaCount.n,
|
|
577
|
+
});
|
|
578
|
+
|
|
579
|
+
if (RUN_SQLITE_QUICK_CHECK_ON_STARTUP) {
|
|
580
|
+
// NOTE: This is slow on non-trivial databases: O(dbSize).
|
|
581
|
+
log('starting sqlite quick_check');
|
|
582
|
+
const [result] = yield* sql<{ quick_check: string }>`PRAGMA quick_check`;
|
|
583
|
+
if (result.quick_check !== 'ok') {
|
|
584
|
+
throw new Error('SQLite quick check failed');
|
|
585
|
+
}
|
|
586
|
+
log('sqlite quick_check passed');
|
|
587
|
+
} else {
|
|
588
|
+
log('sqlite quick_check skipped');
|
|
589
|
+
}
|
|
590
|
+
log('sqlite health check complete');
|
|
591
|
+
});
|
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';
|