@fluidframework/container-runtime 2.0.0-internal.4.2.1 → 2.0.0-internal.4.3.1

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.
Files changed (131) hide show
  1. package/dist/connectionTelemetry.d.ts.map +1 -1
  2. package/dist/connectionTelemetry.js +1 -0
  3. package/dist/connectionTelemetry.js.map +1 -1
  4. package/dist/containerRuntime.d.ts.map +1 -1
  5. package/dist/containerRuntime.js +9 -6
  6. package/dist/containerRuntime.js.map +1 -1
  7. package/dist/dataStores.d.ts +5 -5
  8. package/dist/dataStores.d.ts.map +1 -1
  9. package/dist/dataStores.js +3 -6
  10. package/dist/dataStores.js.map +1 -1
  11. package/dist/gc/garbageCollection.d.ts +1 -27
  12. package/dist/gc/garbageCollection.d.ts.map +1 -1
  13. package/dist/gc/garbageCollection.js +30 -227
  14. package/dist/gc/garbageCollection.js.map +1 -1
  15. package/dist/gc/gcHelpers.d.ts +0 -10
  16. package/dist/gc/gcHelpers.d.ts.map +1 -1
  17. package/dist/gc/gcHelpers.js +1 -20
  18. package/dist/gc/gcHelpers.js.map +1 -1
  19. package/dist/gc/gcTelemetry.d.ts +91 -0
  20. package/dist/gc/gcTelemetry.d.ts.map +1 -0
  21. package/dist/gc/gcTelemetry.js +262 -0
  22. package/dist/gc/gcTelemetry.js.map +1 -0
  23. package/dist/gc/index.d.ts +2 -1
  24. package/dist/gc/index.d.ts.map +1 -1
  25. package/dist/gc/index.js +4 -2
  26. package/dist/gc/index.js.map +1 -1
  27. package/dist/opLifecycle/opGroupingManager.js +1 -1
  28. package/dist/opLifecycle/opGroupingManager.js.map +1 -1
  29. package/dist/opLifecycle/outbox.js +1 -1
  30. package/dist/opLifecycle/outbox.js.map +1 -1
  31. package/dist/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
  32. package/dist/opLifecycle/remoteMessageProcessor.js +25 -22
  33. package/dist/opLifecycle/remoteMessageProcessor.js.map +1 -1
  34. package/dist/packageVersion.d.ts +1 -1
  35. package/dist/packageVersion.js +1 -1
  36. package/dist/packageVersion.js.map +1 -1
  37. package/dist/pendingStateManager.d.ts +1 -1
  38. package/dist/pendingStateManager.d.ts.map +1 -1
  39. package/dist/pendingStateManager.js.map +1 -1
  40. package/dist/summary/orderedClientElection.d.ts.map +1 -1
  41. package/dist/summary/orderedClientElection.js +14 -17
  42. package/dist/summary/orderedClientElection.js.map +1 -1
  43. package/dist/summary/summarizer.d.ts +2 -0
  44. package/dist/summary/summarizer.d.ts.map +1 -1
  45. package/dist/summary/summarizer.js +9 -4
  46. package/dist/summary/summarizer.js.map +1 -1
  47. package/dist/summary/summarizerHeuristics.d.ts +8 -9
  48. package/dist/summary/summarizerHeuristics.d.ts.map +1 -1
  49. package/dist/summary/summarizerHeuristics.js +15 -16
  50. package/dist/summary/summarizerHeuristics.js.map +1 -1
  51. package/dist/summary/summarizerTypes.d.ts +2 -0
  52. package/dist/summary/summarizerTypes.d.ts.map +1 -1
  53. package/dist/summary/summarizerTypes.js.map +1 -1
  54. package/dist/summary/summaryGenerator.d.ts.map +1 -1
  55. package/dist/summary/summaryGenerator.js +4 -3
  56. package/dist/summary/summaryGenerator.js.map +1 -1
  57. package/lib/connectionTelemetry.d.ts.map +1 -1
  58. package/lib/connectionTelemetry.js +1 -0
  59. package/lib/connectionTelemetry.js.map +1 -1
  60. package/lib/containerRuntime.d.ts.map +1 -1
  61. package/lib/containerRuntime.js +9 -6
  62. package/lib/containerRuntime.js.map +1 -1
  63. package/lib/dataStores.d.ts +5 -5
  64. package/lib/dataStores.d.ts.map +1 -1
  65. package/lib/dataStores.js +3 -6
  66. package/lib/dataStores.js.map +1 -1
  67. package/lib/gc/garbageCollection.d.ts +1 -27
  68. package/lib/gc/garbageCollection.d.ts.map +1 -1
  69. package/lib/gc/garbageCollection.js +34 -231
  70. package/lib/gc/garbageCollection.js.map +1 -1
  71. package/lib/gc/gcHelpers.d.ts +0 -10
  72. package/lib/gc/gcHelpers.d.ts.map +1 -1
  73. package/lib/gc/gcHelpers.js +0 -18
  74. package/lib/gc/gcHelpers.js.map +1 -1
  75. package/lib/gc/gcTelemetry.d.ts +91 -0
  76. package/lib/gc/gcTelemetry.d.ts.map +1 -0
  77. package/lib/gc/gcTelemetry.js +257 -0
  78. package/lib/gc/gcTelemetry.js.map +1 -0
  79. package/lib/gc/index.d.ts +2 -1
  80. package/lib/gc/index.d.ts.map +1 -1
  81. package/lib/gc/index.js +2 -1
  82. package/lib/gc/index.js.map +1 -1
  83. package/lib/opLifecycle/opGroupingManager.js +1 -1
  84. package/lib/opLifecycle/opGroupingManager.js.map +1 -1
  85. package/lib/opLifecycle/outbox.js +1 -1
  86. package/lib/opLifecycle/outbox.js.map +1 -1
  87. package/lib/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
  88. package/lib/opLifecycle/remoteMessageProcessor.js +25 -22
  89. package/lib/opLifecycle/remoteMessageProcessor.js.map +1 -1
  90. package/lib/packageVersion.d.ts +1 -1
  91. package/lib/packageVersion.js +1 -1
  92. package/lib/packageVersion.js.map +1 -1
  93. package/lib/pendingStateManager.d.ts +1 -1
  94. package/lib/pendingStateManager.d.ts.map +1 -1
  95. package/lib/pendingStateManager.js.map +1 -1
  96. package/lib/summary/orderedClientElection.d.ts.map +1 -1
  97. package/lib/summary/orderedClientElection.js +14 -17
  98. package/lib/summary/orderedClientElection.js.map +1 -1
  99. package/lib/summary/summarizer.d.ts +2 -0
  100. package/lib/summary/summarizer.d.ts.map +1 -1
  101. package/lib/summary/summarizer.js +9 -4
  102. package/lib/summary/summarizer.js.map +1 -1
  103. package/lib/summary/summarizerHeuristics.d.ts +8 -9
  104. package/lib/summary/summarizerHeuristics.d.ts.map +1 -1
  105. package/lib/summary/summarizerHeuristics.js +15 -16
  106. package/lib/summary/summarizerHeuristics.js.map +1 -1
  107. package/lib/summary/summarizerTypes.d.ts +2 -0
  108. package/lib/summary/summarizerTypes.d.ts.map +1 -1
  109. package/lib/summary/summarizerTypes.js.map +1 -1
  110. package/lib/summary/summaryGenerator.d.ts.map +1 -1
  111. package/lib/summary/summaryGenerator.js +4 -3
  112. package/lib/summary/summaryGenerator.js.map +1 -1
  113. package/package.json +17 -17
  114. package/src/connectionTelemetry.ts +1 -0
  115. package/src/containerRuntime.ts +12 -11
  116. package/src/dataStores.ts +4 -7
  117. package/src/gc/garbageCollection.ts +53 -315
  118. package/src/gc/gcHelpers.ts +1 -38
  119. package/src/gc/gcTelemetry.ts +393 -0
  120. package/src/gc/index.ts +1 -1
  121. package/src/opLifecycle/README.md +13 -0
  122. package/src/opLifecycle/opGroupingManager.ts +1 -1
  123. package/src/opLifecycle/outbox.ts +2 -2
  124. package/src/opLifecycle/remoteMessageProcessor.ts +37 -28
  125. package/src/packageVersion.ts +1 -1
  126. package/src/pendingStateManager.ts +1 -4
  127. package/src/summary/orderedClientElection.ts +14 -17
  128. package/src/summary/summarizer.ts +17 -5
  129. package/src/summary/summarizerHeuristics.ts +15 -16
  130. package/src/summary/summarizerTypes.ts +2 -0
  131. package/src/summary/summaryGenerator.ts +5 -4
@@ -4,7 +4,7 @@
4
4
  */
5
5
 
6
6
  import { ITelemetryLogger } from "@fluidframework/common-definitions";
7
- import { assert, LazyPromise, Timer } from "@fluidframework/common-utils";
7
+ import { LazyPromise, Timer } from "@fluidframework/common-utils";
8
8
  import { ClientSessionExpiredError, DataProcessingError } from "@fluidframework/container-utils";
9
9
  import { IRequestHeader } from "@fluidframework/core-interfaces";
10
10
  import {
@@ -14,21 +14,18 @@ import {
14
14
  ISummarizeResult,
15
15
  ITelemetryContext,
16
16
  } from "@fluidframework/runtime-definitions";
17
- import { packagePathToTelemetryProperty, ReadAndParseBlob } from "@fluidframework/runtime-utils";
17
+ import { ReadAndParseBlob } from "@fluidframework/runtime-utils";
18
18
  import {
19
19
  ChildLogger,
20
- generateStack,
21
20
  loggerToMonitoringContext,
22
21
  MonitoringContext,
23
22
  PerformanceEvent,
24
- TelemetryDataTag,
25
23
  } from "@fluidframework/telemetry-utils";
26
24
 
27
25
  import { RuntimeHeaders } from "../containerRuntime";
28
- import { ICreateContainerMetadata, RefreshSummaryResult } from "../summary";
26
+ import { RefreshSummaryResult } from "../summary";
29
27
  import { generateGCConfigs } from "./gcConfigs";
30
28
  import {
31
- disableSweepLogKey,
32
29
  GCNodeType,
33
30
  IGarbageCollector,
34
31
  IGarbageCollectorCreateParams,
@@ -39,31 +36,12 @@ import {
39
36
  IGCMetadata,
40
37
  IGarbageCollectorConfigs,
41
38
  } from "./gcDefinitions";
42
- import {
43
- cloneGCData,
44
- concatGarbageCollectionData,
45
- getGCDataFromSnapshot,
46
- sendGCUnexpectedUsageEvent,
47
- } from "./gcHelpers";
39
+ import { cloneGCData, concatGarbageCollectionData, getGCDataFromSnapshot } from "./gcHelpers";
48
40
  import { runGarbageCollection } from "./gcReferenceGraphAlgorithm";
49
41
  import { IGarbageCollectionSnapshotData, IGarbageCollectionState } from "./gcSummaryDefinitions";
50
42
  import { GCSummaryStateTracker } from "./gcSummaryStateTracker";
51
43
  import { UnreferencedStateTracker } from "./gcUnreferencedStateTracker";
52
-
53
- /** The event that is logged when unreferenced node is used after a certain time. */
54
- interface IUnreferencedEventProps {
55
- usageType: "Changed" | "Loaded" | "Revived";
56
- state: UnreferencedState;
57
- id: string;
58
- type: GCNodeType;
59
- unrefTime: number;
60
- age: number;
61
- completedGCRuns: number;
62
- fromId?: string;
63
- timeout?: number;
64
- lastSummaryTime?: number;
65
- viaHandle?: boolean;
66
- }
44
+ import { GCTelemetryTracker } from "./gcTelemetry";
67
45
 
68
46
  /**
69
47
  * The garbage collector for the container runtime. It consolidates the garbage collection functionality and maintains
@@ -121,20 +99,14 @@ export class GarbageCollector implements IGarbageCollector {
121
99
  // The Timer responsible for closing the container when the session has expired
122
100
  private sessionExpiryTimer: Timer | undefined;
123
101
 
124
- // Keeps track of unreferenced events that are logged for a node. This is used to limit the log generation to one
125
- // per event per node.
126
- private readonly loggedUnreferencedEvents: Set<string> = new Set();
127
- // Queue for unreferenced events that should be logged the next time GC runs.
128
- private pendingEventsQueue: IUnreferencedEventProps[] = [];
129
-
130
102
  // The number of times GC has successfully completed on this instance of GarbageCollector.
131
103
  private completedRuns = 0;
132
104
 
133
105
  private readonly runtime: IGarbageCollectionRuntime;
134
- private readonly createContainerMetadata: ICreateContainerMetadata;
135
106
  private readonly isSummarizerClient: boolean;
136
107
 
137
108
  private readonly summaryStateTracker: GCSummaryStateTracker;
109
+ private readonly telemetryTracker: GCTelemetryTracker;
138
110
 
139
111
  /** For a given node path, returns the node's package path. */
140
112
  private readonly getNodePackagePath: (
@@ -152,7 +124,6 @@ export class GarbageCollector implements IGarbageCollector {
152
124
  protected constructor(createParams: IGarbageCollectorCreateParams) {
153
125
  this.runtime = createParams.runtime;
154
126
  this.isSummarizerClient = createParams.isSummarizerClient;
155
- this.createContainerMetadata = createParams.createContainerMetadata;
156
127
  this.getNodePackagePath = createParams.getNodePackagePath;
157
128
  this.getLastSummaryTimestampMs = createParams.getLastSummaryTimestampMs;
158
129
  this.activeConnection = createParams.activeConnection;
@@ -189,6 +160,17 @@ export class GarbageCollector implements IGarbageCollector {
189
160
  baseSnapshot?.trees[gcTreeKey] !== undefined /* wasGCRunInBaseSnapshot */,
190
161
  );
191
162
 
163
+ this.telemetryTracker = new GCTelemetryTracker(
164
+ this.mc,
165
+ this.configs,
166
+ this.isSummarizerClient,
167
+ this.runtime.gcTombstoneEnforcementAllowed,
168
+ createParams.createContainerMetadata,
169
+ (nodeId: string) => this.runtime.getNodeType(nodeId),
170
+ (nodeId: string) => this.unreferencedNodesState.get(nodeId),
171
+ this.getNodePackagePath,
172
+ );
173
+
192
174
  // Get the GC data from the base snapshot. Use LazyPromise because we only want to do this once since it
193
175
  // it involves fetching blobs from storage which is expensive.
194
176
  this.baseSnapshotDataP = new LazyPromise<IGarbageCollectionSnapshotData | undefined>(
@@ -546,9 +528,14 @@ export class GarbageCollector implements IGarbageCollector {
546
528
  );
547
529
  this.runtime.updateUsedRoutes(gcResult.referencedNodeIds);
548
530
 
549
- // Log events for objects that are ready to be deleted by sweep. When we have sweep enabled, we will
550
- // delete these objects here instead.
551
- this.logSweepEvents(logger, currentReferenceTimestampMs);
531
+ // Log events for objects that are ready to be deleted by sweep.
532
+ this.telemetryTracker.logSweepEvents(
533
+ logger,
534
+ currentReferenceTimestampMs,
535
+ this.unreferencedNodesState,
536
+ this.completedRuns,
537
+ this.getLastSummaryTimestampMs(),
538
+ );
552
539
 
553
540
  let updatedGCData: IGarbageCollectionData = gcData;
554
541
 
@@ -571,7 +558,7 @@ export class GarbageCollector implements IGarbageCollector {
571
558
  // Log pending unreferenced events such as a node being used after inactive. This is done after GC runs and
572
559
  // updates its state so that we don't send false positives based on intermediate state. For example, we may get
573
560
  // reference to an unreferenced node from another unreferenced node which means the node wasn't revived.
574
- await this.logUnreferencedEvents(logger);
561
+ await this.telemetryTracker.logPendingEvents(logger);
575
562
 
576
563
  return gcStats;
577
564
  }
@@ -686,18 +673,17 @@ export class GarbageCollector implements IGarbageCollector {
686
673
  return;
687
674
  }
688
675
 
689
- const nodeStateTracker = this.unreferencedNodesState.get(nodePath);
690
- if (nodeStateTracker && nodeStateTracker.state !== UnreferencedState.Active) {
691
- this.inactiveNodeUsed(
692
- reason,
693
- nodePath,
694
- nodeStateTracker,
695
- undefined /* fromNodeId */,
696
- packagePath,
697
- timestampMs,
698
- requestHeaders,
699
- );
700
- }
676
+ this.telemetryTracker.nodeUsed({
677
+ nodeId: nodePath,
678
+ usageType: reason,
679
+ currentReferenceTimestampMs:
680
+ timestampMs ?? this.runtime.getCurrentReferenceTimestampMs(),
681
+ packagePath,
682
+ completedGCRuns: this.completedRuns,
683
+ isTombstoned: this.tombstones.includes(nodePath),
684
+ lastSummaryTime: this.getLastSummaryTimestampMs(),
685
+ viaHandle: requestHeaders?.[RuntimeHeaders.viaHandle],
686
+ });
701
687
  }
702
688
 
703
689
  /**
@@ -716,33 +702,16 @@ export class GarbageCollector implements IGarbageCollector {
716
702
  outboundRoutes.push(toNodePath);
717
703
  this.newReferencesSinceLastRun.set(fromNodePath, outboundRoutes);
718
704
 
719
- const nodeStateTracker = this.unreferencedNodesState.get(toNodePath);
720
- if (nodeStateTracker && nodeStateTracker.state !== UnreferencedState.Active) {
721
- this.inactiveNodeUsed("Revived", toNodePath, nodeStateTracker, fromNodePath);
722
- }
723
-
724
- if (this.tombstones.includes(toNodePath)) {
725
- const nodeType = this.runtime.getNodeType(toNodePath);
726
-
727
- let eventName = "GC_Tombstone_SubDatastore_Revived";
728
- if (nodeType === GCNodeType.DataStore) {
729
- eventName = "GC_Tombstone_Datastore_Revived";
730
- } else if (nodeType === GCNodeType.Blob) {
731
- eventName = "GC_Tombstone_Blob_Revived";
732
- }
733
-
734
- sendGCUnexpectedUsageEvent(
735
- this.mc,
736
- {
737
- eventName,
738
- category: "generic",
739
- url: toNodePath,
740
- nodeType,
741
- gcTombstoneEnforcementAllowed: this.runtime.gcTombstoneEnforcementAllowed,
742
- },
743
- undefined /* packagePath */,
744
- );
745
- }
705
+ this.telemetryTracker.nodeUsed({
706
+ nodeId: toNodePath,
707
+ usageType: "Revived",
708
+ currentReferenceTimestampMs: this.runtime.getCurrentReferenceTimestampMs(),
709
+ packagePath: undefined,
710
+ completedGCRuns: this.completedRuns,
711
+ isTombstoned: this.tombstones.includes(toNodePath),
712
+ lastSummaryTime: this.getLastSummaryTimestampMs(),
713
+ fromId: fromNodePath,
714
+ });
746
715
  }
747
716
 
748
717
  /**
@@ -897,23 +866,17 @@ export class GarbageCollector implements IGarbageCollector {
897
866
  return undefined;
898
867
  }
899
868
 
900
- // Find any references that haven't been identified correctly.
901
- const missingExplicitReferences = this.findMissingExplicitReferences(
869
+ /**
870
+ * If there are references that were not explicitly notified to GC, log an error because this should never happen.
871
+ * If it does, this may result in the unreferenced timestamps of these nodes not updated when they were referenced.
872
+ */
873
+ this.telemetryTracker.logIfMissingExplicitReferences(
902
874
  currentGCData,
903
875
  previousGCData,
904
876
  this.newReferencesSinceLastRun,
877
+ logger,
905
878
  );
906
879
 
907
- if (missingExplicitReferences.length > 0) {
908
- missingExplicitReferences.forEach((missingExplicitReference) => {
909
- logger.sendErrorEvent({
910
- eventName: "gcUnknownOutboundReferences",
911
- gcNodeId: missingExplicitReference[0],
912
- gcRoutes: JSON.stringify(missingExplicitReference[1]),
913
- });
914
- });
915
- }
916
-
917
880
  // No references were added since the last run so we don't have to update reference states of any unreferenced
918
881
  // nodes. There is no in between state at this point.
919
882
  if (this.newReferencesSinceLastRun.size === 0) {
@@ -961,64 +924,6 @@ export class GarbageCollector implements IGarbageCollector {
961
924
  return gcResult.referencedNodeIds;
962
925
  }
963
926
 
964
- /**
965
- * Finds all new references or outbound routes in the current graph that haven't been explicitly notified to GC.
966
- * The principle is that every new reference or outbound route must be notified to GC via the
967
- * addedOutboundReference method. It it hasn't, its a bug and we want to identify these scenarios.
968
- *
969
- * In more simple terms:
970
- * Missing Explicit References = Current References - Previous References - Explicitly Added References;
971
- *
972
- * @param currentGCData - The GC data (reference graph) from the current GC run.
973
- * @param previousGCData - The GC data (reference graph) from the previous GC run.
974
- * @param explicitReferences - New references added explicity between the previous and the current run.
975
- * @returns - a list of missing explicit references
976
- */
977
- private findMissingExplicitReferences(
978
- currentGCData: IGarbageCollectionData,
979
- previousGCData: IGarbageCollectionData,
980
- explicitReferences: Map<string, string[]>,
981
- ): [string, string[]][] {
982
- assert(
983
- previousGCData !== undefined,
984
- 0x2b7 /* "Can't validate correctness without GC data from last run" */,
985
- );
986
-
987
- const currentGraph = Object.entries(currentGCData.gcNodes);
988
- const missingExplicitReferences: [string, string[]][] = [];
989
- currentGraph.forEach(([nodeId, currentOutboundRoutes]) => {
990
- const previousRoutes = previousGCData.gcNodes[nodeId] ?? [];
991
- const explicitRoutes = explicitReferences.get(nodeId) ?? [];
992
- const missingExplicitRoutes: string[] = [];
993
-
994
- /**
995
- * 1. For routes in the current GC data, routes that were not present in previous GC data and did not have
996
- * explicit references should be added to missing explicit routes list.
997
- * 2. Only include data store and blob routes since GC only works for these two.
998
- * Note: Due to a bug with de-duped blobs, only adding data store routes for now.
999
- * 3. Ignore DDS routes to their parent datastores since those were added implicitly. So, there won't be
1000
- * explicit routes to them.
1001
- */
1002
- currentOutboundRoutes.forEach((route) => {
1003
- const nodeType = this.runtime.getNodeType(route);
1004
- if (
1005
- (nodeType === GCNodeType.DataStore || nodeType === GCNodeType.Blob) &&
1006
- !nodeId.startsWith(route) &&
1007
- !previousRoutes.includes(route) &&
1008
- !explicitRoutes.includes(route)
1009
- ) {
1010
- missingExplicitRoutes.push(route);
1011
- }
1012
- });
1013
- if (missingExplicitRoutes.length > 0) {
1014
- missingExplicitReferences.push([nodeId, missingExplicitRoutes]);
1015
- }
1016
- });
1017
-
1018
- // Ideally missingExplicitReferences should always have a size 0
1019
- return missingExplicitReferences;
1020
- }
1021
-
1022
927
  /**
1023
928
  * Generates the stats of a garbage collection run from the given results of the run.
1024
929
  * @param gcResult - The result of a GC run.
@@ -1081,171 +986,4 @@ export class GarbageCollector implements IGarbageCollector {
1081
986
 
1082
987
  return gcStats;
1083
988
  }
1084
-
1085
- /**
1086
- * For nodes that are ready to sweep, log an event for now. Until we start running sweep which deletes objects,
1087
- * this will give us a view into how much deleted content a container has.
1088
- */
1089
- private logSweepEvents(logger: ITelemetryLogger, currentReferenceTimestampMs: number) {
1090
- if (
1091
- this.mc.config.getBoolean(disableSweepLogKey) === true ||
1092
- this.configs.sweepTimeoutMs === undefined
1093
- ) {
1094
- return;
1095
- }
1096
-
1097
- this.unreferencedNodesState.forEach((nodeStateTracker, nodeId) => {
1098
- if (nodeStateTracker.state !== UnreferencedState.SweepReady) {
1099
- return;
1100
- }
1101
-
1102
- const nodeType = this.runtime.getNodeType(nodeId);
1103
- if (nodeType !== GCNodeType.DataStore && nodeType !== GCNodeType.Blob) {
1104
- return;
1105
- }
1106
-
1107
- // Log deleted event for each node only once to reduce noise in telemetry.
1108
- const uniqueEventId = `Deleted-${nodeId}`;
1109
- if (this.loggedUnreferencedEvents.has(uniqueEventId)) {
1110
- return;
1111
- }
1112
- this.loggedUnreferencedEvents.add(uniqueEventId);
1113
- logger.sendTelemetryEvent({
1114
- eventName: "GCObjectDeleted",
1115
- id: nodeId,
1116
- type: nodeType,
1117
- age: currentReferenceTimestampMs - nodeStateTracker.unreferencedTimestampMs,
1118
- timeout: this.configs.sweepTimeoutMs,
1119
- completedGCRuns: this.completedRuns,
1120
- lastSummaryTime: this.getLastSummaryTimestampMs(),
1121
- });
1122
- });
1123
- }
1124
-
1125
- /**
1126
- * Called when an inactive node is used after. Queue up an event that will be logged next time GC runs.
1127
- */
1128
- private inactiveNodeUsed(
1129
- usageType: "Changed" | "Loaded" | "Revived",
1130
- nodeId: string,
1131
- nodeStateTracker: UnreferencedStateTracker,
1132
- fromNodeId?: string,
1133
- packagePath?: readonly string[],
1134
- currentReferenceTimestampMs = this.runtime.getCurrentReferenceTimestampMs(),
1135
- requestHeaders?: IRequestHeader,
1136
- ) {
1137
- // If there is no reference timestamp to work with, no ops have been processed after creation. If so, skip
1138
- // logging as nothing interesting would have happened worth logging.
1139
- // If the node is active, skip logging.
1140
- if (
1141
- currentReferenceTimestampMs === undefined ||
1142
- nodeStateTracker.state === UnreferencedState.Active
1143
- ) {
1144
- return;
1145
- }
1146
-
1147
- // We only care about data stores and attachment blobs for this telemetry since GC only marks these objects
1148
- // as unreferenced. Also, if an inactive DDS is used, the corresponding data store store will also be used.
1149
- const nodeType = this.runtime.getNodeType(nodeId);
1150
- if (nodeType !== GCNodeType.DataStore && nodeType !== GCNodeType.Blob) {
1151
- return;
1152
- }
1153
-
1154
- const state = nodeStateTracker.state;
1155
- const uniqueEventId = `${state}-${nodeId}-${usageType}`;
1156
- if (this.loggedUnreferencedEvents.has(uniqueEventId)) {
1157
- return;
1158
- }
1159
- this.loggedUnreferencedEvents.add(uniqueEventId);
1160
-
1161
- const propsToLog = {
1162
- id: nodeId,
1163
- type: nodeType,
1164
- unrefTime: nodeStateTracker.unreferencedTimestampMs,
1165
- age: currentReferenceTimestampMs - nodeStateTracker.unreferencedTimestampMs,
1166
- timeout:
1167
- nodeStateTracker.state === UnreferencedState.Inactive
1168
- ? this.configs.inactiveTimeoutMs
1169
- : this.configs.sweepTimeoutMs,
1170
- completedGCRuns: this.completedRuns,
1171
- lastSummaryTime: this.getLastSummaryTimestampMs(),
1172
- ...this.createContainerMetadata,
1173
- viaHandle: requestHeaders?.[RuntimeHeaders.viaHandle],
1174
- fromId: fromNodeId,
1175
- };
1176
-
1177
- // For summarizer client, queue the event so it is logged the next time GC runs if the event is still valid.
1178
- // For non-summarizer client, log the event now since GC won't run on it. This may result in false positives
1179
- // but it's a good signal nonetheless and we can consume it with a grain of salt.
1180
- // Inactive errors are usages of Objects that are unreferenced for at least a period of 7 days.
1181
- // SweepReady errors are usages of Objects that will be deleted by GC Sweep!
1182
- if (this.isSummarizerClient) {
1183
- this.pendingEventsQueue.push({ ...propsToLog, usageType, state });
1184
- } else {
1185
- // For non-summarizer clients, only log "Loaded" type events since these objects may not be loaded in the
1186
- // summarizer clients if they are based off of user actions (such as scrolling to content for these objects)
1187
- // Events generated:
1188
- // InactiveObject_Loaded, SweepReadyObject_Loaded
1189
- if (usageType === "Loaded") {
1190
- const event = {
1191
- ...propsToLog,
1192
- eventName: `${state}Object_${usageType}`,
1193
- pkg: packagePathToTelemetryProperty(packagePath),
1194
- stack: generateStack(),
1195
- };
1196
-
1197
- // Do not log the inactive object x events as error events as they are not the best signal for
1198
- // detecting something wrong with GC either from the partner or from the runtime itself.
1199
- if (state === UnreferencedState.Inactive) {
1200
- this.mc.logger.sendTelemetryEvent(event);
1201
- } else {
1202
- this.mc.logger.sendErrorEvent(event);
1203
- }
1204
- }
1205
- }
1206
- }
1207
-
1208
- private async logUnreferencedEvents(logger: ITelemetryLogger) {
1209
- // Events sent come only from the summarizer client. In between summaries, events are pushed to a queue and at
1210
- // summary time they are then logged.
1211
- // Events generated:
1212
- // InactiveObject_Loaded, InactiveObject_Changed, InactiveObject_Revived
1213
- // SweepReadyObject_Loaded, SweepReadyObject_Changed, SweepReadyObject_Revived
1214
- for (const eventProps of this.pendingEventsQueue) {
1215
- const { usageType, state, ...propsToLog } = eventProps;
1216
- /**
1217
- * Revived event is logged only if the node is active. If the node is not active, the reference to it was
1218
- * from another unreferenced node and this scenario is not interesting to log.
1219
- * Loaded and Changed events are logged only if the node is not active. If the node is active, it was
1220
- * revived and a Revived event will be logged for it.
1221
- */
1222
- const nodeStateTracker = this.unreferencedNodesState.get(eventProps.id);
1223
- const active =
1224
- nodeStateTracker === undefined ||
1225
- nodeStateTracker.state === UnreferencedState.Active;
1226
- if ((usageType === "Revived") === active) {
1227
- const pkg = await this.getNodePackagePath(eventProps.id);
1228
- const fromPkg = eventProps.fromId
1229
- ? await this.getNodePackagePath(eventProps.fromId)
1230
- : undefined;
1231
- const event = {
1232
- ...propsToLog,
1233
- eventName: `${state}Object_${usageType}`,
1234
- pkg: pkg
1235
- ? { value: pkg.join("/"), tag: TelemetryDataTag.CodeArtifact }
1236
- : undefined,
1237
- fromPkg: fromPkg
1238
- ? { value: fromPkg.join("/"), tag: TelemetryDataTag.CodeArtifact }
1239
- : undefined,
1240
- };
1241
-
1242
- if (state === UnreferencedState.Inactive) {
1243
- logger.sendTelemetryEvent(event);
1244
- } else {
1245
- logger.sendErrorEvent(event);
1246
- }
1247
- }
1248
- }
1249
- this.pendingEventsQueue = [];
1250
- }
1251
989
  }
@@ -3,7 +3,6 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
 
6
- import { ITelemetryGenericEvent } from "@fluidframework/common-definitions";
7
6
  import { assert } from "@fluidframework/common-utils";
8
7
  import { ISnapshotTree } from "@fluidframework/protocol-definitions";
9
8
  import {
@@ -13,17 +12,7 @@ import {
13
12
  IGarbageCollectionData,
14
13
  IGarbageCollectionDetailsBase,
15
14
  } from "@fluidframework/runtime-definitions";
16
- import { packagePathToTelemetryProperty } from "@fluidframework/runtime-utils";
17
- import { MonitoringContext } from "@fluidframework/telemetry-utils";
18
- import {
19
- disableTombstoneKey,
20
- GCFeatureMatrix,
21
- GCVersion,
22
- IGCMetadata,
23
- runSweepKey,
24
- throwOnTombstoneLoadKey,
25
- throwOnTombstoneUsageKey,
26
- } from "./gcDefinitions";
15
+ import { GCFeatureMatrix, GCVersion, IGCMetadata } from "./gcDefinitions";
27
16
  import {
28
17
  IGarbageCollectionNodeData,
29
18
  IGarbageCollectionSnapshotData,
@@ -38,32 +27,6 @@ export function getGCVersion(metadata?: IGCMetadata): GCVersion {
38
27
  return metadata.gcFeature ?? 0;
39
28
  }
40
29
 
41
- /**
42
- * Consolidates info / logic for logging when we encounter unexpected usage of GC'd objects. For example, when a
43
- * tombstoned or deleted object is loaded.
44
- */
45
- export function sendGCUnexpectedUsageEvent(
46
- mc: MonitoringContext,
47
- event: ITelemetryGenericEvent & {
48
- category: "error" | "generic";
49
- gcTombstoneEnforcementAllowed: boolean | undefined;
50
- },
51
- packagePath: readonly string[] | undefined,
52
- error?: unknown,
53
- ) {
54
- event.pkg = packagePathToTelemetryProperty(packagePath);
55
- event.tombstoneFlags = JSON.stringify({
56
- DisableTombstone: mc.config.getBoolean(disableTombstoneKey),
57
- ThrowOnTombstoneUsage: mc.config.getBoolean(throwOnTombstoneUsageKey),
58
- ThrowOnTombstoneLoad: mc.config.getBoolean(throwOnTombstoneLoadKey),
59
- });
60
- event.sweepFlags = JSON.stringify({
61
- EnableSweepFlag: mc.config.getBoolean(runSweepKey),
62
- });
63
-
64
- mc.logger.sendTelemetryEvent(event, error);
65
- }
66
-
67
30
  /**
68
31
  * Indicates whether Tombstone Enforcement is allowed for this document based on the current/persisted
69
32
  * TombstoneGeneration values