@dxos/echo-pipeline 0.3.3-main.e0ded2e → 0.3.3-main.ef9deca

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.
@@ -3,6 +3,12 @@ import { type ItemDemuxer, type ItemManager } from '@dxos/echo-db';
3
3
  import { type FeedWriter } from '@dxos/feed-store';
4
4
  import { type DataMessage } from '@dxos/protocols/proto/dxos/echo/feed';
5
5
  import { EchoEvent, type MutationReceipt, type WriteRequest } from '@dxos/protocols/proto/dxos/echo/service';
6
+ export type DataServiceHostOptions = {
7
+ /**
8
+ * @default true
9
+ */
10
+ deferEvents?: boolean;
11
+ };
6
12
  /**
7
13
  * Provides methods for DataService for a single space.
8
14
  * A DataServiceRouter must be placed before it to route requests to different DataServiceHost instances based on space id.
@@ -11,12 +17,14 @@ export declare class DataServiceHost {
11
17
  private readonly _itemManager;
12
18
  private readonly _itemDemuxer;
13
19
  private readonly _flush;
14
- private readonly _writeStream?;
20
+ private readonly _writeStream;
21
+ private readonly _opts;
15
22
  private readonly _ctx;
16
23
  private readonly _clientTagMap;
17
- constructor(_itemManager: ItemManager, _itemDemuxer: ItemDemuxer, _flush: () => Promise<void>, _writeStream?: FeedWriter<DataMessage> | undefined);
24
+ constructor(_itemManager: ItemManager, _itemDemuxer: ItemDemuxer, _flush: () => Promise<void>, _writeStream: FeedWriter<DataMessage> | undefined, _opts?: DataServiceHostOptions);
18
25
  open(): Promise<void>;
19
26
  close(): Promise<void>;
27
+ private get _deferEvents();
20
28
  /**
21
29
  * Real-time subscription to data objects in a space.
22
30
  */
@@ -1 +1 @@
1
- {"version":3,"file":"data-service-host.d.ts","sourceRoot":"","sources":["../../../../src/db-host/data-service-host.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAE9C,OAAO,EAAuB,KAAK,WAAW,EAAE,KAAK,WAAW,EAAuB,MAAM,eAAe,CAAC;AAC7G,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAInD,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,sCAAsC,CAAC;AAExE,OAAO,EAAE,SAAS,EAAE,KAAK,eAAe,EAAE,KAAK,YAAY,EAAE,MAAM,yCAAyC,CAAC;AAG7G;;;GAGG;AAEH,qBAAa,eAAe;IAQxB,OAAO,CAAC,QAAQ,CAAC,YAAY;IAC7B,OAAO,CAAC,QAAQ,CAAC,YAAY;IAC7B,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC;IAVhC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAiB;IAEtC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAE5B;gBAGiB,YAAY,EAAE,WAAW,EACzB,YAAY,EAAE,WAAW,EACzB,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,EAC3B,YAAY,CAAC,qCAAyB;IAGnD,IAAI;IAEJ,KAAK;IAIX;;OAEG;IACH,SAAS,IAAI,MAAM,CAAC,SAAS,CAAC;IAkDxB,KAAK,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,eAAe,CAAC;IAsBtD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAG7B"}
1
+ {"version":3,"file":"data-service-host.d.ts","sourceRoot":"","sources":["../../../../src/db-host/data-service-host.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAE9C,OAAO,EAAuB,KAAK,WAAW,EAAE,KAAK,WAAW,EAAuB,MAAM,eAAe,CAAC;AAC7G,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAInD,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,sCAAsC,CAAC;AAExE,OAAO,EAAE,SAAS,EAAE,KAAK,eAAe,EAAE,KAAK,YAAY,EAAE,MAAM,yCAAyC,CAAC;AAM7G,MAAM,MAAM,sBAAsB,GAAG;IACnC;;OAEG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,CAAC;AAEF;;;GAGG;AAEH,qBAAa,eAAe;IAQxB,OAAO,CAAC,QAAQ,CAAC,YAAY;IAC7B,OAAO,CAAC,QAAQ,CAAC,YAAY;IAC7B,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,YAAY;IAC7B,OAAO,CAAC,QAAQ,CAAC,KAAK;IAXxB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAiB;IAEtC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAE5B;gBAGiB,YAAY,EAAE,WAAW,EACzB,YAAY,EAAE,WAAW,EACzB,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,EAC3B,YAAY,EAAE,UAAU,CAAC,WAAW,CAAC,GAAG,SAAS,EACjD,KAAK,GAAE,sBAA2B;IAG/C,IAAI;IAEJ,KAAK;IAIX,OAAO,KAAK,YAAY,GAEvB;IAED;;OAEG;IACH,SAAS,IAAI,MAAM,CAAC,SAAS,CAAC;IAmHxB,KAAK,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,eAAe,CAAC;IAsBtD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAG7B"}
@@ -3,7 +3,7 @@ import { type FeedWriter } from '@dxos/feed-store';
3
3
  import { type ModelFactory } from '@dxos/model-factory';
4
4
  import { type DataMessage } from '@dxos/protocols/proto/dxos/echo/feed';
5
5
  import { type EchoSnapshot } from '@dxos/protocols/proto/dxos/echo/snapshot';
6
- import { DataServiceHost } from './data-service-host';
6
+ import { DataServiceHost, type DataServiceHostOptions } from './data-service-host';
7
7
  /**
8
8
  * Database backend that operates on two streams: read and write.
9
9
  * Mutations are read from the incoming streams and applied to the ItemManager via ItemDemuxer.
@@ -22,6 +22,6 @@ export declare class DatabaseHost {
22
22
  close(): Promise<void>;
23
23
  getWriteStream(): FeedWriter<DataMessage> | undefined;
24
24
  createSnapshot(): EchoSnapshot;
25
- createDataServiceHost(): DataServiceHost;
25
+ createDataServiceHost(opts?: DataServiceHostOptions): DataServiceHost;
26
26
  }
27
27
  //# sourceMappingURL=database-host.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"database-host.d.ts","sourceRoot":"","sources":["../../../../src/db-host/database-host.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,aAAa,EAAE,WAAW,EAAE,KAAK,WAAW,EAAE,MAAM,eAAe,CAAC;AAClF,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,sCAAsC,CAAC;AACxE,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,0CAA0C,CAAC;AAE7E,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAEtD;;;;GAIG;AACH,qBAAa,YAAY;IAMrB,OAAO,CAAC,QAAQ,CAAC,eAAe;IAChC,OAAO,CAAC,QAAQ,CAAC,MAAM;IANzB,OAAO,CAAC,cAAc,CAAiB;IACvC,OAAO,CAAC,YAAY,CAAe;IAC5B,YAAY,EAAG,WAAW,CAAC;gBAGf,eAAe,EAAE,UAAU,CAAC,WAAW,CAAC,GAAG,SAAS,EACpD,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC;IAG9C,IAAI,UAAU,IAAI,OAAO,CAExB;IAED,IAAI,aAAa,kBAEhB;IAEK,IAAI,CAAC,WAAW,EAAE,WAAW,EAAE,YAAY,EAAE,YAAY;IAQzD,KAAK;IAEX,cAAc,IAAI,UAAU,CAAC,WAAW,CAAC,GAAG,SAAS;IAIrD,cAAc,IAAI,YAAY;IAI9B,qBAAqB;CAGtB"}
1
+ {"version":3,"file":"database-host.d.ts","sourceRoot":"","sources":["../../../../src/db-host/database-host.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,aAAa,EAAE,WAAW,EAAE,KAAK,WAAW,EAAE,MAAM,eAAe,CAAC;AAClF,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,sCAAsC,CAAC;AACxE,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,0CAA0C,CAAC;AAE7E,OAAO,EAAE,eAAe,EAAE,KAAK,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAEnF;;;;GAIG;AACH,qBAAa,YAAY;IAMrB,OAAO,CAAC,QAAQ,CAAC,eAAe;IAChC,OAAO,CAAC,QAAQ,CAAC,MAAM;IANzB,OAAO,CAAC,cAAc,CAAiB;IACvC,OAAO,CAAC,YAAY,CAAe;IAC5B,YAAY,EAAG,WAAW,CAAC;gBAGf,eAAe,EAAE,UAAU,CAAC,WAAW,CAAC,GAAG,SAAS,EACpD,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC;IAG9C,IAAI,UAAU,IAAI,OAAO,CAExB;IAED,IAAI,aAAa,kBAEhB;IAEK,IAAI,CAAC,WAAW,EAAE,WAAW,EAAE,YAAY,EAAE,YAAY;IAQzD,KAAK;IAEX,cAAc,IAAI,UAAU,CAAC,WAAW,CAAC,GAAG,SAAS;IAIrD,cAAc,IAAI,YAAY;IAI9B,qBAAqB,CAAC,IAAI,GAAE,sBAA2B;CASxD"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxos/echo-pipeline",
3
- "version": "0.3.3-main.e0ded2e",
3
+ "version": "0.3.3-main.ef9deca",
4
4
  "description": "ECHO database.",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
@@ -20,36 +20,36 @@
20
20
  ],
21
21
  "dependencies": {
22
22
  "crc-32": "^1.2.2",
23
- "@dxos/async": "0.3.3-main.e0ded2e",
24
- "@dxos/codec-protobuf": "0.3.3-main.e0ded2e",
25
- "@dxos/context": "0.3.3-main.e0ded2e",
26
- "@dxos/credentials": "0.3.3-main.e0ded2e",
27
- "@dxos/crypto": "0.3.3-main.e0ded2e",
28
- "@dxos/debug": "0.3.3-main.e0ded2e",
29
- "@dxos/document-model": "0.3.3-main.e0ded2e",
30
- "@dxos/echo-db": "0.3.3-main.e0ded2e",
31
- "@dxos/feed-store": "0.3.3-main.e0ded2e",
32
- "@dxos/hypercore": "0.3.3-main.e0ded2e",
33
- "@dxos/invariant": "0.3.3-main.e0ded2e",
34
- "@dxos/keyring": "0.3.3-main.e0ded2e",
35
- "@dxos/keys": "0.3.3-main.e0ded2e",
36
- "@dxos/log": "0.3.3-main.e0ded2e",
37
- "@dxos/messaging": "0.3.3-main.e0ded2e",
38
- "@dxos/model-factory": "0.3.3-main.e0ded2e",
39
- "@dxos/network-manager": "0.3.3-main.e0ded2e",
40
- "@dxos/node-std": "0.3.3-main.e0ded2e",
41
- "@dxos/protocols": "0.3.3-main.e0ded2e",
42
- "@dxos/random-access-storage": "0.3.3-main.e0ded2e",
43
- "@dxos/rpc": "0.3.3-main.e0ded2e",
44
- "@dxos/teleport": "0.3.3-main.e0ded2e",
45
- "@dxos/teleport-extension-gossip": "0.3.3-main.e0ded2e",
46
- "@dxos/teleport-extension-object-sync": "0.3.3-main.e0ded2e",
47
- "@dxos/teleport-extension-replicator": "0.3.3-main.e0ded2e",
48
- "@dxos/text-model": "0.3.3-main.e0ded2e",
49
- "@dxos/timeframe": "0.3.3-main.e0ded2e",
50
- "@dxos/tracing": "0.3.3-main.e0ded2e",
51
- "@dxos/typings": "0.3.3-main.e0ded2e",
52
- "@dxos/util": "0.3.3-main.e0ded2e"
23
+ "@dxos/async": "0.3.3-main.ef9deca",
24
+ "@dxos/codec-protobuf": "0.3.3-main.ef9deca",
25
+ "@dxos/context": "0.3.3-main.ef9deca",
26
+ "@dxos/credentials": "0.3.3-main.ef9deca",
27
+ "@dxos/crypto": "0.3.3-main.ef9deca",
28
+ "@dxos/debug": "0.3.3-main.ef9deca",
29
+ "@dxos/document-model": "0.3.3-main.ef9deca",
30
+ "@dxos/echo-db": "0.3.3-main.ef9deca",
31
+ "@dxos/feed-store": "0.3.3-main.ef9deca",
32
+ "@dxos/hypercore": "0.3.3-main.ef9deca",
33
+ "@dxos/invariant": "0.3.3-main.ef9deca",
34
+ "@dxos/keyring": "0.3.3-main.ef9deca",
35
+ "@dxos/keys": "0.3.3-main.ef9deca",
36
+ "@dxos/log": "0.3.3-main.ef9deca",
37
+ "@dxos/messaging": "0.3.3-main.ef9deca",
38
+ "@dxos/model-factory": "0.3.3-main.ef9deca",
39
+ "@dxos/network-manager": "0.3.3-main.ef9deca",
40
+ "@dxos/node-std": "0.3.3-main.ef9deca",
41
+ "@dxos/protocols": "0.3.3-main.ef9deca",
42
+ "@dxos/random-access-storage": "0.3.3-main.ef9deca",
43
+ "@dxos/rpc": "0.3.3-main.ef9deca",
44
+ "@dxos/teleport": "0.3.3-main.ef9deca",
45
+ "@dxos/teleport-extension-gossip": "0.3.3-main.ef9deca",
46
+ "@dxos/teleport-extension-object-sync": "0.3.3-main.ef9deca",
47
+ "@dxos/teleport-extension-replicator": "0.3.3-main.ef9deca",
48
+ "@dxos/text-model": "0.3.3-main.ef9deca",
49
+ "@dxos/timeframe": "0.3.3-main.ef9deca",
50
+ "@dxos/tracing": "0.3.3-main.ef9deca",
51
+ "@dxos/typings": "0.3.3-main.ef9deca",
52
+ "@dxos/util": "0.3.3-main.ef9deca"
53
53
  },
54
54
  "devDependencies": {
55
55
  "fast-check": "~3.3.0",
@@ -2,6 +2,7 @@
2
2
  // Copyright 2021 DXOS.org
3
3
  //
4
4
 
5
+ import { UpdateScheduler } from '@dxos/async';
5
6
  import { Stream } from '@dxos/codec-protobuf';
6
7
  import { Context } from '@dxos/context';
7
8
  import { tagMutationsInBatch, type ItemDemuxer, type ItemManager, setMetadataOnObject } from '@dxos/echo-db';
@@ -10,10 +11,20 @@ import { invariant } from '@dxos/invariant';
10
11
  import { type PublicKey } from '@dxos/keys';
11
12
  import { log } from '@dxos/log';
12
13
  import { type DataMessage } from '@dxos/protocols/proto/dxos/echo/feed';
13
- import { type EchoObjectBatch } from '@dxos/protocols/proto/dxos/echo/object';
14
+ import { type EchoObject, type EchoObjectBatch } from '@dxos/protocols/proto/dxos/echo/object';
14
15
  import { EchoEvent, type MutationReceipt, type WriteRequest } from '@dxos/protocols/proto/dxos/echo/service';
15
16
  import { ComplexMap } from '@dxos/util';
16
17
 
18
+ // After this limit the incremental object updates will be replaced with the full snapshot of the object.
19
+ const MUTATION_LIMIT_PER_OBJECT = 10;
20
+
21
+ export type DataServiceHostOptions = {
22
+ /**
23
+ * @default true
24
+ */
25
+ deferEvents?: boolean;
26
+ };
27
+
17
28
  /**
18
29
  * Provides methods for DataService for a single space.
19
30
  * A DataServiceRouter must be placed before it to route requests to different DataServiceHost instances based on space id.
@@ -30,7 +41,8 @@ export class DataServiceHost {
30
41
  private readonly _itemManager: ItemManager,
31
42
  private readonly _itemDemuxer: ItemDemuxer,
32
43
  private readonly _flush: () => Promise<void>,
33
- private readonly _writeStream?: FeedWriter<DataMessage>,
44
+ private readonly _writeStream: FeedWriter<DataMessage> | undefined,
45
+ private readonly _opts: DataServiceHostOptions = {},
34
46
  ) {}
35
47
 
36
48
  async open() {}
@@ -39,6 +51,10 @@ export class DataServiceHost {
39
51
  await this._ctx.dispose();
40
52
  }
41
53
 
54
+ private get _deferEvents(): boolean {
55
+ return this._opts.deferEvents ?? true;
56
+ }
57
+
42
58
  /**
43
59
  * Real-time subscription to data objects in a space.
44
60
  */
@@ -55,8 +71,54 @@ export class DataServiceHost {
55
71
  },
56
72
  });
57
73
 
74
+ const updateScheduler = new UpdateScheduler(
75
+ ctx,
76
+ async () => {
77
+ flushPendingUpdate();
78
+ },
79
+ {
80
+ maxFrequency: 10,
81
+ },
82
+ );
83
+
84
+ const pendingUpdates: EchoObject[] = [];
85
+ const mutationsPerObject = new Map<string, number>();
86
+
87
+ const clearPendingUpdates = () => {
88
+ pendingUpdates.length = 0;
89
+ mutationsPerObject.clear();
90
+ };
91
+
92
+ const flushPendingUpdate = () => {
93
+ const stagedEvents: EchoObject[] = [];
94
+ const objectsWithSnapshots = new Set<string>();
95
+ for (const [id, count] of mutationsPerObject) {
96
+ if (count >= MUTATION_LIMIT_PER_OBJECT) {
97
+ objectsWithSnapshots.add(id);
98
+ const entity = this._itemManager.entities.get(id);
99
+ if (entity) {
100
+ stagedEvents.push(entity.createSnapshot());
101
+ }
102
+ }
103
+ }
104
+
105
+ for (const obj of pendingUpdates) {
106
+ if (!objectsWithSnapshots.has(obj.objectId)) {
107
+ stagedEvents.push(obj);
108
+ }
109
+ }
110
+
111
+ next({
112
+ batch: {
113
+ objects: stagedEvents,
114
+ },
115
+ });
116
+ clearPendingUpdates();
117
+ };
118
+
58
119
  // Subscribe to clear events on Epoch processing.
59
120
  this._itemDemuxer.snapshot.on(ctx, (snapshot) => {
121
+ clearPendingUpdates();
60
122
  next({
61
123
  action: EchoEvent.DatabaseAction.RESET,
62
124
  batch: { objects: snapshot.items },
@@ -79,15 +141,34 @@ export class DataServiceHost {
79
141
 
80
142
  // Assign client tag metadata
81
143
  if (clientTag) {
144
+ flushPendingUpdate();
145
+
82
146
  tagMutationsInBatch(batch, clientTag, 0);
83
- }
84
147
 
85
- next({
86
- clientTag,
87
- feedKey: message.meta.feedKey,
88
- seq: message.meta.seq,
89
- batch,
90
- });
148
+ next({
149
+ clientTag,
150
+ feedKey: message.meta.feedKey,
151
+ seq: message.meta.seq,
152
+ batch,
153
+ });
154
+ } else {
155
+ for (const obj of batch.objects ?? []) {
156
+ const newCount = (mutationsPerObject.get(obj.objectId) ?? 0) + 1;
157
+ mutationsPerObject.set(obj.objectId, newCount);
158
+ }
159
+
160
+ for (const obj of batch.objects ?? []) {
161
+ if ((mutationsPerObject.get(obj.objectId) ?? 0) < MUTATION_LIMIT_PER_OBJECT) {
162
+ pendingUpdates.push(obj);
163
+ }
164
+ }
165
+
166
+ if (this._deferEvents) {
167
+ updateScheduler.trigger();
168
+ } else {
169
+ flushPendingUpdate();
170
+ }
171
+ }
91
172
  });
92
173
  });
93
174
  }
@@ -8,7 +8,7 @@ import { type ModelFactory } from '@dxos/model-factory';
8
8
  import { type DataMessage } from '@dxos/protocols/proto/dxos/echo/feed';
9
9
  import { type EchoSnapshot } from '@dxos/protocols/proto/dxos/echo/snapshot';
10
10
 
11
- import { DataServiceHost } from './data-service-host';
11
+ import { DataServiceHost, type DataServiceHostOptions } from './data-service-host';
12
12
 
13
13
  /**
14
14
  * Database backend that operates on two streams: read and write.
@@ -51,7 +51,13 @@ export class DatabaseHost {
51
51
  return this._itemDemuxer.createSnapshot();
52
52
  }
53
53
 
54
- createDataServiceHost() {
55
- return new DataServiceHost(this._itemManager, this._itemDemuxer, this._flush, this._outboundStream ?? undefined);
54
+ createDataServiceHost(opts: DataServiceHostOptions = {}) {
55
+ return new DataServiceHost(
56
+ this._itemManager,
57
+ this._itemDemuxer,
58
+ this._flush,
59
+ this._outboundStream ?? undefined,
60
+ opts,
61
+ );
56
62
  }
57
63
  }
@@ -114,7 +114,7 @@ export class DatabaseTestPeer {
114
114
 
115
115
  this.items = new ItemManager(this.modelFactory);
116
116
  this.proxy = new DatabaseProxy({
117
- service: this.host.createDataServiceHost(),
117
+ service: this.host.createDataServiceHost({ deferEvents: false }),
118
118
  itemManager: this.items,
119
119
  spaceKey: this.spaceKey,
120
120
  });