@dxos/echo-pipeline 0.4.8 → 0.4.9-main.1057b49
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-WAN2XUWE.mjs → chunk-RTEEJ723.mjs} +273 -1131
- package/dist/lib/browser/chunk-RTEEJ723.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +2 -10
- package/dist/lib/browser/index.mjs.map +3 -3
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +4 -60
- package/dist/lib/browser/testing/index.mjs.map +4 -4
- package/dist/lib/node/{chunk-U6J2HC4T.cjs → chunk-7VZVCCNF.cjs} +310 -1166
- package/dist/lib/node/chunk-7VZVCCNF.cjs.map +7 -0
- package/dist/lib/node/index.cjs +22 -30
- package/dist/lib/node/index.cjs.map +3 -3
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node/testing/index.cjs +13 -68
- package/dist/lib/node/testing/index.cjs.map +4 -4
- package/dist/types/src/db-host/data-service.d.ts +3 -13
- package/dist/types/src/db-host/data-service.d.ts.map +1 -1
- package/dist/types/src/db-host/index.d.ts +0 -2
- package/dist/types/src/db-host/index.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 +1 -4
- package/dist/types/src/space/space-manager.d.ts.map +1 -1
- package/dist/types/src/space/space.d.ts +1 -7
- package/dist/types/src/space/space.d.ts.map +1 -1
- package/dist/types/src/testing/index.d.ts +0 -1
- package/dist/types/src/testing/index.d.ts.map +1 -1
- package/dist/types/src/testing/test-agent-builder.d.ts +1 -3
- package/dist/types/src/testing/test-agent-builder.d.ts.map +1 -1
- package/package.json +30 -33
- package/src/automerge/automerge-host.ts +1 -1
- package/src/db-host/data-service.ts +10 -56
- package/src/db-host/index.ts +0 -2
- package/src/space/index.ts +0 -1
- package/src/space/space-manager.ts +1 -13
- package/src/space/space.test.ts +2 -112
- package/src/space/space.ts +2 -60
- package/src/testing/index.ts +0 -1
- package/src/testing/test-agent-builder.ts +3 -8
- package/dist/lib/browser/chunk-WAN2XUWE.mjs.map +0 -7
- package/dist/lib/node/chunk-U6J2HC4T.cjs.map +0 -7
- package/dist/types/src/db-host/data-service-host.d.ts +0 -38
- package/dist/types/src/db-host/data-service-host.d.ts.map +0 -1
- package/dist/types/src/db-host/database-host.d.ts +0 -27
- package/dist/types/src/db-host/database-host.d.ts.map +0 -1
- package/dist/types/src/space/data-pipeline.d.ts +0 -79
- package/dist/types/src/space/data-pipeline.d.ts.map +0 -1
- package/dist/types/src/space/data-pipeline.test.d.ts +0 -1
- package/dist/types/src/space/data-pipeline.test.d.ts.map +0 -1
- package/dist/types/src/testing/util.d.ts +0 -10
- package/dist/types/src/testing/util.d.ts.map +0 -1
- package/src/db-host/data-service-host.ts +0 -233
- package/src/db-host/database-host.ts +0 -63
- package/src/space/data-pipeline.test.ts +0 -3
- package/src/space/data-pipeline.ts +0 -425
- package/src/testing/util.ts +0 -61
|
@@ -1,425 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Copyright 2022 DXOS.org
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
import { Event, scheduleTask, sleep, synchronized, trackLeaks } from '@dxos/async';
|
|
6
|
-
import { Context } from '@dxos/context';
|
|
7
|
-
import {
|
|
8
|
-
type CredentialProcessor,
|
|
9
|
-
type FeedInfo,
|
|
10
|
-
type SpecificCredential,
|
|
11
|
-
checkCredentialType,
|
|
12
|
-
} from '@dxos/credentials';
|
|
13
|
-
import { ItemManager } from '@dxos/echo-db';
|
|
14
|
-
import { type FeedWriter } from '@dxos/feed-store';
|
|
15
|
-
import { invariant } from '@dxos/invariant';
|
|
16
|
-
import { type PublicKey } from '@dxos/keys';
|
|
17
|
-
import { log, omit } from '@dxos/log';
|
|
18
|
-
import { type ModelFactory } from '@dxos/model-factory';
|
|
19
|
-
import { CancelledError, type DataPipelineProcessed } from '@dxos/protocols';
|
|
20
|
-
import { type CreateEpochRequest } from '@dxos/protocols/proto/dxos/client/services';
|
|
21
|
-
import { type DataMessage } from '@dxos/protocols/proto/dxos/echo/feed';
|
|
22
|
-
import { type SpaceSnapshot } from '@dxos/protocols/proto/dxos/echo/snapshot';
|
|
23
|
-
import { type Credential, type Epoch } from '@dxos/protocols/proto/dxos/halo/credentials';
|
|
24
|
-
import { Timeframe } from '@dxos/timeframe';
|
|
25
|
-
import { TimeSeriesCounter, TimeUsageCounter, trace } from '@dxos/tracing';
|
|
26
|
-
import { tracer } from '@dxos/util';
|
|
27
|
-
|
|
28
|
-
import { DatabaseHost, type SnapshotManager } from '../db-host';
|
|
29
|
-
import { type MetadataStore } from '../metadata';
|
|
30
|
-
import { Pipeline } from '../pipeline';
|
|
31
|
-
|
|
32
|
-
export interface PipelineFactory {
|
|
33
|
-
openPipeline: (start: Timeframe) => Promise<Pipeline>;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export type DataPipelineParams = {
|
|
37
|
-
modelFactory: ModelFactory;
|
|
38
|
-
snapshotManager: SnapshotManager;
|
|
39
|
-
metadataStore: MetadataStore;
|
|
40
|
-
memberKey: PublicKey;
|
|
41
|
-
spaceKey: PublicKey;
|
|
42
|
-
feedInfoProvider: (feedKey: PublicKey) => FeedInfo | undefined;
|
|
43
|
-
snapshotId: string | undefined;
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Called once.
|
|
47
|
-
*/
|
|
48
|
-
onPipelineCreated: (pipeline: Pipeline) => Promise<void>;
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Minimum time in MS between recording latest timeframe in metadata.
|
|
53
|
-
*/
|
|
54
|
-
const TIMEFRAME_SAVE_DEBOUNCE_INTERVAL = 5_000;
|
|
55
|
-
|
|
56
|
-
export type CreateEpochOptions = {
|
|
57
|
-
migration?: CreateEpochRequest.Migration;
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Controls data pipeline in the space.
|
|
62
|
-
* Consumes the pipeline and updates the database.
|
|
63
|
-
* Reacts to new epochs to restart the pipeline.
|
|
64
|
-
*/
|
|
65
|
-
@trackLeaks('open', 'close')
|
|
66
|
-
@trace.resource()
|
|
67
|
-
export class DataPipeline implements CredentialProcessor {
|
|
68
|
-
private _ctx = new Context();
|
|
69
|
-
private _pipeline?: Pipeline = undefined;
|
|
70
|
-
private _targetTimeframe?: Timeframe = undefined;
|
|
71
|
-
|
|
72
|
-
private _lastAutomaticSnapshotTimeframe = new Timeframe();
|
|
73
|
-
private _isOpen = false;
|
|
74
|
-
|
|
75
|
-
private _lastTimeframeSaveTime = 0;
|
|
76
|
-
private _lastSnapshotSaveTime = 0;
|
|
77
|
-
private _lastProcessedEpoch = -1;
|
|
78
|
-
private _epochCtx?: Context;
|
|
79
|
-
|
|
80
|
-
@trace.metricsCounter()
|
|
81
|
-
private _usage = new TimeUsageCounter();
|
|
82
|
-
|
|
83
|
-
@trace.metricsCounter()
|
|
84
|
-
private _mutations = new TimeSeriesCounter();
|
|
85
|
-
|
|
86
|
-
public databaseHost?: DatabaseHost;
|
|
87
|
-
|
|
88
|
-
public itemManager!: ItemManager;
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Current epoch. Might be still processing.
|
|
92
|
-
*/
|
|
93
|
-
public currentEpoch?: SpecificCredential<Epoch> = undefined;
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Epoch currently applied.
|
|
97
|
-
*/
|
|
98
|
-
public appliedEpoch?: SpecificCredential<Epoch> = undefined;
|
|
99
|
-
|
|
100
|
-
public readonly onNewEpoch = new Event<Credential>();
|
|
101
|
-
|
|
102
|
-
constructor(private readonly _params: DataPipelineParams) {}
|
|
103
|
-
|
|
104
|
-
get isOpen() {
|
|
105
|
-
return this._isOpen;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
get pipeline() {
|
|
109
|
-
return this._pipeline;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
get pipelineState() {
|
|
113
|
-
return this._pipeline?.state;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
setTargetTimeframe(timeframe: Timeframe) {
|
|
117
|
-
this._targetTimeframe = timeframe;
|
|
118
|
-
this._pipeline?.state.setTargetTimeframe(timeframe);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
async processCredential(credential: Credential) {
|
|
122
|
-
if (!checkCredentialType(credential, 'dxos.halo.credentials.Epoch')) {
|
|
123
|
-
return;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
this.currentEpoch = credential;
|
|
127
|
-
if (this._isOpen) {
|
|
128
|
-
// process epoch
|
|
129
|
-
await this._processEpochInSeparateTask(credential);
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
@synchronized
|
|
134
|
-
async open() {
|
|
135
|
-
if (this._isOpen) {
|
|
136
|
-
return;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
this._pipeline = new Pipeline();
|
|
140
|
-
await this._params.onPipelineCreated(this._pipeline);
|
|
141
|
-
|
|
142
|
-
await this._pipeline.pause(); // Start paused until we have the first epoch.
|
|
143
|
-
await this._pipeline.start();
|
|
144
|
-
|
|
145
|
-
if (this._targetTimeframe) {
|
|
146
|
-
this._pipeline.state.setTargetTimeframe(this._targetTimeframe);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
// Create database backend.
|
|
150
|
-
const feedWriter: FeedWriter<DataMessage> = {
|
|
151
|
-
write: (data, options) => {
|
|
152
|
-
invariant(this._pipeline, 'Pipeline is not initialized.');
|
|
153
|
-
invariant(this.currentEpoch, 'Epoch is not initialized.');
|
|
154
|
-
return this._pipeline.writer.write({ data }, options);
|
|
155
|
-
},
|
|
156
|
-
};
|
|
157
|
-
|
|
158
|
-
this.databaseHost = new DatabaseHost(feedWriter, () => this._flush());
|
|
159
|
-
this.itemManager = new ItemManager(this._params.modelFactory);
|
|
160
|
-
|
|
161
|
-
// Connect pipeline to the database.
|
|
162
|
-
await this.databaseHost.open(this.itemManager, this._params.modelFactory);
|
|
163
|
-
|
|
164
|
-
// Start message processing loop.
|
|
165
|
-
scheduleTask(this._ctx, async () => {
|
|
166
|
-
await this._consumePipeline();
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
this._isOpen = true;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
@synchronized
|
|
173
|
-
async close() {
|
|
174
|
-
if (!this._isOpen) {
|
|
175
|
-
return;
|
|
176
|
-
}
|
|
177
|
-
log('close');
|
|
178
|
-
this._isOpen = false;
|
|
179
|
-
|
|
180
|
-
await this._ctx.dispose();
|
|
181
|
-
await this._pipeline?.stop();
|
|
182
|
-
|
|
183
|
-
// NOTE: Make sure the processing is stopped BEFORE we save the snapshot.
|
|
184
|
-
try {
|
|
185
|
-
if (this._pipeline) {
|
|
186
|
-
await this._saveTargetTimeframe(this._pipeline.state.timeframe);
|
|
187
|
-
}
|
|
188
|
-
} catch (err) {
|
|
189
|
-
log.catch(err);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
await this.databaseHost?.close();
|
|
193
|
-
await this.itemManager?.destroy();
|
|
194
|
-
|
|
195
|
-
this._ctx = new Context();
|
|
196
|
-
this._pipeline = undefined;
|
|
197
|
-
this._targetTimeframe = undefined;
|
|
198
|
-
this._lastAutomaticSnapshotTimeframe = new Timeframe();
|
|
199
|
-
this.currentEpoch = undefined;
|
|
200
|
-
this.appliedEpoch = undefined;
|
|
201
|
-
this._lastProcessedEpoch = -1;
|
|
202
|
-
this._epochCtx = undefined;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
private async _consumePipeline() {
|
|
206
|
-
const pipeline = this._pipeline;
|
|
207
|
-
if (this.currentEpoch) {
|
|
208
|
-
const waitForOneEpoch = this.onNewEpoch.waitForCount(1);
|
|
209
|
-
await this._processEpochInSeparateTask(this.currentEpoch);
|
|
210
|
-
await waitForOneEpoch;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
// CPU bottleneck control.
|
|
214
|
-
let messageCounter = 0;
|
|
215
|
-
|
|
216
|
-
invariant(pipeline, 'Pipeline is not initialized.');
|
|
217
|
-
for await (const msg of pipeline.consume()) {
|
|
218
|
-
const span = this._usage.beginRecording();
|
|
219
|
-
this._mutations.inc();
|
|
220
|
-
|
|
221
|
-
const { feedKey, seq, data } = msg;
|
|
222
|
-
log('processing message', { feedKey, seq });
|
|
223
|
-
|
|
224
|
-
try {
|
|
225
|
-
if (data.payload.data) {
|
|
226
|
-
const feedInfo = this._params.feedInfoProvider(feedKey);
|
|
227
|
-
if (!feedInfo) {
|
|
228
|
-
log.warn('Could not find feed', { feedKey });
|
|
229
|
-
continue;
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
const timer = tracer.mark('dxos.echo.pipeline.data'); // TODO(burdon): Add ID to params to filter.
|
|
233
|
-
this.databaseHost!.echoProcessor({
|
|
234
|
-
batch: data.payload.data.batch,
|
|
235
|
-
meta: {
|
|
236
|
-
feedKey,
|
|
237
|
-
seq,
|
|
238
|
-
timeframe: data.timeframe,
|
|
239
|
-
memberKey: feedInfo.assertion.identityKey,
|
|
240
|
-
},
|
|
241
|
-
});
|
|
242
|
-
|
|
243
|
-
timer.end();
|
|
244
|
-
// TODO(burdon): Reconcile different tracer approaches.
|
|
245
|
-
log.trace('dxos.echo.data-pipeline.processed', {
|
|
246
|
-
feedKey: feedKey.toHex(), // TODO(burdon): Need to flatten?
|
|
247
|
-
seq,
|
|
248
|
-
spaceKey: this._params.spaceKey.toHex(),
|
|
249
|
-
} satisfies DataPipelineProcessed);
|
|
250
|
-
|
|
251
|
-
// Timeframe clock is not updated yet.
|
|
252
|
-
await this._noteTargetStateIfNeeded(pipeline.state.pendingTimeframe);
|
|
253
|
-
}
|
|
254
|
-
} catch (err: any) {
|
|
255
|
-
log.catch(err);
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
span.end();
|
|
259
|
-
|
|
260
|
-
if (++messageCounter > 100) {
|
|
261
|
-
messageCounter = 0;
|
|
262
|
-
// Allow other tasks to process.
|
|
263
|
-
await idle(1_000);
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
private _createSnapshot(): SpaceSnapshot {
|
|
269
|
-
invariant(this.databaseHost, 'Database backend is not initialized.');
|
|
270
|
-
return {
|
|
271
|
-
spaceKey: this._params.spaceKey.asUint8Array(),
|
|
272
|
-
timeframe: this._pipeline!.state.timeframe,
|
|
273
|
-
database: this.databaseHost!.createSnapshot(),
|
|
274
|
-
};
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
private async _saveTargetTimeframe(timeframe: Timeframe) {
|
|
278
|
-
const newTimeframe = Timeframe.merge(this._targetTimeframe ?? new Timeframe(), timeframe);
|
|
279
|
-
await this._params.metadataStore.setSpaceDataLatestTimeframe(this._params.spaceKey, newTimeframe);
|
|
280
|
-
this._targetTimeframe = newTimeframe;
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
private async _noteTargetStateIfNeeded(timeframe: Timeframe) {
|
|
284
|
-
if (!this._pipeline?.state.reachedTarget) {
|
|
285
|
-
return;
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
// TODO(dmaretskyi): Replace this with a proper debounce/throttle.
|
|
289
|
-
|
|
290
|
-
if (Date.now() - this._lastTimeframeSaveTime > TIMEFRAME_SAVE_DEBOUNCE_INTERVAL) {
|
|
291
|
-
this._lastTimeframeSaveTime = Date.now();
|
|
292
|
-
|
|
293
|
-
await this._saveTargetTimeframe(timeframe);
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
private async _processEpochInSeparateTask(epoch: SpecificCredential<Epoch>) {
|
|
298
|
-
if (epoch.subject.assertion.number <= this._lastProcessedEpoch) {
|
|
299
|
-
return;
|
|
300
|
-
}
|
|
301
|
-
await this._epochCtx?.dispose();
|
|
302
|
-
const ctx = new Context({
|
|
303
|
-
onError: (err) => {
|
|
304
|
-
if (err instanceof CancelledError) {
|
|
305
|
-
log('Epoch processing cancelled.');
|
|
306
|
-
} else {
|
|
307
|
-
log.catch(err);
|
|
308
|
-
}
|
|
309
|
-
},
|
|
310
|
-
});
|
|
311
|
-
this._epochCtx = ctx;
|
|
312
|
-
scheduleTask(ctx, async () => {
|
|
313
|
-
if (!this._isOpen) {
|
|
314
|
-
// Space closed before we got to process the epoch.
|
|
315
|
-
return;
|
|
316
|
-
}
|
|
317
|
-
await this._processEpoch(ctx, epoch.subject.assertion);
|
|
318
|
-
|
|
319
|
-
// Carry over the snapshot CID from the previous epoch.
|
|
320
|
-
if (epoch.subject.assertion.snapshotCid === undefined) {
|
|
321
|
-
epoch.subject.assertion.snapshotCid = this.appliedEpoch?.subject.assertion.snapshotCid;
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
this.appliedEpoch = epoch;
|
|
325
|
-
this.onNewEpoch.emit(epoch);
|
|
326
|
-
});
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
@synchronized
|
|
330
|
-
private async _processEpoch(ctx: Context, epoch: Epoch) {
|
|
331
|
-
invariant(this._isOpen, 'Space is closed.');
|
|
332
|
-
invariant(this._pipeline);
|
|
333
|
-
this._lastProcessedEpoch = epoch.number;
|
|
334
|
-
|
|
335
|
-
log('processing', { epoch: omit(epoch, 'proof') });
|
|
336
|
-
if (epoch.snapshotCid) {
|
|
337
|
-
const snapshot = await this._params.snapshotManager.load(ctx, epoch.snapshotCid);
|
|
338
|
-
this.databaseHost!._itemDemuxer.restoreFromSnapshot(snapshot.database);
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
log('restarting pipeline from epoch');
|
|
342
|
-
await this._pipeline.pause();
|
|
343
|
-
await this._pipeline.setCursor(epoch.timeframe);
|
|
344
|
-
await this._pipeline.unpause();
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
async waitUntilTimeframe(timeframe: Timeframe) {
|
|
348
|
-
invariant(this._pipeline, 'Pipeline is not initialized.');
|
|
349
|
-
await this._pipeline.state.waitUntilTimeframe(timeframe);
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
@synchronized
|
|
353
|
-
async createEpoch(): Promise<Epoch> {
|
|
354
|
-
invariant(this._pipeline);
|
|
355
|
-
invariant(this.currentEpoch);
|
|
356
|
-
|
|
357
|
-
await this._pipeline.pause();
|
|
358
|
-
|
|
359
|
-
const snapshot = await this._createSnapshot();
|
|
360
|
-
const snapshotCid = await this._params.snapshotManager.store(snapshot);
|
|
361
|
-
|
|
362
|
-
const epoch: Epoch = {
|
|
363
|
-
previousId: this.currentEpoch.id,
|
|
364
|
-
timeframe: this._pipeline.state.timeframe,
|
|
365
|
-
number: (this.currentEpoch.subject.assertion as Epoch).number + 1,
|
|
366
|
-
snapshotCid,
|
|
367
|
-
};
|
|
368
|
-
|
|
369
|
-
await this._pipeline.unpause();
|
|
370
|
-
|
|
371
|
-
return epoch;
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
async ensureEpochInitialized() {
|
|
375
|
-
await this.onNewEpoch.waitForCondition(() => !!this.currentEpoch);
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
private async _flush() {
|
|
379
|
-
try {
|
|
380
|
-
if (this._pipeline) {
|
|
381
|
-
await this._saveTargetTimeframe(this._pipeline.state.timeframe);
|
|
382
|
-
}
|
|
383
|
-
} catch (err) {
|
|
384
|
-
log.catch(err);
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
await this._params.metadataStore.flush();
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
/**
|
|
392
|
-
* Waits up to `timeout` ms for the browser to be idle.
|
|
393
|
-
*/
|
|
394
|
-
const idle = async (timeout?: number) => {
|
|
395
|
-
if (!('scheduler' in globalThis && typeof (globalThis as any).scheduler.postTask === 'function')) {
|
|
396
|
-
await sleep(1);
|
|
397
|
-
return;
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
await new Promise<void>((resolve) => {
|
|
401
|
-
// const beginTime = performance.now();
|
|
402
|
-
const cleanup = () => {
|
|
403
|
-
clearTimeout(timer);
|
|
404
|
-
controller.abort();
|
|
405
|
-
// log.warn('yielded for', { ms: performance.now() - beginTime });
|
|
406
|
-
};
|
|
407
|
-
|
|
408
|
-
const controller = new AbortController();
|
|
409
|
-
|
|
410
|
-
void (globalThis as any).scheduler
|
|
411
|
-
.postTask(
|
|
412
|
-
() => {
|
|
413
|
-
cleanup();
|
|
414
|
-
resolve();
|
|
415
|
-
},
|
|
416
|
-
{ priority: 'background', signal: controller.signal },
|
|
417
|
-
)
|
|
418
|
-
.catch(() => {});
|
|
419
|
-
|
|
420
|
-
const timer = setTimeout(() => {
|
|
421
|
-
cleanup();
|
|
422
|
-
resolve();
|
|
423
|
-
}, timeout);
|
|
424
|
-
});
|
|
425
|
-
};
|
package/src/testing/util.ts
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Copyright 2021 DXOS.org
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
import { asyncTimeout } from '@dxos/async';
|
|
6
|
-
import { DocumentModel } from '@dxos/document-model';
|
|
7
|
-
import { ItemManager } from '@dxos/echo-db';
|
|
8
|
-
import { MockFeedWriter } from '@dxos/feed-store/testing';
|
|
9
|
-
import { PublicKey } from '@dxos/keys';
|
|
10
|
-
import { ModelFactory } from '@dxos/model-factory';
|
|
11
|
-
import { type DataMessage } from '@dxos/protocols/proto/dxos/echo/feed';
|
|
12
|
-
import { Timeframe } from '@dxos/timeframe';
|
|
13
|
-
|
|
14
|
-
import { DatabaseHost } from '../db-host';
|
|
15
|
-
import { type DataPipeline } from '../space';
|
|
16
|
-
|
|
17
|
-
export const createMemoryDatabase = async (modelFactory: ModelFactory) => {
|
|
18
|
-
const feed = new MockFeedWriter<DataMessage>();
|
|
19
|
-
const backend = new DatabaseHost(feed, async () => {
|
|
20
|
-
// No-op.
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
feed.written.on(([data, meta]) =>
|
|
24
|
-
backend.echoProcessor({
|
|
25
|
-
batch: data.batch,
|
|
26
|
-
meta: {
|
|
27
|
-
...meta,
|
|
28
|
-
memberKey: PublicKey.random(),
|
|
29
|
-
timeframe: new Timeframe([[meta.feedKey, meta.seq]]),
|
|
30
|
-
},
|
|
31
|
-
}),
|
|
32
|
-
);
|
|
33
|
-
|
|
34
|
-
const itemManager = new ItemManager(modelFactory);
|
|
35
|
-
await backend.open(itemManager, new ModelFactory().registerModel(DocumentModel));
|
|
36
|
-
return {
|
|
37
|
-
backend,
|
|
38
|
-
itemManager,
|
|
39
|
-
};
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
export const testLocalDatabase = async (create: DataPipeline, check: DataPipeline = create) => {
|
|
43
|
-
const objectId = PublicKey.random().toHex();
|
|
44
|
-
await create.databaseHost!.getWriteStream()?.write({
|
|
45
|
-
batch: {
|
|
46
|
-
objects: [
|
|
47
|
-
{
|
|
48
|
-
objectId,
|
|
49
|
-
genesis: {
|
|
50
|
-
modelType: DocumentModel.meta.type,
|
|
51
|
-
},
|
|
52
|
-
},
|
|
53
|
-
],
|
|
54
|
-
},
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
await asyncTimeout(
|
|
58
|
-
check.databaseHost!._itemDemuxer.mutation.waitForCondition(() => check.itemManager.entities.has(objectId)),
|
|
59
|
-
2000,
|
|
60
|
-
);
|
|
61
|
-
};
|