@fluidframework/driver-base 2.0.0-dev.3.1.0.125672 → 2.0.0-dev.4.1.0.148229

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.
@@ -30,6 +30,7 @@ import {
30
30
  loggerToMonitoringContext,
31
31
  MonitoringContext,
32
32
  EventEmitterWithErrorHandling,
33
+ normalizeError,
33
34
  } from "@fluidframework/telemetry-utils";
34
35
  import type { Socket } from "socket.io-client";
35
36
  // For now, this package is versioned and released in unison with the specific drivers
@@ -81,10 +82,21 @@ export class DocumentDeltaConnection
81
82
  }
82
83
 
83
84
  public get disposed() {
84
- assert(
85
- this._disposed || this.socket.connected,
86
- 0x244 /* "Socket is closed, but connection is not!" */,
87
- );
85
+ // Increase the stack trace limit temporarily, so as to debug better in case it occurs.
86
+ // We are seeing this in telemetry and we are unable to figure out why it is happening, so this should help.
87
+ const originalStackTraceLimit = (Error as any).stackTraceLimit;
88
+ try {
89
+ (Error as any).stackTraceLimit = 50;
90
+ assert(
91
+ this._disposed || this.socket.connected,
92
+ 0x244 /* "Socket is closed, but connection is not!" */,
93
+ );
94
+ } catch (error) {
95
+ const normalizedError = this.addPropsToError(error);
96
+ throw normalizedError;
97
+ } finally {
98
+ (Error as any).stackTraceLimit = originalStackTraceLimit;
99
+ }
88
100
  return this._disposed;
89
101
  }
90
102
 
@@ -120,6 +132,7 @@ export class DocumentDeltaConnection
120
132
  public documentId: string,
121
133
  logger: ITelemetryLogger,
122
134
  private readonly enableLongPollingDowngrades: boolean = false,
135
+ protected readonly connectionId?: string,
123
136
  ) {
124
137
  super((name, error) => {
125
138
  logger.sendErrorEvent(
@@ -223,7 +236,18 @@ export class DocumentDeltaConnection
223
236
  }
224
237
 
225
238
  private checkNotClosed() {
226
- assert(!this.disposed, 0x20c /* "connection disposed" */);
239
+ // Increase the stack trace limit temporarily, so as to debug better in case it occurs.
240
+ // We are seeing this in telemetry and we are unable to figure out why it is happening, so this should help.
241
+ const originalStackTraceLimit = (Error as any).stackTraceLimit;
242
+ try {
243
+ (Error as any).stackTraceLimit = 50;
244
+ assert(!this.disposed, 0x20c /* "connection disposed" */);
245
+ } catch (error) {
246
+ const normalizedError = this.addPropsToError(error);
247
+ throw normalizedError;
248
+ } finally {
249
+ (Error as any).stackTraceLimit = originalStackTraceLimit;
250
+ }
227
251
  }
228
252
 
229
253
  /**
@@ -318,7 +342,26 @@ export class DocumentDeltaConnection
318
342
  /**
319
343
  * Disconnect from the websocket and close the websocket too.
320
344
  */
321
- protected closeSocket(error: IAnyDriverError) {
345
+ private closeSocket(error: IAnyDriverError) {
346
+ if (this._disposed) {
347
+ // This would be rare situation due to complexity around socket emitting events.
348
+ this.logger.sendTelemetryEvent(
349
+ {
350
+ eventName: "SocketCloseOnDisposedConnection",
351
+ driverVersion,
352
+ details: JSON.stringify({
353
+ ...this.getConnectionDetailsProps(),
354
+ trackedListenerCount: this.trackedListeners.size,
355
+ }),
356
+ },
357
+ error,
358
+ );
359
+ return;
360
+ }
361
+ this.closeSocketCore(error);
362
+ }
363
+
364
+ protected closeSocketCore(error: IAnyDriverError) {
322
365
  this.disconnect(error);
323
366
  }
324
367
 
@@ -332,8 +375,7 @@ export class DocumentDeltaConnection
332
375
  eventName: "ClientClosingDeltaConnection",
333
376
  driverVersion,
334
377
  details: JSON.stringify({
335
- disposed: this._disposed,
336
- socketConnected: this.socket.connected,
378
+ ...this.getConnectionDetailsProps(),
337
379
  }),
338
380
  });
339
381
  this.disconnect(
@@ -361,23 +403,24 @@ export class DocumentDeltaConnection
361
403
  // to prevent normal messages from being emitted.
362
404
  this._disposed = true;
363
405
 
364
- // Let user of connection object know about disconnect. This has to happen in between setting _disposed and
365
- // removing all listeners!
406
+ // Remove all listeners listening on the socket. These are listeners on socket and not on this connection
407
+ // object. Anyway since we have disposed this connection object, nobody should listen to event on socket
408
+ // anymore.
409
+ this.removeTrackedListeners();
410
+
411
+ // Clear the connection/socket before letting the deltaManager/connection manager know about the disconnect.
412
+ this.disconnectCore();
413
+
414
+ // Let user of connection object know about disconnect.
366
415
  this.emit("disconnect", err);
367
416
  this.logger.sendTelemetryEvent({
368
417
  eventName: "AfterDisconnectEvent",
369
418
  driverVersion,
370
419
  details: JSON.stringify({
371
- socketConnected: this.socket.connected,
420
+ ...this.getConnectionDetailsProps(),
372
421
  disconnectListenerCount: this.listenerCount("disconnect"),
373
422
  }),
374
423
  });
375
- // user of DeltaConnection should have processed "disconnect" event and removed all listeners. Not clear
376
- // if we want to enforce that, as some users (like LocalDocumentService) do not unregister any handlers
377
- // assert(this.listenerCount("disconnect") === 0, "'disconnect` events should be processed synchronously");
378
-
379
- this.removeTrackedListeners();
380
- this.disconnectCore();
381
424
  }
382
425
 
383
426
  /**
@@ -403,14 +446,34 @@ export class DocumentDeltaConnection
403
446
 
404
447
  this._details = await new Promise<IConnected>((resolve, reject) => {
405
448
  const failAndCloseSocket = (err: IAnyDriverError) => {
406
- this.closeSocket(err);
449
+ try {
450
+ this.closeSocket(err);
451
+ } catch (failError) {
452
+ const normalizedError = this.addPropsToError(failError);
453
+ this.logger.sendErrorEvent({ eventName: "CloseSocketError" }, normalizedError);
454
+ }
407
455
  reject(err);
408
456
  };
409
457
 
410
458
  const failConnection = (err: IAnyDriverError) => {
411
- this.disconnect(err);
459
+ try {
460
+ this.disconnect(err);
461
+ } catch (failError) {
462
+ const normalizedError = this.addPropsToError(failError);
463
+ this.logger.sendErrorEvent(
464
+ { eventName: "FailConnectionError" },
465
+ normalizedError,
466
+ );
467
+ }
412
468
  reject(err);
413
469
  };
470
+
471
+ // Immediately set the connection timeout.
472
+ // Give extra 2 seconds for handshake on top of socket connection timeout.
473
+ this.socketConnectionTimeout = setTimeout(() => {
474
+ failConnection(this.createErrorObject("orderingServiceHandshakeTimeout"));
475
+ }, timeout + 2000);
476
+
414
477
  // Listen for connection issues
415
478
  this.addConnectionListener("connect_error", (error) => {
416
479
  internalSocketConnectionFailureCount++;
@@ -560,16 +623,31 @@ export class DocumentDeltaConnection
560
623
  });
561
624
 
562
625
  this.socket.emit("connect_document", connectMessage);
563
-
564
- // Give extra 2 seconds for handshake on top of socket connection timeout
565
- this.socketConnectionTimeout = setTimeout(() => {
566
- failConnection(this.createErrorObject("orderingServiceHandshakeTimeout"));
567
- }, timeout + 2000);
568
626
  });
569
627
 
570
628
  assert(!this.disposed, 0x246 /* "checking consistency of socket & _disposed flags" */);
571
629
  }
572
630
 
631
+ private addPropsToError(errorToBeNormalized: unknown) {
632
+ const normalizedError = normalizeError(errorToBeNormalized, {
633
+ props: {
634
+ details: JSON.stringify({
635
+ ...this.getConnectionDetailsProps(),
636
+ }),
637
+ },
638
+ });
639
+ return normalizedError;
640
+ }
641
+
642
+ private getConnectionDetailsProps() {
643
+ return {
644
+ disposed: this._disposed,
645
+ socketConnected: this.socket?.connected,
646
+ clientId: this._details?.clientId,
647
+ connectionId: this.connectionId,
648
+ };
649
+ }
650
+
573
651
  protected earlyOpHandler = (documentId: string, msgs: ISequencedDocumentMessage[]) => {
574
652
  this.queuedMessages.push(...msgs);
575
653
  };
@@ -654,7 +732,12 @@ export class DocumentDeltaConnection
654
732
  const errorObj = createGenericNetworkError(
655
733
  `socket.io (${handler}): ${message}`,
656
734
  { canRetry },
657
- { driverVersion },
735
+ {
736
+ driverVersion,
737
+ details: JSON.stringify({
738
+ ...this.getConnectionDetailsProps(),
739
+ }),
740
+ },
658
741
  );
659
742
 
660
743
  return errorObj;
@@ -6,4 +6,4 @@
6
6
  */
7
7
 
8
8
  export const pkgName = "@fluidframework/driver-base";
9
- export const pkgVersion = "2.0.0-dev.3.1.0.125672";
9
+ export const pkgVersion = "2.0.0-dev.4.1.0.148229";