@fluidframework/container-runtime 2.50.0-345060 → 2.50.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 (42) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/container-runtime.test-files.tar +0 -0
  3. package/dist/blobManager/blobManager.d.ts +1 -5
  4. package/dist/blobManager/blobManager.d.ts.map +1 -1
  5. package/dist/blobManager/blobManager.js +0 -7
  6. package/dist/blobManager/blobManager.js.map +1 -1
  7. package/dist/channelCollection.d.ts +1 -1
  8. package/dist/channelCollection.js.map +1 -1
  9. package/dist/dataStoreContext.d.ts +45 -7
  10. package/dist/dataStoreContext.d.ts.map +1 -1
  11. package/dist/dataStoreContext.js +26 -12
  12. package/dist/dataStoreContext.js.map +1 -1
  13. package/dist/gc/gcTelemetry.d.ts +3 -1
  14. package/dist/gc/gcTelemetry.d.ts.map +1 -1
  15. package/dist/gc/gcTelemetry.js.map +1 -1
  16. package/dist/packageVersion.d.ts +1 -1
  17. package/dist/packageVersion.d.ts.map +1 -1
  18. package/dist/packageVersion.js +1 -1
  19. package/dist/packageVersion.js.map +1 -1
  20. package/lib/blobManager/blobManager.d.ts +1 -5
  21. package/lib/blobManager/blobManager.d.ts.map +1 -1
  22. package/lib/blobManager/blobManager.js +0 -7
  23. package/lib/blobManager/blobManager.js.map +1 -1
  24. package/lib/channelCollection.d.ts +1 -1
  25. package/lib/channelCollection.js.map +1 -1
  26. package/lib/dataStoreContext.d.ts +45 -7
  27. package/lib/dataStoreContext.d.ts.map +1 -1
  28. package/lib/dataStoreContext.js +26 -12
  29. package/lib/dataStoreContext.js.map +1 -1
  30. package/lib/gc/gcTelemetry.d.ts +3 -1
  31. package/lib/gc/gcTelemetry.d.ts.map +1 -1
  32. package/lib/gc/gcTelemetry.js.map +1 -1
  33. package/lib/packageVersion.d.ts +1 -1
  34. package/lib/packageVersion.d.ts.map +1 -1
  35. package/lib/packageVersion.js +1 -1
  36. package/lib/packageVersion.js.map +1 -1
  37. package/package.json +17 -17
  38. package/src/blobManager/blobManager.ts +0 -9
  39. package/src/channelCollection.ts +1 -1
  40. package/src/dataStoreContext.ts +61 -23
  41. package/src/gc/gcTelemetry.ts +3 -1
  42. package/src/packageVersion.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 { IGarbageCollectionData } from \"@fluidframework/runtime-definitions/internal\";\nimport {\n\tITelemetryLoggerExt,\n\tMonitoringContext,\n\tgenerateStack,\n\ttagCodeArtifacts,\n\ttype ITelemetryPropertiesExt,\n} from \"@fluidframework/telemetry-utils/internal\";\n\nimport { RuntimeHeaderData } from \"../containerRuntime.js\";\nimport { ICreateContainerMetadata } from \"../summary/index.js\";\n\nimport {\n\tGCFeatureMatrix,\n\tGCNodeType,\n\tIGarbageCollectorConfigs,\n\tUnreferencedState,\n} from \"./gcDefinitions.js\";\nimport { 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\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 { IGarbageCollectionData } from \"@fluidframework/runtime-definitions/internal\";\nimport {\n\tITelemetryLoggerExt,\n\tMonitoringContext,\n\tgenerateStack,\n\ttagCodeArtifacts,\n\ttype ITelemetryPropertiesExt,\n} from \"@fluidframework/telemetry-utils/internal\";\n\nimport { RuntimeHeaderData } from \"../containerRuntime.js\";\nimport { ICreateContainerMetadata } from \"../summary/index.js\";\n\nimport {\n\tGCFeatureMatrix,\n\tGCNodeType,\n\tIGarbageCollectorConfigs,\n\tUnreferencedState,\n} from \"./gcDefinitions.js\";\nimport { 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"]}
@@ -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.50.0-345060";
8
+ export declare const pkgVersion = "2.50.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.50.0-345060";
8
+ export const pkgVersion = "2.50.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.50.0-345060\";\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.50.0\";\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fluidframework/container-runtime",
3
- "version": "2.50.0-345060",
3
+ "version": "2.50.0",
4
4
  "description": "Fluid container runtime",
5
5
  "homepage": "https://fluidframework.com",
6
6
  "repository": {
@@ -119,18 +119,18 @@
119
119
  "temp-directory": "nyc/.nyc_output"
120
120
  },
121
121
  "dependencies": {
122
- "@fluid-internal/client-utils": "2.50.0-345060",
123
- "@fluidframework/container-definitions": "2.50.0-345060",
124
- "@fluidframework/container-runtime-definitions": "2.50.0-345060",
125
- "@fluidframework/core-interfaces": "2.50.0-345060",
126
- "@fluidframework/core-utils": "2.50.0-345060",
127
- "@fluidframework/datastore": "2.50.0-345060",
128
- "@fluidframework/driver-definitions": "2.50.0-345060",
129
- "@fluidframework/driver-utils": "2.50.0-345060",
130
- "@fluidframework/id-compressor": "2.50.0-345060",
131
- "@fluidframework/runtime-definitions": "2.50.0-345060",
132
- "@fluidframework/runtime-utils": "2.50.0-345060",
133
- "@fluidframework/telemetry-utils": "2.50.0-345060",
122
+ "@fluid-internal/client-utils": "~2.50.0",
123
+ "@fluidframework/container-definitions": "~2.50.0",
124
+ "@fluidframework/container-runtime-definitions": "~2.50.0",
125
+ "@fluidframework/core-interfaces": "~2.50.0",
126
+ "@fluidframework/core-utils": "~2.50.0",
127
+ "@fluidframework/datastore": "~2.50.0",
128
+ "@fluidframework/driver-definitions": "~2.50.0",
129
+ "@fluidframework/driver-utils": "~2.50.0",
130
+ "@fluidframework/id-compressor": "~2.50.0",
131
+ "@fluidframework/runtime-definitions": "~2.50.0",
132
+ "@fluidframework/runtime-utils": "~2.50.0",
133
+ "@fluidframework/telemetry-utils": "~2.50.0",
134
134
  "@tylerbu/sorted-btree-es6": "^1.8.0",
135
135
  "double-ended-queue": "^2.1.0-0",
136
136
  "lz4js": "^0.2.0",
@@ -140,16 +140,16 @@
140
140
  "devDependencies": {
141
141
  "@arethetypeswrong/cli": "^0.17.1",
142
142
  "@biomejs/biome": "~1.9.3",
143
- "@fluid-internal/mocha-test-setup": "2.50.0-345060",
144
- "@fluid-private/stochastic-test-utils": "2.50.0-345060",
145
- "@fluid-private/test-pairwise-generator": "2.50.0-345060",
143
+ "@fluid-internal/mocha-test-setup": "~2.50.0",
144
+ "@fluid-private/stochastic-test-utils": "~2.50.0",
145
+ "@fluid-private/test-pairwise-generator": "~2.50.0",
146
146
  "@fluid-tools/benchmark": "^0.51.0",
147
147
  "@fluid-tools/build-cli": "^0.56.0",
148
148
  "@fluidframework/build-common": "^2.0.3",
149
149
  "@fluidframework/build-tools": "^0.56.0",
150
150
  "@fluidframework/container-runtime-previous": "npm:@fluidframework/container-runtime@2.43.0",
151
151
  "@fluidframework/eslint-config-fluid": "^5.7.4",
152
- "@fluidframework/test-runtime-utils": "2.50.0-345060",
152
+ "@fluidframework/test-runtime-utils": "~2.50.0",
153
153
  "@microsoft/api-extractor": "7.52.8",
154
154
  "@types/double-ended-queue": "^2.1.0",
155
155
  "@types/lz4js": "^0.2.0",
@@ -13,7 +13,6 @@ import type {
13
13
  IEmitter,
14
14
  IEventProvider,
15
15
  IFluidHandleContext,
16
- IFluidHandleInternal,
17
16
  IFluidHandleInternalPayloadPending,
18
17
  ILocalFluidHandle,
19
18
  ILocalFluidHandleEvents,
@@ -125,14 +124,6 @@ export class BlobHandle
125
124
  this.onAttachGraph?.();
126
125
  }
127
126
  }
128
-
129
- // eslint-disable-next-line jsdoc/require-description
130
- /**
131
- * @deprecated No replacement provided. Arbitrary handles may not serve as a bind source.
132
- */
133
- public bind(handle: IFluidHandleInternal): void {
134
- throw new Error("Cannot bind to blob handle");
135
- }
136
127
  }
137
128
 
138
129
  // Restrict the IContainerRuntime interface to the subset required by BlobManager. This helps to make
@@ -121,7 +121,7 @@ interface FluidDataStoreMessage {
121
121
  }
122
122
 
123
123
  /**
124
- * This version of the interface is private to this the package. it should never be exported under any tag.
124
+ * This version of the interface is private to this the package. It should never be exported under any tag.
125
125
  * It is used to manage interactions within the container-runtime package. If something is needed
126
126
  * cross package, it is likely it is also being used cross layer (ContainerRuntime * DataStoreRuntime).
127
127
  * If that is the case, the change likely needs to be staged directly on IFluidParentContext. Changes
@@ -62,7 +62,7 @@ import {
62
62
  type IPendingMessagesState,
63
63
  type IRuntimeMessageCollection,
64
64
  type IFluidDataStoreFactory,
65
- type IFluidParentContext,
65
+ type PackagePath,
66
66
  } from "@fluidframework/runtime-definitions/internal";
67
67
  import {
68
68
  addBlobToSummary,
@@ -149,7 +149,13 @@ export interface IFluidDataStoreContextProps {
149
149
  readonly storage: IDocumentStorageService;
150
150
  readonly scope: FluidObject;
151
151
  readonly createSummarizerNodeFn: CreateChildSummarizerNodeFn;
152
- readonly pkg?: Readonly<string[]>;
152
+ /**
153
+ * See {@link FluidDataStoreContext.pkg}.
154
+ */
155
+ readonly pkg?: PackagePath;
156
+ /**
157
+ * See {@link FluidDataStoreContext.loadingGroupId}.
158
+ */
153
159
  readonly loadingGroupId?: string;
154
160
  }
155
161
 
@@ -233,13 +239,13 @@ class ContextDeltaManagerProxy extends BaseDeltaManagerProxy {
233
239
  }
234
240
 
235
241
  /**
236
- * Represents the context for the store. This context is passed to the store runtime.
242
+ * {@link IFluidDataStoreContext} for the implementations of {@link IFluidDataStoreChannel} which powers the {@link IDataStore}s.
237
243
  */
238
244
  export abstract class FluidDataStoreContext
239
245
  extends TypedEventEmitter<IFluidDataStoreContextEvents>
240
- implements IFluidDataStoreContextInternal, IFluidParentContext, IDisposable
246
+ implements IFluidDataStoreContextInternal, IFluidDataStoreContext, IDisposable
241
247
  {
242
- public get packagePath(): readonly string[] {
248
+ public get packagePath(): PackagePath {
243
249
  assert(this.pkg !== undefined, 0x139 /* "Undefined package path" */);
244
250
  return this.pkg;
245
251
  }
@@ -411,14 +417,32 @@ export abstract class FluidDataStoreContext
411
417
 
412
418
  public readonly id: string;
413
419
  private readonly _containerRuntime: IContainerRuntimeBase;
420
+ /**
421
+ * Information for this data store from its parent.
422
+ *
423
+ * @remarks
424
+ * The parent which provided this information currently can be the container runtime or a datastore (if the datastore this context is for is nested under another one).
425
+ */
414
426
  private readonly parentContext: IFluidParentContextPrivate;
415
427
  public readonly storage: IDocumentStorageService;
416
428
  public readonly scope: FluidObject;
417
- // Represents the group to which the data store belongs too.
429
+ /**
430
+ * The loading group to which the data store belongs to.
431
+ */
418
432
  public readonly loadingGroupId: string | undefined;
419
- protected pkg?: readonly string[];
433
+ /**
434
+ * {@link PackagePath} of this data store.
435
+ *
436
+ * This can be undefined when a data store is delay loaded, i.e., the attributes of this data store in the snapshot are not fetched until this data store is actually used.
437
+ * At that time, the attributes blob is fetched and the pkg is updated from it.
438
+ *
439
+ * @see {@link PackagePath}.
440
+ * @see {@link IFluidDataStoreContext.packagePath}.
441
+ * @see {@link factoryFromPackagePath}.
442
+ */
443
+ protected pkg?: PackagePath;
420
444
 
421
- constructor(
445
+ public constructor(
422
446
  props: IFluidDataStoreContextProps,
423
447
  private readonly existing: boolean,
424
448
  public readonly isLocalDataStore: boolean,
@@ -520,10 +544,13 @@ export abstract class FluidDataStoreContext
520
544
  attachState: AttachState.Attaching | AttachState.Attached,
521
545
  ): void;
522
546
 
523
- private rejectDeferredRealize(
547
+ /**
548
+ * Throw a {@link LoggingError} indicating that {@link factoryFromPackagePath} failed.
549
+ */
550
+ private factoryFromPackagePathError(
524
551
  reason: string,
525
552
  failedPkgPath?: string,
526
- fullPackageName?: readonly string[],
553
+ fullPackageName?: PackagePath,
527
554
  ): never {
528
555
  throw new LoggingError(
529
556
  reason,
@@ -558,34 +585,45 @@ export abstract class FluidDataStoreContext
558
585
  return this.channelP;
559
586
  }
560
587
 
588
+ /**
589
+ * Gets the factory that would be used to instantiate this data store by calling `instantiateDataStore` based on {@link pkg}.
590
+ * @remarks
591
+ * Also populates {@link registry}.
592
+ *
593
+ * Must be called after {@link pkg} is set, and only called once.
594
+ *
595
+ * @see {@link @fluidframework/container-runtime-definitions#IContainerRuntimeBase.createDataStore}.
596
+ * @see {@link FluidDataStoreContext.pkg}.
597
+ */
561
598
  protected async factoryFromPackagePath(): Promise<IFluidDataStoreFactory> {
562
- const packages = this.pkg;
563
- if (packages === undefined) {
564
- this.rejectDeferredRealize("packages is undefined");
599
+ const path = this.pkg;
600
+ if (path === undefined) {
601
+ this.factoryFromPackagePathError("packages is undefined");
565
602
  }
566
603
 
567
604
  let entry: FluidDataStoreRegistryEntry | undefined;
568
605
  let registry: IFluidDataStoreRegistry | undefined =
569
606
  this.parentContext.IFluidDataStoreRegistry;
570
- let lastPkg: string | undefined;
571
- for (const pkg of packages) {
607
+ let lastIdentifier: string | undefined;
608
+ // Follow the path, looking up each identifier in the registry along the way:
609
+ for (const identifier of path) {
572
610
  if (!registry) {
573
- this.rejectDeferredRealize("No registry for package", lastPkg, packages);
611
+ this.factoryFromPackagePathError("No registry for package", lastIdentifier, path);
574
612
  }
575
- lastPkg = pkg;
576
- entry = registry.getSync?.(pkg) ?? (await registry.get(pkg));
613
+ lastIdentifier = identifier;
614
+ entry = registry.getSync?.(identifier) ?? (await registry.get(identifier));
577
615
  if (!entry) {
578
- this.rejectDeferredRealize(
616
+ this.factoryFromPackagePathError(
579
617
  "Registry does not contain entry for the package",
580
- pkg,
581
- packages,
618
+ identifier,
619
+ path,
582
620
  );
583
621
  }
584
622
  registry = entry.IFluidDataStoreRegistry;
585
623
  }
586
624
  const factory = entry?.IFluidDataStoreFactory;
587
625
  if (factory === undefined) {
588
- this.rejectDeferredRealize("Can't find factory for package", lastPkg, packages);
626
+ this.factoryFromPackagePathError("Can't find factory for package", lastIdentifier, path);
589
627
  }
590
628
 
591
629
  assert(this.registry === undefined, 0x157 /* "datastore registry already attached" */);
@@ -594,7 +632,7 @@ export abstract class FluidDataStoreContext
594
632
  return factory;
595
633
  }
596
634
 
597
- createChildDataStore<T extends IFluidDataStoreFactory>(
635
+ public createChildDataStore<T extends IFluidDataStoreFactory>(
598
636
  childFactory: T,
599
637
  ): ReturnType<Exclude<T["createDataStore"], undefined>> {
600
638
  const maybe = this.registry?.getSync?.(childFactory.type);
@@ -77,7 +77,9 @@ interface INodeUsageProps extends ICommonProps {
77
77
  */
78
78
  currentReferenceTimestampMs: number;
79
79
  /**
80
- * The package path of the node. This may not be available if the node hasn't been loaded yet
80
+ * The package path of the node. This may not be available if the node hasn't been loaded yet.
81
+ *
82
+ * @see {@link @fluidframework/runtime-definitions#IFluidDataStoreContext.packagePath} for more details.
81
83
  */
82
84
  packagePath: readonly string[] | undefined;
83
85
  /**
@@ -6,4 +6,4 @@
6
6
  */
7
7
 
8
8
  export const pkgName = "@fluidframework/container-runtime";
9
- export const pkgVersion = "2.50.0-345060";
9
+ export const pkgVersion = "2.50.0";