@dxos/echo-pipeline 0.1.34 → 0.1.36-next.ef27157
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-6CRSMR4G.mjs → chunk-LIPDNX3G.mjs} +73 -46
- package/dist/lib/browser/chunk-LIPDNX3G.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +3 -1
- package/dist/lib/browser/index.mjs.map +1 -1
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +1 -1
- package/dist/lib/node/index.cjs +63 -35
- package/dist/lib/node/index.cjs.map +3 -3
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node/testing/index.cjs +59 -37
- package/dist/lib/node/testing/index.cjs.map +3 -3
- package/dist/types/src/pipeline/pipeline.d.ts +1 -0
- 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/package.json +30 -29
- package/src/pipeline/pipeline.test.ts +214 -0
- package/src/pipeline/pipeline.ts +14 -5
- package/src/pipeline/timeframe-clock.ts +4 -1
- package/src/space/data-pipeline.ts +2 -2
- package/dist/lib/browser/chunk-6CRSMR4G.mjs.map +0 -7
|
@@ -36,6 +36,7 @@ export declare class PipelineState {
|
|
|
36
36
|
get endTimeframe(): Timeframe;
|
|
37
37
|
get timeframe(): Timeframe;
|
|
38
38
|
get targetTimeframe(): Timeframe;
|
|
39
|
+
get feeds(): FeedWrapper<any>[];
|
|
39
40
|
waitUntilTimeframe(target: Timeframe): Promise<void>;
|
|
40
41
|
setTargetTimeframe(target: Timeframe): void;
|
|
41
42
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../../../../src/pipeline/pipeline.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,OAAO,EAAmB,MAAM,eAAe,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC5E,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvC,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sCAAsC,CAAC;AACxE,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAI5C,OAAO,
|
|
1
|
+
{"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../../../../src/pipeline/pipeline.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,OAAO,EAAmB,MAAM,eAAe,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC5E,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvC,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sCAAsC,CAAC;AACxE,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAI5C,OAAO,EAAyC,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAE1F,oBAAY,4BAA4B,GAAG;IACzC;;OAEG;IACH,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;OAEG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB,CAAC;AAEF;;GAEG;AACH,qBAAa,aAAa;IAatB,OAAO,CAAC,SAAS;IACjB,OAAO,CAAC,eAAe;IAZzB,SAAgB,eAAe,yCAA+B;IAE9D;;OAEG;IACH,OAAO,CAAC,gBAAgB,CAAwB;IAEhD,OAAO,CAAC,qBAAqB,CAA4B;gBAI/C,SAAS,EAAE,eAAe,CAAC,GAAG,CAAC,EAC/B,eAAe,EAAE,cAAc;IAGzC;;;OAGG;IAEH,IAAI,YAAY,cASf;IAED,IAAI,SAAS,cAEZ;IAED,IAAI,eAAe,cAElB;IAED,IAAI,KAAK,uBAER;IAEK,kBAAkB,CAAC,MAAM,EAAE,SAAS;IAI1C,kBAAkB,CAAC,MAAM,EAAE,SAAS;IAIpC;;;;;;OAMG;IACG,+BAA+B,CAAC,EACpC,GAAmB,EACnB,OAAO,EACP,YAAmB,EACpB,GAAE,4BAAiC;CAuCrC;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,aAAa,CAAC;IACrB,MAAM,EAAE,UAAU,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;CACzC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,qBAAa,QAAS,YAAW,gBAAgB;IAsB7C,OAAO,CAAC,QAAQ,CAAC,iBAAiB;IArBpC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAA8C;IAG9E,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAG9B;IAGH,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAiF;IAGxG,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAwB;IAG3D,OAAO,CAAC,OAAO,CAA8C;IAE7D,OAAO,CAAC,OAAO,CAAS;gBAIL,iBAAiB,GAAE,SAA2B;IAOjE,IAAI,KAAK,kBAER;IAED,IAAI,MAAM,IAAI,UAAU,CAAC,WAAW,CAAC,OAAO,CAAC,CAG5C;IAED,QAAQ,IAAI,WAAW,CAAC,WAAW,CAAC,EAAE;IAMhC,OAAO,CAAC,IAAI,EAAE,WAAW,CAAC,WAAW,CAAC;IAI5C,OAAO,CAAC,OAAO,EAAE,SAAS;IAI1B,YAAY,CAAC,IAAI,EAAE,WAAW,CAAC,WAAW,CAAC;IAcrC,KAAK;IAOL,IAAI;IAOV;;;OAGG;IACI,OAAO,IAAI,aAAa,CAAC,gBAAgB,CAAC;CAelD"}
|
|
@@ -4,6 +4,7 @@ import { PublicKey } from '@dxos/keys';
|
|
|
4
4
|
import { Timeframe } from '@dxos/timeframe';
|
|
5
5
|
export declare const mapTimeframeToFeedIndexes: (timeframe: Timeframe) => FeedIndex[];
|
|
6
6
|
export declare const mapFeedIndexesToTimeframe: (indexes: FeedIndex[]) => Timeframe;
|
|
7
|
+
export declare const startAfter: (timeframe: Timeframe) => FeedIndex[];
|
|
7
8
|
/**
|
|
8
9
|
* Keeps state of the last timeframe that was processed by ECHO.
|
|
9
10
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"timeframe-clock.d.ts","sourceRoot":"","sources":["../../../../src/pipeline/timeframe-clock.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAEpC,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvC,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C,eAAO,MAAM,yBAAyB,cAAe,SAAS,KAAG,SAAS,EACN,CAAC;AAErE,eAAO,MAAM,yBAAyB,YAAa,SAAS,EAAE,KAAG,SACK,CAAC;AAEvE;;GAEG;AACH,qBAAa,cAAc;IAKvB,OAAO,CAAC,UAAU;IAJpB,QAAQ,CAAC,MAAM,mBAA0B;gBAI/B,UAAU,YAAkB;IAGtC,IAAI,SAAS,cAEZ;IAED,eAAe,CAAC,GAAG,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM;IAK3C,OAAO,CAAC,SAAS,EAAE,SAAS;IAMtB,gBAAgB,CAAC,MAAM,EAAE,SAAS;CAYzC"}
|
|
1
|
+
{"version":3,"file":"timeframe-clock.d.ts","sourceRoot":"","sources":["../../../../src/pipeline/timeframe-clock.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAEpC,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvC,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C,eAAO,MAAM,yBAAyB,cAAe,SAAS,KAAG,SAAS,EACN,CAAC;AAErE,eAAO,MAAM,yBAAyB,YAAa,SAAS,EAAE,KAAG,SACK,CAAC;AAEvE,eAAO,MAAM,UAAU,cAAe,SAAS,KAAG,SAAS,EACoB,CAAC;AAEhF;;GAEG;AACH,qBAAa,cAAc;IAKvB,OAAO,CAAC,UAAU;IAJpB,QAAQ,CAAC,MAAM,mBAA0B;gBAI/B,UAAU,YAAkB;IAGtC,IAAI,SAAS,cAEZ;IAED,eAAe,CAAC,GAAG,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM;IAK3C,OAAO,CAAC,SAAS,EAAE,SAAS;IAMtB,gBAAgB,CAAC,MAAM,EAAE,SAAS;CAYzC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/echo-pipeline",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.36-next.ef27157",
|
|
4
4
|
"description": "ECHO database.",
|
|
5
5
|
"homepage": "https://dxos.org",
|
|
6
6
|
"bugs": "https://github.com/dxos/dxos/issues",
|
|
@@ -20,40 +20,41 @@
|
|
|
20
20
|
],
|
|
21
21
|
"dependencies": {
|
|
22
22
|
"debug": "^4.3.3",
|
|
23
|
-
"@dxos/async": "0.1.
|
|
24
|
-
"@dxos/codec-protobuf": "0.1.
|
|
25
|
-
"@dxos/context": "0.1.
|
|
26
|
-
"@dxos/credentials": "0.1.
|
|
27
|
-
"@dxos/crypto": "0.1.
|
|
28
|
-
"@dxos/debug": "0.1.
|
|
29
|
-
"@dxos/document-model": "0.1.
|
|
30
|
-
"@dxos/echo-db": "0.1.
|
|
31
|
-
"@dxos/feed-store": "0.1.
|
|
32
|
-
"@dxos/hypercore": "0.1.
|
|
33
|
-
"@dxos/keyring": "0.1.
|
|
34
|
-
"@dxos/keys": "0.1.
|
|
35
|
-
"@dxos/log": "0.1.
|
|
36
|
-
"@dxos/messaging": "0.1.
|
|
37
|
-
"@dxos/model-factory": "0.1.
|
|
38
|
-
"@dxos/network-manager": "0.1.
|
|
39
|
-
"@dxos/node-std": "0.1.
|
|
40
|
-
"@dxos/protocols": "0.1.
|
|
41
|
-
"@dxos/random-access-storage": "0.1.
|
|
42
|
-
"@dxos/rpc": "0.1.
|
|
43
|
-
"@dxos/teleport": "0.1.
|
|
44
|
-
"@dxos/teleport-extension-gossip": "0.1.
|
|
45
|
-
"@dxos/teleport-extension-object-sync": "0.1.
|
|
46
|
-
"@dxos/teleport-extension-replicator": "0.1.
|
|
47
|
-
"@dxos/timeframe": "0.1.
|
|
48
|
-
"@dxos/typings": "0.1.
|
|
49
|
-
"@dxos/util": "0.1.
|
|
23
|
+
"@dxos/async": "0.1.36-next.ef27157",
|
|
24
|
+
"@dxos/codec-protobuf": "0.1.36-next.ef27157",
|
|
25
|
+
"@dxos/context": "0.1.36-next.ef27157",
|
|
26
|
+
"@dxos/credentials": "0.1.36-next.ef27157",
|
|
27
|
+
"@dxos/crypto": "0.1.36-next.ef27157",
|
|
28
|
+
"@dxos/debug": "0.1.36-next.ef27157",
|
|
29
|
+
"@dxos/document-model": "0.1.36-next.ef27157",
|
|
30
|
+
"@dxos/echo-db": "0.1.36-next.ef27157",
|
|
31
|
+
"@dxos/feed-store": "0.1.36-next.ef27157",
|
|
32
|
+
"@dxos/hypercore": "0.1.36-next.ef27157",
|
|
33
|
+
"@dxos/keyring": "0.1.36-next.ef27157",
|
|
34
|
+
"@dxos/keys": "0.1.36-next.ef27157",
|
|
35
|
+
"@dxos/log": "0.1.36-next.ef27157",
|
|
36
|
+
"@dxos/messaging": "0.1.36-next.ef27157",
|
|
37
|
+
"@dxos/model-factory": "0.1.36-next.ef27157",
|
|
38
|
+
"@dxos/network-manager": "0.1.36-next.ef27157",
|
|
39
|
+
"@dxos/node-std": "0.1.36-next.ef27157",
|
|
40
|
+
"@dxos/protocols": "0.1.36-next.ef27157",
|
|
41
|
+
"@dxos/random-access-storage": "0.1.36-next.ef27157",
|
|
42
|
+
"@dxos/rpc": "0.1.36-next.ef27157",
|
|
43
|
+
"@dxos/teleport": "0.1.36-next.ef27157",
|
|
44
|
+
"@dxos/teleport-extension-gossip": "0.1.36-next.ef27157",
|
|
45
|
+
"@dxos/teleport-extension-object-sync": "0.1.36-next.ef27157",
|
|
46
|
+
"@dxos/teleport-extension-replicator": "0.1.36-next.ef27157",
|
|
47
|
+
"@dxos/timeframe": "0.1.36-next.ef27157",
|
|
48
|
+
"@dxos/typings": "0.1.36-next.ef27157",
|
|
49
|
+
"@dxos/util": "0.1.36-next.ef27157"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
52
|
"@types/debug": "^4.1.7",
|
|
53
|
+
"fast-check": "~3.3.0",
|
|
53
54
|
"hypercore-protocol": "^8.0.7",
|
|
54
55
|
"source-map-support": "^0.5.12",
|
|
55
56
|
"wait-for-expect": "^3.0.2",
|
|
56
|
-
"@dxos/text-model": "0.1.
|
|
57
|
+
"@dxos/text-model": "0.1.36-next.ef27157"
|
|
57
58
|
},
|
|
58
59
|
"publishConfig": {
|
|
59
60
|
"access": "public"
|
|
@@ -2,7 +2,16 @@
|
|
|
2
2
|
// Copyright 2022 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
+
import expect from 'expect';
|
|
6
|
+
import * as fc from 'fast-check';
|
|
7
|
+
import { inspect } from 'util';
|
|
8
|
+
|
|
9
|
+
import { asyncTimeout } from '@dxos/async';
|
|
5
10
|
import { checkType } from '@dxos/debug';
|
|
11
|
+
import { FeedStore, FeedWrapper } from '@dxos/feed-store';
|
|
12
|
+
import { PublicKey } from '@dxos/keys';
|
|
13
|
+
import { log } from '@dxos/log';
|
|
14
|
+
import { FeedMessageBlock } from '@dxos/protocols';
|
|
6
15
|
import { FeedMessage } from '@dxos/protocols/proto/dxos/echo/feed';
|
|
7
16
|
import { describe, test, afterTest } from '@dxos/test';
|
|
8
17
|
import { Timeframe } from '@dxos/timeframe';
|
|
@@ -76,4 +85,209 @@ describe('pipeline/Pipeline', () => {
|
|
|
76
85
|
}
|
|
77
86
|
}
|
|
78
87
|
});
|
|
88
|
+
|
|
89
|
+
test
|
|
90
|
+
.skip('stress', async () => {
|
|
91
|
+
const builder = new TestFeedBuilder();
|
|
92
|
+
|
|
93
|
+
const NUM_AGENTS = 2;
|
|
94
|
+
const NUM_MESSAGES = 100;
|
|
95
|
+
|
|
96
|
+
class Agent {
|
|
97
|
+
public startingTimeframe = new Timeframe();
|
|
98
|
+
public pipeline!: Pipeline;
|
|
99
|
+
public feed!: FeedWrapper<FeedMessage>;
|
|
100
|
+
public messages: FeedMessageBlock[] = [];
|
|
101
|
+
public writePromise: Promise<any> = Promise.resolve();
|
|
102
|
+
|
|
103
|
+
constructor(public id: string, public feedStore: FeedStore<FeedMessage>) {}
|
|
104
|
+
|
|
105
|
+
async open() {
|
|
106
|
+
const key = await builder.keyring.createKey();
|
|
107
|
+
this.feed = await this.feedStore.openFeed(key, { writable: true });
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async start() {
|
|
111
|
+
this.pipeline = new Pipeline(this.startingTimeframe);
|
|
112
|
+
|
|
113
|
+
await this.pipeline.start();
|
|
114
|
+
|
|
115
|
+
// NOTE: not awaiting here breaks the test.
|
|
116
|
+
await Promise.all(this.feedStore.feeds.map((feed) => this.pipeline.addFeed(feed)));
|
|
117
|
+
this.pipeline.setWriteFeed(this.feed);
|
|
118
|
+
|
|
119
|
+
// consume in async task.
|
|
120
|
+
void this.consume();
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
async stop() {
|
|
124
|
+
await this.pipeline.stop();
|
|
125
|
+
this.startingTimeframe = this.pipeline.state.timeframe;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async close() {
|
|
129
|
+
await this.feed.close();
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
write(message: FeedMessage.Payload) {
|
|
133
|
+
const prev = this.writePromise;
|
|
134
|
+
const promise = this.pipeline.writer!.write(message);
|
|
135
|
+
this.writePromise = Promise.all([prev, promise]);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
async consume() {
|
|
139
|
+
for await (const msg of this.pipeline.consume()) {
|
|
140
|
+
this.messages.push(msg);
|
|
141
|
+
}
|
|
142
|
+
log('stopped consuming');
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
type Model = {};
|
|
147
|
+
type Real = {
|
|
148
|
+
feedStore: FeedStore<FeedMessage>;
|
|
149
|
+
agents: Map<string, Agent>;
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
class WriteCommand implements fc.AsyncCommand<Model, Real> {
|
|
153
|
+
constructor(public agent: string, public count: number) {}
|
|
154
|
+
|
|
155
|
+
check = () => true;
|
|
156
|
+
|
|
157
|
+
async run(model: Model, real: Real) {
|
|
158
|
+
console.log(`WriteCommand(${this.agent}, ${this.count})`);
|
|
159
|
+
|
|
160
|
+
const agent = real.agents.get(this.agent)!;
|
|
161
|
+
|
|
162
|
+
const toWrite = Math.min(this.count, NUM_MESSAGES - agent.feed.length);
|
|
163
|
+
if (toWrite > 0) {
|
|
164
|
+
for (const _ of range(toWrite)) {
|
|
165
|
+
agent.write({}); // Content is not important.
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
toString = () => `WriteCommand(${this.agent}, ${this.count})`;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
class SyncCommand implements fc.AsyncCommand<Model, Real> {
|
|
174
|
+
check = () => true;
|
|
175
|
+
|
|
176
|
+
async run(model: Model, real: Real) {
|
|
177
|
+
console.log('SyncCommand()');
|
|
178
|
+
const targets: any = {};
|
|
179
|
+
|
|
180
|
+
try {
|
|
181
|
+
for (const agent of real.agents.values()) {
|
|
182
|
+
await agent.writePromise;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
for (const agent of real.agents.values()) {
|
|
186
|
+
targets[agent.id] = agent.pipeline.state.endTimeframe;
|
|
187
|
+
if (agent.pipeline.state.endTimeframe.isEmpty()) {
|
|
188
|
+
console.log('empty endtimeframe', {
|
|
189
|
+
id: agent.id,
|
|
190
|
+
endTimeframe: agent.pipeline.state.endTimeframe,
|
|
191
|
+
feeds: agent.pipeline.getFeeds().map((feed) => [feed.key.toString(), feed.length])
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
await asyncTimeout(agent.pipeline.state.waitUntilTimeframe(agent.pipeline.state.endTimeframe), 1000);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const tf: Timeframe = real.agents.values().next().value!.pipeline.state.timeframe;
|
|
198
|
+
for (const agent of real.agents.values()) {
|
|
199
|
+
expect(agent.pipeline.state.timeframe.equals(tf)).toEqual(true);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const totalMessages = real.feedStore.feeds.reduce((acc, feed) => acc + feed.length, 0);
|
|
203
|
+
for (const agent of real.agents.values()) {
|
|
204
|
+
expect(agent.messages.length).toEqual(totalMessages);
|
|
205
|
+
}
|
|
206
|
+
} catch (err) {
|
|
207
|
+
console.log(
|
|
208
|
+
inspect(
|
|
209
|
+
{
|
|
210
|
+
agents: Array.from(real.agents.values()).map((agent) => ({
|
|
211
|
+
id: agent.id,
|
|
212
|
+
messages: agent.messages.length,
|
|
213
|
+
feeds: agent.pipeline.getFeeds().map((feed) => [feed.key, feed.length]),
|
|
214
|
+
timeframe: agent.pipeline.state.timeframe,
|
|
215
|
+
endTimeframe: agent.pipeline.state.endTimeframe
|
|
216
|
+
})),
|
|
217
|
+
feeds: real.feedStore.feeds.map((feed) => [feed.key, feed.length]),
|
|
218
|
+
targets
|
|
219
|
+
},
|
|
220
|
+
false,
|
|
221
|
+
null,
|
|
222
|
+
true
|
|
223
|
+
)
|
|
224
|
+
);
|
|
225
|
+
throw err;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
toString = () => 'SyncCommand()';
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
class RestartCommand implements fc.AsyncCommand<Model, Real> {
|
|
233
|
+
constructor(public agent: string) {}
|
|
234
|
+
|
|
235
|
+
check = () => true;
|
|
236
|
+
|
|
237
|
+
async run(model: Model, real: Real) {
|
|
238
|
+
console.log(`RestartCommand(${this.agent})`);
|
|
239
|
+
|
|
240
|
+
const agent = real.agents.get(this.agent)!;
|
|
241
|
+
|
|
242
|
+
await agent.writePromise;
|
|
243
|
+
await agent.stop();
|
|
244
|
+
await agent.start();
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
toString = () => `RestartCommand(${this.agent})`;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const agentIds = range(NUM_AGENTS).map(() => PublicKey.random().toHex().slice(0, 8));
|
|
251
|
+
const anAgentId = fc.constantFrom(...agentIds);
|
|
252
|
+
|
|
253
|
+
const commands = fc.commands(
|
|
254
|
+
[
|
|
255
|
+
fc.tuple(anAgentId, fc.integer({ min: 1, max: 10 })).map(([agent, count]) => new WriteCommand(agent, count)),
|
|
256
|
+
fc.constant(new SyncCommand()),
|
|
257
|
+
anAgentId.map((agent) => new RestartCommand(agent))
|
|
258
|
+
],
|
|
259
|
+
{ size: 'large' }
|
|
260
|
+
);
|
|
261
|
+
|
|
262
|
+
const model = fc.asyncProperty(commands, async (commands) => {
|
|
263
|
+
const feedStore = builder.createFeedStore();
|
|
264
|
+
|
|
265
|
+
const agents = new Map(agentIds.map((id) => [id, new Agent(id, feedStore)]));
|
|
266
|
+
await Promise.all(Array.from(agents.values()).map((agent) => agent.open()));
|
|
267
|
+
await Promise.all(Array.from(agents.values()).map((agent) => agent.start()));
|
|
268
|
+
|
|
269
|
+
const setup: fc.ModelRunSetup<Model, Real> = () => ({
|
|
270
|
+
model: {},
|
|
271
|
+
real: {
|
|
272
|
+
feedStore,
|
|
273
|
+
agents
|
|
274
|
+
}
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
try {
|
|
278
|
+
await fc.asyncModelRun(setup, [...commands, new SyncCommand()]);
|
|
279
|
+
} finally {
|
|
280
|
+
await Promise.all(Array.from(agents.values()).map((agent) => agent.stop()));
|
|
281
|
+
await Promise.all(Array.from(agents.values()).map((agent) => agent.close()));
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
const examples: [commands: Iterable<fc.AsyncCommand<Model, Real, boolean>>][] = [
|
|
286
|
+
[[new WriteCommand(agentIds[0], 10), new WriteCommand(agentIds[1], 10), new SyncCommand()]],
|
|
287
|
+
[[new WriteCommand(agentIds[0], 4), new RestartCommand(agentIds[0]), new SyncCommand()]]
|
|
288
|
+
];
|
|
289
|
+
|
|
290
|
+
await fc.assert(model, { examples });
|
|
291
|
+
})
|
|
292
|
+
.timeout(60_000);
|
|
79
293
|
});
|
package/src/pipeline/pipeline.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import assert from 'node:assert';
|
|
6
6
|
|
|
7
|
-
import { sleep, Trigger } from '@dxos/async';
|
|
7
|
+
import { sleep, synchronized, Trigger } from '@dxos/async';
|
|
8
8
|
import { Context, rejectOnDispose } from '@dxos/context';
|
|
9
9
|
import { FeedSetIterator, FeedWrapper, FeedWriter } from '@dxos/feed-store';
|
|
10
10
|
import { PublicKey } from '@dxos/keys';
|
|
@@ -15,7 +15,7 @@ import { Timeframe } from '@dxos/timeframe';
|
|
|
15
15
|
|
|
16
16
|
import { createMappedFeedWriter } from '../common';
|
|
17
17
|
import { createMessageSelector } from './message-selector';
|
|
18
|
-
import { mapFeedIndexesToTimeframe,
|
|
18
|
+
import { mapFeedIndexesToTimeframe, startAfter, TimeframeClock } from './timeframe-clock';
|
|
19
19
|
|
|
20
20
|
export type WaitUntilReachedTargetParams = {
|
|
21
21
|
/**
|
|
@@ -54,13 +54,14 @@ export class PipelineState {
|
|
|
54
54
|
* Latest theoretical timeframe based on the last mutation in each feed.
|
|
55
55
|
* NOTE: This might never be reached if the mutation dependencies
|
|
56
56
|
*/
|
|
57
|
+
// TODO(dmaretskyi): Rename `totalTimeframe`? or `lastTimeframe`.
|
|
57
58
|
get endTimeframe() {
|
|
58
59
|
return mapFeedIndexesToTimeframe(
|
|
59
60
|
this._iterator.feeds
|
|
60
|
-
.filter((feed) => feed.
|
|
61
|
+
.filter((feed) => feed.length > 0)
|
|
61
62
|
.map((feed) => ({
|
|
62
63
|
feedKey: feed.key,
|
|
63
|
-
index: feed.
|
|
64
|
+
index: feed.length - 1
|
|
64
65
|
}))
|
|
65
66
|
);
|
|
66
67
|
}
|
|
@@ -73,6 +74,10 @@ export class PipelineState {
|
|
|
73
74
|
return this._targetTimeframe ? this._targetTimeframe : new Timeframe();
|
|
74
75
|
}
|
|
75
76
|
|
|
77
|
+
get feeds() {
|
|
78
|
+
return this._iterator.feeds;
|
|
79
|
+
}
|
|
80
|
+
|
|
76
81
|
async waitUntilTimeframe(target: Timeframe) {
|
|
77
82
|
await this._timeframeClock.waitUntilReached(target);
|
|
78
83
|
}
|
|
@@ -175,7 +180,7 @@ export class Pipeline implements PipelineAccessor {
|
|
|
175
180
|
|
|
176
181
|
// Inbound feed stream.
|
|
177
182
|
private readonly _feedSetIterator = new FeedSetIterator<FeedMessage>(createMessageSelector(this._timeframeClock), {
|
|
178
|
-
start:
|
|
183
|
+
start: startAfter(this._initialTimeframe),
|
|
179
184
|
stallTimeout: 1000
|
|
180
185
|
});
|
|
181
186
|
|
|
@@ -212,6 +217,8 @@ export class Pipeline implements PipelineAccessor {
|
|
|
212
217
|
return this._feedSetIterator.feeds;
|
|
213
218
|
}
|
|
214
219
|
|
|
220
|
+
// NOTE: This cannot be synchronized with `stop` because stop waits for the mutation processing to complete,
|
|
221
|
+
// which might be opening feeds during the mutation processing, which w
|
|
215
222
|
async addFeed(feed: FeedWrapper<FeedMessage>) {
|
|
216
223
|
await this._feedSetIterator.addFeed(feed);
|
|
217
224
|
}
|
|
@@ -233,12 +240,14 @@ export class Pipeline implements PipelineAccessor {
|
|
|
233
240
|
);
|
|
234
241
|
}
|
|
235
242
|
|
|
243
|
+
@synchronized
|
|
236
244
|
async start() {
|
|
237
245
|
log('starting...', {});
|
|
238
246
|
await this._feedSetIterator.open();
|
|
239
247
|
log('started');
|
|
240
248
|
}
|
|
241
249
|
|
|
250
|
+
@synchronized
|
|
242
251
|
async stop() {
|
|
243
252
|
log('stopping...', {});
|
|
244
253
|
await this._feedSetIterator.close();
|
|
@@ -15,6 +15,9 @@ export const mapTimeframeToFeedIndexes = (timeframe: Timeframe): FeedIndex[] =>
|
|
|
15
15
|
export const mapFeedIndexesToTimeframe = (indexes: FeedIndex[]): Timeframe =>
|
|
16
16
|
new Timeframe(indexes.map(({ feedKey, index }) => [feedKey, index]));
|
|
17
17
|
|
|
18
|
+
export const startAfter = (timeframe: Timeframe): FeedIndex[] =>
|
|
19
|
+
timeframe.frames().map(([feedKey, index]) => ({ feedKey, index: index + 1 }));
|
|
20
|
+
|
|
18
21
|
/**
|
|
19
22
|
* Keeps state of the last timeframe that was processed by ECHO.
|
|
20
23
|
*/
|
|
@@ -42,7 +45,7 @@ export class TimeframeClock {
|
|
|
42
45
|
|
|
43
46
|
@timed(5_000)
|
|
44
47
|
async waitUntilReached(target: Timeframe) {
|
|
45
|
-
log.
|
|
48
|
+
log.info('waitUntilReached', { target, current: this._timeframe });
|
|
46
49
|
await this.update.waitForCondition(() => {
|
|
47
50
|
log('check if reached', {
|
|
48
51
|
target,
|
|
@@ -53,7 +53,7 @@ const TIMEFRAME_SAVE_DEBOUNCE_INTERVAL = 500;
|
|
|
53
53
|
/**
|
|
54
54
|
* Flag to disable automatic local snapshots.
|
|
55
55
|
*/
|
|
56
|
-
const DISABLE_SNAPSHOT_CACHE =
|
|
56
|
+
const DISABLE_SNAPSHOT_CACHE = true;
|
|
57
57
|
|
|
58
58
|
/**
|
|
59
59
|
* Controls data pipeline in the space.
|
|
@@ -106,7 +106,7 @@ export class DataPipeline {
|
|
|
106
106
|
}
|
|
107
107
|
|
|
108
108
|
this._spaceContext = spaceContext;
|
|
109
|
-
if (this._params.snapshotId) {
|
|
109
|
+
if (this._params.snapshotId && !DISABLE_SNAPSHOT_CACHE) {
|
|
110
110
|
this._snapshot = await this._params.snapshotManager.load(this._params.snapshotId);
|
|
111
111
|
this._lastAutomaticSnapshotTimeframe = this._snapshot?.timeframe ?? new Timeframe();
|
|
112
112
|
}
|