@fluidframework/container-runtime 2.62.0-356644 → 2.62.0

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 (112) hide show
  1. package/.eslintrc.cjs +4 -72
  2. package/CHANGELOG.md +4 -0
  3. package/container-runtime.test-files.tar +0 -0
  4. package/dist/blobManager/blobManager.d.ts.map +1 -1
  5. package/dist/blobManager/blobManager.js +10 -7
  6. package/dist/blobManager/blobManager.js.map +1 -1
  7. package/dist/blobManager/blobManagerSnapSum.js.map +1 -1
  8. package/dist/channelCollection.js +2 -2
  9. package/dist/channelCollection.js.map +1 -1
  10. package/dist/containerRuntime.d.ts +1 -2
  11. package/dist/containerRuntime.d.ts.map +1 -1
  12. package/dist/containerRuntime.js +93 -96
  13. package/dist/containerRuntime.js.map +1 -1
  14. package/dist/dataStoreContext.d.ts.map +1 -1
  15. package/dist/dataStoreContext.js +2 -2
  16. package/dist/dataStoreContext.js.map +1 -1
  17. package/dist/deltaScheduler.js +1 -1
  18. package/dist/deltaScheduler.js.map +1 -1
  19. package/dist/gc/garbageCollection.d.ts.map +1 -1
  20. package/dist/gc/garbageCollection.js +3 -1
  21. package/dist/gc/garbageCollection.js.map +1 -1
  22. package/dist/gc/gcConfigs.js +3 -1
  23. package/dist/gc/gcConfigs.js.map +1 -1
  24. package/dist/gc/gcReferenceGraphAlgorithm.js.map +1 -1
  25. package/dist/gc/gcTelemetry.d.ts.map +1 -1
  26. package/dist/gc/gcTelemetry.js +4 -3
  27. package/dist/gc/gcTelemetry.js.map +1 -1
  28. package/dist/inboundBatchAggregator.js +1 -1
  29. package/dist/inboundBatchAggregator.js.map +1 -1
  30. package/dist/metadata.d.ts.map +1 -1
  31. package/dist/metadata.js +2 -1
  32. package/dist/metadata.js.map +1 -1
  33. package/dist/opLifecycle/opGroupingManager.js +2 -2
  34. package/dist/opLifecycle/opGroupingManager.js.map +1 -1
  35. package/dist/packageVersion.d.ts +1 -1
  36. package/dist/packageVersion.d.ts.map +1 -1
  37. package/dist/packageVersion.js +1 -1
  38. package/dist/packageVersion.js.map +1 -1
  39. package/dist/pendingStateManager.d.ts.map +1 -1
  40. package/dist/pendingStateManager.js +8 -3
  41. package/dist/pendingStateManager.js.map +1 -1
  42. package/dist/summary/summaryDelayLoadedModule/runningSummarizer.d.ts.map +1 -1
  43. package/dist/summary/summaryDelayLoadedModule/runningSummarizer.js +3 -2
  44. package/dist/summary/summaryDelayLoadedModule/runningSummarizer.js.map +1 -1
  45. package/dist/summary/summaryDelayLoadedModule/summarizer.js +1 -1
  46. package/dist/summary/summaryDelayLoadedModule/summarizer.js.map +1 -1
  47. package/dist/summary/summaryDelayLoadedModule/summaryGenerator.js +1 -1
  48. package/dist/summary/summaryDelayLoadedModule/summaryGenerator.js.map +1 -1
  49. package/lib/blobManager/blobManager.d.ts.map +1 -1
  50. package/lib/blobManager/blobManager.js +10 -7
  51. package/lib/blobManager/blobManager.js.map +1 -1
  52. package/lib/blobManager/blobManagerSnapSum.js.map +1 -1
  53. package/lib/channelCollection.js +2 -2
  54. package/lib/channelCollection.js.map +1 -1
  55. package/lib/containerRuntime.d.ts +1 -2
  56. package/lib/containerRuntime.d.ts.map +1 -1
  57. package/lib/containerRuntime.js +22 -25
  58. package/lib/containerRuntime.js.map +1 -1
  59. package/lib/dataStoreContext.d.ts.map +1 -1
  60. package/lib/dataStoreContext.js +2 -2
  61. package/lib/dataStoreContext.js.map +1 -1
  62. package/lib/deltaScheduler.js +1 -1
  63. package/lib/deltaScheduler.js.map +1 -1
  64. package/lib/gc/garbageCollection.d.ts.map +1 -1
  65. package/lib/gc/garbageCollection.js +3 -1
  66. package/lib/gc/garbageCollection.js.map +1 -1
  67. package/lib/gc/gcConfigs.js +3 -1
  68. package/lib/gc/gcConfigs.js.map +1 -1
  69. package/lib/gc/gcReferenceGraphAlgorithm.js.map +1 -1
  70. package/lib/gc/gcTelemetry.d.ts.map +1 -1
  71. package/lib/gc/gcTelemetry.js +4 -3
  72. package/lib/gc/gcTelemetry.js.map +1 -1
  73. package/lib/inboundBatchAggregator.js +1 -1
  74. package/lib/inboundBatchAggregator.js.map +1 -1
  75. package/lib/metadata.d.ts.map +1 -1
  76. package/lib/metadata.js +2 -1
  77. package/lib/metadata.js.map +1 -1
  78. package/lib/opLifecycle/opGroupingManager.js +2 -2
  79. package/lib/opLifecycle/opGroupingManager.js.map +1 -1
  80. package/lib/packageVersion.d.ts +1 -1
  81. package/lib/packageVersion.d.ts.map +1 -1
  82. package/lib/packageVersion.js +1 -1
  83. package/lib/packageVersion.js.map +1 -1
  84. package/lib/pendingStateManager.d.ts.map +1 -1
  85. package/lib/pendingStateManager.js +8 -3
  86. package/lib/pendingStateManager.js.map +1 -1
  87. package/lib/summary/summaryDelayLoadedModule/runningSummarizer.d.ts.map +1 -1
  88. package/lib/summary/summaryDelayLoadedModule/runningSummarizer.js +3 -2
  89. package/lib/summary/summaryDelayLoadedModule/runningSummarizer.js.map +1 -1
  90. package/lib/summary/summaryDelayLoadedModule/summarizer.js +1 -1
  91. package/lib/summary/summaryDelayLoadedModule/summarizer.js.map +1 -1
  92. package/lib/summary/summaryDelayLoadedModule/summaryGenerator.js +1 -1
  93. package/lib/summary/summaryDelayLoadedModule/summaryGenerator.js.map +1 -1
  94. package/package.json +18 -18
  95. package/src/blobManager/blobManager.ts +12 -7
  96. package/src/blobManager/blobManagerSnapSum.ts +1 -1
  97. package/src/channelCollection.ts +3 -3
  98. package/src/containerRuntime.ts +58 -65
  99. package/src/dataStoreContext.ts +3 -3
  100. package/src/deltaScheduler.ts +1 -1
  101. package/src/gc/garbageCollection.ts +5 -2
  102. package/src/gc/gcConfigs.ts +3 -3
  103. package/src/gc/gcReferenceGraphAlgorithm.ts +1 -1
  104. package/src/gc/gcTelemetry.ts +4 -5
  105. package/src/inboundBatchAggregator.ts +1 -1
  106. package/src/metadata.ts +2 -1
  107. package/src/opLifecycle/opGroupingManager.ts +2 -2
  108. package/src/packageVersion.ts +1 -1
  109. package/src/pendingStateManager.ts +11 -5
  110. package/src/summary/summaryDelayLoadedModule/runningSummarizer.ts +3 -2
  111. package/src/summary/summaryDelayLoadedModule/summarizer.ts +1 -1
  112. package/src/summary/summaryDelayLoadedModule/summaryGenerator.ts +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"gcTelemetry.js","sourceRoot":"","sources":["../../src/gc/gcTelemetry.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport type { Tagged } from \"@fluidframework/core-interfaces\";\nimport type { IGarbageCollectionData } from \"@fluidframework/runtime-definitions/internal\";\nimport {\n\ttype ITelemetryLoggerExt,\n\ttype MonitoringContext,\n\tgenerateStack,\n\ttagCodeArtifacts,\n\ttype ITelemetryPropertiesExt,\n} from \"@fluidframework/telemetry-utils/internal\";\n\nimport type { RuntimeHeaderData } from \"../containerRuntime.js\";\nimport type { ICreateContainerMetadata } from \"../summary/index.js\";\n\nimport {\n\ttype GCFeatureMatrix,\n\tGCNodeType,\n\ttype IGarbageCollectorConfigs,\n\tUnreferencedState,\n} from \"./gcDefinitions.js\";\nimport type { UnreferencedStateTracker } from \"./gcUnreferencedStateTracker.js\";\n\ntype NodeUsageType = \"Changed\" | \"Loaded\" | \"Revived\" | \"Realized\";\n\n/**\n * Properties that are common to IUnreferencedEventProps and INodeUsageProps\n */\ninterface ICommonProps {\n\tusageType: NodeUsageType;\n\tcompletedGCRuns: number;\n\tisTombstoned: boolean;\n\tlastSummaryTime?: number;\n\theaders?: RuntimeHeaderData;\n\tadditionalProps?: ITelemetryPropertiesExt;\n}\n\n/**\n * The event that is logged when unreferenced node is used after a certain time.\n */\n\ninterface IUnreferencedEventProps extends ICreateContainerMetadata, ICommonProps {\n\t/**\n\t * The id that GC uses to track the node. May or may not match id\n\t */\n\ttrackedId: string;\n\tstate: UnreferencedState;\n\t/**\n\t * The full path (in GC Path format) to the node in question\n\t */\n\tid: Tagged<string>;\n\tfromId?: Tagged<string>;\n\n\ttype: GCNodeType;\n\tunrefTime: number;\n\tage: number;\n\t// Expanding GC feature matrix. Without doing this, the configs cannot be logged in telemetry directly.\n\tgcConfigs: Omit<IGarbageCollectorConfigs, \"persistedGcFeatureMatrix\"> & {\n\t\t[K in keyof GCFeatureMatrix]: GCFeatureMatrix[K];\n\t};\n\ttimeout?: number;\n}\n\n/**\n * Properties passed to nodeUsed function when a node is used.\n */\ninterface INodeUsageProps extends ICommonProps {\n\t/**\n\t * The full path (in GC Path format) to the node in question\n\t */\n\tid: string;\n\t/**\n\t * Latest timestamp received from the server, as a baseline for computing GC state/age\n\t */\n\tcurrentReferenceTimestampMs: number;\n\t/**\n\t * The package path of the node. This may not be available if the node hasn't been loaded yet.\n\t *\n\t * @see {@link @fluidframework/runtime-definitions#IFluidDataStoreContext.packagePath} for more details.\n\t */\n\tpackagePath: readonly string[] | undefined;\n\t/**\n\t * In case of Revived - what node added the reference?\n\t */\n\tfromId?: string;\n\t/**\n\t * In case of Revived - was it revived due to autorecovery?\n\t */\n\tautorecovery?: true;\n\t/**\n\t * URL (including query string) if this usage came from a request\n\t */\n\trequestUrl?: string;\n\t/**\n\t * Original request headers if this usage came from a request or handle.get\n\t */\n\trequestHeaders?: string;\n}\n\n/**\n * Encapsulates the logic that tracks the various telemetry logged by the Garbage Collector.\n *\n * These events are not logged as errors, just generic events, since there can be false positives:\n *\n * 1. inactiveObject telemetry - When an inactive node is used - A node that has been unreferenced for inactiveTimeoutMs.\n * 2. tombstoneReadyObject telemetry - When a tombstone-ready node is used - A node that has been unreferenced for tombstoneTimeoutMs.\n * 3. sweepReadyObject telemetry - When a sweep-ready node is used - A node that has been unreferenced for tombstoneTimeoutMs + sweepGracePeriodMs.\n *\n * These events are logged as errors since they are based on the core GC logic:\n *\n * 1. Tombstone telemetry - When a tombstoned node is used - A node that has been marked as tombstone.\n * 2. Unknown outbound reference telemetry - When a node is referenced but GC was not notified of it when the new reference appeared.\n *\n * Note: The telemetry for a Deleted node being used is logged elsewhere in this package.\n */\nexport class GCTelemetryTracker {\n\t// Keeps track of unreferenced events that are logged for a node. This is used to limit the log generation to one\n\t// per event per node.\n\tprivate readonly loggedUnreferencedEvents: Set<string> = new Set();\n\t// Queue for unreferenced events that should be logged the next time GC runs.\n\tprivate pendingEventsQueue: IUnreferencedEventProps[] = [];\n\n\tconstructor(\n\t\tprivate readonly mc: MonitoringContext,\n\t\tprivate readonly configs: IGarbageCollectorConfigs,\n\t\tprivate readonly isSummarizerClient: boolean,\n\n\t\tprivate readonly createContainerMetadata: ICreateContainerMetadata,\n\n\t\tprivate readonly getNodeType: (nodeId: string) => GCNodeType,\n\t\tprivate readonly getNodeStateTracker: (\n\t\t\tnodeId: string,\n\t\t) => UnreferencedStateTracker | undefined,\n\t\tprivate readonly getNodePackagePath: (\n\t\t\tnodePath: string,\n\t\t) => Promise<readonly string[] | undefined>,\n\t) {}\n\n\t/**\n\t * Returns whether an event should be logged for a node that isn't active anymore.\n\t *\n\t * @remarks\n\t * This does not apply to tombstoned nodes for which an event is always logged. Some scenarios where we won't log:\n\t *\n\t * 1. When a DDS is changed. The corresponding data store's event will be logged instead.\n\t *\n\t * 2. An event is logged only once per container instance per event per node.\n\t */\n\tprivate shouldLogNonActiveEvent(\n\t\tnodeType: GCNodeType,\n\t\tusageType: NodeUsageType,\n\t\tnodeStateTracker: UnreferencedStateTracker,\n\t\tuniqueEventId: string,\n\t): boolean {\n\t\tif (nodeStateTracker.state === UnreferencedState.Active) {\n\t\t\treturn false;\n\t\t}\n\n\t\tif (nodeType === GCNodeType.Other) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// For sub data store (DDS) nodes, if they are changed, its data store will also be changed,\n\t\t// so skip logging to make the telemetry less noisy.\n\n\t\tif (nodeType === GCNodeType.SubDataStore && usageType === \"Changed\") {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Non-tombstone events are logged once per event per node. A unique id is generated by joining\n\t\t// node state (inactive / sweep ready), node's id and usage (loaded / changed / revived).\n\t\tif (this.loggedUnreferencedEvents.has(uniqueEventId)) {\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * Called when a node is used. If the node is inactive or tombstoned, log telemetry indicating object is used\n\t * when it should not have been.\n\t * @param trackedId - The id that GC uses to track the node. For SubDataStore nodes, this should be the DataStore ID.\n\t * @param INodeUsageProps - All kind of details about this event to be logged\n\t */\n\tpublic nodeUsed(\n\t\ttrackedId: string,\n\t\t{\n\t\t\tusageType,\n\t\t\tcurrentReferenceTimestampMs,\n\t\t\tpackagePath,\n\t\t\tid: untaggedId,\n\t\t\tfromId: untaggedFromId,\n\t\t\tisTombstoned,\n\t\t\t...otherNodeUsageProps\n\t\t}: INodeUsageProps,\n\t): void {\n\t\t// Note: For SubDataStore Load usage, trackedId will be the DataStore's id, not the full path in question.\n\t\t// This is necessary because the SubDataStore path may be unrecognized by GC (if suited for a custom request handler)\n\t\tconst nodeStateTracker = this.getNodeStateTracker(trackedId);\n\t\tconst nodeType = this.getNodeType(untaggedId);\n\n\t\tconst timeout = (() => {\n\t\t\tswitch (nodeStateTracker?.state) {\n\t\t\t\tcase UnreferencedState.Inactive: {\n\t\t\t\t\treturn this.configs.inactiveTimeoutMs;\n\t\t\t\t}\n\t\t\t\tcase UnreferencedState.TombstoneReady: {\n\t\t\t\t\treturn this.configs.tombstoneTimeoutMs;\n\t\t\t\t}\n\t\t\t\tcase UnreferencedState.SweepReady: {\n\t\t\t\t\treturn (\n\t\t\t\t\t\tthis.configs.tombstoneTimeoutMs &&\n\t\t\t\t\t\tthis.configs.tombstoneTimeoutMs + this.configs.sweepGracePeriodMs\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tdefault: {\n\t\t\t\t\treturn undefined;\n\t\t\t\t}\n\t\t\t}\n\t\t})();\n\t\tconst { persistedGcFeatureMatrix, ...configs } = this.configs;\n\t\tconst unrefEventProps = {\n\t\t\ttrackedId,\n\t\t\ttype: nodeType,\n\t\t\tunrefTime: nodeStateTracker?.unreferencedTimestampMs ?? -1,\n\t\t\tage:\n\t\t\t\tnodeStateTracker === undefined\n\t\t\t\t\t? -1\n\t\t\t\t\t: currentReferenceTimestampMs - nodeStateTracker.unreferencedTimestampMs,\n\t\t\ttimeout,\n\t\t\tisTombstoned,\n\t\t\t...tagCodeArtifacts({ id: untaggedId, fromId: untaggedFromId }),\n\t\t\t...otherNodeUsageProps,\n\t\t\t...this.createContainerMetadata,\n\t\t\tgcConfigs: { ...configs, ...persistedGcFeatureMatrix },\n\t\t} satisfies Omit<IUnreferencedEventProps, \"state\" | \"usageType\"> &\n\t\t\ttypeof otherNodeUsageProps;\n\n\t\t// If the node that is used is tombstoned, log a tombstone telemetry.\n\t\tif (isTombstoned) {\n\t\t\tthis.logTombstoneUsageTelemetry(unrefEventProps, nodeType, usageType, packagePath);\n\t\t}\n\n\t\t// After logging tombstone telemetry, if the node's unreferenced state is not tracked, there is nothing\n\t\t// else to log.\n\t\tif (nodeStateTracker === undefined) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst state = nodeStateTracker.state;\n\t\tconst uniqueEventId = `${state}-${untaggedId}-${usageType}`;\n\n\t\tif (!this.shouldLogNonActiveEvent(nodeType, usageType, nodeStateTracker, uniqueEventId)) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Add the unique event id so that we don't generate a log for this event again in this session.\n\t\tthis.loggedUnreferencedEvents.add(uniqueEventId);\n\n\t\t// For summarizer client, queue the event so it is logged the next time GC runs if the event is still valid.\n\t\t// For non-summarizer client, log the event now since GC won't run on it. This may result in false positives\n\t\t// but it's a good signal nonetheless and we can consume it with a grain of salt.\n\t\t// Inactive errors are usages of Objects that are unreferenced for at least a period of 7 days.\n\t\t// SweepReady errors are usages of Objects that will be deleted by GC Sweep!\n\t\tif (this.isSummarizerClient) {\n\t\t\tthis.pendingEventsQueue.push({\n\t\t\t\t...unrefEventProps, // Note: Contains some properties from INodeUsageProps as well\n\t\t\t\tusageType,\n\t\t\t\tstate,\n\t\t\t});\n\t\t} else {\n\t\t\t// For non-summarizer clients, only log \"Loaded\" type events since these objects may not be loaded in the\n\t\t\t// summarizer clients if they are based off of user actions (such as scrolling to content for these objects)\n\t\t\t// Events generated:\n\t\t\t// InactiveObject_Loaded, SweepReadyObject_Loaded\n\t\t\tif (usageType === \"Loaded\") {\n\t\t\t\tconst { id, fromId, headers, gcConfigs, additionalProps, ...detailedProps } =\n\t\t\t\t\tunrefEventProps;\n\t\t\t\tconst event = {\n\t\t\t\t\teventName: `${state}Object_${usageType}`,\n\t\t\t\t\t...tagCodeArtifacts({ pkg: packagePath?.join(\"/\") }),\n\t\t\t\t\tstack: generateStack(30),\n\t\t\t\t\tid,\n\t\t\t\t\tfromId,\n\t\t\t\t\theaders: { ...headers },\n\t\t\t\t\tdetails: { ...detailedProps, ...additionalProps },\n\t\t\t\t\tgcConfigs,\n\t\t\t\t};\n\n\t\t\t\t// These are logged as generic events and not errors because there can be false positives. The Tombstone\n\t\t\t\t// and Delete errors are separately logged and are reliable.\n\t\t\t\tthis.mc.logger.sendTelemetryEvent(event);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Logs telemetry when a tombstoned object is changed, revived or loaded.\n\t */\n\tprivate logTombstoneUsageTelemetry(\n\t\tunrefEventProps: Omit<IUnreferencedEventProps, \"state\" | \"usageType\">,\n\n\t\tnodeType: GCNodeType,\n\t\tusageType: NodeUsageType,\n\t\tpackagePath?: readonly string[],\n\t): void {\n\t\t// This will log the following events:\n\t\t// GC_Tombstone_DataStore_Requested, GC_Tombstone_DataStore_Changed, GC_Tombstone_DataStore_Revived\n\t\t// GC_Tombstone_SubDataStore_Requested, GC_Tombstone_SubDataStore_Changed, GC_Tombstone_SubDataStore_Revived\n\t\t// GC_Tombstone_Blob_Requested, GC_Tombstone_Blob_Changed, GC_Tombstone_Blob_Revived\n\t\tconst { id, fromId, headers, gcConfigs, additionalProps, ...detailedProps } =\n\t\t\tunrefEventProps;\n\t\tconst eventUsageName = usageType === \"Loaded\" ? \"Requested\" : usageType;\n\t\tconst event = {\n\t\t\teventName: `GC_Tombstone_${nodeType}_${eventUsageName}`,\n\t\t\t...tagCodeArtifacts({ pkg: packagePath?.join(\"/\") }),\n\t\t\tstack: generateStack(30),\n\t\t\tid,\n\t\t\tfromId,\n\t\t\theaders: { ...headers },\n\t\t\tdetails: { ...detailedProps, ...additionalProps }, // Also includes some properties from INodeUsageProps type\n\t\t\tgcConfigs,\n\t\t};\n\n\t\tif (\n\t\t\tusageType === \"Loaded\" &&\n\t\t\tthis.configs.throwOnTombstoneLoad &&\n\t\t\t!headers?.allowTombstone\n\t\t) {\n\t\t\tthis.mc.logger.sendErrorEvent(event);\n\t\t} else {\n\t\t\tthis.mc.logger.sendTelemetryEvent(event);\n\t\t}\n\t}\n\n\t/**\n\t * Log all new references or outbound routes in the current graph that haven't been explicitly notified to GC.\n\t * The principle is that every new reference or outbound route must be notified to GC via the\n\t * addedOutboundReference method. It it hasn't, its a bug and we want to identify these scenarios.\n\t *\n\t * In more simple terms:\n\t * Missing Explicit References = Current References - Previous References - Explicitly Added References;\n\t *\n\t * @param currentGCData - The GC data (reference graph) from the current GC run.\n\t * @param previousGCData - The GC data (reference graph) from the previous GC run.\n\t * @param explicitReferences - New references added explicity between the previous and the current run.\n\t */\n\tpublic logIfMissingExplicitReferences(\n\t\tcurrentGCData: IGarbageCollectionData,\n\t\tpreviousGCData: IGarbageCollectionData,\n\t\texplicitReferences: Map<string, string[]>,\n\t\tlogger: ITelemetryLoggerExt,\n\t): void {\n\t\tfor (const [nodeId, currentOutboundRoutes] of Object.entries(currentGCData.gcNodes)) {\n\t\t\tconst previousRoutes = previousGCData.gcNodes[nodeId] ?? [];\n\t\t\tconst explicitRoutes = explicitReferences.get(nodeId) ?? [];\n\n\t\t\t/**\n\t\t\t * 1. For routes in the current GC data, routes that were not present in previous GC data and did not have\n\t\t\t * explicit references should be added to missing explicit routes list.\n\t\t\t * 2. Only include data store and blob routes since GC only works for these two.\n\t\t\t * Note: Due to a bug with de-duped blobs, only adding data store routes for now.\n\t\t\t * 3. Ignore DDS routes to their parent datastores since those were added implicitly. So, there won't be\n\t\t\t * explicit routes to them.\n\t\t\t */\n\t\t\tconst missingExplicitRoutes: string[] = [];\n\t\t\tfor (const route of currentOutboundRoutes) {\n\t\t\t\tconst nodeType = this.getNodeType(route);\n\t\t\t\tif (\n\t\t\t\t\t(nodeType === GCNodeType.DataStore || nodeType === GCNodeType.Blob) &&\n\t\t\t\t\t!nodeId.startsWith(route) &&\n\t\t\t\t\t!previousRoutes.includes(route) &&\n\t\t\t\t\t!explicitRoutes.includes(route)\n\t\t\t\t) {\n\t\t\t\t\tmissingExplicitRoutes.push(route);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (missingExplicitRoutes.length > 0) {\n\t\t\t\t// Send as Generic not Error since there are known corner cases where this will fire.\n\t\t\t\t// E.g. If an old client re-references a node via an attach op (that doesn't include GC Data)\n\t\t\t\tlogger.sendTelemetryEvent({\n\t\t\t\t\teventName: \"gcUnknownOutboundReferences\",\n\t\t\t\t\t...tagCodeArtifacts({\n\t\t\t\t\t\tid: nodeId,\n\t\t\t\t\t\troutes: JSON.stringify(missingExplicitRoutes),\n\t\t\t\t\t}),\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Log events that are pending in pendingEventsQueue. This is called after GC runs in the summarizer client\n\t * so that the state of an unreferenced node is updated.\n\t */\n\tpublic async logPendingEvents(logger: ITelemetryLoggerExt): Promise<void> {\n\t\t// Events sent come only from the summarizer client. In between summaries, events are pushed to a queue and at\n\t\t// summary time they are then logged.\n\t\t// Events generated:\n\t\t// InactiveObject_Loaded, InactiveObject_Changed, InactiveObject_Revived\n\t\t// SweepReadyObject_Loaded, SweepReadyObject_Changed, SweepReadyObject_Revived\n\t\tfor (const eventProps of this.pendingEventsQueue) {\n\t\t\tconst {\n\t\t\t\tusageType,\n\t\t\t\tstate,\n\t\t\t\tid,\n\t\t\t\tfromId,\n\t\t\t\theaders,\n\t\t\t\tgcConfigs,\n\t\t\t\tadditionalProps,\n\t\t\t\t...detailedProps\n\t\t\t} = eventProps;\n\t\t\t/**\n\t\t\t * Revived event is logged only if the node is active. If the node is not active, the reference to it was\n\t\t\t * from another unreferenced node and this scenario is not interesting to log.\n\t\t\t * Loaded and Changed events are logged only if the node is not active. If the node is active, it was\n\t\t\t * revived and a Revived event will be logged for it.\n\t\t\t */\n\t\t\tconst nodeStateTracker = this.getNodeStateTracker(detailedProps.trackedId); // Note: This is never SubDataStore path\n\t\t\tconst active =\n\t\t\t\tnodeStateTracker === undefined || nodeStateTracker.state === UnreferencedState.Active;\n\t\t\tif ((usageType === \"Revived\") === active) {\n\t\t\t\tconst pkg = await this.getNodePackagePath(id.value);\n\t\t\t\tconst fromPkg = fromId ? await this.getNodePackagePath(fromId.value) : undefined;\n\t\t\t\tconst event = {\n\t\t\t\t\teventName: `${state}Object_${usageType}`,\n\t\t\t\t\tid,\n\t\t\t\t\tfromId,\n\t\t\t\t\theaders: { ...headers },\n\t\t\t\t\tdetails: { ...detailedProps, ...additionalProps },\n\t\t\t\t\tgcConfigs,\n\t\t\t\t\t...tagCodeArtifacts({\n\t\t\t\t\t\tpkg: pkg?.join(\"/\"),\n\t\t\t\t\t\tfromPkg: fromPkg?.join(\"/\"),\n\t\t\t\t\t}),\n\t\t\t\t};\n\n\t\t\t\t// These are logged as generic events and not errors because there can be false positives. The Tombstone\n\t\t\t\t// and Delete errors are separately logged and are reliable.\n\t\t\t\tlogger.sendTelemetryEvent(event);\n\t\t\t}\n\t\t}\n\t\tthis.pendingEventsQueue = [];\n\t}\n}\n"]}
1
+ {"version":3,"file":"gcTelemetry.js","sourceRoot":"","sources":["../../src/gc/gcTelemetry.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport type { Tagged } from \"@fluidframework/core-interfaces\";\nimport type { IGarbageCollectionData } from \"@fluidframework/runtime-definitions/internal\";\nimport {\n\ttype ITelemetryLoggerExt,\n\ttype MonitoringContext,\n\tgenerateStack,\n\ttagCodeArtifacts,\n\ttype ITelemetryPropertiesExt,\n} from \"@fluidframework/telemetry-utils/internal\";\n\nimport type { RuntimeHeaderData } from \"../containerRuntime.js\";\nimport type { ICreateContainerMetadata } from \"../summary/index.js\";\n\nimport {\n\ttype GCFeatureMatrix,\n\tGCNodeType,\n\ttype IGarbageCollectorConfigs,\n\tUnreferencedState,\n} from \"./gcDefinitions.js\";\nimport type { UnreferencedStateTracker } from \"./gcUnreferencedStateTracker.js\";\n\ntype NodeUsageType = \"Changed\" | \"Loaded\" | \"Revived\" | \"Realized\";\n\n/**\n * Properties that are common to IUnreferencedEventProps and INodeUsageProps\n */\ninterface ICommonProps {\n\tusageType: NodeUsageType;\n\tcompletedGCRuns: number;\n\tisTombstoned: boolean;\n\tlastSummaryTime?: number;\n\theaders?: RuntimeHeaderData;\n\tadditionalProps?: ITelemetryPropertiesExt;\n}\n\n/**\n * The event that is logged when unreferenced node is used after a certain time.\n */\n\ninterface IUnreferencedEventProps extends ICreateContainerMetadata, ICommonProps {\n\t/**\n\t * The id that GC uses to track the node. May or may not match id\n\t */\n\ttrackedId: string;\n\tstate: UnreferencedState;\n\t/**\n\t * The full path (in GC Path format) to the node in question\n\t */\n\tid: Tagged<string>;\n\tfromId?: Tagged<string>;\n\n\ttype: GCNodeType;\n\tunrefTime: number;\n\tage: number;\n\t// Expanding GC feature matrix. Without doing this, the configs cannot be logged in telemetry directly.\n\tgcConfigs: Omit<IGarbageCollectorConfigs, \"persistedGcFeatureMatrix\"> & {\n\t\t[K in keyof GCFeatureMatrix]: GCFeatureMatrix[K];\n\t};\n\ttimeout?: number;\n}\n\n/**\n * Properties passed to nodeUsed function when a node is used.\n */\ninterface INodeUsageProps extends ICommonProps {\n\t/**\n\t * The full path (in GC Path format) to the node in question\n\t */\n\tid: string;\n\t/**\n\t * Latest timestamp received from the server, as a baseline for computing GC state/age\n\t */\n\tcurrentReferenceTimestampMs: number;\n\t/**\n\t * The package path of the node. This may not be available if the node hasn't been loaded yet.\n\t *\n\t * @see {@link @fluidframework/runtime-definitions#IFluidDataStoreContext.packagePath} for more details.\n\t */\n\tpackagePath: readonly string[] | undefined;\n\t/**\n\t * In case of Revived - what node added the reference?\n\t */\n\tfromId?: string;\n\t/**\n\t * In case of Revived - was it revived due to autorecovery?\n\t */\n\tautorecovery?: true;\n\t/**\n\t * URL (including query string) if this usage came from a request\n\t */\n\trequestUrl?: string;\n\t/**\n\t * Original request headers if this usage came from a request or handle.get\n\t */\n\trequestHeaders?: string;\n}\n\n/**\n * Encapsulates the logic that tracks the various telemetry logged by the Garbage Collector.\n *\n * These events are not logged as errors, just generic events, since there can be false positives:\n *\n * 1. inactiveObject telemetry - When an inactive node is used - A node that has been unreferenced for inactiveTimeoutMs.\n * 2. tombstoneReadyObject telemetry - When a tombstone-ready node is used - A node that has been unreferenced for tombstoneTimeoutMs.\n * 3. sweepReadyObject telemetry - When a sweep-ready node is used - A node that has been unreferenced for tombstoneTimeoutMs + sweepGracePeriodMs.\n *\n * These events are logged as errors since they are based on the core GC logic:\n *\n * 1. Tombstone telemetry - When a tombstoned node is used - A node that has been marked as tombstone.\n * 2. Unknown outbound reference telemetry - When a node is referenced but GC was not notified of it when the new reference appeared.\n *\n * Note: The telemetry for a Deleted node being used is logged elsewhere in this package.\n */\nexport class GCTelemetryTracker {\n\t// Keeps track of unreferenced events that are logged for a node. This is used to limit the log generation to one\n\t// per event per node.\n\tprivate readonly loggedUnreferencedEvents: Set<string> = new Set();\n\t// Queue for unreferenced events that should be logged the next time GC runs.\n\tprivate pendingEventsQueue: IUnreferencedEventProps[] = [];\n\n\tconstructor(\n\t\tprivate readonly mc: MonitoringContext,\n\t\tprivate readonly configs: IGarbageCollectorConfigs,\n\t\tprivate readonly isSummarizerClient: boolean,\n\n\t\tprivate readonly createContainerMetadata: ICreateContainerMetadata,\n\n\t\tprivate readonly getNodeType: (nodeId: string) => GCNodeType,\n\t\tprivate readonly getNodeStateTracker: (\n\t\t\tnodeId: string,\n\t\t) => UnreferencedStateTracker | undefined,\n\t\tprivate readonly getNodePackagePath: (\n\t\t\tnodePath: string,\n\t\t) => Promise<readonly string[] | undefined>,\n\t) {}\n\n\t/**\n\t * Returns whether an event should be logged for a node that isn't active anymore.\n\t *\n\t * @remarks\n\t * This does not apply to tombstoned nodes for which an event is always logged. Some scenarios where we won't log:\n\t *\n\t * 1. When a DDS is changed. The corresponding data store's event will be logged instead.\n\t *\n\t * 2. An event is logged only once per container instance per event per node.\n\t */\n\tprivate shouldLogNonActiveEvent(\n\t\tnodeType: GCNodeType,\n\t\tusageType: NodeUsageType,\n\t\tnodeStateTracker: UnreferencedStateTracker,\n\t\tuniqueEventId: string,\n\t): boolean {\n\t\tif (nodeStateTracker.state === UnreferencedState.Active) {\n\t\t\treturn false;\n\t\t}\n\n\t\tif (nodeType === GCNodeType.Other) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// For sub data store (DDS) nodes, if they are changed, its data store will also be changed,\n\t\t// so skip logging to make the telemetry less noisy.\n\n\t\tif (nodeType === GCNodeType.SubDataStore && usageType === \"Changed\") {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Non-tombstone events are logged once per event per node. A unique id is generated by joining\n\t\t// node state (inactive / sweep ready), node's id and usage (loaded / changed / revived).\n\t\tif (this.loggedUnreferencedEvents.has(uniqueEventId)) {\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * Called when a node is used. If the node is inactive or tombstoned, log telemetry indicating object is used\n\t * when it should not have been.\n\t * @param trackedId - The id that GC uses to track the node. For SubDataStore nodes, this should be the DataStore ID.\n\t * @param INodeUsageProps - All kind of details about this event to be logged\n\t */\n\tpublic nodeUsed(\n\t\ttrackedId: string,\n\t\t{\n\t\t\tusageType,\n\t\t\tcurrentReferenceTimestampMs,\n\t\t\tpackagePath,\n\t\t\tid: untaggedId,\n\t\t\tfromId: untaggedFromId,\n\t\t\tisTombstoned,\n\t\t\t...otherNodeUsageProps\n\t\t}: INodeUsageProps,\n\t): void {\n\t\t// Note: For SubDataStore Load usage, trackedId will be the DataStore's id, not the full path in question.\n\t\t// This is necessary because the SubDataStore path may be unrecognized by GC (if suited for a custom request handler)\n\t\tconst nodeStateTracker = this.getNodeStateTracker(trackedId);\n\t\tconst nodeType = this.getNodeType(untaggedId);\n\n\t\tconst timeout = (() => {\n\t\t\tswitch (nodeStateTracker?.state) {\n\t\t\t\tcase UnreferencedState.Inactive: {\n\t\t\t\t\treturn this.configs.inactiveTimeoutMs;\n\t\t\t\t}\n\t\t\t\tcase UnreferencedState.TombstoneReady: {\n\t\t\t\t\treturn this.configs.tombstoneTimeoutMs;\n\t\t\t\t}\n\t\t\t\tcase UnreferencedState.SweepReady: {\n\t\t\t\t\treturn this.configs.tombstoneTimeoutMs === undefined\n\t\t\t\t\t\t? undefined\n\t\t\t\t\t\t: this.configs.tombstoneTimeoutMs + this.configs.sweepGracePeriodMs;\n\t\t\t\t}\n\t\t\t\tdefault: {\n\t\t\t\t\treturn undefined;\n\t\t\t\t}\n\t\t\t}\n\t\t})();\n\t\tconst { persistedGcFeatureMatrix, ...configs } = this.configs;\n\t\tconst unrefEventProps = {\n\t\t\ttrackedId,\n\t\t\ttype: nodeType,\n\t\t\tunrefTime: nodeStateTracker?.unreferencedTimestampMs ?? -1,\n\t\t\tage:\n\t\t\t\tnodeStateTracker === undefined\n\t\t\t\t\t? -1\n\t\t\t\t\t: currentReferenceTimestampMs - nodeStateTracker.unreferencedTimestampMs,\n\t\t\ttimeout,\n\t\t\tisTombstoned,\n\t\t\t...tagCodeArtifacts({ id: untaggedId, fromId: untaggedFromId }),\n\t\t\t...otherNodeUsageProps,\n\t\t\t...this.createContainerMetadata,\n\t\t\tgcConfigs: { ...configs, ...persistedGcFeatureMatrix },\n\t\t} satisfies Omit<IUnreferencedEventProps, \"state\" | \"usageType\"> &\n\t\t\ttypeof otherNodeUsageProps;\n\n\t\t// If the node that is used is tombstoned, log a tombstone telemetry.\n\t\tif (isTombstoned) {\n\t\t\tthis.logTombstoneUsageTelemetry(unrefEventProps, nodeType, usageType, packagePath);\n\t\t}\n\n\t\t// After logging tombstone telemetry, if the node's unreferenced state is not tracked, there is nothing\n\t\t// else to log.\n\t\tif (nodeStateTracker === undefined) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst state = nodeStateTracker.state;\n\t\tconst uniqueEventId = `${state}-${untaggedId}-${usageType}`;\n\n\t\tif (!this.shouldLogNonActiveEvent(nodeType, usageType, nodeStateTracker, uniqueEventId)) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Add the unique event id so that we don't generate a log for this event again in this session.\n\t\tthis.loggedUnreferencedEvents.add(uniqueEventId);\n\n\t\t// For summarizer client, queue the event so it is logged the next time GC runs if the event is still valid.\n\t\t// For non-summarizer client, log the event now since GC won't run on it. This may result in false positives\n\t\t// but it's a good signal nonetheless and we can consume it with a grain of salt.\n\t\t// Inactive errors are usages of Objects that are unreferenced for at least a period of 7 days.\n\t\t// SweepReady errors are usages of Objects that will be deleted by GC Sweep!\n\t\tif (this.isSummarizerClient) {\n\t\t\tthis.pendingEventsQueue.push({\n\t\t\t\t...unrefEventProps, // Note: Contains some properties from INodeUsageProps as well\n\t\t\t\tusageType,\n\t\t\t\tstate,\n\t\t\t});\n\t\t} else {\n\t\t\t// For non-summarizer clients, only log \"Loaded\" type events since these objects may not be loaded in the\n\t\t\t// summarizer clients if they are based off of user actions (such as scrolling to content for these objects)\n\t\t\t// Events generated:\n\t\t\t// InactiveObject_Loaded, SweepReadyObject_Loaded\n\t\t\tif (usageType === \"Loaded\") {\n\t\t\t\tconst { id, fromId, headers, gcConfigs, additionalProps, ...detailedProps } =\n\t\t\t\t\tunrefEventProps;\n\t\t\t\tconst event = {\n\t\t\t\t\teventName: `${state}Object_${usageType}`,\n\t\t\t\t\t...tagCodeArtifacts({ pkg: packagePath?.join(\"/\") }),\n\t\t\t\t\tstack: generateStack(30),\n\t\t\t\t\tid,\n\t\t\t\t\tfromId,\n\t\t\t\t\theaders: { ...headers },\n\t\t\t\t\tdetails: { ...detailedProps, ...additionalProps },\n\t\t\t\t\tgcConfigs,\n\t\t\t\t};\n\n\t\t\t\t// These are logged as generic events and not errors because there can be false positives. The Tombstone\n\t\t\t\t// and Delete errors are separately logged and are reliable.\n\t\t\t\tthis.mc.logger.sendTelemetryEvent(event);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Logs telemetry when a tombstoned object is changed, revived or loaded.\n\t */\n\tprivate logTombstoneUsageTelemetry(\n\t\tunrefEventProps: Omit<IUnreferencedEventProps, \"state\" | \"usageType\">,\n\n\t\tnodeType: GCNodeType,\n\t\tusageType: NodeUsageType,\n\t\tpackagePath?: readonly string[],\n\t): void {\n\t\t// This will log the following events:\n\t\t// GC_Tombstone_DataStore_Requested, GC_Tombstone_DataStore_Changed, GC_Tombstone_DataStore_Revived\n\t\t// GC_Tombstone_SubDataStore_Requested, GC_Tombstone_SubDataStore_Changed, GC_Tombstone_SubDataStore_Revived\n\t\t// GC_Tombstone_Blob_Requested, GC_Tombstone_Blob_Changed, GC_Tombstone_Blob_Revived\n\t\tconst { id, fromId, headers, gcConfigs, additionalProps, ...detailedProps } =\n\t\t\tunrefEventProps;\n\t\tconst eventUsageName = usageType === \"Loaded\" ? \"Requested\" : usageType;\n\t\tconst event = {\n\t\t\teventName: `GC_Tombstone_${nodeType}_${eventUsageName}`,\n\t\t\t...tagCodeArtifacts({ pkg: packagePath?.join(\"/\") }),\n\t\t\tstack: generateStack(30),\n\t\t\tid,\n\t\t\tfromId,\n\t\t\theaders: { ...headers },\n\t\t\tdetails: { ...detailedProps, ...additionalProps }, // Also includes some properties from INodeUsageProps type\n\t\t\tgcConfigs,\n\t\t};\n\n\t\tif (\n\t\t\tusageType === \"Loaded\" &&\n\t\t\tthis.configs.throwOnTombstoneLoad &&\n\t\t\theaders?.allowTombstone !== true\n\t\t) {\n\t\t\tthis.mc.logger.sendErrorEvent(event);\n\t\t} else {\n\t\t\tthis.mc.logger.sendTelemetryEvent(event);\n\t\t}\n\t}\n\n\t/**\n\t * Log all new references or outbound routes in the current graph that haven't been explicitly notified to GC.\n\t * The principle is that every new reference or outbound route must be notified to GC via the\n\t * addedOutboundReference method. It it hasn't, its a bug and we want to identify these scenarios.\n\t *\n\t * In more simple terms:\n\t * Missing Explicit References = Current References - Previous References - Explicitly Added References;\n\t *\n\t * @param currentGCData - The GC data (reference graph) from the current GC run.\n\t * @param previousGCData - The GC data (reference graph) from the previous GC run.\n\t * @param explicitReferences - New references added explicity between the previous and the current run.\n\t */\n\tpublic logIfMissingExplicitReferences(\n\t\tcurrentGCData: IGarbageCollectionData,\n\t\tpreviousGCData: IGarbageCollectionData,\n\t\texplicitReferences: Map<string, string[]>,\n\t\tlogger: ITelemetryLoggerExt,\n\t): void {\n\t\tfor (const [nodeId, currentOutboundRoutes] of Object.entries(currentGCData.gcNodes)) {\n\t\t\tconst previousRoutes = previousGCData.gcNodes[nodeId] ?? [];\n\t\t\tconst explicitRoutes = explicitReferences.get(nodeId) ?? [];\n\n\t\t\t/**\n\t\t\t * 1. For routes in the current GC data, routes that were not present in previous GC data and did not have\n\t\t\t * explicit references should be added to missing explicit routes list.\n\t\t\t * 2. Only include data store and blob routes since GC only works for these two.\n\t\t\t * Note: Due to a bug with de-duped blobs, only adding data store routes for now.\n\t\t\t * 3. Ignore DDS routes to their parent datastores since those were added implicitly. So, there won't be\n\t\t\t * explicit routes to them.\n\t\t\t */\n\t\t\tconst missingExplicitRoutes: string[] = [];\n\t\t\tfor (const route of currentOutboundRoutes) {\n\t\t\t\tconst nodeType = this.getNodeType(route);\n\t\t\t\tif (\n\t\t\t\t\t(nodeType === GCNodeType.DataStore || nodeType === GCNodeType.Blob) &&\n\t\t\t\t\t!nodeId.startsWith(route) &&\n\t\t\t\t\t!previousRoutes.includes(route) &&\n\t\t\t\t\t!explicitRoutes.includes(route)\n\t\t\t\t) {\n\t\t\t\t\tmissingExplicitRoutes.push(route);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (missingExplicitRoutes.length > 0) {\n\t\t\t\t// Send as Generic not Error since there are known corner cases where this will fire.\n\t\t\t\t// E.g. If an old client re-references a node via an attach op (that doesn't include GC Data)\n\t\t\t\tlogger.sendTelemetryEvent({\n\t\t\t\t\teventName: \"gcUnknownOutboundReferences\",\n\t\t\t\t\t...tagCodeArtifacts({\n\t\t\t\t\t\tid: nodeId,\n\t\t\t\t\t\troutes: JSON.stringify(missingExplicitRoutes),\n\t\t\t\t\t}),\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Log events that are pending in pendingEventsQueue. This is called after GC runs in the summarizer client\n\t * so that the state of an unreferenced node is updated.\n\t */\n\tpublic async logPendingEvents(logger: ITelemetryLoggerExt): Promise<void> {\n\t\t// Events sent come only from the summarizer client. In between summaries, events are pushed to a queue and at\n\t\t// summary time they are then logged.\n\t\t// Events generated:\n\t\t// InactiveObject_Loaded, InactiveObject_Changed, InactiveObject_Revived\n\t\t// SweepReadyObject_Loaded, SweepReadyObject_Changed, SweepReadyObject_Revived\n\t\tfor (const eventProps of this.pendingEventsQueue) {\n\t\t\tconst {\n\t\t\t\tusageType,\n\t\t\t\tstate,\n\t\t\t\tid,\n\t\t\t\tfromId,\n\t\t\t\theaders,\n\t\t\t\tgcConfigs,\n\t\t\t\tadditionalProps,\n\t\t\t\t...detailedProps\n\t\t\t} = eventProps;\n\t\t\t/**\n\t\t\t * Revived event is logged only if the node is active. If the node is not active, the reference to it was\n\t\t\t * from another unreferenced node and this scenario is not interesting to log.\n\t\t\t * Loaded and Changed events are logged only if the node is not active. If the node is active, it was\n\t\t\t * revived and a Revived event will be logged for it.\n\t\t\t */\n\t\t\tconst nodeStateTracker = this.getNodeStateTracker(detailedProps.trackedId); // Note: This is never SubDataStore path\n\t\t\tconst active =\n\t\t\t\tnodeStateTracker === undefined || nodeStateTracker.state === UnreferencedState.Active;\n\t\t\tif ((usageType === \"Revived\") === active) {\n\t\t\t\tconst pkg = await this.getNodePackagePath(id.value);\n\t\t\t\tconst fromPkg = fromId ? await this.getNodePackagePath(fromId.value) : undefined;\n\t\t\t\tconst event = {\n\t\t\t\t\teventName: `${state}Object_${usageType}`,\n\t\t\t\t\tid,\n\t\t\t\t\tfromId,\n\t\t\t\t\theaders: { ...headers },\n\t\t\t\t\tdetails: { ...detailedProps, ...additionalProps },\n\t\t\t\t\tgcConfigs,\n\t\t\t\t\t...tagCodeArtifacts({\n\t\t\t\t\t\tpkg: pkg?.join(\"/\"),\n\t\t\t\t\t\tfromPkg: fromPkg?.join(\"/\"),\n\t\t\t\t\t}),\n\t\t\t\t};\n\n\t\t\t\t// These are logged as generic events and not errors because there can be false positives. The Tombstone\n\t\t\t\t// and Delete errors are separately logged and are reliable.\n\t\t\t\tlogger.sendTelemetryEvent(event);\n\t\t\t}\n\t\t}\n\t\tthis.pendingEventsQueue = [];\n\t}\n}\n"]}
@@ -108,7 +108,7 @@ export class InboundBatchAggregator {
108
108
  // - in afterOpProcessing() - processing ops until reaching start of incomplete batch
109
109
  // - here, when queue was empty and start of batch showed up (batchMetadata === true below).
110
110
  // 2. resumed when batch end comes in (batchMetadata === false below)
111
- if (batchMetadata) {
111
+ if (batchMetadata === true) {
112
112
  assert(this.currentBatchClientId === undefined, 0x29e /* "there can't be active batch" */);
113
113
  assert(!this.localPaused, 0x29f /* "we should be processing ops when there is no active batch" */);
114
114
  this.pauseSequenceNumber = message.sequenceNumber;
@@ -1 +1 @@
1
- {"version":3,"file":"inboundBatchAggregator.js","sourceRoot":"","sources":["../src/inboundBatchAggregator.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { performanceNow } from \"@fluid-internal/client-utils\";\nimport type { IDeltaManagerFull } from \"@fluidframework/container-definitions/internal\";\nimport { assert } from \"@fluidframework/core-utils/internal\";\nimport type { ISequencedDocumentMessage } from \"@fluidframework/driver-definitions/internal\";\nimport { isRuntimeMessage } from \"@fluidframework/driver-utils/internal\";\nimport {\n\ttype ITelemetryLoggerExt,\n\tDataCorruptionError,\n\tDataProcessingError,\n\textractSafePropertiesFromMessage,\n} from \"@fluidframework/telemetry-utils/internal\";\n\nimport type { IBatchMetadata } from \"./metadata.js\";\nimport { pkgVersion } from \"./packageVersion.js\";\n\ntype IRuntimeMessageMetadata =\n\t| undefined\n\t| {\n\t\t\tbatch?: boolean;\n\t };\n\n/**\n * This class ensures that we aggregate a complete batch of incoming ops before processing them. It basically ensures\n * that we never start processing ops in a batch IF we do not have all ops in the batch.\n */\nexport class InboundBatchAggregator {\n\tprivate pauseSequenceNumber: number | undefined;\n\tprivate currentBatchClientId: string | undefined;\n\tprivate localPaused = false;\n\tprivate timePaused = 0;\n\tprivate batchCount = 0;\n\n\tconstructor(\n\t\tprivate readonly deltaManager: IDeltaManagerFull,\n\t\tprivate readonly getClientId: () => string | undefined,\n\t\tprivate readonly logger: ITelemetryLoggerExt,\n\t) {\n\t\t// Listen for updates and peek at the inbound\n\t\tthis.deltaManager.inbound.on(\"push\", this.trackPending);\n\n\t\tconst allPending = this.deltaManager.inbound.toArray();\n\t\tfor (const pending of allPending) {\n\t\t\tthis.trackPending(pending);\n\t\t}\n\n\t\t// We are intentionally directly listening to the \"op\" to inspect system ops as well.\n\t\t// If we do not observe system ops, we are likely to hit an error when system ops\n\t\t// precedes start of incomplete batch.\n\t\tthis.deltaManager.on(\"op\", this.afterOpProcessing);\n\t}\n\n\tpublic dispose(): void {\n\t\tthis.deltaManager.off(\"op\", this.afterOpProcessing);\n\t\tthis.deltaManager.inbound.off(\"push\", this.trackPending);\n\t}\n\n\t/**\n\t * Callback for DeltaManager's \"op\" event, for us to make decision if op processing should\n\t * be paused or not after that.\n\t */\n\tprivate readonly afterOpProcessing = (message: ISequencedDocumentMessage): void => {\n\t\tassert(\n\t\t\t!this.localPaused,\n\t\t\t0x294 /* \"can't have op processing paused if we are processing an op\" */,\n\t\t);\n\n\t\t// If the inbound queue is ever empty, nothing to do!\n\t\tif (this.deltaManager.inbound.length === 0) {\n\t\t\tassert(\n\t\t\t\tthis.pauseSequenceNumber === undefined,\n\t\t\t\t0x295 /* \"there should be no pending batch if we have no ops\" */,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\t// The queue is\n\t\t// 1. paused only when the next message to be processed is the beginning of a batch. Done in two places:\n\t\t// - here (processing ops until reaching start of incomplete batch)\n\t\t// - in trackPending(), when queue was empty and start of batch showed up.\n\t\t// 2. resumed when batch end comes in (in trackPending())\n\n\t\t// do we have incomplete batch to worry about?\n\t\tif (this.pauseSequenceNumber !== undefined) {\n\t\t\tif (message.sequenceNumber >= this.pauseSequenceNumber) {\n\t\t\t\tthrow DataProcessingError.create(\n\t\t\t\t\t// Former assert 0x296\n\t\t\t\t\t\"Incomplete batch\",\n\t\t\t\t\t\"InboundBatchAggregator\",\n\t\t\t\t\tmessage,\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: message.type,\n\t\t\t\t\t\tcontentType: typeof message.contents,\n\t\t\t\t\t\tbatch: (message.metadata as IBatchMetadata | undefined)?.batch,\n\t\t\t\t\t\tcompression: message.compression,\n\t\t\t\t\t\tpauseSeqNum: this.pauseSequenceNumber,\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// If the next op is the start of incomplete batch, then we can't process it until it's fully in - pause!\n\t\t\tif (message.sequenceNumber + 1 === this.pauseSequenceNumber) {\n\t\t\t\tthis.pauseQueue();\n\t\t\t}\n\t\t}\n\t};\n\n\t/**\n\t * Called for each incoming op (i.e. inbound \"push\" notification)\n\t */\n\tprivate readonly trackPending = (message: ISequencedDocumentMessage): void => {\n\t\tassert(\n\t\t\tthis.deltaManager.inbound.length > 0,\n\t\t\t0x298 /* \"we have something in the queue that generates this event\" */,\n\t\t);\n\n\t\tassert(\n\t\t\t(this.currentBatchClientId === undefined) === (this.pauseSequenceNumber === undefined),\n\t\t\t0x299 /* \"non-synchronized state\" */,\n\t\t);\n\n\t\tconst metadata = message.metadata as IRuntimeMessageMetadata;\n\t\t// batchMetadata will be true for the message that starts a batch, false for the one that ends it, and\n\t\t// undefined for all other messages.\n\t\tconst batchMetadata = metadata?.batch;\n\n\t\t// Protocol messages are never part of a runtime batch of messages\n\t\tif (!isRuntimeMessage(message)) {\n\t\t\t// Protocol messages should never show up in the middle of the batch!\n\t\t\tif (this.currentBatchClientId !== undefined) {\n\t\t\t\tthrow DataProcessingError.create(\n\t\t\t\t\t\"Received a system message during batch processing\", // Formerly known as assert 0x29a\n\t\t\t\t\t\"trackPending\",\n\t\t\t\t\tmessage,\n\t\t\t\t\t{\n\t\t\t\t\t\truntimeVersion: pkgVersion,\n\t\t\t\t\t\tbatchClientId:\n\t\t\t\t\t\t\t// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing\n\t\t\t\t\t\t\tthis.currentBatchClientId === null ? \"null\" : this.currentBatchClientId,\n\t\t\t\t\t\tpauseSequenceNumber: this.pauseSequenceNumber,\n\t\t\t\t\t\tlocalBatch: this.currentBatchClientId === this.getClientId(),\n\t\t\t\t\t\tmessageType: message.type,\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tassert(batchMetadata === undefined, 0x29b /* \"system op in a batch?\" */);\n\t\t\tassert(\n\t\t\t\t!this.localPaused,\n\t\t\t\t0x29c /* \"we should be processing ops when there is no active batch\" */,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\tif (this.currentBatchClientId === undefined && batchMetadata === undefined) {\n\t\t\tassert(\n\t\t\t\t!this.localPaused,\n\t\t\t\t0x29d /* \"we should be processing ops when there is no active batch\" */,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\t// If we got here, the message is part of a batch. Either starting, in progress, or ending.\n\n\t\t// If this is not the start of the batch, error out if the message was sent by a client other than the one that\n\t\t// started the current batch (it should not be possible for ops from other clients to get interleaved with a batch).\n\t\tif (\n\t\t\tthis.currentBatchClientId !== undefined &&\n\t\t\tthis.currentBatchClientId !== message.clientId\n\t\t) {\n\t\t\tthrow new DataCorruptionError(\"OpBatchIncomplete\", {\n\t\t\t\truntimeVersion: pkgVersion,\n\t\t\t\tbatchClientId:\n\t\t\t\t\t// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing\n\t\t\t\t\tthis.currentBatchClientId === null ? \"null\" : this.currentBatchClientId,\n\t\t\t\tpauseSequenceNumber: this.pauseSequenceNumber,\n\t\t\t\tlocalBatch: this.currentBatchClientId === this.getClientId(),\n\t\t\t\tlocalMessage: message.clientId === this.getClientId(),\n\t\t\t\t...extractSafePropertiesFromMessage(message),\n\t\t\t});\n\t\t}\n\n\t\t// The queue is\n\t\t// 1. paused only when the next message to be processed is the beginning of a batch. Done in two places:\n\t\t// - in afterOpProcessing() - processing ops until reaching start of incomplete batch\n\t\t// - here, when queue was empty and start of batch showed up (batchMetadata === true below).\n\t\t// 2. resumed when batch end comes in (batchMetadata === false below)\n\n\t\tif (batchMetadata) {\n\t\t\tassert(\n\t\t\t\tthis.currentBatchClientId === undefined,\n\t\t\t\t0x29e /* \"there can't be active batch\" */,\n\t\t\t);\n\t\t\tassert(\n\t\t\t\t!this.localPaused,\n\t\t\t\t0x29f /* \"we should be processing ops when there is no active batch\" */,\n\t\t\t);\n\t\t\tthis.pauseSequenceNumber = message.sequenceNumber;\n\t\t\t// TODO: Verify whether this should be able to handle server-generated ops (with null clientId)\n\n\t\t\tthis.currentBatchClientId = message.clientId as string;\n\t\t\t// Start of the batch\n\t\t\t// Only pause processing if queue has no other ops!\n\t\t\t// If there are any other ops in the queue, processing will be stopped when they are processed!\n\t\t\tif (this.deltaManager.inbound.length === 1) {\n\t\t\t\tthis.pauseQueue();\n\t\t\t}\n\t\t} else if (batchMetadata === false) {\n\t\t\tassert(\n\t\t\t\tthis.pauseSequenceNumber !== undefined,\n\t\t\t\t0x2a0 /* \"batch presence was validated above\" */,\n\t\t\t);\n\t\t\t// Batch is complete, we can process it!\n\t\t\tthis.resumeQueue(this.pauseSequenceNumber, message);\n\t\t\tthis.pauseSequenceNumber = undefined;\n\t\t\tthis.currentBatchClientId = undefined;\n\t\t} else {\n\t\t\t// Continuation of current batch. Do nothing\n\t\t\tassert(this.currentBatchClientId !== undefined, 0x2a1 /* \"logic error\" */);\n\t\t}\n\t};\n\n\tprivate pauseQueue(): void {\n\t\tassert(!this.localPaused, 0x297 /* \"always called from resumed state\" */);\n\t\tthis.localPaused = true;\n\t\tthis.timePaused = performanceNow();\n\t\t// eslint-disable-next-line @typescript-eslint/no-floating-promises\n\t\tthis.deltaManager.inbound.pause();\n\t}\n\n\tprivate resumeQueue(startBatch: number, messageEndBatch: ISequencedDocumentMessage): void {\n\t\tconst endBatch = messageEndBatch.sequenceNumber;\n\t\tconst duration = this.localPaused ? performanceNow() - this.timePaused : undefined;\n\n\t\tthis.batchCount++;\n\t\tif (this.batchCount % 1000 === 1) {\n\t\t\tthis.logger.sendTelemetryEvent({\n\t\t\t\teventName: \"BatchStats\",\n\t\t\t\tsequenceNumber: endBatch,\n\t\t\t\tlength: endBatch - startBatch + 1,\n\t\t\t\tmsnDistance: endBatch - messageEndBatch.minimumSequenceNumber,\n\t\t\t\tduration,\n\t\t\t\tbatchCount: this.batchCount,\n\t\t\t\tinterrupted: this.localPaused,\n\t\t\t});\n\t\t}\n\n\t\t// Return early if no change in value\n\t\tif (!this.localPaused) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis.localPaused = false;\n\n\t\tthis.deltaManager.inbound.resume();\n\t}\n}\n"]}
1
+ {"version":3,"file":"inboundBatchAggregator.js","sourceRoot":"","sources":["../src/inboundBatchAggregator.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { performanceNow } from \"@fluid-internal/client-utils\";\nimport type { IDeltaManagerFull } from \"@fluidframework/container-definitions/internal\";\nimport { assert } from \"@fluidframework/core-utils/internal\";\nimport type { ISequencedDocumentMessage } from \"@fluidframework/driver-definitions/internal\";\nimport { isRuntimeMessage } from \"@fluidframework/driver-utils/internal\";\nimport {\n\ttype ITelemetryLoggerExt,\n\tDataCorruptionError,\n\tDataProcessingError,\n\textractSafePropertiesFromMessage,\n} from \"@fluidframework/telemetry-utils/internal\";\n\nimport type { IBatchMetadata } from \"./metadata.js\";\nimport { pkgVersion } from \"./packageVersion.js\";\n\ntype IRuntimeMessageMetadata =\n\t| undefined\n\t| {\n\t\t\tbatch?: boolean;\n\t };\n\n/**\n * This class ensures that we aggregate a complete batch of incoming ops before processing them. It basically ensures\n * that we never start processing ops in a batch IF we do not have all ops in the batch.\n */\nexport class InboundBatchAggregator {\n\tprivate pauseSequenceNumber: number | undefined;\n\tprivate currentBatchClientId: string | undefined;\n\tprivate localPaused = false;\n\tprivate timePaused = 0;\n\tprivate batchCount = 0;\n\n\tconstructor(\n\t\tprivate readonly deltaManager: IDeltaManagerFull,\n\t\tprivate readonly getClientId: () => string | undefined,\n\t\tprivate readonly logger: ITelemetryLoggerExt,\n\t) {\n\t\t// Listen for updates and peek at the inbound\n\t\tthis.deltaManager.inbound.on(\"push\", this.trackPending);\n\n\t\tconst allPending = this.deltaManager.inbound.toArray();\n\t\tfor (const pending of allPending) {\n\t\t\tthis.trackPending(pending);\n\t\t}\n\n\t\t// We are intentionally directly listening to the \"op\" to inspect system ops as well.\n\t\t// If we do not observe system ops, we are likely to hit an error when system ops\n\t\t// precedes start of incomplete batch.\n\t\tthis.deltaManager.on(\"op\", this.afterOpProcessing);\n\t}\n\n\tpublic dispose(): void {\n\t\tthis.deltaManager.off(\"op\", this.afterOpProcessing);\n\t\tthis.deltaManager.inbound.off(\"push\", this.trackPending);\n\t}\n\n\t/**\n\t * Callback for DeltaManager's \"op\" event, for us to make decision if op processing should\n\t * be paused or not after that.\n\t */\n\tprivate readonly afterOpProcessing = (message: ISequencedDocumentMessage): void => {\n\t\tassert(\n\t\t\t!this.localPaused,\n\t\t\t0x294 /* \"can't have op processing paused if we are processing an op\" */,\n\t\t);\n\n\t\t// If the inbound queue is ever empty, nothing to do!\n\t\tif (this.deltaManager.inbound.length === 0) {\n\t\t\tassert(\n\t\t\t\tthis.pauseSequenceNumber === undefined,\n\t\t\t\t0x295 /* \"there should be no pending batch if we have no ops\" */,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\t// The queue is\n\t\t// 1. paused only when the next message to be processed is the beginning of a batch. Done in two places:\n\t\t// - here (processing ops until reaching start of incomplete batch)\n\t\t// - in trackPending(), when queue was empty and start of batch showed up.\n\t\t// 2. resumed when batch end comes in (in trackPending())\n\n\t\t// do we have incomplete batch to worry about?\n\t\tif (this.pauseSequenceNumber !== undefined) {\n\t\t\tif (message.sequenceNumber >= this.pauseSequenceNumber) {\n\t\t\t\tthrow DataProcessingError.create(\n\t\t\t\t\t// Former assert 0x296\n\t\t\t\t\t\"Incomplete batch\",\n\t\t\t\t\t\"InboundBatchAggregator\",\n\t\t\t\t\tmessage,\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: message.type,\n\t\t\t\t\t\tcontentType: typeof message.contents,\n\t\t\t\t\t\tbatch: (message.metadata as IBatchMetadata | undefined)?.batch,\n\t\t\t\t\t\tcompression: message.compression,\n\t\t\t\t\t\tpauseSeqNum: this.pauseSequenceNumber,\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// If the next op is the start of incomplete batch, then we can't process it until it's fully in - pause!\n\t\t\tif (message.sequenceNumber + 1 === this.pauseSequenceNumber) {\n\t\t\t\tthis.pauseQueue();\n\t\t\t}\n\t\t}\n\t};\n\n\t/**\n\t * Called for each incoming op (i.e. inbound \"push\" notification)\n\t */\n\tprivate readonly trackPending = (message: ISequencedDocumentMessage): void => {\n\t\tassert(\n\t\t\tthis.deltaManager.inbound.length > 0,\n\t\t\t0x298 /* \"we have something in the queue that generates this event\" */,\n\t\t);\n\n\t\tassert(\n\t\t\t(this.currentBatchClientId === undefined) === (this.pauseSequenceNumber === undefined),\n\t\t\t0x299 /* \"non-synchronized state\" */,\n\t\t);\n\n\t\tconst metadata = message.metadata as IRuntimeMessageMetadata;\n\t\t// batchMetadata will be true for the message that starts a batch, false for the one that ends it, and\n\t\t// undefined for all other messages.\n\t\tconst batchMetadata = metadata?.batch;\n\n\t\t// Protocol messages are never part of a runtime batch of messages\n\t\tif (!isRuntimeMessage(message)) {\n\t\t\t// Protocol messages should never show up in the middle of the batch!\n\t\t\tif (this.currentBatchClientId !== undefined) {\n\t\t\t\tthrow DataProcessingError.create(\n\t\t\t\t\t\"Received a system message during batch processing\", // Formerly known as assert 0x29a\n\t\t\t\t\t\"trackPending\",\n\t\t\t\t\tmessage,\n\t\t\t\t\t{\n\t\t\t\t\t\truntimeVersion: pkgVersion,\n\t\t\t\t\t\tbatchClientId:\n\t\t\t\t\t\t\t// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing\n\t\t\t\t\t\t\tthis.currentBatchClientId === null ? \"null\" : this.currentBatchClientId,\n\t\t\t\t\t\tpauseSequenceNumber: this.pauseSequenceNumber,\n\t\t\t\t\t\tlocalBatch: this.currentBatchClientId === this.getClientId(),\n\t\t\t\t\t\tmessageType: message.type,\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tassert(batchMetadata === undefined, 0x29b /* \"system op in a batch?\" */);\n\t\t\tassert(\n\t\t\t\t!this.localPaused,\n\t\t\t\t0x29c /* \"we should be processing ops when there is no active batch\" */,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\tif (this.currentBatchClientId === undefined && batchMetadata === undefined) {\n\t\t\tassert(\n\t\t\t\t!this.localPaused,\n\t\t\t\t0x29d /* \"we should be processing ops when there is no active batch\" */,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\t// If we got here, the message is part of a batch. Either starting, in progress, or ending.\n\n\t\t// If this is not the start of the batch, error out if the message was sent by a client other than the one that\n\t\t// started the current batch (it should not be possible for ops from other clients to get interleaved with a batch).\n\t\tif (\n\t\t\tthis.currentBatchClientId !== undefined &&\n\t\t\tthis.currentBatchClientId !== message.clientId\n\t\t) {\n\t\t\tthrow new DataCorruptionError(\"OpBatchIncomplete\", {\n\t\t\t\truntimeVersion: pkgVersion,\n\t\t\t\tbatchClientId:\n\t\t\t\t\t// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing\n\t\t\t\t\tthis.currentBatchClientId === null ? \"null\" : this.currentBatchClientId,\n\t\t\t\tpauseSequenceNumber: this.pauseSequenceNumber,\n\t\t\t\tlocalBatch: this.currentBatchClientId === this.getClientId(),\n\t\t\t\tlocalMessage: message.clientId === this.getClientId(),\n\t\t\t\t...extractSafePropertiesFromMessage(message),\n\t\t\t});\n\t\t}\n\n\t\t// The queue is\n\t\t// 1. paused only when the next message to be processed is the beginning of a batch. Done in two places:\n\t\t// - in afterOpProcessing() - processing ops until reaching start of incomplete batch\n\t\t// - here, when queue was empty and start of batch showed up (batchMetadata === true below).\n\t\t// 2. resumed when batch end comes in (batchMetadata === false below)\n\n\t\tif (batchMetadata === true) {\n\t\t\tassert(\n\t\t\t\tthis.currentBatchClientId === undefined,\n\t\t\t\t0x29e /* \"there can't be active batch\" */,\n\t\t\t);\n\t\t\tassert(\n\t\t\t\t!this.localPaused,\n\t\t\t\t0x29f /* \"we should be processing ops when there is no active batch\" */,\n\t\t\t);\n\t\t\tthis.pauseSequenceNumber = message.sequenceNumber;\n\t\t\t// TODO: Verify whether this should be able to handle server-generated ops (with null clientId)\n\n\t\t\tthis.currentBatchClientId = message.clientId as string;\n\t\t\t// Start of the batch\n\t\t\t// Only pause processing if queue has no other ops!\n\t\t\t// If there are any other ops in the queue, processing will be stopped when they are processed!\n\t\t\tif (this.deltaManager.inbound.length === 1) {\n\t\t\t\tthis.pauseQueue();\n\t\t\t}\n\t\t} else if (batchMetadata === false) {\n\t\t\tassert(\n\t\t\t\tthis.pauseSequenceNumber !== undefined,\n\t\t\t\t0x2a0 /* \"batch presence was validated above\" */,\n\t\t\t);\n\t\t\t// Batch is complete, we can process it!\n\t\t\tthis.resumeQueue(this.pauseSequenceNumber, message);\n\t\t\tthis.pauseSequenceNumber = undefined;\n\t\t\tthis.currentBatchClientId = undefined;\n\t\t} else {\n\t\t\t// Continuation of current batch. Do nothing\n\t\t\tassert(this.currentBatchClientId !== undefined, 0x2a1 /* \"logic error\" */);\n\t\t}\n\t};\n\n\tprivate pauseQueue(): void {\n\t\tassert(!this.localPaused, 0x297 /* \"always called from resumed state\" */);\n\t\tthis.localPaused = true;\n\t\tthis.timePaused = performanceNow();\n\t\t// eslint-disable-next-line @typescript-eslint/no-floating-promises\n\t\tthis.deltaManager.inbound.pause();\n\t}\n\n\tprivate resumeQueue(startBatch: number, messageEndBatch: ISequencedDocumentMessage): void {\n\t\tconst endBatch = messageEndBatch.sequenceNumber;\n\t\tconst duration = this.localPaused ? performanceNow() - this.timePaused : undefined;\n\n\t\tthis.batchCount++;\n\t\tif (this.batchCount % 1000 === 1) {\n\t\t\tthis.logger.sendTelemetryEvent({\n\t\t\t\teventName: \"BatchStats\",\n\t\t\t\tsequenceNumber: endBatch,\n\t\t\t\tlength: endBatch - startBatch + 1,\n\t\t\t\tmsnDistance: endBatch - messageEndBatch.minimumSequenceNumber,\n\t\t\t\tduration,\n\t\t\t\tbatchCount: this.batchCount,\n\t\t\t\tinterrupted: this.localPaused,\n\t\t\t});\n\t\t}\n\n\t\t// Return early if no change in value\n\t\tif (!this.localPaused) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis.localPaused = false;\n\n\t\tthis.deltaManager.inbound.resume();\n\t}\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"metadata.d.ts","sourceRoot":"","sources":["../src/metadata.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AAEtD;;GAEG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,OAAO,GAAG,OAAO,CAAC,cAAc,CAAC,GAAG,SAAS,CAEtF;AAED;;GAEG;AACH,wBAAgB,2BAA2B,CAC1C,eAAe,EAAE,OAAO,GACtB,mBAAmB,GAAG,SAAS,CAEjC;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAEnC,UAAU,CAAC,EAAE,IAAI,CAAC;CAClB;AACD;;GAEG;AACH,MAAM,WAAW,cAAc;IAC9B;;OAEG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB;;OAEG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CAChB;AAED,eAAO,MAAM,cAAc,aAAc,OAAO,8BAM/C,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAChC,OAAO,CAAC,EAAE,OAAO,CAAC;CAClB"}
1
+ {"version":3,"file":"metadata.d.ts","sourceRoot":"","sources":["../src/metadata.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AAEtD;;GAEG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,OAAO,GAAG,OAAO,CAAC,cAAc,CAAC,GAAG,SAAS,CAEtF;AAED;;GAEG;AACH,wBAAgB,2BAA2B,CAC1C,eAAe,EAAE,OAAO,GACtB,mBAAmB,GAAG,SAAS,CAEjC;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAEnC,UAAU,CAAC,EAAE,IAAI,CAAC;CAClB;AACD;;GAEG;AACH,MAAM,WAAW,cAAc;IAC9B;;OAEG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB;;OAEG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CAChB;AAED,eAAO,MAAM,cAAc,aAAc,OAAO,8BAO/C,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAChC,OAAO,CAAC,EAAE,OAAO,CAAC;CAClB"}
package/lib/metadata.js CHANGED
@@ -15,7 +15,8 @@ export function asEmptyBatchLocalOpMetadata(localOpMetadata) {
15
15
  return localOpMetadata;
16
16
  }
17
17
  export const isBlobMetadata = (metadata) => {
18
- return (!!metadata &&
18
+ return (typeof metadata === "object" &&
19
+ metadata !== null &&
19
20
  typeof metadata.blobId === "string" &&
20
21
  typeof metadata.localId === "string");
21
22
  };
@@ -1 +1 @@
1
- {"version":3,"file":"metadata.js","sourceRoot":"","sources":["../src/metadata.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,QAAiB;IAChD,OAAO,QAA+C,CAAC;AACxD,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,2BAA2B,CAC1C,eAAwB;IAExB,OAAO,eAAkD,CAAC;AAC3D,CAAC;AA+BD,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,QAAiB,EAA6B,EAAE;IAC9E,OAAO,CACN,CAAC,CAAC,QAAQ;QACV,OAAQ,QAA0B,CAAC,MAAM,KAAK,QAAQ;QACtD,OAAQ,QAA0B,CAAC,OAAO,KAAK,QAAQ,CACvD,CAAC;AACH,CAAC,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport type { BatchId } from \"./opLifecycle/index.js\";\n\n/**\n * Syntactic sugar for casting\n */\nexport function asBatchMetadata(metadata: unknown): Partial<IBatchMetadata> | undefined {\n\treturn metadata as Partial<IBatchMetadata> | undefined;\n}\n\n/**\n * Syntactic sugar for casting\n */\nexport function asEmptyBatchLocalOpMetadata(\n\tlocalOpMetadata: unknown,\n): IEmptyBatchMetadata | undefined {\n\treturn localOpMetadata as IEmptyBatchMetadata | undefined;\n}\n\n/**\n * Properties put on the localOpMetadata object for empty batches\n */\nexport interface IEmptyBatchMetadata {\n\t// Set to true on localOpMetadata for empty batches\n\temptyBatch?: true;\n}\n/**\n * Properties put on the op metadata object for batch tracking\n */\nexport interface IBatchMetadata {\n\t/**\n\t * Set on first/last messages of a multi-message batch, to true/false respectively\n\t */\n\tbatch?: boolean;\n\t/**\n\t * Maybe set on first message of a batch, to the batchId generated when resubmitting (set/fixed on first resubmit)\n\t */\n\tbatchId?: BatchId;\n}\n\n/**\n * Blob handling makes assumptions about what might be on the metadata. This interface codifies those assumptions, but does not validate them.\n */\nexport interface IBlobMetadata {\n\tblobId: string;\n\tlocalId: string;\n}\n\nexport const isBlobMetadata = (metadata: unknown): metadata is IBlobMetadata => {\n\treturn (\n\t\t!!metadata &&\n\t\ttypeof (metadata as IBlobMetadata).blobId === \"string\" &&\n\t\ttypeof (metadata as IBlobMetadata).localId === \"string\"\n\t);\n};\n\n/**\n * ContainerRuntime needs to know if this is a replayed savedOp as those need to be skipped in stashed ops scenarios.\n */\nexport interface ISavedOpMetadata {\n\tsavedOp?: boolean;\n}\n"]}
1
+ {"version":3,"file":"metadata.js","sourceRoot":"","sources":["../src/metadata.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,QAAiB;IAChD,OAAO,QAA+C,CAAC;AACxD,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,2BAA2B,CAC1C,eAAwB;IAExB,OAAO,eAAkD,CAAC;AAC3D,CAAC;AA+BD,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,QAAiB,EAA6B,EAAE;IAC9E,OAAO,CACN,OAAO,QAAQ,KAAK,QAAQ;QAC5B,QAAQ,KAAK,IAAI;QACjB,OAAQ,QAA0B,CAAC,MAAM,KAAK,QAAQ;QACtD,OAAQ,QAA0B,CAAC,OAAO,KAAK,QAAQ,CACvD,CAAC;AACH,CAAC,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport type { BatchId } from \"./opLifecycle/index.js\";\n\n/**\n * Syntactic sugar for casting\n */\nexport function asBatchMetadata(metadata: unknown): Partial<IBatchMetadata> | undefined {\n\treturn metadata as Partial<IBatchMetadata> | undefined;\n}\n\n/**\n * Syntactic sugar for casting\n */\nexport function asEmptyBatchLocalOpMetadata(\n\tlocalOpMetadata: unknown,\n): IEmptyBatchMetadata | undefined {\n\treturn localOpMetadata as IEmptyBatchMetadata | undefined;\n}\n\n/**\n * Properties put on the localOpMetadata object for empty batches\n */\nexport interface IEmptyBatchMetadata {\n\t// Set to true on localOpMetadata for empty batches\n\temptyBatch?: true;\n}\n/**\n * Properties put on the op metadata object for batch tracking\n */\nexport interface IBatchMetadata {\n\t/**\n\t * Set on first/last messages of a multi-message batch, to true/false respectively\n\t */\n\tbatch?: boolean;\n\t/**\n\t * Maybe set on first message of a batch, to the batchId generated when resubmitting (set/fixed on first resubmit)\n\t */\n\tbatchId?: BatchId;\n}\n\n/**\n * Blob handling makes assumptions about what might be on the metadata. This interface codifies those assumptions, but does not validate them.\n */\nexport interface IBlobMetadata {\n\tblobId: string;\n\tlocalId: string;\n}\n\nexport const isBlobMetadata = (metadata: unknown): metadata is IBlobMetadata => {\n\treturn (\n\t\ttypeof metadata === \"object\" &&\n\t\tmetadata !== null &&\n\t\ttypeof (metadata as IBlobMetadata).blobId === \"string\" &&\n\t\ttypeof (metadata as IBlobMetadata).localId === \"string\"\n\t);\n};\n\n/**\n * ContainerRuntime needs to know if this is a replayed savedOp as those need to be skipped in stashed ops scenarios.\n */\nexport interface ISavedOpMetadata {\n\tsavedOp?: boolean;\n}\n"]}
@@ -69,9 +69,9 @@ export class OpGroupingManager {
69
69
  // We expect this will be on the first message, if present at all.
70
70
  let groupedBatchId;
71
71
  for (const message of batch.messages) {
72
- if (message.metadata) {
72
+ if (message.metadata !== undefined) {
73
73
  const { batch: _batch, batchId, ...rest } = message.metadata;
74
- if (batchId) {
74
+ if (batchId !== undefined) {
75
75
  groupedBatchId = batchId;
76
76
  }
77
77
  assert(Object.keys(rest).length === 0, 0x5dd /* cannot group ops with metadata */);
@@ -1 +1 @@
1
- {"version":3,"file":"opGroupingManager.js","sourceRoot":"","sources":["../../src/opLifecycle/opGroupingManager.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport type { ITelemetryBaseLogger } from \"@fluidframework/core-interfaces\";\nimport { assert } from \"@fluidframework/core-utils/internal\";\nimport type { ISequencedDocumentMessage } from \"@fluidframework/driver-definitions/internal\";\nimport {\n\tcreateChildLogger,\n\ttype ITelemetryLoggerExt,\n} from \"@fluidframework/telemetry-utils/internal\";\n\nimport type {\n\tLocalEmptyBatchPlaceholder,\n\tOutboundBatch,\n\tOutboundSingletonBatch,\n} from \"./definitions.js\";\n\n/**\n * Grouping makes assumptions about the shape of message contents. This interface codifies those assumptions, but does not validate them.\n */\ninterface IGroupedBatchMessageContents {\n\ttype: typeof OpGroupingManager.groupedBatchOp;\n\tcontents: IGroupedMessage[];\n}\n\ninterface IGroupedMessage {\n\tcontents?: unknown;\n\tmetadata?: Record<string, unknown>;\n\tcompression?: string;\n}\n\nfunction isGroupContents(opContents: unknown): opContents is IGroupedBatchMessageContents {\n\treturn (\n\t\t(opContents as Partial<IGroupedBatchMessageContents>)?.type ===\n\t\tOpGroupingManager.groupedBatchOp\n\t);\n}\n\nexport function isGroupedBatch(op: ISequencedDocumentMessage): boolean {\n\treturn isGroupContents(op.contents);\n}\n\nexport interface OpGroupingManagerConfig {\n\treadonly groupedBatchingEnabled: boolean;\n}\n\n/**\n * This is the type of an empty grouped batch we send over the wire\n * We also put this in the placeholder for an empty batch in the PendingStateManager.\n * But most places throughout the ContainerRuntime, this will not be used (just as Grouped Batches in general don't appear outside opLifecycle dir)\n */\nexport interface EmptyGroupedBatch {\n\ttype: typeof OpGroupingManager.groupedBatchOp;\n\tcontents: readonly [];\n}\n\nexport class OpGroupingManager {\n\tstatic readonly groupedBatchOp = \"groupedBatch\";\n\tprivate readonly logger: ITelemetryLoggerExt;\n\n\tconstructor(\n\t\tprivate readonly config: OpGroupingManagerConfig,\n\t\tlogger: ITelemetryBaseLogger,\n\t) {\n\t\tthis.logger = createChildLogger({ logger, namespace: \"OpGroupingManager\" });\n\t}\n\n\t/**\n\t * Creates a new batch with a single message of type \"groupedBatch\" and empty contents.\n\t * This is needed as a placeholder if a batch becomes empty on resubmit, but we are tracking batch IDs.\n\t * @param resubmittingBatchId - batch ID of the resubmitting batch\n\t * @param referenceSequenceNumber - reference sequence number\n\t * @returns - The outbound batch as well as the interior placeholder message\n\t */\n\tpublic createEmptyGroupedBatch(\n\t\tresubmittingBatchId: string,\n\t\treferenceSequenceNumber: number,\n\t): {\n\t\toutboundBatch: OutboundSingletonBatch;\n\t\tplaceholderMessage: LocalEmptyBatchPlaceholder;\n\t} {\n\t\tassert(\n\t\t\tthis.config.groupedBatchingEnabled,\n\t\t\t0xa00 /* cannot create empty grouped batch when grouped batching is disabled */,\n\t\t);\n\n\t\tconst emptyGroupedBatch: EmptyGroupedBatch = {\n\t\t\ttype: \"groupedBatch\",\n\t\t\tcontents: [],\n\t\t};\n\t\tconst serializedOp = JSON.stringify(emptyGroupedBatch);\n\n\t\tconst placeholderMessage: LocalEmptyBatchPlaceholder = {\n\t\t\tmetadata: { batchId: resubmittingBatchId },\n\t\t\tlocalOpMetadata: { emptyBatch: true },\n\t\t\treferenceSequenceNumber,\n\t\t\truntimeOp: emptyGroupedBatch,\n\t\t};\n\t\tconst outboundBatch: OutboundSingletonBatch = {\n\t\t\tcontentSizeInBytes: 0,\n\t\t\tmessages: [{ ...placeholderMessage, runtimeOp: undefined, contents: serializedOp }],\n\t\t\treferenceSequenceNumber,\n\t\t};\n\t\treturn { outboundBatch, placeholderMessage };\n\t}\n\n\t/**\n\t * Converts the given batch into a \"grouped batch\" - a batch with a single message of type \"groupedBatch\",\n\t * with contents being an array of the original batch's messages.\n\t *\n\t * If the batch already has only 1 message, it is returned as-is.\n\t *\n\t * @remarks - Remember that a BatchMessage has its content JSON serialized, so the incoming batch message contents\n\t * must be parsed first, and then the type and contents mentioned above are hidden in that JSON serialization.\n\t */\n\tpublic groupBatch(batch: OutboundBatch): OutboundSingletonBatch {\n\t\tassert(this.groupedBatchingEnabled(), 0xb79 /* grouping disabled! */);\n\t\tassert(batch.messages.length > 0, 0xb7a /* Unexpected attempt to group an empty batch */);\n\n\t\tif (batch.messages.length === 1) {\n\t\t\treturn batch as OutboundSingletonBatch;\n\t\t}\n\n\t\tif (batch.messages.length >= 1000) {\n\t\t\tthis.logger.sendTelemetryEvent({\n\t\t\t\teventName: \"GroupLargeBatch\",\n\t\t\t\tlength: batch.messages.length,\n\t\t\t\treentrant: batch.hasReentrantOps,\n\t\t\t\treferenceSequenceNumber: batch.messages[0].referenceSequenceNumber,\n\t\t\t});\n\t\t}\n\t\t// We expect this will be on the first message, if present at all.\n\t\tlet groupedBatchId;\n\t\tfor (const message of batch.messages) {\n\t\t\tif (message.metadata) {\n\t\t\t\tconst { batch: _batch, batchId, ...rest } = message.metadata;\n\t\t\t\tif (batchId) {\n\t\t\t\t\tgroupedBatchId = batchId;\n\t\t\t\t}\n\t\t\t\tassert(Object.keys(rest).length === 0, 0x5dd /* cannot group ops with metadata */);\n\t\t\t}\n\t\t}\n\n\t\tconst serializedContent = JSON.stringify({\n\t\t\ttype: OpGroupingManager.groupedBatchOp,\n\t\t\tcontents: batch.messages.map<IGroupedMessage>((message) => ({\n\t\t\t\tcontents: message.contents === undefined ? undefined : JSON.parse(message.contents),\n\t\t\t\tmetadata: message.metadata,\n\t\t\t\tcompression: message.compression,\n\t\t\t})),\n\t\t});\n\n\t\tconst groupedBatch: OutboundSingletonBatch = {\n\t\t\t...batch,\n\t\t\tmessages: [\n\t\t\t\t{\n\t\t\t\t\tmetadata: { batchId: groupedBatchId },\n\t\t\t\t\treferenceSequenceNumber: batch.messages[0].referenceSequenceNumber,\n\t\t\t\t\tcontents: serializedContent,\n\t\t\t\t},\n\t\t\t],\n\t\t};\n\t\treturn groupedBatch;\n\t}\n\n\tpublic ungroupOp(op: ISequencedDocumentMessage): ISequencedDocumentMessage[] {\n\t\tassert(isGroupContents(op.contents), 0x947 /* can only ungroup a grouped batch */);\n\t\tconst contents: IGroupedBatchMessageContents = op.contents;\n\n\t\tlet fakeCsn = 1;\n\t\treturn contents.contents.map((subMessage) => ({\n\t\t\t...op,\n\t\t\tclientSequenceNumber: fakeCsn++,\n\t\t\tcontents: subMessage.contents,\n\t\t\tmetadata: subMessage.metadata,\n\t\t\tcompression: subMessage.compression,\n\t\t}));\n\t}\n\n\tpublic groupedBatchingEnabled(): boolean {\n\t\treturn this.config.groupedBatchingEnabled;\n\t}\n}\n"]}
1
+ {"version":3,"file":"opGroupingManager.js","sourceRoot":"","sources":["../../src/opLifecycle/opGroupingManager.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport type { ITelemetryBaseLogger } from \"@fluidframework/core-interfaces\";\nimport { assert } from \"@fluidframework/core-utils/internal\";\nimport type { ISequencedDocumentMessage } from \"@fluidframework/driver-definitions/internal\";\nimport {\n\tcreateChildLogger,\n\ttype ITelemetryLoggerExt,\n} from \"@fluidframework/telemetry-utils/internal\";\n\nimport type {\n\tLocalEmptyBatchPlaceholder,\n\tOutboundBatch,\n\tOutboundSingletonBatch,\n} from \"./definitions.js\";\n\n/**\n * Grouping makes assumptions about the shape of message contents. This interface codifies those assumptions, but does not validate them.\n */\ninterface IGroupedBatchMessageContents {\n\ttype: typeof OpGroupingManager.groupedBatchOp;\n\tcontents: IGroupedMessage[];\n}\n\ninterface IGroupedMessage {\n\tcontents?: unknown;\n\tmetadata?: Record<string, unknown>;\n\tcompression?: string;\n}\n\nfunction isGroupContents(opContents: unknown): opContents is IGroupedBatchMessageContents {\n\treturn (\n\t\t(opContents as Partial<IGroupedBatchMessageContents>)?.type ===\n\t\tOpGroupingManager.groupedBatchOp\n\t);\n}\n\nexport function isGroupedBatch(op: ISequencedDocumentMessage): boolean {\n\treturn isGroupContents(op.contents);\n}\n\nexport interface OpGroupingManagerConfig {\n\treadonly groupedBatchingEnabled: boolean;\n}\n\n/**\n * This is the type of an empty grouped batch we send over the wire\n * We also put this in the placeholder for an empty batch in the PendingStateManager.\n * But most places throughout the ContainerRuntime, this will not be used (just as Grouped Batches in general don't appear outside opLifecycle dir)\n */\nexport interface EmptyGroupedBatch {\n\ttype: typeof OpGroupingManager.groupedBatchOp;\n\tcontents: readonly [];\n}\n\nexport class OpGroupingManager {\n\tstatic readonly groupedBatchOp = \"groupedBatch\";\n\tprivate readonly logger: ITelemetryLoggerExt;\n\n\tconstructor(\n\t\tprivate readonly config: OpGroupingManagerConfig,\n\t\tlogger: ITelemetryBaseLogger,\n\t) {\n\t\tthis.logger = createChildLogger({ logger, namespace: \"OpGroupingManager\" });\n\t}\n\n\t/**\n\t * Creates a new batch with a single message of type \"groupedBatch\" and empty contents.\n\t * This is needed as a placeholder if a batch becomes empty on resubmit, but we are tracking batch IDs.\n\t * @param resubmittingBatchId - batch ID of the resubmitting batch\n\t * @param referenceSequenceNumber - reference sequence number\n\t * @returns - The outbound batch as well as the interior placeholder message\n\t */\n\tpublic createEmptyGroupedBatch(\n\t\tresubmittingBatchId: string,\n\t\treferenceSequenceNumber: number,\n\t): {\n\t\toutboundBatch: OutboundSingletonBatch;\n\t\tplaceholderMessage: LocalEmptyBatchPlaceholder;\n\t} {\n\t\tassert(\n\t\t\tthis.config.groupedBatchingEnabled,\n\t\t\t0xa00 /* cannot create empty grouped batch when grouped batching is disabled */,\n\t\t);\n\n\t\tconst emptyGroupedBatch: EmptyGroupedBatch = {\n\t\t\ttype: \"groupedBatch\",\n\t\t\tcontents: [],\n\t\t};\n\t\tconst serializedOp = JSON.stringify(emptyGroupedBatch);\n\n\t\tconst placeholderMessage: LocalEmptyBatchPlaceholder = {\n\t\t\tmetadata: { batchId: resubmittingBatchId },\n\t\t\tlocalOpMetadata: { emptyBatch: true },\n\t\t\treferenceSequenceNumber,\n\t\t\truntimeOp: emptyGroupedBatch,\n\t\t};\n\t\tconst outboundBatch: OutboundSingletonBatch = {\n\t\t\tcontentSizeInBytes: 0,\n\t\t\tmessages: [{ ...placeholderMessage, runtimeOp: undefined, contents: serializedOp }],\n\t\t\treferenceSequenceNumber,\n\t\t};\n\t\treturn { outboundBatch, placeholderMessage };\n\t}\n\n\t/**\n\t * Converts the given batch into a \"grouped batch\" - a batch with a single message of type \"groupedBatch\",\n\t * with contents being an array of the original batch's messages.\n\t *\n\t * If the batch already has only 1 message, it is returned as-is.\n\t *\n\t * @remarks - Remember that a BatchMessage has its content JSON serialized, so the incoming batch message contents\n\t * must be parsed first, and then the type and contents mentioned above are hidden in that JSON serialization.\n\t */\n\tpublic groupBatch(batch: OutboundBatch): OutboundSingletonBatch {\n\t\tassert(this.groupedBatchingEnabled(), 0xb79 /* grouping disabled! */);\n\t\tassert(batch.messages.length > 0, 0xb7a /* Unexpected attempt to group an empty batch */);\n\n\t\tif (batch.messages.length === 1) {\n\t\t\treturn batch as OutboundSingletonBatch;\n\t\t}\n\n\t\tif (batch.messages.length >= 1000) {\n\t\t\tthis.logger.sendTelemetryEvent({\n\t\t\t\teventName: \"GroupLargeBatch\",\n\t\t\t\tlength: batch.messages.length,\n\t\t\t\treentrant: batch.hasReentrantOps,\n\t\t\t\treferenceSequenceNumber: batch.messages[0].referenceSequenceNumber,\n\t\t\t});\n\t\t}\n\t\t// We expect this will be on the first message, if present at all.\n\t\tlet groupedBatchId;\n\t\tfor (const message of batch.messages) {\n\t\t\tif (message.metadata !== undefined) {\n\t\t\t\tconst { batch: _batch, batchId, ...rest } = message.metadata;\n\t\t\t\tif (batchId !== undefined) {\n\t\t\t\t\tgroupedBatchId = batchId;\n\t\t\t\t}\n\t\t\t\tassert(Object.keys(rest).length === 0, 0x5dd /* cannot group ops with metadata */);\n\t\t\t}\n\t\t}\n\n\t\tconst serializedContent = JSON.stringify({\n\t\t\ttype: OpGroupingManager.groupedBatchOp,\n\t\t\tcontents: batch.messages.map<IGroupedMessage>((message) => ({\n\t\t\t\tcontents: message.contents === undefined ? undefined : JSON.parse(message.contents),\n\t\t\t\tmetadata: message.metadata,\n\t\t\t\tcompression: message.compression,\n\t\t\t})),\n\t\t});\n\n\t\tconst groupedBatch: OutboundSingletonBatch = {\n\t\t\t...batch,\n\t\t\tmessages: [\n\t\t\t\t{\n\t\t\t\t\tmetadata: { batchId: groupedBatchId },\n\t\t\t\t\treferenceSequenceNumber: batch.messages[0].referenceSequenceNumber,\n\t\t\t\t\tcontents: serializedContent,\n\t\t\t\t},\n\t\t\t],\n\t\t};\n\t\treturn groupedBatch;\n\t}\n\n\tpublic ungroupOp(op: ISequencedDocumentMessage): ISequencedDocumentMessage[] {\n\t\tassert(isGroupContents(op.contents), 0x947 /* can only ungroup a grouped batch */);\n\t\tconst contents: IGroupedBatchMessageContents = op.contents;\n\n\t\tlet fakeCsn = 1;\n\t\treturn contents.contents.map((subMessage) => ({\n\t\t\t...op,\n\t\t\tclientSequenceNumber: fakeCsn++,\n\t\t\tcontents: subMessage.contents,\n\t\t\tmetadata: subMessage.metadata,\n\t\t\tcompression: subMessage.compression,\n\t\t}));\n\t}\n\n\tpublic groupedBatchingEnabled(): boolean {\n\t\treturn this.config.groupedBatchingEnabled;\n\t}\n}\n"]}
@@ -5,5 +5,5 @@
5
5
  * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY
6
6
  */
7
7
  export declare const pkgName = "@fluidframework/container-runtime";
8
- export declare const pkgVersion = "2.62.0-356644";
8
+ export declare const pkgVersion = "2.62.0";
9
9
  //# sourceMappingURL=packageVersion.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"packageVersion.d.ts","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,eAAO,MAAM,OAAO,sCAAsC,CAAC;AAC3D,eAAO,MAAM,UAAU,kBAAkB,CAAC"}
1
+ {"version":3,"file":"packageVersion.d.ts","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,eAAO,MAAM,OAAO,sCAAsC,CAAC;AAC3D,eAAO,MAAM,UAAU,WAAW,CAAC"}
@@ -5,5 +5,5 @@
5
5
  * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY
6
6
  */
7
7
  export const pkgName = "@fluidframework/container-runtime";
8
- export const pkgVersion = "2.62.0-356644";
8
+ export const pkgVersion = "2.62.0";
9
9
  //# sourceMappingURL=packageVersion.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"packageVersion.js","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,CAAC,MAAM,OAAO,GAAG,mCAAmC,CAAC;AAC3D,MAAM,CAAC,MAAM,UAAU,GAAG,eAAe,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n *\n * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY\n */\n\nexport const pkgName = \"@fluidframework/container-runtime\";\nexport const pkgVersion = \"2.62.0-356644\";\n"]}
1
+ {"version":3,"file":"packageVersion.js","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,CAAC,MAAM,OAAO,GAAG,mCAAmC,CAAC;AAC3D,MAAM,CAAC,MAAM,UAAU,GAAG,QAAQ,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n *\n * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY\n */\n\nexport const pkgName = \"@fluidframework/container-runtime\";\nexport const pkgVersion = \"2.62.0\";\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"pendingStateManager.d.ts","sourceRoot":"","sources":["../src/pendingStateManager.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"pendingStateManager.d.ts","sourceRoot":"","sources":["../src/pendingStateManager.ts"],"names":[],"mappings":"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"}
@@ -21,6 +21,8 @@ function buildPendingMessageContent(message) {
21
21
  return JSON.stringify({ type, contents });
22
22
  }
23
23
  function typesOfKeys(obj) {
24
+ // TODO: Fix this violation and remove the disable
25
+ // eslint-disable-next-line unicorn/no-array-reduce
24
26
  return Object.keys(obj).reduce((acc, key) => {
25
27
  acc[key] = typeof obj[key];
26
28
  return acc;
@@ -31,7 +33,10 @@ function scrubAndStringify(message) {
31
33
  const scrubbed = typesOfKeys(message);
32
34
  // For these known/expected keys, we can either drill into the object (for contents)
33
35
  // or just use the value as-is (since it's not personal info)
34
- scrubbed.contents = message.contents && typesOfKeys(message.contents);
36
+ scrubbed.contents =
37
+ typeof message.contents === "object" && message.contents !== null
38
+ ? typesOfKeys(message.contents)
39
+ : undefined;
35
40
  scrubbed.type = message.type;
36
41
  return JSON.stringify(scrubbed);
37
42
  }
@@ -437,7 +442,7 @@ export class PendingStateManager {
437
442
  pendingMessageBatchMetadata: asBatchMetadata(pendingMessage.opMetadata)?.batch,
438
443
  messageBatchMetadata: asBatchMetadata(firstMessage?.metadata)?.batch,
439
444
  },
440
- messageDetails: firstMessage && extractSafePropertiesFromMessage(firstMessage),
445
+ messageDetails: extractSafePropertiesFromMessage(firstMessage),
441
446
  });
442
447
  }
443
448
  }
@@ -482,7 +487,7 @@ export class PendingStateManager {
482
487
  const batchMetadataFlag = asBatchMetadata(pendingMessage.opMetadata)?.batch;
483
488
  assert(batchMetadataFlag !== false, 0x41b /* We cannot process batches in chunks */);
484
489
  // The next message starts a batch (possibly single-message), and we'll need its batchId.
485
- const batchId = pendingMessage.batchInfo.ignoreBatchId
490
+ const batchId = pendingMessage.batchInfo.ignoreBatchId === true
486
491
  ? undefined
487
492
  : getEffectiveBatchId(pendingMessage);
488
493
  const staged = pendingMessage.batchInfo.staged;