@fluidframework/shared-object-base 1.2.6 → 2.0.0-dev.1.3.0.96595

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.
@@ -4,7 +4,7 @@
4
4
  */
5
5
 
6
6
  import { v4 as uuid } from "uuid";
7
- import { ITelemetryLogger } from "@fluidframework/common-definitions";
7
+ import { ITelemetryLogger, ITelemetryProperties } from "@fluidframework/common-definitions";
8
8
  import { assert, EventEmitterEventType } from "@fluidframework/common-utils";
9
9
  import { AttachState } from "@fluidframework/container-definitions";
10
10
  import { IFluidHandle } from "@fluidframework/core-interfaces";
@@ -22,7 +22,14 @@ import {
22
22
  blobCountPropertyName,
23
23
  totalBlobSizePropertyName,
24
24
  } from "@fluidframework/runtime-definitions";
25
- import { ChildLogger, EventEmitterWithErrorHandling } from "@fluidframework/telemetry-utils";
25
+ import {
26
+ ChildLogger,
27
+ EventEmitterWithErrorHandling,
28
+ loggerToMonitoringContext,
29
+ MonitoringContext,
30
+ SampledTelemetryHelper,
31
+ TelemetryDataTag,
32
+ } from "@fluidframework/telemetry-utils";
26
33
  import { DataProcessingError } from "@fluidframework/container-utils";
27
34
  import { FluidSerializer, IFluidSerializer } from "./serializer";
28
35
  import { SharedObjectHandle } from "./handle";
@@ -30,12 +37,15 @@ import { SummarySerializer } from "./summarySerializer";
30
37
  import { ISharedObject, ISharedObjectEvents } from "./types";
31
38
 
32
39
  /**
33
- * Base class from which all shared objects derive
40
+ * Base class from which all shared objects derive.
34
41
  */
35
42
  export abstract class SharedObjectCore<TEvent extends ISharedObjectEvents = ISharedObjectEvents>
36
43
  extends EventEmitterWithErrorHandling<TEvent> implements ISharedObject<TEvent> {
37
44
  public get IFluidLoadable() { return this; }
38
45
 
46
+ private readonly opProcessingHelper: SampledTelemetryHelper;
47
+ private readonly callbacksHelper: SampledTelemetryHelper;
48
+
39
49
  /**
40
50
  * The handle referring to this SharedObject
41
51
  */
@@ -45,6 +55,7 @@ export abstract class SharedObjectCore<TEvent extends ISharedObjectEvents = ISha
45
55
  * Telemetry logger for the shared object
46
56
  */
47
57
  protected readonly logger: ITelemetryLogger;
58
+ private readonly mc: MonitoringContext;
48
59
 
49
60
  /**
50
61
  * Connection state
@@ -95,12 +106,61 @@ export abstract class SharedObjectCore<TEvent extends ISharedObjectEvents = ISha
95
106
  this.logger = ChildLogger.create(
96
107
  runtime.logger,
97
108
  undefined,
98
- { all: { sharedObjectId: uuid() } },
109
+ {
110
+ all: {
111
+ sharedObjectId: uuid(),
112
+ ddsType: {
113
+ value: this.attributes.type,
114
+ tag: TelemetryDataTag.CodeArtifact,
115
+ },
116
+ },
117
+ },
99
118
  );
119
+ this.mc = loggerToMonitoringContext(this.logger);
120
+
121
+ [this.opProcessingHelper, this.callbacksHelper] = this.setUpSampledTelemetryHelpers();
100
122
 
101
123
  this.attachListeners();
102
124
  }
103
125
 
126
+ /**
127
+ * This function is only supposed to be called from SharedObjectCore's constructor and
128
+ * depends on a few things being set already. assert() calls make sure of it.
129
+ * @returns The telemetry sampling helpers, so the constructor can be the one to assign them
130
+ * to variables to avoid complaints from TypeScript.
131
+ */
132
+ private setUpSampledTelemetryHelpers(): SampledTelemetryHelper[] {
133
+ assert(this.mc !== undefined && this.logger !== undefined,
134
+ 0x349 /* this.mc and/or this.logger has not been set */);
135
+ const opProcessingHelper = new SampledTelemetryHelper(
136
+ {
137
+ eventName: "ddsOpProcessing",
138
+ category: "performance",
139
+ },
140
+ this.logger,
141
+ this.mc.config.getNumber("Fluid.SharedObject.OpProcessingTelemetrySampling") ?? 100,
142
+ true,
143
+ new Map<string, ITelemetryProperties>([
144
+ ["local", { localOp: true }],
145
+ ["remote", { localOp: false }],
146
+ ]));
147
+ const callbacksHelper = new SampledTelemetryHelper(
148
+ {
149
+ eventName: "ddsEventCallbacks",
150
+ category: "performance",
151
+ },
152
+ this.logger,
153
+ this.mc.config.getNumber("Fluid.SharedObject.DdsCallbacksTelemetrySampling") ?? 100,
154
+ true);
155
+
156
+ this.runtime.once("dispose", () => {
157
+ this.callbacksHelper.dispose();
158
+ this.opProcessingHelper.dispose();
159
+ });
160
+
161
+ return [opProcessingHelper, callbacksHelper];
162
+ }
163
+
104
164
  /**
105
165
  * Marks this objects as closed. Any attempt to change it (local changes or processing remote ops)
106
166
  * would result in same error thrown. If called multiple times, only first error is remembered.
@@ -413,9 +473,13 @@ export abstract class SharedObjectCore<TEvent extends ISharedObjectEvents = ISha
413
473
  */
414
474
  private process(message: ISequencedDocumentMessage, local: boolean, localOpMetadata: unknown) {
415
475
  this.verifyNotClosed(); // This will result in container closure.
416
- this.emit("pre-op", message, local, this);
417
- this.processCore(message, local, localOpMetadata);
418
- this.emit("op", message, local, this);
476
+ this.emitInternal("pre-op", message, local, this);
477
+
478
+ this.opProcessingHelper.measure(
479
+ () => { this.processCore(message, local, localOpMetadata); },
480
+ local ? "local" : "remote");
481
+
482
+ this.emitInternal("op", message, local, this);
419
483
  }
420
484
 
421
485
  /**
@@ -444,6 +508,36 @@ export abstract class SharedObjectCore<TEvent extends ISharedObjectEvents = ISha
444
508
  * when the op is ACKed or resubmitted, respectively
445
509
  */
446
510
  protected abstract applyStashedOp(content: any): unknown;
511
+
512
+ /**
513
+ * Emit an event. This function is only intended for use by DDS classes that extend SharedObject/SharedObjectCore,
514
+ * specifically to emit events that are part of the public interface of the DDS (i.e. those that can have listeners
515
+ * attached to them by the consumers of the DDS). It should not be called from outside the class or to emit events
516
+ * which are only internal to the DDS. Support for calling it from outside the DDS instance might be removed in the
517
+ * future.
518
+ *
519
+ * @internal
520
+ *
521
+ * @param event - The event to emit.
522
+ * @param args - Arguments to pass to the event listeners.
523
+ * @returns `true` if the event had listeners, `false` otherwise.
524
+ */
525
+ public emit(event: EventEmitterEventType, ...args: any[]): boolean {
526
+ return this.callbacksHelper.measure(() => super.emit(event, ...args));
527
+ }
528
+
529
+ /**
530
+ * Use to emit events inside {@link SharedObjectCore}, with no telemetry measurement
531
+ * done on the duration of the callbacks. Simply calls `super.emit()`.
532
+ * @param event - Event to emit
533
+ * @param args - Arguments for the event
534
+ * @returns Whatever `super.emit()` returns.
535
+ */
536
+ private emitInternal(
537
+ event: EventEmitterEventType,
538
+ ...args: any[]): boolean {
539
+ return super.emit(event, ...args);
540
+ }
447
541
  }
448
542
 
449
543
  /**
package/src/types.ts CHANGED
@@ -8,9 +8,30 @@ import { IChannel } from "@fluidframework/datastore-definitions";
8
8
  import { ISequencedDocumentMessage } from "@fluidframework/protocol-definitions";
9
9
  import { IGarbageCollectionData } from "@fluidframework/runtime-definitions";
10
10
 
11
+ /**
12
+ * Events emitted by {@link ISharedObject}.
13
+ */
11
14
  export interface ISharedObjectEvents extends IErrorEvent {
12
- (event: "pre-op" | "op",
13
- listener: (op: ISequencedDocumentMessage, local: boolean, target: IEventThisPlaceHolder) => void);
15
+ /**
16
+ * Fires before an incoming operation (op) is applied to the shared object.
17
+ *
18
+ * @remarks Note: this should be considered an internal implementation detail. It is not recommended for external
19
+ * use.
20
+ *
21
+ * @eventProperty
22
+ */
23
+ (event: "pre-op", listener: (op: ISequencedDocumentMessage, local: boolean, target: IEventThisPlaceHolder) => void);
24
+
25
+ /**
26
+ * Fires after an incoming op is applied to the shared object.
27
+ *
28
+ * @remarks Note: this should be considered an internal implementation detail. It is not recommended for external
29
+ * use.
30
+ *
31
+ * @eventProperty
32
+ */
33
+ // eslint-disable-next-line @typescript-eslint/unified-signatures
34
+ (event: "op", listener: (op: ISequencedDocumentMessage, local: boolean, target: IEventThisPlaceHolder) => void);
14
35
  }
15
36
 
16
37
  /**