@dxos/echo-pipeline 0.1.41 → 0.1.45-next.45667d4
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-4YAT2XJW.mjs → chunk-2NGLSBL4.mjs} +1128 -1017
- package/dist/lib/browser/chunk-2NGLSBL4.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +1 -3
- package/dist/lib/browser/index.mjs.map +1 -1
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +72 -48
- package/dist/lib/browser/testing/index.mjs.map +3 -3
- package/dist/lib/node/index.cjs +1048 -938
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node/testing/index.cjs +735 -540
- package/dist/lib/node/testing/index.cjs.map +4 -4
- package/dist/types/src/dbhost/database-host.d.ts +2 -4
- package/dist/types/src/dbhost/database-host.d.ts.map +1 -1
- package/dist/types/src/dbhost/snapshot-store.d.ts +2 -0
- package/dist/types/src/dbhost/snapshot-store.d.ts.map +1 -1
- package/dist/types/src/metadata/metadata-store.d.ts +3 -2
- package/dist/types/src/metadata/metadata-store.d.ts.map +1 -1
- package/dist/types/src/pipeline/pipeline.d.ts +27 -11
- package/dist/types/src/pipeline/pipeline.d.ts.map +1 -1
- package/dist/types/src/pipeline/timeframe-clock.d.ts +1 -0
- package/dist/types/src/pipeline/timeframe-clock.d.ts.map +1 -1
- package/dist/types/src/space/auth.d.ts +4 -4
- package/dist/types/src/space/auth.d.ts.map +1 -1
- package/dist/types/src/space/control-pipeline.d.ts +2 -4
- package/dist/types/src/space/control-pipeline.d.ts.map +1 -1
- package/dist/types/src/space/data-pipeline.d.ts +19 -10
- package/dist/types/src/space/data-pipeline.d.ts.map +1 -1
- package/dist/types/src/space/index.d.ts +0 -1
- package/dist/types/src/space/index.d.ts.map +1 -1
- package/dist/types/src/space/space-manager.d.ts +14 -14
- package/dist/types/src/space/space-manager.d.ts.map +1 -1
- package/dist/types/src/space/space-protocol.d.ts +2 -2
- package/dist/types/src/space/space-protocol.d.ts.map +1 -1
- package/dist/types/src/space/space.d.ts +20 -7
- package/dist/types/src/space/space.d.ts.map +1 -1
- package/dist/types/src/testing/database-test-rig.d.ts.map +1 -1
- package/dist/types/src/testing/test-agent-builder.d.ts +15 -5
- package/dist/types/src/testing/test-agent-builder.d.ts.map +1 -1
- package/package.json +30 -30
- package/src/common/feeds.ts +2 -2
- package/src/dbhost/data-service-host.ts +10 -10
- package/src/dbhost/data-service.ts +1 -1
- package/src/dbhost/database-host.ts +1 -9
- package/src/dbhost/snapshot-store.test.ts +2 -2
- package/src/dbhost/snapshot-store.ts +16 -1
- package/src/metadata/metadata-store.ts +14 -16
- package/src/pipeline/message-selector.ts +1 -1
- package/src/pipeline/pipeline.test.ts +68 -44
- package/src/pipeline/pipeline.ts +118 -35
- package/src/pipeline/timeframe-clock.ts +8 -2
- package/src/space/auth.ts +6 -6
- package/src/space/control-pipeline.test.ts +19 -20
- package/src/space/control-pipeline.ts +3 -5
- package/src/space/data-pipeline.ts +131 -69
- package/src/space/index.ts +0 -1
- package/src/space/replication.browser.test.ts +8 -8
- package/src/space/space-manager.browser.test.ts +3 -3
- package/src/space/space-manager.ts +25 -20
- package/src/space/space-protocol.browser.test.ts +1 -1
- package/src/space/space-protocol.test.ts +6 -6
- package/src/space/space-protocol.ts +7 -7
- package/src/space/space.test.ts +137 -48
- package/src/space/space.ts +82 -40
- package/src/testing/database-test-rig.ts +25 -24
- package/src/testing/test-agent-builder.ts +88 -49
- package/src/testing/test-feed-builder.ts +1 -1
- package/src/testing/util.ts +15 -15
- package/src/tests/database-unit.test.ts +4 -4
- package/src/tests/database.test.ts +1 -1
- package/dist/lib/browser/chunk-4YAT2XJW.mjs.map +0 -7
- package/dist/types/src/space/genesis.d.ts +0 -5
- package/dist/types/src/space/genesis.d.ts.map +0 -1
- package/src/space/genesis.ts +0 -69
package/src/space/space.test.ts
CHANGED
|
@@ -4,10 +4,11 @@
|
|
|
4
4
|
|
|
5
5
|
import expect from 'expect';
|
|
6
6
|
import assert from 'node:assert';
|
|
7
|
+
import { promisify } from 'node:util';
|
|
7
8
|
|
|
8
|
-
import { CredentialGenerator } from '@dxos/credentials';
|
|
9
|
-
import {
|
|
10
|
-
import { describe, test
|
|
9
|
+
import { createCredential, CredentialGenerator } from '@dxos/credentials';
|
|
10
|
+
import { log } from '@dxos/log';
|
|
11
|
+
import { afterTest, describe, test } from '@dxos/test';
|
|
11
12
|
|
|
12
13
|
import { TestAgentBuilder, testLocalDatabase } from '../testing';
|
|
13
14
|
|
|
@@ -19,32 +20,21 @@ describe('space/space', () => {
|
|
|
19
20
|
const builder = new TestAgentBuilder();
|
|
20
21
|
afterTest(async () => await builder.close());
|
|
21
22
|
const agent = await builder.createPeer();
|
|
22
|
-
const
|
|
23
|
+
const space = await agent.createSpace();
|
|
23
24
|
|
|
24
25
|
await space.open();
|
|
26
|
+
|
|
25
27
|
expect(space.isOpen).toBeTruthy();
|
|
26
28
|
afterTest(() => space.close());
|
|
27
29
|
|
|
28
|
-
|
|
29
|
-
// Genesis
|
|
30
|
-
const generator = new CredentialGenerator(agent.keyring, agent.identityKey, agent.deviceKey);
|
|
31
|
-
const credentials = [
|
|
32
|
-
...(await generator.createSpaceGenesis(space.key, space.controlFeedKey!)),
|
|
33
|
-
await generator.createFeedAdmission(space.key, space.dataFeedKey!, AdmittedFeed.Designation.DATA)
|
|
34
|
-
];
|
|
30
|
+
await agent.spaceGenesis(space);
|
|
35
31
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// TODO(burdon): Debugging only.
|
|
43
|
-
await space.controlPipeline.state!.waitUntilTimeframe(space.controlPipeline.state!.endTimeframe);
|
|
44
|
-
}
|
|
32
|
+
await space.controlPipeline.state!.waitUntilTimeframe(space.controlPipeline.state!.endTimeframe);
|
|
33
|
+
await space.initializeDataPipeline();
|
|
34
|
+
await space.dataPipeline.ensureEpochInitialized();
|
|
45
35
|
|
|
46
|
-
assert(
|
|
47
|
-
await testLocalDatabase(
|
|
36
|
+
assert(space.dataPipeline.databaseHost);
|
|
37
|
+
await testLocalDatabase(space.dataPipeline);
|
|
48
38
|
|
|
49
39
|
await builder.close();
|
|
50
40
|
expect(space.isOpen).toBeFalsy();
|
|
@@ -57,47 +47,36 @@ describe('space/space', () => {
|
|
|
57
47
|
//
|
|
58
48
|
// Agent 1
|
|
59
49
|
//
|
|
60
|
-
const [agent1, space1
|
|
50
|
+
const [agent1, space1] = await run(async () => {
|
|
61
51
|
const agent = await builder.createPeer();
|
|
62
|
-
const
|
|
52
|
+
const space = await agent.createSpace(agent.identityKey);
|
|
63
53
|
|
|
64
54
|
await space.open();
|
|
65
55
|
expect(space.isOpen).toBeTruthy();
|
|
66
56
|
afterTest(() => space.close());
|
|
67
57
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
await generator.createFeedAdmission(space.key, space.dataFeedKey!, AdmittedFeed.Designation.DATA)
|
|
74
|
-
];
|
|
75
|
-
|
|
76
|
-
for (const credential of credentials) {
|
|
77
|
-
await space.controlPipeline.writer.write({
|
|
78
|
-
credential: { credential }
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
await space.controlPipeline.state!.waitUntilTimeframe(space.controlPipeline.state!.endTimeframe);
|
|
83
|
-
}
|
|
58
|
+
await agent.spaceGenesis(space);
|
|
59
|
+
|
|
60
|
+
await space.controlPipeline.state!.waitUntilTimeframe(space.controlPipeline.state!.endTimeframe);
|
|
61
|
+
await space.initializeDataPipeline();
|
|
62
|
+
await space.dataPipeline.ensureEpochInitialized();
|
|
84
63
|
|
|
85
|
-
return [agent, space
|
|
64
|
+
return [agent, space];
|
|
86
65
|
});
|
|
87
66
|
|
|
88
67
|
//
|
|
89
68
|
// Agent 2
|
|
90
69
|
//
|
|
91
|
-
const [agent2, space2
|
|
70
|
+
const [agent2, space2] = await run(async () => {
|
|
92
71
|
// NOTE: The genesisKey would be passed as part of the invitation.
|
|
93
72
|
const agent = await builder.createPeer();
|
|
94
|
-
const
|
|
73
|
+
const space = await agent.createSpace(agent.identityKey, space1.key, space1.genesisFeedKey);
|
|
95
74
|
|
|
96
75
|
await space.open();
|
|
97
76
|
expect(space.isOpen).toBeTruthy();
|
|
98
77
|
afterTest(() => space.close());
|
|
99
78
|
|
|
100
|
-
return [agent, space
|
|
79
|
+
return [agent, space];
|
|
101
80
|
});
|
|
102
81
|
|
|
103
82
|
expect(agent1).toBeDefined();
|
|
@@ -112,16 +91,19 @@ describe('space/space', () => {
|
|
|
112
91
|
agent2.deviceKey,
|
|
113
92
|
space2.controlFeedKey!,
|
|
114
93
|
space2.dataFeedKey!,
|
|
115
|
-
space1.genesisFeedKey
|
|
94
|
+
space1.genesisFeedKey,
|
|
116
95
|
);
|
|
117
96
|
|
|
118
97
|
for (const credential of credentials) {
|
|
119
98
|
await space1.controlPipeline.writer.write({
|
|
120
|
-
credential: { credential }
|
|
99
|
+
credential: { credential },
|
|
121
100
|
});
|
|
122
101
|
}
|
|
123
102
|
}
|
|
124
103
|
|
|
104
|
+
await space2.initializeDataPipeline();
|
|
105
|
+
await space2.dataPipeline.ensureEpochInitialized();
|
|
106
|
+
|
|
125
107
|
{
|
|
126
108
|
// Initial data exchange.
|
|
127
109
|
|
|
@@ -134,11 +116,118 @@ describe('space/space', () => {
|
|
|
134
116
|
|
|
135
117
|
// TODO(burdon): Write multiple items (extract for all tests).
|
|
136
118
|
|
|
137
|
-
await testLocalDatabase(
|
|
138
|
-
await testLocalDatabase(
|
|
119
|
+
await testLocalDatabase(space1.dataPipeline, space2.dataPipeline);
|
|
120
|
+
await testLocalDatabase(space2.dataPipeline, space1.dataPipeline);
|
|
139
121
|
|
|
140
122
|
await builder.close();
|
|
141
123
|
expect(space1.isOpen).toBeFalsy();
|
|
142
124
|
expect(space2.isOpen).toBeFalsy();
|
|
143
125
|
});
|
|
126
|
+
|
|
127
|
+
test('open & close', async () => {
|
|
128
|
+
const builder = new TestAgentBuilder();
|
|
129
|
+
afterTest(async () => await builder.close());
|
|
130
|
+
const agent = await builder.createPeer();
|
|
131
|
+
const space1 = await agent.createSpace();
|
|
132
|
+
|
|
133
|
+
await space1.open();
|
|
134
|
+
|
|
135
|
+
expect(space1.isOpen).toBeTruthy();
|
|
136
|
+
afterTest(() => space1.close());
|
|
137
|
+
|
|
138
|
+
await agent.spaceGenesis(space1);
|
|
139
|
+
|
|
140
|
+
await space1.controlPipeline.state!.waitUntilTimeframe(space1.controlPipeline.state!.endTimeframe);
|
|
141
|
+
await space1.initializeDataPipeline();
|
|
142
|
+
await space1.dataPipeline.ensureEpochInitialized();
|
|
143
|
+
|
|
144
|
+
assert(space1.dataPipeline.databaseHost);
|
|
145
|
+
await testLocalDatabase(space1.dataPipeline);
|
|
146
|
+
|
|
147
|
+
const objectCount = space1.dataPipeline.itemManager.entities.size;
|
|
148
|
+
|
|
149
|
+
await space1.close();
|
|
150
|
+
expect(space1.isOpen).toBeFalsy();
|
|
151
|
+
|
|
152
|
+
// Re-open.
|
|
153
|
+
const space2 = await agent.createSpace(agent.identityKey, space1.key, space1.genesisFeedKey, space1.dataFeedKey);
|
|
154
|
+
|
|
155
|
+
await space2.open();
|
|
156
|
+
|
|
157
|
+
await space2.controlPipeline.state!.waitUntilTimeframe(space2.controlPipeline.state!.endTimeframe);
|
|
158
|
+
await space2.initializeDataPipeline();
|
|
159
|
+
await space2.dataPipeline.ensureEpochInitialized();
|
|
160
|
+
|
|
161
|
+
space2.dataPipeline.setTargetTimeframe(space2.dataPipeline.pipelineState!.endTimeframe);
|
|
162
|
+
await space2.dataPipeline.pipelineState!.waitUntilReachedTargetTimeframe();
|
|
163
|
+
|
|
164
|
+
assert(space2.dataPipeline.databaseHost);
|
|
165
|
+
expect(space2.dataPipeline.itemManager.entities.size).toEqual(objectCount);
|
|
166
|
+
|
|
167
|
+
await testLocalDatabase(space2.dataPipeline);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
test('create epoch', async () => {
|
|
171
|
+
const builder = new TestAgentBuilder();
|
|
172
|
+
afterTest(async () => await builder.close());
|
|
173
|
+
const agent = await builder.createPeer();
|
|
174
|
+
const space1 = await agent.createSpace();
|
|
175
|
+
|
|
176
|
+
await space1.open();
|
|
177
|
+
|
|
178
|
+
expect(space1.isOpen).toBeTruthy();
|
|
179
|
+
afterTest(() => space1.close());
|
|
180
|
+
|
|
181
|
+
await agent.spaceGenesis(space1);
|
|
182
|
+
|
|
183
|
+
await space1.controlPipeline.state!.waitUntilTimeframe(space1.controlPipeline.state!.endTimeframe);
|
|
184
|
+
await space1.initializeDataPipeline();
|
|
185
|
+
await space1.dataPipeline.ensureEpochInitialized();
|
|
186
|
+
|
|
187
|
+
assert(space1.dataPipeline.databaseHost);
|
|
188
|
+
await testLocalDatabase(space1.dataPipeline);
|
|
189
|
+
|
|
190
|
+
const objectCount = space1.dataPipeline.itemManager.entities.size;
|
|
191
|
+
|
|
192
|
+
const epoch = await space1.dataPipeline.createEpoch();
|
|
193
|
+
await space1.controlPipeline.writer.write({
|
|
194
|
+
credential: {
|
|
195
|
+
credential: await createCredential({
|
|
196
|
+
issuer: agent.identityKey,
|
|
197
|
+
subject: space1.key,
|
|
198
|
+
assertion: {
|
|
199
|
+
'@type': 'dxos.halo.credentials.Epoch',
|
|
200
|
+
...epoch,
|
|
201
|
+
},
|
|
202
|
+
signer: agent.keyring,
|
|
203
|
+
}),
|
|
204
|
+
},
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
await space1.close();
|
|
208
|
+
expect(space1.isOpen).toBeFalsy();
|
|
209
|
+
|
|
210
|
+
// Clear the data feed - epoch snapshot should have the data.
|
|
211
|
+
const feed = await agent.feedStore.openFeed(space1.dataFeedKey!);
|
|
212
|
+
await promisify(feed.core.clear.bind(feed.core))(0, feed.length);
|
|
213
|
+
|
|
214
|
+
log.break();
|
|
215
|
+
|
|
216
|
+
// Re-open.
|
|
217
|
+
const space2 = await agent.createSpace(agent.identityKey, space1.key, space1.genesisFeedKey, space1.dataFeedKey);
|
|
218
|
+
|
|
219
|
+
await space2.open();
|
|
220
|
+
|
|
221
|
+
await space2.controlPipeline.state!.waitUntilTimeframe(space2.controlPipeline.state!.endTimeframe);
|
|
222
|
+
await space2.initializeDataPipeline();
|
|
223
|
+
await space2.dataPipeline.ensureEpochInitialized();
|
|
224
|
+
|
|
225
|
+
space2.dataPipeline.setTargetTimeframe(space2.dataPipeline.pipelineState!.endTimeframe);
|
|
226
|
+
await space2.dataPipeline.pipelineState!.waitUntilReachedTargetTimeframe();
|
|
227
|
+
|
|
228
|
+
assert(space2.dataPipeline.databaseHost);
|
|
229
|
+
expect(space2.dataPipeline.itemManager.entities.size).toEqual(objectCount);
|
|
230
|
+
|
|
231
|
+
await testLocalDatabase(space2.dataPipeline);
|
|
232
|
+
});
|
|
144
233
|
});
|
package/src/space/space.ts
CHANGED
|
@@ -5,17 +5,21 @@
|
|
|
5
5
|
import assert from 'node:assert';
|
|
6
6
|
|
|
7
7
|
import { Event, synchronized, trackLeaks, Lock } from '@dxos/async';
|
|
8
|
-
import { FeedInfo } from '@dxos/credentials';
|
|
8
|
+
import { CredentialConsumer, FeedInfo } from '@dxos/credentials';
|
|
9
9
|
import { FeedWrapper } from '@dxos/feed-store';
|
|
10
10
|
import { PublicKey } from '@dxos/keys';
|
|
11
11
|
import { log, logInfo } from '@dxos/log';
|
|
12
|
+
import { ModelFactory } from '@dxos/model-factory';
|
|
12
13
|
import type { FeedMessage } from '@dxos/protocols/proto/dxos/echo/feed';
|
|
13
14
|
import { AdmittedFeed, Credential } from '@dxos/protocols/proto/dxos/halo/credentials';
|
|
14
15
|
import { Timeframe } from '@dxos/timeframe';
|
|
15
16
|
import { AsyncCallback, Callback } from '@dxos/util';
|
|
16
17
|
|
|
17
|
-
import {
|
|
18
|
+
import { SnapshotManager } from '../dbhost';
|
|
19
|
+
import { MetadataStore } from '../metadata';
|
|
20
|
+
import { PipelineAccessor } from '../pipeline';
|
|
18
21
|
import { ControlPipeline } from './control-pipeline';
|
|
22
|
+
import { DataPipeline } from './data-pipeline';
|
|
19
23
|
import { SpaceProtocol } from './space-protocol';
|
|
20
24
|
|
|
21
25
|
// TODO(burdon): Factor out?
|
|
@@ -26,6 +30,13 @@ export type SpaceParams = {
|
|
|
26
30
|
protocol: SpaceProtocol;
|
|
27
31
|
genesisFeed: FeedWrapper<FeedMessage>;
|
|
28
32
|
feedProvider: FeedProvider;
|
|
33
|
+
modelFactory: ModelFactory;
|
|
34
|
+
metadataStore: MetadataStore;
|
|
35
|
+
snapshotManager: SnapshotManager;
|
|
36
|
+
memberKey: PublicKey;
|
|
37
|
+
|
|
38
|
+
// TODO(dmaretskyi): Superseded by epochs.
|
|
39
|
+
snapshotId?: string | undefined;
|
|
29
40
|
};
|
|
30
41
|
|
|
31
42
|
export type CreatePipelineParams = {
|
|
@@ -49,36 +60,44 @@ export class Space {
|
|
|
49
60
|
private readonly _genesisFeedKey: PublicKey;
|
|
50
61
|
private readonly _feedProvider: FeedProvider;
|
|
51
62
|
private readonly _controlPipeline: ControlPipeline;
|
|
63
|
+
private readonly _dataPipeline: DataPipeline;
|
|
64
|
+
private readonly _snapshotManager: SnapshotManager;
|
|
65
|
+
|
|
66
|
+
// Processes epoch credentials.
|
|
67
|
+
private _dataPipelineCredentialConsumer?: CredentialConsumer<any> = undefined;
|
|
52
68
|
|
|
53
69
|
private _isOpen = false;
|
|
54
70
|
private _controlFeed?: FeedWrapper<FeedMessage>;
|
|
55
71
|
private _dataFeed?: FeedWrapper<FeedMessage>;
|
|
56
|
-
private _dataPipeline?: Pipeline;
|
|
57
72
|
|
|
58
|
-
constructor(
|
|
59
|
-
assert(spaceKey && feedProvider);
|
|
60
|
-
this._key = spaceKey;
|
|
61
|
-
this._genesisFeedKey = genesisFeed.key;
|
|
62
|
-
this._feedProvider = feedProvider;
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
this._controlPipeline = new ControlPipeline({
|
|
73
|
+
constructor(params: SpaceParams) {
|
|
74
|
+
assert(params.spaceKey && params.feedProvider);
|
|
75
|
+
this._key = params.spaceKey;
|
|
76
|
+
this._genesisFeedKey = params.genesisFeed.key;
|
|
77
|
+
this._feedProvider = params.feedProvider;
|
|
78
|
+
this._snapshotManager = params.snapshotManager;
|
|
79
|
+
|
|
80
|
+
this._controlPipeline = new ControlPipeline({
|
|
81
|
+
spaceKey: params.spaceKey,
|
|
82
|
+
genesisFeed: params.genesisFeed,
|
|
83
|
+
feedProvider: params.feedProvider,
|
|
84
|
+
});
|
|
66
85
|
|
|
67
86
|
// TODO(dmaretskyi): Feed set abstraction.
|
|
68
87
|
this._controlPipeline.onFeedAdmitted.set(async (info) => {
|
|
69
88
|
if (info.assertion.designation === AdmittedFeed.Designation.DATA) {
|
|
70
89
|
// We will add all existing data feeds when the data pipeline is initialized.
|
|
71
90
|
await this._addFeedLock.executeSynchronized(async () => {
|
|
72
|
-
if (this._dataPipeline) {
|
|
73
|
-
if (!this._dataPipeline.hasFeed(info.key)) {
|
|
74
|
-
return this._dataPipeline.addFeed(await this._feedProvider(info.key));
|
|
91
|
+
if (this._dataPipeline.pipeline) {
|
|
92
|
+
if (!this._dataPipeline.pipeline.hasFeed(info.key)) {
|
|
93
|
+
return this._dataPipeline.pipeline.addFeed(await this._feedProvider(info.key));
|
|
75
94
|
}
|
|
76
95
|
}
|
|
77
96
|
});
|
|
78
97
|
}
|
|
79
98
|
|
|
80
|
-
if (!info.key.equals(genesisFeed.key)) {
|
|
81
|
-
this.protocol.addFeed(await feedProvider(info.key));
|
|
99
|
+
if (!info.key.equals(params.genesisFeed.key)) {
|
|
100
|
+
this.protocol.addFeed(await params.feedProvider(info.key));
|
|
82
101
|
}
|
|
83
102
|
});
|
|
84
103
|
|
|
@@ -89,8 +108,32 @@ export class Space {
|
|
|
89
108
|
});
|
|
90
109
|
|
|
91
110
|
// Start replicating the genesis feed.
|
|
92
|
-
this.protocol = protocol;
|
|
93
|
-
this.protocol.addFeed(genesisFeed);
|
|
111
|
+
this.protocol = params.protocol;
|
|
112
|
+
this.protocol.addFeed(params.genesisFeed);
|
|
113
|
+
|
|
114
|
+
this._dataPipeline = new DataPipeline({
|
|
115
|
+
modelFactory: params.modelFactory,
|
|
116
|
+
metadataStore: params.metadataStore,
|
|
117
|
+
snapshotManager: params.snapshotManager,
|
|
118
|
+
memberKey: params.memberKey,
|
|
119
|
+
spaceKey: this._key,
|
|
120
|
+
feedInfoProvider: (feedKey) => this._controlPipeline.spaceState.feeds.get(feedKey),
|
|
121
|
+
snapshotId: params.snapshotId,
|
|
122
|
+
onPipelineCreated: async (pipeline) => {
|
|
123
|
+
if (this._dataFeed) {
|
|
124
|
+
pipeline.setWriteFeed(this._dataFeed);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Add existing feeds.
|
|
128
|
+
await this._addFeedLock.executeSynchronized(async () => {
|
|
129
|
+
for (const feed of this._controlPipeline.spaceState.feeds.values()) {
|
|
130
|
+
if (feed.assertion.designation === AdmittedFeed.Designation.DATA && !pipeline.hasFeed(feed.key)) {
|
|
131
|
+
await pipeline.addFeed(await this._feedProvider(feed.key));
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
},
|
|
136
|
+
});
|
|
94
137
|
}
|
|
95
138
|
|
|
96
139
|
@logInfo
|
|
@@ -125,6 +168,14 @@ export class Space {
|
|
|
125
168
|
return this._controlPipeline.pipeline;
|
|
126
169
|
}
|
|
127
170
|
|
|
171
|
+
get dataPipeline(): DataPipeline {
|
|
172
|
+
return this._dataPipeline;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
get snapshotManager(): SnapshotManager {
|
|
176
|
+
return this._snapshotManager;
|
|
177
|
+
}
|
|
178
|
+
|
|
128
179
|
setControlFeed(feed: FeedWrapper<FeedMessage>) {
|
|
129
180
|
assert(!this._controlFeed, 'Control feed already set.');
|
|
130
181
|
this._controlFeed = feed;
|
|
@@ -135,7 +186,7 @@ export class Space {
|
|
|
135
186
|
setDataFeed(feed: FeedWrapper<FeedMessage>) {
|
|
136
187
|
assert(!this._dataFeed, 'Data feed already set.');
|
|
137
188
|
this._dataFeed = feed;
|
|
138
|
-
this._dataPipeline?.setWriteFeed(feed);
|
|
189
|
+
this._dataPipeline.pipeline?.setWriteFeed(feed);
|
|
139
190
|
return this;
|
|
140
191
|
}
|
|
141
192
|
|
|
@@ -163,6 +214,10 @@ export class Space {
|
|
|
163
214
|
await this._controlPipeline.start();
|
|
164
215
|
await this.protocol.start();
|
|
165
216
|
|
|
217
|
+
this._dataPipelineCredentialConsumer = this._controlPipeline.spaceState.registerProcessor(
|
|
218
|
+
this._dataPipeline.createCredentialProcessor(),
|
|
219
|
+
);
|
|
220
|
+
|
|
166
221
|
this._isOpen = true;
|
|
167
222
|
log('opened');
|
|
168
223
|
}
|
|
@@ -173,6 +228,10 @@ export class Space {
|
|
|
173
228
|
if (!this._isOpen) {
|
|
174
229
|
return;
|
|
175
230
|
}
|
|
231
|
+
await this._dataPipelineCredentialConsumer?.close();
|
|
232
|
+
this._dataPipelineCredentialConsumer = undefined;
|
|
233
|
+
|
|
234
|
+
await this._dataPipeline.close();
|
|
176
235
|
|
|
177
236
|
// Closes in reverse order to open.
|
|
178
237
|
await this.protocol.stop();
|
|
@@ -182,27 +241,10 @@ export class Space {
|
|
|
182
241
|
log('closed');
|
|
183
242
|
}
|
|
184
243
|
|
|
185
|
-
|
|
186
|
-
|
|
244
|
+
async initializeDataPipeline() {
|
|
245
|
+
log.info('initializeDataPipeline');
|
|
187
246
|
assert(this._isOpen, 'Space must be open to initialize data pipeline.');
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
const pipeline = new Pipeline(start);
|
|
191
|
-
if (this._dataFeed) {
|
|
192
|
-
pipeline.setWriteFeed(this._dataFeed);
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
this._dataPipeline = pipeline;
|
|
196
|
-
|
|
197
|
-
// Add existing feeds.
|
|
198
|
-
await this._addFeedLock.executeSynchronized(async () => {
|
|
199
|
-
for (const feed of this._controlPipeline.spaceState.feeds.values()) {
|
|
200
|
-
if (feed.assertion.designation === AdmittedFeed.Designation.DATA && !pipeline.hasFeed(feed.key)) {
|
|
201
|
-
await pipeline.addFeed(await this._feedProvider(feed.key));
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
});
|
|
205
|
-
|
|
206
|
-
return pipeline;
|
|
247
|
+
await this._dataPipeline.open();
|
|
248
|
+
await this._dataPipelineCredentialConsumer!.open();
|
|
207
249
|
}
|
|
208
250
|
}
|
|
@@ -64,27 +64,28 @@ export class DatabaseTestPeer {
|
|
|
64
64
|
|
|
65
65
|
async open() {
|
|
66
66
|
this.hostItems = new ItemManager(this.modelFactory);
|
|
67
|
-
this.host = new DatabaseHost(
|
|
68
|
-
{
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
this.
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
};
|
|
83
|
-
}
|
|
67
|
+
this.host = new DatabaseHost({
|
|
68
|
+
write: async (message) => {
|
|
69
|
+
const seq =
|
|
70
|
+
this.feedMessages.push({
|
|
71
|
+
timeframe: this.timeframe,
|
|
72
|
+
payload: {
|
|
73
|
+
data: message,
|
|
74
|
+
},
|
|
75
|
+
}) - 1;
|
|
76
|
+
|
|
77
|
+
await this._onConfirm.waitFor(() => this.confirmed >= seq);
|
|
78
|
+
return {
|
|
79
|
+
seq,
|
|
80
|
+
feedKey: this.key,
|
|
81
|
+
};
|
|
84
82
|
},
|
|
85
|
-
|
|
86
|
-
|
|
83
|
+
});
|
|
84
|
+
|
|
87
85
|
await this.host.open(this.hostItems, this.modelFactory);
|
|
86
|
+
if (this.snapshot) {
|
|
87
|
+
this.host._itemDemuxer.restoreFromSnapshot(this.snapshot.database);
|
|
88
|
+
}
|
|
88
89
|
|
|
89
90
|
this.proxy = new DatabaseProxy(this.host.createDataServiceHost(), SPACE_KEY);
|
|
90
91
|
this.items = new ItemManager(this.modelFactory);
|
|
@@ -109,7 +110,7 @@ export class DatabaseTestPeer {
|
|
|
109
110
|
replicate(to?: Timeframe) {
|
|
110
111
|
const toTimeframe = Timeframe.merge(
|
|
111
112
|
to ?? new Timeframe(Array.from(this.rig.peers.values()).map((peer) => [peer.key, peer.confirmed])),
|
|
112
|
-
this.timeframe
|
|
113
|
+
this.timeframe,
|
|
113
114
|
);
|
|
114
115
|
toTimeframe.set(this.key, this.confirmed);
|
|
115
116
|
|
|
@@ -133,7 +134,7 @@ export class DatabaseTestPeer {
|
|
|
133
134
|
this.snapshot = {
|
|
134
135
|
spaceKey: SPACE_KEY.asUint8Array(),
|
|
135
136
|
database: this.host.createSnapshot(),
|
|
136
|
-
timeframe: this.timeframe
|
|
137
|
+
timeframe: this.timeframe,
|
|
137
138
|
};
|
|
138
139
|
return this.snapshot;
|
|
139
140
|
}
|
|
@@ -151,7 +152,7 @@ export class DatabaseTestPeer {
|
|
|
151
152
|
message && {
|
|
152
153
|
feedKey: peer.key,
|
|
153
154
|
seq: seq + 1,
|
|
154
|
-
data: message
|
|
155
|
+
data: message,
|
|
155
156
|
}
|
|
156
157
|
);
|
|
157
158
|
})
|
|
@@ -186,8 +187,8 @@ export class DatabaseTestPeer {
|
|
|
186
187
|
feedKey: candidate.feedKey,
|
|
187
188
|
seq: candidate.seq,
|
|
188
189
|
memberKey: candidate.feedKey,
|
|
189
|
-
timeframe: candidate.data.timeframe
|
|
190
|
-
}
|
|
190
|
+
timeframe: candidate.data.timeframe,
|
|
191
|
+
},
|
|
191
192
|
});
|
|
192
193
|
this.timeframe = Timeframe.merge(this.timeframe, new Timeframe([[candidate.feedKey, candidate.seq]]));
|
|
193
194
|
}
|