@dxos/echo-pipeline 0.7.4 → 0.7.5-main.499c70c

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.
@@ -2,8 +2,7 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import { Mutex, scheduleTask, scheduleMicroTask } from '@dxos/async';
6
- import * as A from '@dxos/automerge/automerge';
5
+ import { Mutex, scheduleTask, scheduleMicroTask, Trigger } from '@dxos/async';
7
6
  import { cbor } from '@dxos/automerge/automerge-repo';
8
7
  import { Context, Resource } from '@dxos/context';
9
8
  import { randomUUID } from '@dxos/crypto';
@@ -184,9 +183,11 @@ type EdgeReplicatorConnectionsParams = {
184
183
  onRestartRequested: () => Promise<void>;
185
184
  };
186
185
 
186
+ const MAX_INFLIGHT_REQUESTS = 5;
187
+
187
188
  class EdgeReplicatorConnection extends Resource implements ReplicatorConnection {
188
189
  private readonly _edgeConnection: EdgeConnection;
189
- private _remotePeerId: string | null = null;
190
+ private readonly _remotePeerId: string | null = null;
190
191
  private readonly _targetServiceId: string;
191
192
  private readonly _spaceId: SpaceId;
192
193
  private readonly _context: EchoReplicatorContext;
@@ -195,6 +196,16 @@ class EdgeReplicatorConnection extends Resource implements ReplicatorConnection
195
196
  private readonly _onRemoteDisconnected: () => Promise<void>;
196
197
  private readonly _onRestartRequested: () => void;
197
198
 
199
+ /**
200
+ * Prevents sending too many messages to edge over this connection so that we don't overwhelm
201
+ * a replicator durable object.
202
+ * inflightRequests counter is incremented on outgoing sync messages and decremented on incoming messages.
203
+ * The trigger is waiting while the counter is above MAX_INFLIGHT_REQUESTS.
204
+ * The counter can go negative because we receive edge-initiated sync messages on doc change broadcasts.
205
+ */
206
+ private _outgoingRequestsBarrier = new Trigger();
207
+ private _inflightRequests = 0;
208
+
198
209
  private _readableStreamController!: ReadableStreamDefaultController<AutomergeProtocolMessage>;
199
210
 
200
211
  public readable: ReadableStream<AutomergeProtocolMessage>;
@@ -213,7 +224,6 @@ class EdgeReplicatorConnection extends Resource implements ReplicatorConnection
213
224
  this._edgeConnection = edgeConnection;
214
225
  this._spaceId = spaceId;
215
226
  this._context = context;
216
-
217
227
  // Generate a unique peer id for every connection.
218
228
  // This way automerge-repo will have separate sync states for every connection.
219
229
  // This is important because the previous connection might have had some messages that failed to deliver
@@ -225,6 +235,8 @@ class EdgeReplicatorConnection extends Resource implements ReplicatorConnection
225
235
  this._onRemoteDisconnected = onRemoteDisconnected;
226
236
  this._onRestartRequested = onRestartRequested;
227
237
 
238
+ this._outgoingRequestsBarrier.wake();
239
+
228
240
  this.readable = new ReadableStream<AutomergeProtocolMessage>({
229
241
  start: (controller) => {
230
242
  this._readableStreamController = controller;
@@ -233,6 +245,13 @@ class EdgeReplicatorConnection extends Resource implements ReplicatorConnection
233
245
 
234
246
  this.writable = new WritableStream<AutomergeProtocolMessage>({
235
247
  write: async (message: AutomergeProtocolMessage, controller) => {
248
+ await this._outgoingRequestsBarrier.wait();
249
+
250
+ this._inflightRequests++;
251
+ if (this._inflightRequests === MAX_INFLIGHT_REQUESTS) {
252
+ this._outgoingRequestsBarrier.reset();
253
+ }
254
+
236
255
  await this._sendMessage(message);
237
256
  },
238
257
  });
@@ -253,6 +272,9 @@ class EdgeReplicatorConnection extends Resource implements ReplicatorConnection
253
272
  protected override async _close(): Promise<void> {
254
273
  log('close');
255
274
  this._readableStreamController.close();
275
+
276
+ this._outgoingRequestsBarrier.throw(new Error('Connection closed.'));
277
+
256
278
  await this._onRemoteDisconnected();
257
279
  }
258
280
 
@@ -272,9 +294,10 @@ class EdgeReplicatorConnection extends Resource implements ReplicatorConnection
272
294
  peerId: this._remotePeerId as PeerId,
273
295
  });
274
296
 
275
- log.info('document not found locally for share policy check, accepting the remote document', {
297
+ log.verbose('edge-replicator document not found locally for share policy check', {
276
298
  documentId: params.documentId,
277
- remoteDocumentExists,
299
+ acceptDocument: remoteDocumentExists,
300
+ remoteId: this._remotePeerId,
278
301
  });
279
302
 
280
303
  // If a document is not present locally return true only if it already exists on edge.
@@ -290,7 +313,8 @@ class EdgeReplicatorConnection extends Resource implements ReplicatorConnection
290
313
  return true;
291
314
  }
292
315
  const spaceId = getSpaceIdFromCollectionId(params.collectionId as CollectionId);
293
- return spaceId === this._spaceId;
316
+ // Only sync collections of form space:id:rootDoc, edge ignores legacy space:id collections
317
+ return spaceId === this._spaceId && params.collectionId.split(':').length === 3;
294
318
  }
295
319
 
296
320
  private _onMessage(message: RouterMessage) {
@@ -299,15 +323,12 @@ class EdgeReplicatorConnection extends Resource implements ReplicatorConnection
299
323
  }
300
324
 
301
325
  const payload = cbor.decode(message.payload!.value) as AutomergeProtocolMessage;
302
- log('recv', () => {
303
- const decodedData =
304
- payload.type === 'sync' && payload.data
305
- ? A.decodeSyncMessage(payload.data)
306
- : payload.type === 'collection-state'
307
- ? (payload as any).state
308
- : payload;
309
- return { from: message.serviceId, type: payload.type, decodedData };
326
+ log.verbose('edge replicator receive', {
327
+ type: payload.type,
328
+ documentId: payload.type === 'sync' && payload.documentId,
329
+ remoteId: this._remotePeerId,
310
330
  });
331
+
311
332
  // Fix the peer id.
312
333
  payload.senderId = this._remotePeerId! as PeerId;
313
334
  this._processMessage(payload);
@@ -322,6 +343,13 @@ class EdgeReplicatorConnection extends Resource implements ReplicatorConnection
322
343
  return;
323
344
  }
324
345
 
346
+ if (message.type === 'sync') {
347
+ this._inflightRequests--;
348
+ if (this._inflightRequests === MAX_INFLIGHT_REQUESTS - 1) {
349
+ this._outgoingRequestsBarrier.wake();
350
+ }
351
+ }
352
+
325
353
  this._readableStreamController.enqueue(message);
326
354
  }
327
355
 
@@ -329,12 +357,12 @@ class EdgeReplicatorConnection extends Resource implements ReplicatorConnection
329
357
  // Fix the peer id.
330
358
  (message as any).targetId = this._targetServiceId as PeerId;
331
359
 
332
- log('send', {
360
+ log.verbose('edge replicator send', {
333
361
  type: message.type,
334
- senderId: message.senderId,
335
- targetId: (message as any).targetId,
336
- documentId: (message as any).documentId,
362
+ documentId: message.type === 'sync' && message.documentId,
363
+ remoteId: this._remotePeerId,
337
364
  });
365
+
338
366
  const encoded = cbor.encode(message);
339
367
 
340
368
  await this._edgeConnection.send(