@fluidframework/container-runtime 2.0.0-dev.4.4.0.161784 → 2.0.0-dev.4.4.0.162253

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 (61) hide show
  1. package/dist/containerRuntime.js +4 -4
  2. package/dist/containerRuntime.js.map +1 -1
  3. package/dist/gc/garbageCollection.d.ts +2 -0
  4. package/dist/gc/garbageCollection.d.ts.map +1 -1
  5. package/dist/gc/garbageCollection.js +6 -0
  6. package/dist/gc/garbageCollection.js.map +1 -1
  7. package/dist/gc/gcDefinitions.d.ts +2 -0
  8. package/dist/gc/gcDefinitions.d.ts.map +1 -1
  9. package/dist/gc/gcDefinitions.js.map +1 -1
  10. package/dist/gc/gcSummaryStateTracker.d.ts +6 -1
  11. package/dist/gc/gcSummaryStateTracker.d.ts.map +1 -1
  12. package/dist/gc/gcSummaryStateTracker.js +10 -0
  13. package/dist/gc/gcSummaryStateTracker.js.map +1 -1
  14. package/dist/gc/index.d.ts +0 -1
  15. package/dist/gc/index.d.ts.map +1 -1
  16. package/dist/gc/index.js +1 -5
  17. package/dist/gc/index.js.map +1 -1
  18. package/dist/packageVersion.d.ts +1 -1
  19. package/dist/packageVersion.js +1 -1
  20. package/dist/packageVersion.js.map +1 -1
  21. package/dist/scheduleManager.js +15 -4
  22. package/dist/scheduleManager.js.map +1 -1
  23. package/lib/containerRuntime.js +4 -4
  24. package/lib/containerRuntime.js.map +1 -1
  25. package/lib/gc/garbageCollection.d.ts +2 -0
  26. package/lib/gc/garbageCollection.d.ts.map +1 -1
  27. package/lib/gc/garbageCollection.js +6 -0
  28. package/lib/gc/garbageCollection.js.map +1 -1
  29. package/lib/gc/gcDefinitions.d.ts +2 -0
  30. package/lib/gc/gcDefinitions.d.ts.map +1 -1
  31. package/lib/gc/gcDefinitions.js.map +1 -1
  32. package/lib/gc/gcSummaryStateTracker.d.ts +6 -1
  33. package/lib/gc/gcSummaryStateTracker.d.ts.map +1 -1
  34. package/lib/gc/gcSummaryStateTracker.js +10 -0
  35. package/lib/gc/gcSummaryStateTracker.js.map +1 -1
  36. package/lib/gc/index.d.ts +0 -1
  37. package/lib/gc/index.d.ts.map +1 -1
  38. package/lib/gc/index.js +0 -1
  39. package/lib/gc/index.js.map +1 -1
  40. package/lib/packageVersion.d.ts +1 -1
  41. package/lib/packageVersion.js +1 -1
  42. package/lib/packageVersion.js.map +1 -1
  43. package/lib/scheduleManager.js +15 -4
  44. package/lib/scheduleManager.js.map +1 -1
  45. package/package.json +15 -15
  46. package/src/containerRuntime.ts +1 -1
  47. package/src/gc/garbageCollection.ts +7 -0
  48. package/src/gc/gcDefinitions.ts +2 -0
  49. package/src/gc/gcSummaryStateTracker.ts +13 -1
  50. package/src/gc/index.ts +0 -5
  51. package/src/packageVersion.ts +1 -1
  52. package/src/scheduleManager.ts +19 -7
  53. package/dist/gc/gcSweepReadyUsageDetection.d.ts +0 -53
  54. package/dist/gc/gcSweepReadyUsageDetection.d.ts.map +0 -1
  55. package/dist/gc/gcSweepReadyUsageDetection.js +0 -130
  56. package/dist/gc/gcSweepReadyUsageDetection.js.map +0 -1
  57. package/lib/gc/gcSweepReadyUsageDetection.d.ts +0 -53
  58. package/lib/gc/gcSweepReadyUsageDetection.d.ts.map +0 -1
  59. package/lib/gc/gcSweepReadyUsageDetection.js +0 -125
  60. package/lib/gc/gcSweepReadyUsageDetection.js.map +0 -1
  61. package/src/gc/gcSweepReadyUsageDetection.ts +0 -145
@@ -1 +1 @@
1
- {"version":3,"file":"gcSummaryStateTracker.js","sourceRoot":"","sources":["../../src/gc/gcSummaryStateTracker.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 { SummaryType } from \"@fluidframework/protocol-definitions\";\nimport {\n\tgcBlobPrefix,\n\tgcDeletedBlobKey,\n\tgcTombstoneBlobKey,\n\tgcTreeKey,\n\tISummarizeResult,\n\tISummaryTreeWithStats,\n} from \"@fluidframework/runtime-definitions\";\nimport { mergeStats, ReadAndParseBlob, SummaryTreeBuilder } from \"@fluidframework/runtime-utils\";\nimport { IContainerRuntimeMetadata, metadataBlobName, RefreshSummaryResult } from \"../summary\";\nimport { GCVersion } from \"./gcDefinitions\";\nimport { getGCDataFromSnapshot, generateSortedGCState, getGCVersion } from \"./gcHelpers\";\nimport { IGarbageCollectionSnapshotData, IGarbageCollectionState } from \"./gcSummaryDefinitions\";\nimport { IGarbageCollectorConfigs } from \".\";\n\nexport const gcStateBlobKey = `${gcBlobPrefix}_root`;\n\n/**\n * The GC data that is tracked for a summary.\n */\nexport interface IGCSummaryTrackingData {\n\tserializedGCState: string | undefined;\n\tserializedTombstones: string | undefined;\n\tserializedDeletedNodes: string | undefined;\n}\n\n/**\n * Encapsulates the garbage collection state that is tracked across summaries.\n * It maintains the GC state as per the latest summary in by the server. It updates state when a summary tracked by this\n * client is acked by the server or from a snapshot is downloaded from the server.\n * On summarize, it decides whether to write new state or re-use previous summary's state.\n */\nexport class GCSummaryStateTracker {\n\t// The current version of GC running.\n\tpublic readonly currentGCVersion: GCVersion = this.configs.gcVersionInEffect;\n\t// This is the version of GC data in the latest summary being tracked.\n\tprivate latestSummaryGCVersion: GCVersion;\n\n\t// Keeps track of the GC data from the latest summary successfully acked by the server.\n\tprivate latestSummaryData: IGCSummaryTrackingData | undefined;\n\t// Keeps track of the GC data from the last summary submitted to the server but not yet acked.\n\tprivate pendingSummaryData: IGCSummaryTrackingData | undefined;\n\n\t// Tracks whether there was GC was run in latest summary being tracked.\n\tprivate wasGCRunInLatestSummary: boolean;\n\n\tconstructor(\n\t\t// Tells whether GC should run or not.\n\t\tprivate readonly configs: Pick<\n\t\t\tIGarbageCollectorConfigs,\n\t\t\t\"shouldRunGC\" | \"tombstoneMode\" | \"gcVersionInBaseSnapshot\" | \"gcVersionInEffect\"\n\t\t>,\n\t\t// Tells whether GC was run in the base snapshot this container loaded from.\n\t\twasGCRunInBaseSnapshot: boolean,\n\t) {\n\t\tthis.wasGCRunInLatestSummary = wasGCRunInBaseSnapshot;\n\t\t// For existing document, the latest summary is the one that we loaded from. So, use its GC version as the\n\t\t// latest tracked GC version. For new documents, we will be writing the first summary with the current version.\n\t\tthis.latestSummaryGCVersion = this.configs.gcVersionInBaseSnapshot ?? this.currentGCVersion;\n\t}\n\n\t/**\n\t * Tells whether the GC state needs to be reset. This can happen under 3 conditions:\n\t *\n\t * 1. The base snapshot contains GC state but GC is disabled. This will happen the first time GC is disabled after\n\t * it was enabled before. GC state needs to be removed from summary and all nodes should be marked referenced.\n\t *\n\t * 2. The base snapshot does not have GC state but GC is enabled. This will happen the very first time GC runs on\n\t * a document and the first time GC is enabled after is was disabled before.\n\t *\n\t * 3. GC is enabled and the latest summary state is refreshed from a snapshot that had GC disabled and vice-versa.\n\t *\n\t * Note that the state will be reset only once for the first summary generated after this returns true. After that,\n\t * this will return false.\n\t */\n\tpublic get doesGCStateNeedReset(): boolean {\n\t\treturn this.wasGCRunInLatestSummary !== this.configs.shouldRunGC;\n\t}\n\n\t/**\n\t * Tells whether the GC state needs to be reset in the next summary. We need to do this if:\n\t *\n\t * 1. GC was enabled and is now disabled. The GC state needs to be removed and everything becomes referenced.\n\t *\n\t * 2. GC was disabled and is now enabled. The GC state needs to be regenerated and added to summary.\n\t *\n\t * 3. GC is enabled and the latest summary state is refreshed from a snapshot that had GC disabled and vice-versa.\n\t *\n\t * 4. The GC version in the latest summary is different from the current GC version. This can happen if:\n\t *\n\t * 4.1. The summary this client loaded with has data from a different GC version.\n\t *\n\t * 4.2. This client's latest summary was updated from a snapshot that has a different GC version.\n\t */\n\tpublic get doesSummaryStateNeedReset(): boolean {\n\t\treturn (\n\t\t\tthis.doesGCStateNeedReset ||\n\t\t\t(this.configs.shouldRunGC && this.latestSummaryGCVersion !== this.currentGCVersion)\n\t\t);\n\t}\n\n\t/**\n\t * Called during GC initialization. Initialize the latest summary data from the base snapshot data.\n\t */\n\tpublic initializeBaseState(baseSnapshotData: IGarbageCollectionSnapshotData) {\n\t\t// If tracking state across summaries, update latest summary data from the snapshot's GC data.\n\t\tthis.latestSummaryData = {\n\t\t\tserializedGCState: baseSnapshotData.gcState\n\t\t\t\t? JSON.stringify(generateSortedGCState(baseSnapshotData.gcState))\n\t\t\t\t: undefined,\n\t\t\tserializedTombstones: JSON.stringify(baseSnapshotData.tombstones),\n\t\t\tserializedDeletedNodes: JSON.stringify(baseSnapshotData.deletedNodes),\n\t\t};\n\t}\n\n\t/**\n\t * Summarizes three component of the GC data - GC state, tombstones and deleted nodes.\n\t * It does incremental summary, i.e., it writes summary tree / summary blob only for the component that changed.\n\t * For components that did not change, a summary handle is returned that points to the previous successful summary.\n\t * If none of the components changed, it returns a summary handle for the entire GC data.\n\t */\n\tpublic summarize(\n\t\tfullTree: boolean,\n\t\ttrackState: boolean,\n\t\tgcState: IGarbageCollectionState,\n\t\tdeletedNodes: Set<string>,\n\t\ttombstones: string[],\n\t): ISummarizeResult | undefined {\n\t\tif (!this.configs.shouldRunGC) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst serializedGCState = JSON.stringify(generateSortedGCState(gcState));\n\t\t// Serialize and write deleted nodes, if any. This is done irrespective of whether sweep is enabled or not so\n\t\t// to identify deleted nodes' usage.\n\t\tconst serializedDeletedNodes =\n\t\t\tdeletedNodes.size > 0 ? JSON.stringify(Array.from(deletedNodes).sort()) : undefined;\n\t\t// If running in tombstone mode, serialize and write tombstones, if any.\n\t\tconst serializedTombstones = this.configs.tombstoneMode\n\t\t\t? tombstones.length > 0\n\t\t\t\t? JSON.stringify(tombstones.sort())\n\t\t\t\t: undefined\n\t\t\t: undefined;\n\n\t\t/**\n\t\t * Incremental summary of GC data - If none of GC state, deleted nodes or tombstones changed since last summary,\n\t\t * write summary handle instead of summary tree for GC.\n\t\t * Otherwise, write the GC summary tree. In the tree, for each of these that changed, write a summary blob and\n\t\t * for each of these that did not change, write a summary handle.\n\t\t */\n\t\tthis.pendingSummaryData = {\n\t\t\tserializedGCState,\n\t\t\tserializedTombstones,\n\t\t\tserializedDeletedNodes,\n\t\t};\n\n\t\tif (trackState && !fullTree && this.latestSummaryData !== undefined) {\n\t\t\t// If nothing changed since last summary, send a summary handle for the entire GC data.\n\t\t\tif (\n\t\t\t\tthis.latestSummaryData.serializedGCState === serializedGCState &&\n\t\t\t\tthis.latestSummaryData.serializedTombstones === serializedTombstones &&\n\t\t\t\tthis.latestSummaryData.serializedDeletedNodes === serializedDeletedNodes\n\t\t\t) {\n\t\t\t\tconst stats = mergeStats();\n\t\t\t\tstats.handleNodeCount++;\n\t\t\t\treturn {\n\t\t\t\t\tsummary: {\n\t\t\t\t\t\ttype: SummaryType.Handle,\n\t\t\t\t\t\thandle: `/${gcTreeKey}`,\n\t\t\t\t\t\thandleType: SummaryType.Tree,\n\t\t\t\t\t},\n\t\t\t\t\tstats,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\t// If some state changed, build a GC summary tree.\n\t\t\treturn this.buildGCSummaryTree(\n\t\t\t\tserializedGCState,\n\t\t\t\tserializedTombstones,\n\t\t\t\tserializedDeletedNodes,\n\t\t\t\ttrue /* trackState */,\n\t\t\t);\n\t\t}\n\t\t// If not tracking GC state, build a GC summary tree without any summary handles.\n\t\treturn this.buildGCSummaryTree(\n\t\t\tserializedGCState,\n\t\t\tserializedTombstones,\n\t\t\tserializedDeletedNodes,\n\t\t\tfalse /* trackState */,\n\t\t);\n\t}\n\n\t/**\n\t * Builds the GC summary tree which contains GC state, deleted nodes and tombstones.\n\t * If trackState is false, all of GC state, deleted nodes and tombstones are written as summary blobs.\n\t * If trackState is true, only states that changed are written. Rest are written as handles.\n\t * @param serializedGCState - The GC state serialized as string.\n\t * @param serializedTombstones - The tombstone state serialized as string.\n\t * @param serializedDeletedNodes - Deleted nodes serialized as string.\n\t * @param trackState - Whether we are tracking GC state across summaries.\n\t * @returns the GC summary tree.\n\t */\n\tprivate buildGCSummaryTree(\n\t\tserializedGCState: string,\n\t\tserializedTombstones: string | undefined,\n\t\tserializedDeletedNodes: string | undefined,\n\t\ttrackState: boolean,\n\t): ISummaryTreeWithStats {\n\t\tconst builder = new SummaryTreeBuilder();\n\n\t\t// If the GC state hasn't changed, write a summary handle, else write a summary blob for it.\n\t\tif (this.latestSummaryData?.serializedGCState === serializedGCState && trackState) {\n\t\t\tbuilder.addHandle(gcStateBlobKey, SummaryType.Blob, `/${gcTreeKey}/${gcStateBlobKey}`);\n\t\t} else {\n\t\t\tbuilder.addBlob(gcStateBlobKey, serializedGCState);\n\t\t}\n\n\t\t// If tombstones exist, write a summary handle if it hasn't changed. If it has changed, write a\n\t\t// summary blob.\n\t\tif (serializedTombstones !== undefined) {\n\t\t\tif (\n\t\t\t\tthis.latestSummaryData?.serializedTombstones === serializedTombstones &&\n\t\t\t\ttrackState\n\t\t\t) {\n\t\t\t\tbuilder.addHandle(\n\t\t\t\t\tgcTombstoneBlobKey,\n\t\t\t\t\tSummaryType.Blob,\n\t\t\t\t\t`/${gcTreeKey}/${gcTombstoneBlobKey}`,\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tbuilder.addBlob(gcTombstoneBlobKey, serializedTombstones);\n\t\t\t}\n\t\t}\n\n\t\t// If there are no deleted nodes, return the summary tree.\n\t\tif (serializedDeletedNodes === undefined) {\n\t\t\treturn builder.getSummaryTree();\n\t\t}\n\n\t\t// If the deleted nodes hasn't changed, write a summary handle, else write a summary blob for it.\n\t\tif (\n\t\t\tthis.latestSummaryData?.serializedDeletedNodes === serializedDeletedNodes &&\n\t\t\ttrackState\n\t\t) {\n\t\t\tbuilder.addHandle(\n\t\t\t\tgcDeletedBlobKey,\n\t\t\t\tSummaryType.Blob,\n\t\t\t\t`/${gcTreeKey}/${gcDeletedBlobKey}`,\n\t\t\t);\n\t\t} else {\n\t\t\tbuilder.addBlob(gcDeletedBlobKey, serializedDeletedNodes);\n\t\t}\n\t\treturn builder.getSummaryTree();\n\t}\n\n\t/**\n\t * Called to refresh the latest summary state. This happens when either a pending summary is acked or a snapshot\n\t * is downloaded and should be used to update the state.\n\t */\n\tpublic async refreshLatestSummary(\n\t\tproposalHandle: string | undefined,\n\t\tresult: RefreshSummaryResult,\n\t\treadAndParseBlob: ReadAndParseBlob,\n\t): Promise<IGarbageCollectionSnapshotData | undefined> {\n\t\t// If the latest summary was updated and the summary was tracked, this client is the one that generated this\n\t\t// summary. So, update wasGCRunInLatestSummary.\n\t\t// Note that this has to be updated if GC did not run too. Otherwise, `gcStateNeedsReset` will always return\n\t\t// true in scenarios where GC is disabled but enabled in the snapshot we loaded from.\n\t\tif (result.latestSummaryUpdated && result.wasSummaryTracked) {\n\t\t\tthis.wasGCRunInLatestSummary = this.configs.shouldRunGC;\n\t\t}\n\n\t\tif (!result.latestSummaryUpdated || !this.configs.shouldRunGC) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\t// If the summary was tracked by this client, it was the one that generated the summary in the first place.\n\t\t// Update latest state from pending.\n\t\tif (result.wasSummaryTracked) {\n\t\t\tthis.latestSummaryGCVersion = this.currentGCVersion;\n\t\t\tthis.latestSummaryData = this.pendingSummaryData;\n\t\t\tthis.pendingSummaryData = undefined;\n\t\t\treturn undefined;\n\t\t}\n\n\t\t// If the summary was not tracked by this client, the state should be updated from the downloaded snapshot.\n\t\tconst snapshotTree = result.snapshotTree;\n\t\tconst metadataBlobId = snapshotTree.blobs[metadataBlobName];\n\t\tconst metadata = metadataBlobId\n\t\t\t? await readAndParseBlob<IContainerRuntimeMetadata>(metadataBlobId)\n\t\t\t: undefined;\n\t\tthis.latestSummaryGCVersion = getGCVersion(metadata);\n\n\t\tconst gcSnapshotTree = snapshotTree.trees[gcTreeKey];\n\t\t// If GC ran in the container that generated this snapshot, it will have a GC tree.\n\t\tthis.wasGCRunInLatestSummary = gcSnapshotTree !== undefined;\n\n\t\tif (gcSnapshotTree === undefined) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tlet snapshotData = await getGCDataFromSnapshot(gcSnapshotTree, readAndParseBlob);\n\n\t\t// If the GC version in the snapshot does not match the GC version currently in effect, the GC data\n\t\t// in the snapshot cannot be interpreted correctly. Set everything to undefined except for deletedNodes\n\t\t// because irrespective of GC versions, these nodes have been deleted and cannot be brought back. The\n\t\t// deletedNodes info is needed to identify when these nodes are used.\n\t\tif (getGCVersion(metadata) !== this.currentGCVersion) {\n\t\t\tsnapshotData = {\n\t\t\t\tgcState: undefined,\n\t\t\t\ttombstones: undefined,\n\t\t\t\tdeletedNodes: snapshotData.deletedNodes,\n\t\t\t};\n\t\t}\n\n\t\tthis.latestSummaryData = {\n\t\t\tserializedGCState: JSON.stringify(snapshotData.gcState),\n\t\t\tserializedTombstones: JSON.stringify(snapshotData.tombstones),\n\t\t\tserializedDeletedNodes: JSON.stringify(snapshotData.deletedNodes),\n\t\t};\n\t\treturn snapshotData;\n\t}\n}\n"]}
1
+ {"version":3,"file":"gcSummaryStateTracker.js","sourceRoot":"","sources":["../../src/gc/gcSummaryStateTracker.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 { SummaryType } from \"@fluidframework/protocol-definitions\";\nimport {\n\tgcBlobPrefix,\n\tgcDeletedBlobKey,\n\tgcTombstoneBlobKey,\n\tgcTreeKey,\n\tISummarizeResult,\n\tISummaryTreeWithStats,\n} from \"@fluidframework/runtime-definitions\";\nimport { mergeStats, ReadAndParseBlob, SummaryTreeBuilder } from \"@fluidframework/runtime-utils\";\nimport { IContainerRuntimeMetadata, metadataBlobName, RefreshSummaryResult } from \"../summary\";\nimport { GCVersion, IGCStats } from \"./gcDefinitions\";\nimport { getGCDataFromSnapshot, generateSortedGCState, getGCVersion } from \"./gcHelpers\";\nimport { IGarbageCollectionSnapshotData, IGarbageCollectionState } from \"./gcSummaryDefinitions\";\nimport { IGarbageCollectorConfigs } from \".\";\n\nexport const gcStateBlobKey = `${gcBlobPrefix}_root`;\n\n/**\n * The GC data that is tracked for a summary.\n */\nexport interface IGCSummaryTrackingData {\n\tserializedGCState: string | undefined;\n\tserializedTombstones: string | undefined;\n\tserializedDeletedNodes: string | undefined;\n}\n\n/**\n * Encapsulates the garbage collection state that is tracked across summaries.\n * It maintains the GC state as per the latest summary in by the server. It updates state when a summary tracked by this\n * client is acked by the server or from a snapshot is downloaded from the server.\n * On summarize, it decides whether to write new state or re-use previous summary's state.\n */\nexport class GCSummaryStateTracker {\n\t// The current version of GC running.\n\tpublic readonly currentGCVersion: GCVersion = this.configs.gcVersionInEffect;\n\t// This is the version of GC data in the latest summary being tracked.\n\tprivate latestSummaryGCVersion: GCVersion;\n\n\t// Keeps track of the GC data from the latest summary successfully acked by the server.\n\tprivate latestSummaryData: IGCSummaryTrackingData | undefined;\n\t// Keeps track of the GC data from the last summary submitted to the server but not yet acked.\n\tprivate pendingSummaryData: IGCSummaryTrackingData | undefined;\n\n\t// Tracks whether there was GC was run in latest summary being tracked.\n\tprivate wasGCRunInLatestSummary: boolean;\n\n\t// Tracks the count of data stores whose state updated since the last summary, i.e., they went from referenced\n\t// to unreferenced or vice-versa.\n\tpublic updatedDSCountSinceLastSummary: number = 0;\n\n\tconstructor(\n\t\t// Tells whether GC should run or not.\n\t\tprivate readonly configs: Pick<\n\t\t\tIGarbageCollectorConfigs,\n\t\t\t\"shouldRunGC\" | \"tombstoneMode\" | \"gcVersionInBaseSnapshot\" | \"gcVersionInEffect\"\n\t\t>,\n\t\t// Tells whether GC was run in the base snapshot this container loaded from.\n\t\twasGCRunInBaseSnapshot: boolean,\n\t) {\n\t\tthis.wasGCRunInLatestSummary = wasGCRunInBaseSnapshot;\n\t\t// For existing document, the latest summary is the one that we loaded from. So, use its GC version as the\n\t\t// latest tracked GC version. For new documents, we will be writing the first summary with the current version.\n\t\tthis.latestSummaryGCVersion = this.configs.gcVersionInBaseSnapshot ?? this.currentGCVersion;\n\t}\n\n\t/**\n\t * Tells whether the GC state needs to be reset. This can happen under 3 conditions:\n\t *\n\t * 1. The base snapshot contains GC state but GC is disabled. This will happen the first time GC is disabled after\n\t * it was enabled before. GC state needs to be removed from summary and all nodes should be marked referenced.\n\t *\n\t * 2. The base snapshot does not have GC state but GC is enabled. This will happen the very first time GC runs on\n\t * a document and the first time GC is enabled after is was disabled before.\n\t *\n\t * 3. GC is enabled and the latest summary state is refreshed from a snapshot that had GC disabled and vice-versa.\n\t *\n\t * Note that the state will be reset only once for the first summary generated after this returns true. After that,\n\t * this will return false.\n\t */\n\tpublic get doesGCStateNeedReset(): boolean {\n\t\treturn this.wasGCRunInLatestSummary !== this.configs.shouldRunGC;\n\t}\n\n\t/**\n\t * Tells whether the GC state needs to be reset in the next summary. We need to do this if:\n\t *\n\t * 1. GC was enabled and is now disabled. The GC state needs to be removed and everything becomes referenced.\n\t *\n\t * 2. GC was disabled and is now enabled. The GC state needs to be regenerated and added to summary.\n\t *\n\t * 3. GC is enabled and the latest summary state is refreshed from a snapshot that had GC disabled and vice-versa.\n\t *\n\t * 4. The GC version in the latest summary is different from the current GC version. This can happen if:\n\t *\n\t * 4.1. The summary this client loaded with has data from a different GC version.\n\t *\n\t * 4.2. This client's latest summary was updated from a snapshot that has a different GC version.\n\t */\n\tpublic get doesSummaryStateNeedReset(): boolean {\n\t\treturn (\n\t\t\tthis.doesGCStateNeedReset ||\n\t\t\t(this.configs.shouldRunGC && this.latestSummaryGCVersion !== this.currentGCVersion)\n\t\t);\n\t}\n\n\t/**\n\t * Called during GC initialization. Initialize the latest summary data from the base snapshot data.\n\t */\n\tpublic initializeBaseState(baseSnapshotData: IGarbageCollectionSnapshotData) {\n\t\t// If tracking state across summaries, update latest summary data from the snapshot's GC data.\n\t\tthis.latestSummaryData = {\n\t\t\tserializedGCState: baseSnapshotData.gcState\n\t\t\t\t? JSON.stringify(generateSortedGCState(baseSnapshotData.gcState))\n\t\t\t\t: undefined,\n\t\t\tserializedTombstones: JSON.stringify(baseSnapshotData.tombstones),\n\t\t\tserializedDeletedNodes: JSON.stringify(baseSnapshotData.deletedNodes),\n\t\t};\n\t}\n\n\t/**\n\t * Summarizes three component of the GC data - GC state, tombstones and deleted nodes.\n\t * It does incremental summary, i.e., it writes summary tree / summary blob only for the component that changed.\n\t * For components that did not change, a summary handle is returned that points to the previous successful summary.\n\t * If none of the components changed, it returns a summary handle for the entire GC data.\n\t */\n\tpublic summarize(\n\t\tfullTree: boolean,\n\t\ttrackState: boolean,\n\t\tgcState: IGarbageCollectionState,\n\t\tdeletedNodes: Set<string>,\n\t\ttombstones: string[],\n\t): ISummarizeResult | undefined {\n\t\tif (!this.configs.shouldRunGC) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst serializedGCState = JSON.stringify(generateSortedGCState(gcState));\n\t\t// Serialize and write deleted nodes, if any. This is done irrespective of whether sweep is enabled or not so\n\t\t// to identify deleted nodes' usage.\n\t\tconst serializedDeletedNodes =\n\t\t\tdeletedNodes.size > 0 ? JSON.stringify(Array.from(deletedNodes).sort()) : undefined;\n\t\t// If running in tombstone mode, serialize and write tombstones, if any.\n\t\tconst serializedTombstones = this.configs.tombstoneMode\n\t\t\t? tombstones.length > 0\n\t\t\t\t? JSON.stringify(tombstones.sort())\n\t\t\t\t: undefined\n\t\t\t: undefined;\n\n\t\t/**\n\t\t * Incremental summary of GC data - If none of GC state, deleted nodes or tombstones changed since last summary,\n\t\t * write summary handle instead of summary tree for GC.\n\t\t * Otherwise, write the GC summary tree. In the tree, for each of these that changed, write a summary blob and\n\t\t * for each of these that did not change, write a summary handle.\n\t\t */\n\t\tthis.pendingSummaryData = {\n\t\t\tserializedGCState,\n\t\t\tserializedTombstones,\n\t\t\tserializedDeletedNodes,\n\t\t};\n\n\t\tif (trackState && !fullTree && this.latestSummaryData !== undefined) {\n\t\t\t// If nothing changed since last summary, send a summary handle for the entire GC data.\n\t\t\tif (\n\t\t\t\tthis.latestSummaryData.serializedGCState === serializedGCState &&\n\t\t\t\tthis.latestSummaryData.serializedTombstones === serializedTombstones &&\n\t\t\t\tthis.latestSummaryData.serializedDeletedNodes === serializedDeletedNodes\n\t\t\t) {\n\t\t\t\tconst stats = mergeStats();\n\t\t\t\tstats.handleNodeCount++;\n\t\t\t\treturn {\n\t\t\t\t\tsummary: {\n\t\t\t\t\t\ttype: SummaryType.Handle,\n\t\t\t\t\t\thandle: `/${gcTreeKey}`,\n\t\t\t\t\t\thandleType: SummaryType.Tree,\n\t\t\t\t\t},\n\t\t\t\t\tstats,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\t// If some state changed, build a GC summary tree.\n\t\t\treturn this.buildGCSummaryTree(\n\t\t\t\tserializedGCState,\n\t\t\t\tserializedTombstones,\n\t\t\t\tserializedDeletedNodes,\n\t\t\t\ttrue /* trackState */,\n\t\t\t);\n\t\t}\n\t\t// If not tracking GC state, build a GC summary tree without any summary handles.\n\t\treturn this.buildGCSummaryTree(\n\t\t\tserializedGCState,\n\t\t\tserializedTombstones,\n\t\t\tserializedDeletedNodes,\n\t\t\tfalse /* trackState */,\n\t\t);\n\t}\n\n\t/**\n\t * Builds the GC summary tree which contains GC state, deleted nodes and tombstones.\n\t * If trackState is false, all of GC state, deleted nodes and tombstones are written as summary blobs.\n\t * If trackState is true, only states that changed are written. Rest are written as handles.\n\t * @param serializedGCState - The GC state serialized as string.\n\t * @param serializedTombstones - The tombstone state serialized as string.\n\t * @param serializedDeletedNodes - Deleted nodes serialized as string.\n\t * @param trackState - Whether we are tracking GC state across summaries.\n\t * @returns the GC summary tree.\n\t */\n\tprivate buildGCSummaryTree(\n\t\tserializedGCState: string,\n\t\tserializedTombstones: string | undefined,\n\t\tserializedDeletedNodes: string | undefined,\n\t\ttrackState: boolean,\n\t): ISummaryTreeWithStats {\n\t\tconst builder = new SummaryTreeBuilder();\n\n\t\t// If the GC state hasn't changed, write a summary handle, else write a summary blob for it.\n\t\tif (this.latestSummaryData?.serializedGCState === serializedGCState && trackState) {\n\t\t\tbuilder.addHandle(gcStateBlobKey, SummaryType.Blob, `/${gcTreeKey}/${gcStateBlobKey}`);\n\t\t} else {\n\t\t\tbuilder.addBlob(gcStateBlobKey, serializedGCState);\n\t\t}\n\n\t\t// If tombstones exist, write a summary handle if it hasn't changed. If it has changed, write a\n\t\t// summary blob.\n\t\tif (serializedTombstones !== undefined) {\n\t\t\tif (\n\t\t\t\tthis.latestSummaryData?.serializedTombstones === serializedTombstones &&\n\t\t\t\ttrackState\n\t\t\t) {\n\t\t\t\tbuilder.addHandle(\n\t\t\t\t\tgcTombstoneBlobKey,\n\t\t\t\t\tSummaryType.Blob,\n\t\t\t\t\t`/${gcTreeKey}/${gcTombstoneBlobKey}`,\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tbuilder.addBlob(gcTombstoneBlobKey, serializedTombstones);\n\t\t\t}\n\t\t}\n\n\t\t// If there are no deleted nodes, return the summary tree.\n\t\tif (serializedDeletedNodes === undefined) {\n\t\t\treturn builder.getSummaryTree();\n\t\t}\n\n\t\t// If the deleted nodes hasn't changed, write a summary handle, else write a summary blob for it.\n\t\tif (\n\t\t\tthis.latestSummaryData?.serializedDeletedNodes === serializedDeletedNodes &&\n\t\t\ttrackState\n\t\t) {\n\t\t\tbuilder.addHandle(\n\t\t\t\tgcDeletedBlobKey,\n\t\t\t\tSummaryType.Blob,\n\t\t\t\t`/${gcTreeKey}/${gcDeletedBlobKey}`,\n\t\t\t);\n\t\t} else {\n\t\t\tbuilder.addBlob(gcDeletedBlobKey, serializedDeletedNodes);\n\t\t}\n\t\treturn builder.getSummaryTree();\n\t}\n\n\t/**\n\t * Called to refresh the latest summary state. This happens when either a pending summary is acked or a snapshot\n\t * is downloaded and should be used to update the state.\n\t */\n\tpublic async refreshLatestSummary(\n\t\tproposalHandle: string | undefined,\n\t\tresult: RefreshSummaryResult,\n\t\treadAndParseBlob: ReadAndParseBlob,\n\t): Promise<IGarbageCollectionSnapshotData | undefined> {\n\t\t// If the latest summary was updated and the summary was tracked, this client is the one that generated this\n\t\t// summary. So, update wasGCRunInLatestSummary.\n\t\t// Note that this has to be updated if GC did not run too. Otherwise, `gcStateNeedsReset` will always return\n\t\t// true in scenarios where GC is disabled but enabled in the snapshot we loaded from.\n\t\tif (result.latestSummaryUpdated && result.wasSummaryTracked) {\n\t\t\tthis.wasGCRunInLatestSummary = this.configs.shouldRunGC;\n\t\t}\n\n\t\tif (!result.latestSummaryUpdated || !this.configs.shouldRunGC) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\t// If the summary was tracked by this client, it was the one that generated the summary in the first place.\n\t\t// Update latest state from pending.\n\t\tif (result.wasSummaryTracked) {\n\t\t\tthis.latestSummaryGCVersion = this.currentGCVersion;\n\t\t\tthis.latestSummaryData = this.pendingSummaryData;\n\t\t\tthis.pendingSummaryData = undefined;\n\t\t\tthis.updatedDSCountSinceLastSummary = 0;\n\t\t\treturn undefined;\n\t\t}\n\n\t\t// If the summary was not tracked by this client, the state should be updated from the downloaded snapshot.\n\t\tconst snapshotTree = result.snapshotTree;\n\t\tconst metadataBlobId = snapshotTree.blobs[metadataBlobName];\n\t\tconst metadata = metadataBlobId\n\t\t\t? await readAndParseBlob<IContainerRuntimeMetadata>(metadataBlobId)\n\t\t\t: undefined;\n\t\tthis.latestSummaryGCVersion = getGCVersion(metadata);\n\n\t\tconst gcSnapshotTree = snapshotTree.trees[gcTreeKey];\n\t\t// If GC ran in the container that generated this snapshot, it will have a GC tree.\n\t\tthis.wasGCRunInLatestSummary = gcSnapshotTree !== undefined;\n\n\t\tif (gcSnapshotTree === undefined) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tlet snapshotData = await getGCDataFromSnapshot(gcSnapshotTree, readAndParseBlob);\n\n\t\t// If the GC version in the snapshot does not match the GC version currently in effect, the GC data\n\t\t// in the snapshot cannot be interpreted correctly. Set everything to undefined except for deletedNodes\n\t\t// because irrespective of GC versions, these nodes have been deleted and cannot be brought back. The\n\t\t// deletedNodes info is needed to identify when these nodes are used.\n\t\tif (getGCVersion(metadata) !== this.currentGCVersion) {\n\t\t\tsnapshotData = {\n\t\t\t\tgcState: undefined,\n\t\t\t\ttombstones: undefined,\n\t\t\t\tdeletedNodes: snapshotData.deletedNodes,\n\t\t\t};\n\t\t}\n\n\t\tthis.latestSummaryData = {\n\t\t\tserializedGCState: JSON.stringify(snapshotData.gcState),\n\t\t\tserializedTombstones: JSON.stringify(snapshotData.tombstones),\n\t\t\tserializedDeletedNodes: JSON.stringify(snapshotData.deletedNodes),\n\t\t};\n\t\treturn snapshotData;\n\t}\n\n\t/**\n\t * Called to update the state from a GC run's stats. Used to update the count of data stores whose state updated.\n\t */\n\tpublic updateStateFromGCRunStats(stats: IGCStats) {\n\t\tthis.updatedDSCountSinceLastSummary += stats.updatedDataStoreCount;\n\t}\n}\n"]}
@@ -8,7 +8,6 @@ export { cloneGCData, concatGarbageCollectionStates, getGCDataFromSnapshot, shou
8
8
  export { runGarbageCollection } from "./gcReferenceGraphAlgorithm";
9
9
  export { IGarbageCollectionNodeData, IGarbageCollectionSnapshotData, IGarbageCollectionState, IGarbageCollectionSummaryDetailsLegacy, } from "./gcSummaryDefinitions";
10
10
  export { gcStateBlobKey, GCSummaryStateTracker, IGCSummaryTrackingData, } from "./gcSummaryStateTracker";
11
- export { skipClosureForXDaysKey, closuresMapLocalStorageKey, SweepReadyUsageDetectionHandler, } from "./gcSweepReadyUsageDetection";
12
11
  export { GCTelemetryTracker, sendGCUnexpectedUsageEvent } from "./gcTelemetry";
13
12
  export { UnreferencedStateTracker } from "./gcUnreferencedStateTracker";
14
13
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/gc/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,EACN,gBAAgB,EAChB,wBAAwB,EACxB,8BAA8B,EAC9B,kBAAkB,EAClB,UAAU,EACV,aAAa,EACb,+BAA+B,EAC/B,2BAA2B,EAC3B,eAAe,EACf,SAAS,EACT,uBAAuB,EACvB,yBAAyB,EACzB,iBAAiB,EACjB,wBAAwB,EACxB,6BAA6B,EAC7B,WAAW,EACX,SAAS,EACT,iBAAiB,EACjB,QAAQ,EACR,QAAQ,EACR,QAAQ,EACR,mBAAmB,EACnB,WAAW,EACX,eAAe,EACf,uBAAuB,EACvB,kBAAkB,EAClB,uBAAuB,EACvB,wBAAwB,EACxB,iBAAiB,GACjB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACN,WAAW,EACX,6BAA6B,EAC7B,qBAAqB,EACrB,iCAAiC,EACjC,kBAAkB,EAClB,6BAA6B,EAC7B,yBAAyB,EACzB,iBAAiB,GACjB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AACnE,OAAO,EACN,0BAA0B,EAC1B,8BAA8B,EAC9B,uBAAuB,EACvB,sCAAsC,GACtC,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACN,cAAc,EACd,qBAAqB,EACrB,sBAAsB,GACtB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACN,sBAAsB,EACtB,0BAA0B,EAC1B,+BAA+B,GAC/B,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,kBAAkB,EAAE,0BAA0B,EAAE,MAAM,eAAe,CAAC;AAC/E,OAAO,EAAE,wBAAwB,EAAE,MAAM,8BAA8B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/gc/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,EACN,gBAAgB,EAChB,wBAAwB,EACxB,8BAA8B,EAC9B,kBAAkB,EAClB,UAAU,EACV,aAAa,EACb,+BAA+B,EAC/B,2BAA2B,EAC3B,eAAe,EACf,SAAS,EACT,uBAAuB,EACvB,yBAAyB,EACzB,iBAAiB,EACjB,wBAAwB,EACxB,6BAA6B,EAC7B,WAAW,EACX,SAAS,EACT,iBAAiB,EACjB,QAAQ,EACR,QAAQ,EACR,QAAQ,EACR,mBAAmB,EACnB,WAAW,EACX,eAAe,EACf,uBAAuB,EACvB,kBAAkB,EAClB,uBAAuB,EACvB,wBAAwB,EACxB,iBAAiB,GACjB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACN,WAAW,EACX,6BAA6B,EAC7B,qBAAqB,EACrB,iCAAiC,EACjC,kBAAkB,EAClB,6BAA6B,EAC7B,yBAAyB,EACzB,iBAAiB,GACjB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AACnE,OAAO,EACN,0BAA0B,EAC1B,8BAA8B,EAC9B,uBAAuB,EACvB,sCAAsC,GACtC,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACN,cAAc,EACd,qBAAqB,EACrB,sBAAsB,GACtB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,kBAAkB,EAAE,0BAA0B,EAAE,MAAM,eAAe,CAAC;AAC/E,OAAO,EAAE,wBAAwB,EAAE,MAAM,8BAA8B,CAAC"}
package/dist/gc/index.js CHANGED
@@ -4,7 +4,7 @@
4
4
  * Licensed under the MIT License.
5
5
  */
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.UnreferencedStateTracker = exports.sendGCUnexpectedUsageEvent = exports.GCTelemetryTracker = exports.SweepReadyUsageDetectionHandler = exports.closuresMapLocalStorageKey = exports.skipClosureForXDaysKey = exports.GCSummaryStateTracker = exports.gcStateBlobKey = exports.runGarbageCollection = exports.tagAsCodeArtifact = exports.unpackChildNodesGCDetails = exports.trimLeadingAndTrailingSlashes = exports.shouldAllowGcSweep = exports.shouldAllowGcTombstoneEnforcement = exports.getGCDataFromSnapshot = exports.concatGarbageCollectionStates = exports.cloneGCData = exports.UnreferencedState = exports.throwOnTombstoneUsageKey = exports.throwOnTombstoneLoadKey = exports.sweepDatastoresKey = exports.sweepAttachmentBlobsKey = exports.stableGCVersion = exports.runSweepKey = exports.runSessionExpiryKey = exports.runGCKey = exports.oneDayMs = exports.gcVersionUpgradeToV3Key = exports.gcSweepGenerationOptionName = exports.gcTombstoneGenerationOptionName = exports.gcTestModeKey = exports.GCNodeType = exports.disableSweepLogKey = exports.defaultSessionExpiryDurationMs = exports.defaultInactiveTimeoutMs = exports.currentGCVersion = exports.GarbageCollector = void 0;
7
+ exports.UnreferencedStateTracker = exports.sendGCUnexpectedUsageEvent = exports.GCTelemetryTracker = exports.GCSummaryStateTracker = exports.gcStateBlobKey = exports.runGarbageCollection = exports.tagAsCodeArtifact = exports.unpackChildNodesGCDetails = exports.trimLeadingAndTrailingSlashes = exports.shouldAllowGcSweep = exports.shouldAllowGcTombstoneEnforcement = exports.getGCDataFromSnapshot = exports.concatGarbageCollectionStates = exports.cloneGCData = exports.UnreferencedState = exports.throwOnTombstoneUsageKey = exports.throwOnTombstoneLoadKey = exports.sweepDatastoresKey = exports.sweepAttachmentBlobsKey = exports.stableGCVersion = exports.runSweepKey = exports.runSessionExpiryKey = exports.runGCKey = exports.oneDayMs = exports.gcVersionUpgradeToV3Key = exports.gcSweepGenerationOptionName = exports.gcTombstoneGenerationOptionName = exports.gcTestModeKey = exports.GCNodeType = exports.disableSweepLogKey = exports.defaultSessionExpiryDurationMs = exports.defaultInactiveTimeoutMs = exports.currentGCVersion = exports.GarbageCollector = void 0;
8
8
  var garbageCollection_1 = require("./garbageCollection");
9
9
  Object.defineProperty(exports, "GarbageCollector", { enumerable: true, get: function () { return garbageCollection_1.GarbageCollector; } });
10
10
  var gcDefinitions_1 = require("./gcDefinitions");
@@ -41,10 +41,6 @@ Object.defineProperty(exports, "runGarbageCollection", { enumerable: true, get:
41
41
  var gcSummaryStateTracker_1 = require("./gcSummaryStateTracker");
42
42
  Object.defineProperty(exports, "gcStateBlobKey", { enumerable: true, get: function () { return gcSummaryStateTracker_1.gcStateBlobKey; } });
43
43
  Object.defineProperty(exports, "GCSummaryStateTracker", { enumerable: true, get: function () { return gcSummaryStateTracker_1.GCSummaryStateTracker; } });
44
- var gcSweepReadyUsageDetection_1 = require("./gcSweepReadyUsageDetection");
45
- Object.defineProperty(exports, "skipClosureForXDaysKey", { enumerable: true, get: function () { return gcSweepReadyUsageDetection_1.skipClosureForXDaysKey; } });
46
- Object.defineProperty(exports, "closuresMapLocalStorageKey", { enumerable: true, get: function () { return gcSweepReadyUsageDetection_1.closuresMapLocalStorageKey; } });
47
- Object.defineProperty(exports, "SweepReadyUsageDetectionHandler", { enumerable: true, get: function () { return gcSweepReadyUsageDetection_1.SweepReadyUsageDetectionHandler; } });
48
44
  var gcTelemetry_1 = require("./gcTelemetry");
49
45
  Object.defineProperty(exports, "GCTelemetryTracker", { enumerable: true, get: function () { return gcTelemetry_1.GCTelemetryTracker; } });
50
46
  Object.defineProperty(exports, "sendGCUnexpectedUsageEvent", { enumerable: true, get: function () { return gcTelemetry_1.sendGCUnexpectedUsageEvent; } });
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/gc/index.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAEH,yDAAuD;AAA9C,qHAAA,gBAAgB,OAAA;AACzB,iDA8ByB;AA7BxB,iHAAA,gBAAgB,OAAA;AAChB,yHAAA,wBAAwB,OAAA;AACxB,+HAAA,8BAA8B,OAAA;AAC9B,mHAAA,kBAAkB,OAAA;AAClB,2GAAA,UAAU,OAAA;AACV,8GAAA,aAAa,OAAA;AACb,gIAAA,+BAA+B,OAAA;AAC/B,4HAAA,2BAA2B,OAAA;AAG3B,wHAAA,uBAAuB,OAAA;AASvB,yGAAA,QAAQ,OAAA;AACR,yGAAA,QAAQ,OAAA;AACR,oHAAA,mBAAmB,OAAA;AACnB,4GAAA,WAAW,OAAA;AACX,gHAAA,eAAe,OAAA;AACf,wHAAA,uBAAuB,OAAA;AACvB,mHAAA,kBAAkB,OAAA;AAClB,wHAAA,uBAAuB,OAAA;AACvB,yHAAA,wBAAwB,OAAA;AACxB,kHAAA,iBAAiB,OAAA;AAElB,yCASqB;AARpB,wGAAA,WAAW,OAAA;AACX,0HAAA,6BAA6B,OAAA;AAC7B,kHAAA,qBAAqB,OAAA;AACrB,8HAAA,iCAAiC,OAAA;AACjC,+GAAA,kBAAkB,OAAA;AAClB,0HAAA,6BAA6B,OAAA;AAC7B,sHAAA,yBAAyB,OAAA;AACzB,8GAAA,iBAAiB,OAAA;AAElB,yEAAmE;AAA1D,iIAAA,oBAAoB,OAAA;AAO7B,iEAIiC;AAHhC,uHAAA,cAAc,OAAA;AACd,8HAAA,qBAAqB,OAAA;AAGtB,2EAIsC;AAHrC,oIAAA,sBAAsB,OAAA;AACtB,wIAAA,0BAA0B,OAAA;AAC1B,6IAAA,+BAA+B,OAAA;AAEhC,6CAA+E;AAAtE,iHAAA,kBAAkB,OAAA;AAAE,yHAAA,0BAA0B,OAAA;AACvD,2EAAwE;AAA/D,sIAAA,wBAAwB,OAAA","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nexport { GarbageCollector } from \"./garbageCollection\";\nexport {\n\tcurrentGCVersion,\n\tdefaultInactiveTimeoutMs,\n\tdefaultSessionExpiryDurationMs,\n\tdisableSweepLogKey,\n\tGCNodeType,\n\tgcTestModeKey,\n\tgcTombstoneGenerationOptionName,\n\tgcSweepGenerationOptionName,\n\tGCFeatureMatrix,\n\tGCVersion,\n\tgcVersionUpgradeToV3Key,\n\tIGarbageCollectionRuntime,\n\tIGarbageCollector,\n\tIGarbageCollectorConfigs,\n\tIGarbageCollectorCreateParams,\n\tIGCMetadata,\n\tIGCResult,\n\tIGCRuntimeOptions,\n\tIGCStats,\n\toneDayMs,\n\trunGCKey,\n\trunSessionExpiryKey,\n\trunSweepKey,\n\tstableGCVersion,\n\tsweepAttachmentBlobsKey,\n\tsweepDatastoresKey,\n\tthrowOnTombstoneLoadKey,\n\tthrowOnTombstoneUsageKey,\n\tUnreferencedState,\n} from \"./gcDefinitions\";\nexport {\n\tcloneGCData,\n\tconcatGarbageCollectionStates,\n\tgetGCDataFromSnapshot,\n\tshouldAllowGcTombstoneEnforcement,\n\tshouldAllowGcSweep,\n\ttrimLeadingAndTrailingSlashes,\n\tunpackChildNodesGCDetails,\n\ttagAsCodeArtifact,\n} from \"./gcHelpers\";\nexport { runGarbageCollection } from \"./gcReferenceGraphAlgorithm\";\nexport {\n\tIGarbageCollectionNodeData,\n\tIGarbageCollectionSnapshotData,\n\tIGarbageCollectionState,\n\tIGarbageCollectionSummaryDetailsLegacy,\n} from \"./gcSummaryDefinitions\";\nexport {\n\tgcStateBlobKey,\n\tGCSummaryStateTracker,\n\tIGCSummaryTrackingData,\n} from \"./gcSummaryStateTracker\";\nexport {\n\tskipClosureForXDaysKey,\n\tclosuresMapLocalStorageKey,\n\tSweepReadyUsageDetectionHandler,\n} from \"./gcSweepReadyUsageDetection\";\nexport { GCTelemetryTracker, sendGCUnexpectedUsageEvent } from \"./gcTelemetry\";\nexport { UnreferencedStateTracker } from \"./gcUnreferencedStateTracker\";\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/gc/index.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAEH,yDAAuD;AAA9C,qHAAA,gBAAgB,OAAA;AACzB,iDA8ByB;AA7BxB,iHAAA,gBAAgB,OAAA;AAChB,yHAAA,wBAAwB,OAAA;AACxB,+HAAA,8BAA8B,OAAA;AAC9B,mHAAA,kBAAkB,OAAA;AAClB,2GAAA,UAAU,OAAA;AACV,8GAAA,aAAa,OAAA;AACb,gIAAA,+BAA+B,OAAA;AAC/B,4HAAA,2BAA2B,OAAA;AAG3B,wHAAA,uBAAuB,OAAA;AASvB,yGAAA,QAAQ,OAAA;AACR,yGAAA,QAAQ,OAAA;AACR,oHAAA,mBAAmB,OAAA;AACnB,4GAAA,WAAW,OAAA;AACX,gHAAA,eAAe,OAAA;AACf,wHAAA,uBAAuB,OAAA;AACvB,mHAAA,kBAAkB,OAAA;AAClB,wHAAA,uBAAuB,OAAA;AACvB,yHAAA,wBAAwB,OAAA;AACxB,kHAAA,iBAAiB,OAAA;AAElB,yCASqB;AARpB,wGAAA,WAAW,OAAA;AACX,0HAAA,6BAA6B,OAAA;AAC7B,kHAAA,qBAAqB,OAAA;AACrB,8HAAA,iCAAiC,OAAA;AACjC,+GAAA,kBAAkB,OAAA;AAClB,0HAAA,6BAA6B,OAAA;AAC7B,sHAAA,yBAAyB,OAAA;AACzB,8GAAA,iBAAiB,OAAA;AAElB,yEAAmE;AAA1D,iIAAA,oBAAoB,OAAA;AAO7B,iEAIiC;AAHhC,uHAAA,cAAc,OAAA;AACd,8HAAA,qBAAqB,OAAA;AAGtB,6CAA+E;AAAtE,iHAAA,kBAAkB,OAAA;AAAE,yHAAA,0BAA0B,OAAA;AACvD,2EAAwE;AAA/D,sIAAA,wBAAwB,OAAA","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nexport { GarbageCollector } from \"./garbageCollection\";\nexport {\n\tcurrentGCVersion,\n\tdefaultInactiveTimeoutMs,\n\tdefaultSessionExpiryDurationMs,\n\tdisableSweepLogKey,\n\tGCNodeType,\n\tgcTestModeKey,\n\tgcTombstoneGenerationOptionName,\n\tgcSweepGenerationOptionName,\n\tGCFeatureMatrix,\n\tGCVersion,\n\tgcVersionUpgradeToV3Key,\n\tIGarbageCollectionRuntime,\n\tIGarbageCollector,\n\tIGarbageCollectorConfigs,\n\tIGarbageCollectorCreateParams,\n\tIGCMetadata,\n\tIGCResult,\n\tIGCRuntimeOptions,\n\tIGCStats,\n\toneDayMs,\n\trunGCKey,\n\trunSessionExpiryKey,\n\trunSweepKey,\n\tstableGCVersion,\n\tsweepAttachmentBlobsKey,\n\tsweepDatastoresKey,\n\tthrowOnTombstoneLoadKey,\n\tthrowOnTombstoneUsageKey,\n\tUnreferencedState,\n} from \"./gcDefinitions\";\nexport {\n\tcloneGCData,\n\tconcatGarbageCollectionStates,\n\tgetGCDataFromSnapshot,\n\tshouldAllowGcTombstoneEnforcement,\n\tshouldAllowGcSweep,\n\ttrimLeadingAndTrailingSlashes,\n\tunpackChildNodesGCDetails,\n\ttagAsCodeArtifact,\n} from \"./gcHelpers\";\nexport { runGarbageCollection } from \"./gcReferenceGraphAlgorithm\";\nexport {\n\tIGarbageCollectionNodeData,\n\tIGarbageCollectionSnapshotData,\n\tIGarbageCollectionState,\n\tIGarbageCollectionSummaryDetailsLegacy,\n} from \"./gcSummaryDefinitions\";\nexport {\n\tgcStateBlobKey,\n\tGCSummaryStateTracker,\n\tIGCSummaryTrackingData,\n} from \"./gcSummaryStateTracker\";\nexport { GCTelemetryTracker, sendGCUnexpectedUsageEvent } from \"./gcTelemetry\";\nexport { UnreferencedStateTracker } from \"./gcUnreferencedStateTracker\";\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.0.0-dev.4.4.0.161784";
8
+ export declare const pkgVersion = "2.0.0-dev.4.4.0.162253";
9
9
  //# sourceMappingURL=packageVersion.d.ts.map
@@ -8,5 +8,5 @@
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
9
  exports.pkgVersion = exports.pkgName = void 0;
10
10
  exports.pkgName = "@fluidframework/container-runtime";
11
- exports.pkgVersion = "2.0.0-dev.4.4.0.161784";
11
+ exports.pkgVersion = "2.0.0-dev.4.4.0.162253";
12
12
  //# sourceMappingURL=packageVersion.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"packageVersion.js","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;AAEU,QAAA,OAAO,GAAG,mCAAmC,CAAC;AAC9C,QAAA,UAAU,GAAG,wBAAwB,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.0.0-dev.4.4.0.161784\";\n"]}
1
+ {"version":3,"file":"packageVersion.js","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;AAEU,QAAA,OAAO,GAAG,mCAAmC,CAAC;AAC9C,QAAA,UAAU,GAAG,wBAAwB,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.0.0-dev.4.4.0.162253\";\n"]}
@@ -106,13 +106,14 @@ class ScheduleManagerCore {
106
106
  // We are intentionally directly listening to the "op" to inspect system ops as well.
107
107
  // If we do not observe system ops, we are likely to hit 0x296 assert when system ops
108
108
  // precedes start of incomplete batch.
109
- this.deltaManager.on("op", (message) => this.afterOpProcessing(message.sequenceNumber));
109
+ this.deltaManager.on("op", (message) => this.afterOpProcessing(message));
110
110
  }
111
111
  /**
112
112
  * The only public function in this class - called when we processed an op,
113
113
  * to make decision if op processing should be paused or not after that.
114
114
  */
115
- afterOpProcessing(sequenceNumber) {
115
+ afterOpProcessing(message) {
116
+ var _a;
116
117
  (0, common_utils_1.assert)(!this.localPaused, 0x294 /* "can't have op processing paused if we are processing an op" */);
117
118
  // If the inbound queue is ever empty, nothing to do!
118
119
  if (this.deltaManager.inbound.length === 0) {
@@ -126,9 +127,19 @@ class ScheduleManagerCore {
126
127
  // 2. resumed when batch end comes in (in trackPending())
127
128
  // do we have incomplete batch to worry about?
128
129
  if (this.pauseSequenceNumber !== undefined) {
129
- (0, common_utils_1.assert)(sequenceNumber < this.pauseSequenceNumber, 0x296 /* "we should never start processing incomplete batch!" */);
130
+ if (message.sequenceNumber >= this.pauseSequenceNumber) {
131
+ throw container_utils_1.DataProcessingError.create(
132
+ // Former assert 0x296
133
+ "Incomplete batch", "ScheduleManager", message, {
134
+ type: message.type,
135
+ contentType: typeof message.contents,
136
+ batch: (_a = message.metadata) === null || _a === void 0 ? void 0 : _a.batch,
137
+ compression: message.compression,
138
+ pauseSeqNum: this.pauseSequenceNumber,
139
+ });
140
+ }
130
141
  // If the next op is the start of incomplete batch, then we can't process it until it's fully in - pause!
131
- if (sequenceNumber + 1 === this.pauseSequenceNumber) {
142
+ if (message.sequenceNumber + 1 === this.pauseSequenceNumber) {
132
143
  this.pauseQueue();
133
144
  }
134
145
  }
@@ -1 +1 @@
1
- {"version":3,"file":"scheduleManager.js","sourceRoot":"","sources":["../src/scheduleManager.ts"],"names":[],"mappings":";;;AAQA,qEAA8D;AAC9D,+DAAmE;AACnE,+DAAgE;AAChE,qEAIyC;AACzC,qDAAkD;AAClD,qDAA8C;AAQ9C;;;;;;;;GAQG;AACH,MAAa,eAAe;IAK3B,YACkB,YAAwE,EACxE,OAAqB,EAC7B,WAAqC,EAC7B,MAAwB;QAHxB,iBAAY,GAAZ,YAAY,CAA4D;QACxE,YAAO,GAAP,OAAO,CAAc;QAC7B,gBAAW,GAAX,WAAW,CAA0B;QAC7B,WAAM,GAAN,MAAM,CAAkB;QANlC,aAAQ,GAAG,KAAK,CAAC;QAQxB,IAAI,CAAC,cAAc,GAAG,IAAI,+BAAc,CACvC,IAAI,CAAC,YAAY,EACjB,6BAAW,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,gBAAgB,CAAC,CACjD,CAAC;QACF,KAAK,IAAI,mBAAmB,CAAC,YAAY,EAAE,WAAW,EAAE,MAAM,CAAC,CAAC;IACjE,CAAC;IAEM,kBAAkB,CAAC,OAAkC;;QAC3D,IAAI,IAAI,CAAC,aAAa,KAAK,OAAO,CAAC,QAAQ,EAAE;YAC5C,IAAA,qBAAM,EACL,IAAI,CAAC,aAAa,KAAK,SAAS,EAChC,KAAK,CAAC,mFAAmF,CACzF,CAAC;YAEF,uEAAuE;YACvE,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;YACzC,IAAI,CAAC,cAAc,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;YAExC,MAAM,KAAK,GAAG,MAAC,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,QAAoC,0CAAE,KAAK,CAAC;YACpE,IAAI,CAAC,aAAa,GAAG,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC;SAC1D;IACF,CAAC;IAEM,iBAAiB,CAAC,KAAsB,EAAE,OAAkC;;QAClF,uFAAuF;QACvF,IAAA,qBAAM,EAAC,CAAC,IAAI,CAAC,QAAQ,EAAE,KAAK,CAAC,+CAA+C,CAAC,CAAC;QAE9E,IAAI,KAAK,EAAE;YACV,sFAAsF;YACtF,2FAA2F;YAC3F,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;YACrB,IAAI,CAAC,aAAa,GAAG,SAAS,CAAC;YAC/B,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,UAAU,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC;YAC9C,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;YACtC,OAAO;SACP;QAED,MAAM,KAAK,GAAG,MAAC,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,QAAoC,0CAAE,KAAK,CAAC;QACpE,sFAAsF;QACtF,wDAAwD;QACxD,IAAI,IAAI,CAAC,aAAa,KAAK,SAAS,IAAI,KAAK,KAAK,KAAK,EAAE;YACxD,IAAI,CAAC,aAAa,GAAG,SAAS,CAAC;YAC/B,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,UAAU,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;YAClD,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;YACtC,OAAO;SACP;IACF,CAAC;CACD;AA1DD,0CA0DC;AAED;;;GAGG;AACH,MAAM,mBAAmB;IAOxB,YACkB,YAAwE,EACxE,WAAqC,EACrC,MAAwB;QAFxB,iBAAY,GAAZ,YAAY,CAA4D;QACxE,gBAAW,GAAX,WAAW,CAA0B;QACrC,WAAM,GAAN,MAAM,CAAkB;QAPlC,gBAAW,GAAG,KAAK,CAAC;QACpB,eAAU,GAAG,CAAC,CAAC;QACf,eAAU,GAAG,CAAC,CAAC;QAOtB,oEAAoE;QACpE,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC,aAAa,EAAE,CAAC,QAA4B,EAAE,EAAE;YACpE,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE;gBAC1B,OAAO;aACP;YAED,6EAA6E;YAC7E,MAAM,oBAAoB,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAmC,CAAC;YAC7E,IAAI,CAAC,CAAA,oBAAoB,aAApB,oBAAoB,uBAApB,oBAAoB,CAAE,KAAK,CAAA,EAAE;gBACjC,OAAO;aACP;YAED,gEAAgE;YAChE,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE;gBAC1B,OAAO,oBAAoB,CAAC,KAAK,CAAC;gBAClC,OAAO;aACP;YAED,wFAAwF;YACxF,MAAM,WAAW,GAAG,QAAQ,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;YAClD,WAAW,CAAC,QAAQ,mCAAQ,WAAW,CAAC,QAAQ,KAAE,KAAK,EAAE,KAAK,GAAE,CAAC;QAClE,CAAC,CAAC,CAAC;QAEH,6CAA6C;QAC7C,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,OAAkC,EAAE,EAAE;YAC3E,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;QAC5B,CAAC,CAAC,CAAC;QAEH,6CAA6C;QAC7C,IAAA,qBAAM,EAAC,CAAC,IAAI,CAAC,WAAW,EAAE,KAAK,CAAC,qBAAqB,CAAC,CAAC;QAEvD,MAAM,UAAU,GAAG,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;QACvD,KAAK,MAAM,OAAO,IAAI,UAAU,EAAE;YACjC,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;SAC3B;QAED,qFAAqF;QACrF,qFAAqF;QACrF,sCAAsC;QACtC,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,IAAI,CAAC,iBAAiB,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC;IACzF,CAAC;IAED;;;OAGG;IACI,iBAAiB,CAAC,cAAsB;QAC9C,IAAA,qBAAM,EACL,CAAC,IAAI,CAAC,WAAW,EACjB,KAAK,CAAC,kEAAkE,CACxE,CAAC;QAEF,qDAAqD;QACrD,IAAI,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE;YAC3C,IAAA,qBAAM,EACL,IAAI,CAAC,mBAAmB,KAAK,SAAS,EACtC,KAAK,CAAC,0DAA0D,CAChE,CAAC;YACF,OAAO;SACP;QAED,eAAe;QACf,wGAAwG;QACxG,sEAAsE;QACtE,6EAA6E;QAC7E,yDAAyD;QAEzD,8CAA8C;QAC9C,IAAI,IAAI,CAAC,mBAAmB,KAAK,SAAS,EAAE;YAC3C,IAAA,qBAAM,EACL,cAAc,GAAG,IAAI,CAAC,mBAAmB,EACzC,KAAK,CAAC,0DAA0D,CAChE,CAAC;YACF,yGAAyG;YACzG,IAAI,cAAc,GAAG,CAAC,KAAK,IAAI,CAAC,mBAAmB,EAAE;gBACpD,IAAI,CAAC,UAAU,EAAE,CAAC;aAClB;SACD;IACF,CAAC;IAEO,UAAU;QACjB,IAAA,qBAAM,EAAC,CAAC,IAAI,CAAC,WAAW,EAAE,KAAK,CAAC,wCAAwC,CAAC,CAAC;QAC1E,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;QACxB,IAAI,CAAC,UAAU,GAAG,0BAAW,CAAC,GAAG,EAAE,CAAC;QACpC,mEAAmE;QACnE,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;IACnC,CAAC;IAEO,WAAW,CAAC,UAAkB,EAAE,eAA0C;QACjF,MAAM,QAAQ,GAAG,eAAe,CAAC,cAAc,CAAC;QAChD,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,0BAAW,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC;QAEpF,IAAI,CAAC,UAAU,EAAE,CAAC;QAClB,IAAI,IAAI,CAAC,UAAU,GAAG,IAAI,KAAK,CAAC,EAAE;YACjC,IAAI,CAAC,MAAM,CAAC,kBAAkB,CAAC;gBAC9B,SAAS,EAAE,YAAY;gBACvB,cAAc,EAAE,QAAQ;gBACxB,MAAM,EAAE,QAAQ,GAAG,UAAU,GAAG,CAAC;gBACjC,WAAW,EAAE,QAAQ,GAAG,eAAe,CAAC,qBAAqB;gBAC7D,QAAQ;gBACR,UAAU,EAAE,IAAI,CAAC,UAAU;gBAC3B,WAAW,EAAE,IAAI,CAAC,WAAW;aAC7B,CAAC,CAAC;SACH;QAED,qCAAqC;QACrC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE;YACtB,OAAO;SACP;QAED,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC;QAEzB,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;IACpC,CAAC;IAED;;OAEG;IACK,YAAY,CAAC,OAAkC;QACtD,IAAA,qBAAM,EACL,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,EACtC,KAAK,CAAC,gEAAgE,CACtE,CAAC;QAEF,IAAA,qBAAM,EACL,CAAC,IAAI,CAAC,oBAAoB,KAAK,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,mBAAmB,KAAK,SAAS,CAAC,EACtF,KAAK,CAAC,8BAA8B,CACpC,CAAC;QAEF,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAmC,CAAC;QAC7D,sGAAsG;QACtG,oCAAoC;QACpC,MAAM,aAAa,GAAG,QAAQ,aAAR,QAAQ,uBAAR,QAAQ,CAAE,KAAK,CAAC;QAEtC,kEAAkE;QAClE,IAAI,CAAC,IAAA,+BAAgB,EAAC,OAAO,CAAC,EAAE;YAC/B,qEAAqE;YACrE,IAAI,IAAI,CAAC,oBAAoB,KAAK,SAAS,EAAE;gBAC5C,MAAM,qCAAmB,CAAC,MAAM,CAC/B,mDAAmD,EAAE,iCAAiC;gBACtF,cAAc,EACd,OAAO,EACP;oBACC,cAAc,EAAE,2BAAU;oBAC1B,aAAa,EAAE,IAAI,CAAC,oBAAoB;oBACxC,mBAAmB,EAAE,IAAI,CAAC,mBAAmB;oBAC7C,UAAU,EAAE,IAAI,CAAC,oBAAoB,KAAK,IAAI,CAAC,WAAW,EAAE;oBAC5D,WAAW,EAAE,OAAO,CAAC,IAAI;iBACzB,CACD,CAAC;aACF;YAED,IAAA,qBAAM,EAAC,aAAa,KAAK,SAAS,EAAE,KAAK,CAAC,6BAA6B,CAAC,CAAC;YACzE,IAAA,qBAAM,EACL,CAAC,IAAI,CAAC,WAAW,EACjB,KAAK,CAAC,iEAAiE,CACvE,CAAC;YACF,OAAO;SACP;QAED,IAAI,IAAI,CAAC,oBAAoB,KAAK,SAAS,IAAI,aAAa,KAAK,SAAS,EAAE;YAC3E,IAAA,qBAAM,EACL,CAAC,IAAI,CAAC,WAAW,EACjB,KAAK,CAAC,iEAAiE,CACvE,CAAC;YACF,OAAO;SACP;QAED,2FAA2F;QAE3F,+GAA+G;QAC/G,oHAAoH;QACpH,IACC,IAAI,CAAC,oBAAoB,KAAK,SAAS;YACvC,IAAI,CAAC,oBAAoB,KAAK,OAAO,CAAC,QAAQ,EAC7C;YACD,MAAM,IAAI,qCAAmB,CAAC,mBAAmB,kBAChD,cAAc,EAAE,2BAAU,EAC1B,aAAa,EAAE,IAAI,CAAC,oBAAoB,EACxC,mBAAmB,EAAE,IAAI,CAAC,mBAAmB,EAC7C,UAAU,EAAE,IAAI,CAAC,oBAAoB,KAAK,IAAI,CAAC,WAAW,EAAE,EAC5D,YAAY,EAAE,OAAO,CAAC,QAAQ,KAAK,IAAI,CAAC,WAAW,EAAE,IAClD,IAAA,kDAAgC,EAAC,OAAO,CAAC,EAC3C,CAAC;SACH;QAED,eAAe;QACf,wGAAwG;QACxG,wFAAwF;QACxF,+FAA+F;QAC/F,qEAAqE;QAErE,IAAI,aAAa,EAAE;YAClB,IAAA,qBAAM,EACL,IAAI,CAAC,oBAAoB,KAAK,SAAS,EACvC,KAAK,CAAC,mCAAmC,CACzC,CAAC;YACF,IAAA,qBAAM,EACL,CAAC,IAAI,CAAC,WAAW,EACjB,KAAK,CAAC,iEAAiE,CACvE,CAAC;YACF,IAAI,CAAC,mBAAmB,GAAG,OAAO,CAAC,cAAc,CAAC;YAClD,IAAI,CAAC,oBAAoB,GAAG,OAAO,CAAC,QAAQ,CAAC;YAC7C,qBAAqB;YACrB,mDAAmD;YACnD,+FAA+F;YAC/F,IAAI,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE;gBAC3C,IAAI,CAAC,UAAU,EAAE,CAAC;aAClB;SACD;aAAM,IAAI,aAAa,KAAK,KAAK,EAAE;YACnC,IAAA,qBAAM,EACL,IAAI,CAAC,mBAAmB,KAAK,SAAS,EACtC,KAAK,CAAC,0CAA0C,CAChD,CAAC;YACF,wCAAwC;YACxC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,mBAAmB,EAAE,OAAO,CAAC,CAAC;YACpD,IAAI,CAAC,mBAAmB,GAAG,SAAS,CAAC;YACrC,IAAI,CAAC,oBAAoB,GAAG,SAAS,CAAC;SACtC;aAAM;YACN,4CAA4C;YAC5C,IAAA,qBAAM,EAAC,IAAI,CAAC,oBAAoB,KAAK,SAAS,EAAE,KAAK,CAAC,mBAAmB,CAAC,CAAC;SAC3E;IACF,CAAC;CACD","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\nimport { EventEmitter } from \"events\";\nimport { IDeltaManager } from \"@fluidframework/container-definitions\";\nimport { IDocumentMessage, ISequencedDocumentMessage } from \"@fluidframework/protocol-definitions\";\nimport { ITelemetryLogger } from \"@fluidframework/common-definitions\";\nimport { ChildLogger } from \"@fluidframework/telemetry-utils\";\nimport { assert, performance } from \"@fluidframework/common-utils\";\nimport { isRuntimeMessage } from \"@fluidframework/driver-utils\";\nimport {\n\tDataCorruptionError,\n\tDataProcessingError,\n\textractSafePropertiesFromMessage,\n} from \"@fluidframework/container-utils\";\nimport { DeltaScheduler } from \"./deltaScheduler\";\nimport { pkgVersion } from \"./packageVersion\";\n\ntype IRuntimeMessageMetadata =\n\t| undefined\n\t| {\n\t\t\tbatch?: boolean;\n\t };\n\n/**\n * This class has the following responsibilities:\n *\n * 1. It tracks batches as we process ops and raises \"batchBegin\" and \"batchEnd\" events.\n * As part of it, it validates batch correctness (i.e. no system ops in the middle of batch)\n *\n * 2. It creates instance of ScheduleManagerCore that ensures we never start processing ops from batch\n * unless all ops of the batch are in.\n */\nexport class ScheduleManager {\n\tprivate readonly deltaScheduler: DeltaScheduler;\n\tprivate batchClientId: string | undefined;\n\tprivate hitError = false;\n\n\tconstructor(\n\t\tprivate readonly deltaManager: IDeltaManager<ISequencedDocumentMessage, IDocumentMessage>,\n\t\tprivate readonly emitter: EventEmitter,\n\t\treadonly getClientId: () => string | undefined,\n\t\tprivate readonly logger: ITelemetryLogger,\n\t) {\n\t\tthis.deltaScheduler = new DeltaScheduler(\n\t\t\tthis.deltaManager,\n\t\t\tChildLogger.create(this.logger, \"DeltaScheduler\"),\n\t\t);\n\t\tvoid new ScheduleManagerCore(deltaManager, getClientId, logger);\n\t}\n\n\tpublic beforeOpProcessing(message: ISequencedDocumentMessage) {\n\t\tif (this.batchClientId !== message.clientId) {\n\t\t\tassert(\n\t\t\t\tthis.batchClientId === undefined,\n\t\t\t\t0x2a2 /* \"Batch is interrupted by other client op. Should be caught by trackPending()\" */,\n\t\t\t);\n\n\t\t\t// This could be the beginning of a new batch or an individual message.\n\t\t\tthis.emitter.emit(\"batchBegin\", message);\n\t\t\tthis.deltaScheduler.batchBegin(message);\n\n\t\t\tconst batch = (message?.metadata as IRuntimeMessageMetadata)?.batch;\n\t\t\tthis.batchClientId = batch ? message.clientId : undefined;\n\t\t}\n\t}\n\n\tpublic afterOpProcessing(error: any | undefined, message: ISequencedDocumentMessage) {\n\t\t// If this is no longer true, we need to revisit what we do where we set this.hitError.\n\t\tassert(!this.hitError, 0x2a3 /* \"container should be closed on any error\" */);\n\n\t\tif (error) {\n\t\t\t// We assume here that loader will close container and stop processing all future ops.\n\t\t\t// This is implicit dependency. If this flow changes, this code might no longer be correct.\n\t\t\tthis.hitError = true;\n\t\t\tthis.batchClientId = undefined;\n\t\t\tthis.emitter.emit(\"batchEnd\", error, message);\n\t\t\tthis.deltaScheduler.batchEnd(message);\n\t\t\treturn;\n\t\t}\n\n\t\tconst batch = (message?.metadata as IRuntimeMessageMetadata)?.batch;\n\t\t// If no batchClientId has been set then we're in an individual batch. Else, if we get\n\t\t// batch end metadata, this is end of the current batch.\n\t\tif (this.batchClientId === undefined || batch === false) {\n\t\t\tthis.batchClientId = undefined;\n\t\t\tthis.emitter.emit(\"batchEnd\", undefined, message);\n\t\t\tthis.deltaScheduler.batchEnd(message);\n\t\t\treturn;\n\t\t}\n\t}\n}\n\n/**\n * This class controls pausing and resuming of inbound queue to ensure that we never\n * start processing ops in a batch IF we do not have all ops in the batch.\n */\nclass ScheduleManagerCore {\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: IDeltaManager<ISequencedDocumentMessage, IDocumentMessage>,\n\t\tprivate readonly getClientId: () => string | undefined,\n\t\tprivate readonly logger: ITelemetryLogger,\n\t) {\n\t\t// Listen for delta manager sends and add batch metadata to messages\n\t\tthis.deltaManager.on(\"prepareSend\", (messages: IDocumentMessage[]) => {\n\t\t\tif (messages.length === 0) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// First message will have the batch flag set to true if doing a batched send\n\t\t\tconst firstMessageMetadata = messages[0].metadata as IRuntimeMessageMetadata;\n\t\t\tif (!firstMessageMetadata?.batch) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// If the batch contains only a single op, clear the batch flag.\n\t\t\tif (messages.length === 1) {\n\t\t\t\tdelete firstMessageMetadata.batch;\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Set the batch flag to false on the last message to indicate the end of the send batch\n\t\t\tconst lastMessage = messages[messages.length - 1];\n\t\t\tlastMessage.metadata = { ...lastMessage.metadata, batch: false };\n\t\t});\n\n\t\t// Listen for updates and peek at the inbound\n\t\tthis.deltaManager.inbound.on(\"push\", (message: ISequencedDocumentMessage) => {\n\t\t\tthis.trackPending(message);\n\t\t});\n\n\t\t// Start with baseline - empty inbound queue.\n\t\tassert(!this.localPaused, 0x293 /* \"initial state\" */);\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 0x296 assert when system ops\n\t\t// precedes start of incomplete batch.\n\t\tthis.deltaManager.on(\"op\", (message) => this.afterOpProcessing(message.sequenceNumber));\n\t}\n\n\t/**\n\t * The only public function in this class - called when we processed an op,\n\t * to make decision if op processing should be paused or not after that.\n\t */\n\tpublic afterOpProcessing(sequenceNumber: number) {\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\tassert(\n\t\t\t\tsequenceNumber < this.pauseSequenceNumber,\n\t\t\t\t0x296 /* \"we should never start processing incomplete batch!\" */,\n\t\t\t);\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 (sequenceNumber + 1 === this.pauseSequenceNumber) {\n\t\t\t\tthis.pauseQueue();\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate pauseQueue() {\n\t\tassert(!this.localPaused, 0x297 /* \"always called from resumed state\" */);\n\t\tthis.localPaused = true;\n\t\tthis.timePaused = performance.now();\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) {\n\t\tconst endBatch = messageEndBatch.sequenceNumber;\n\t\tconst duration = this.localPaused ? performance.now() - 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\t/**\n\t * Called for each incoming op (i.e. inbound \"push\" notification)\n\t */\n\tprivate trackPending(message: ISequencedDocumentMessage) {\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: 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: 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\tthis.currentBatchClientId = message.clientId;\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"]}
1
+ {"version":3,"file":"scheduleManager.js","sourceRoot":"","sources":["../src/scheduleManager.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 */\nimport { EventEmitter } from \"events\";\nimport { IDeltaManager } from \"@fluidframework/container-definitions\";\nimport { IDocumentMessage, ISequencedDocumentMessage } from \"@fluidframework/protocol-definitions\";\nimport { ITelemetryLogger } from \"@fluidframework/common-definitions\";\nimport { ChildLogger } from \"@fluidframework/telemetry-utils\";\nimport { assert, performance } from \"@fluidframework/common-utils\";\nimport { isRuntimeMessage } from \"@fluidframework/driver-utils\";\nimport {\n\tDataCorruptionError,\n\tDataProcessingError,\n\textractSafePropertiesFromMessage,\n} from \"@fluidframework/container-utils\";\nimport { DeltaScheduler } from \"./deltaScheduler\";\nimport { pkgVersion } from \"./packageVersion\";\n\ntype IRuntimeMessageMetadata =\n\t| undefined\n\t| {\n\t\t\tbatch?: boolean;\n\t };\n\n/**\n * This class has the following responsibilities:\n *\n * 1. It tracks batches as we process ops and raises \"batchBegin\" and \"batchEnd\" events.\n * As part of it, it validates batch correctness (i.e. no system ops in the middle of batch)\n *\n * 2. It creates instance of ScheduleManagerCore that ensures we never start processing ops from batch\n * unless all ops of the batch are in.\n */\nexport class ScheduleManager {\n\tprivate readonly deltaScheduler: DeltaScheduler;\n\tprivate batchClientId: string | undefined;\n\tprivate hitError = false;\n\n\tconstructor(\n\t\tprivate readonly deltaManager: IDeltaManager<ISequencedDocumentMessage, IDocumentMessage>,\n\t\tprivate readonly emitter: EventEmitter,\n\t\treadonly getClientId: () => string | undefined,\n\t\tprivate readonly logger: ITelemetryLogger,\n\t) {\n\t\tthis.deltaScheduler = new DeltaScheduler(\n\t\t\tthis.deltaManager,\n\t\t\tChildLogger.create(this.logger, \"DeltaScheduler\"),\n\t\t);\n\t\tvoid new ScheduleManagerCore(deltaManager, getClientId, logger);\n\t}\n\n\tpublic beforeOpProcessing(message: ISequencedDocumentMessage) {\n\t\tif (this.batchClientId !== message.clientId) {\n\t\t\tassert(\n\t\t\t\tthis.batchClientId === undefined,\n\t\t\t\t0x2a2 /* \"Batch is interrupted by other client op. Should be caught by trackPending()\" */,\n\t\t\t);\n\n\t\t\t// This could be the beginning of a new batch or an individual message.\n\t\t\tthis.emitter.emit(\"batchBegin\", message);\n\t\t\tthis.deltaScheduler.batchBegin(message);\n\n\t\t\tconst batch = (message?.metadata as IRuntimeMessageMetadata)?.batch;\n\t\t\tthis.batchClientId = batch ? message.clientId : undefined;\n\t\t}\n\t}\n\n\tpublic afterOpProcessing(error: any | undefined, message: ISequencedDocumentMessage) {\n\t\t// If this is no longer true, we need to revisit what we do where we set this.hitError.\n\t\tassert(!this.hitError, 0x2a3 /* \"container should be closed on any error\" */);\n\n\t\tif (error) {\n\t\t\t// We assume here that loader will close container and stop processing all future ops.\n\t\t\t// This is implicit dependency. If this flow changes, this code might no longer be correct.\n\t\t\tthis.hitError = true;\n\t\t\tthis.batchClientId = undefined;\n\t\t\tthis.emitter.emit(\"batchEnd\", error, message);\n\t\t\tthis.deltaScheduler.batchEnd(message);\n\t\t\treturn;\n\t\t}\n\n\t\tconst batch = (message?.metadata as IRuntimeMessageMetadata)?.batch;\n\t\t// If no batchClientId has been set then we're in an individual batch. Else, if we get\n\t\t// batch end metadata, this is end of the current batch.\n\t\tif (this.batchClientId === undefined || batch === false) {\n\t\t\tthis.batchClientId = undefined;\n\t\t\tthis.emitter.emit(\"batchEnd\", undefined, message);\n\t\t\tthis.deltaScheduler.batchEnd(message);\n\t\t\treturn;\n\t\t}\n\t}\n}\n\n/**\n * This class controls pausing and resuming of inbound queue to ensure that we never\n * start processing ops in a batch IF we do not have all ops in the batch.\n */\nclass ScheduleManagerCore {\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: IDeltaManager<ISequencedDocumentMessage, IDocumentMessage>,\n\t\tprivate readonly getClientId: () => string | undefined,\n\t\tprivate readonly logger: ITelemetryLogger,\n\t) {\n\t\t// Listen for delta manager sends and add batch metadata to messages\n\t\tthis.deltaManager.on(\"prepareSend\", (messages: IDocumentMessage[]) => {\n\t\t\tif (messages.length === 0) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// First message will have the batch flag set to true if doing a batched send\n\t\t\tconst firstMessageMetadata = messages[0].metadata as IRuntimeMessageMetadata;\n\t\t\tif (!firstMessageMetadata?.batch) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// If the batch contains only a single op, clear the batch flag.\n\t\t\tif (messages.length === 1) {\n\t\t\t\tdelete firstMessageMetadata.batch;\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Set the batch flag to false on the last message to indicate the end of the send batch\n\t\t\tconst lastMessage = messages[messages.length - 1];\n\t\t\tlastMessage.metadata = { ...lastMessage.metadata, batch: false };\n\t\t});\n\n\t\t// Listen for updates and peek at the inbound\n\t\tthis.deltaManager.inbound.on(\"push\", (message: ISequencedDocumentMessage) => {\n\t\t\tthis.trackPending(message);\n\t\t});\n\n\t\t// Start with baseline - empty inbound queue.\n\t\tassert(!this.localPaused, 0x293 /* \"initial state\" */);\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 0x296 assert when system ops\n\t\t// precedes start of incomplete batch.\n\t\tthis.deltaManager.on(\"op\", (message) => this.afterOpProcessing(message));\n\t}\n\n\t/**\n\t * The only public function in this class - called when we processed an op,\n\t * to make decision if op processing should be paused or not after that.\n\t */\n\tpublic afterOpProcessing(message: ISequencedDocumentMessage) {\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\"ScheduleManager\",\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?.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\tprivate pauseQueue() {\n\t\tassert(!this.localPaused, 0x297 /* \"always called from resumed state\" */);\n\t\tthis.localPaused = true;\n\t\tthis.timePaused = performance.now();\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) {\n\t\tconst endBatch = messageEndBatch.sequenceNumber;\n\t\tconst duration = this.localPaused ? performance.now() - 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\t/**\n\t * Called for each incoming op (i.e. inbound \"push\" notification)\n\t */\n\tprivate trackPending(message: ISequencedDocumentMessage) {\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: 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: 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\tthis.currentBatchClientId = message.clientId;\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"]}
@@ -1562,7 +1562,7 @@ export class ContainerRuntime extends TypedEventEmitter {
1562
1562
  * @param options - options controlling how the summary is generated or submitted
1563
1563
  */
1564
1564
  async submitSummary(options) {
1565
- var _a, _b, _c, _d;
1565
+ var _a, _b, _c;
1566
1566
  const { fullTree = false, refreshLatestAck, summaryLogger } = options;
1567
1567
  // The summary number for this summary. This will be updated during the summary process, so get it now and
1568
1568
  // use it for all events logged during this summary.
@@ -1667,7 +1667,7 @@ export class ContainerRuntime extends TypedEventEmitter {
1667
1667
  const gcSummaryTreeStats = summaryTree.tree[gcTreeKey]
1668
1668
  ? calculateStats(summaryTree.tree[gcTreeKey])
1669
1669
  : undefined;
1670
- const summaryStats = Object.assign({ dataStoreCount: this.dataStores.size, summarizedDataStoreCount: this.dataStores.size - handleCount, gcStateUpdatedDataStoreCount: (_a = summarizeResult.gcStats) === null || _a === void 0 ? void 0 : _a.updatedDataStoreCount, gcBlobNodeCount: gcSummaryTreeStats === null || gcSummaryTreeStats === void 0 ? void 0 : gcSummaryTreeStats.blobNodeCount, gcTotalBlobsSize: gcSummaryTreeStats === null || gcSummaryTreeStats === void 0 ? void 0 : gcSummaryTreeStats.totalBlobSize, summaryNumber }, partialStats);
1670
+ const summaryStats = Object.assign({ dataStoreCount: this.dataStores.size, summarizedDataStoreCount: this.dataStores.size - handleCount, gcStateUpdatedDataStoreCount: this.garbageCollector.updatedDSCountSinceLastSummary, gcBlobNodeCount: gcSummaryTreeStats === null || gcSummaryTreeStats === void 0 ? void 0 : gcSummaryTreeStats.blobNodeCount, gcTotalBlobsSize: gcSummaryTreeStats === null || gcSummaryTreeStats === void 0 ? void 0 : gcSummaryTreeStats.totalBlobSize, summaryNumber }, partialStats);
1671
1671
  const generateSummaryData = {
1672
1672
  referenceSequenceNumber: summaryRefSeqNum,
1673
1673
  minimumSequenceNumber,
@@ -1697,7 +1697,7 @@ export class ContainerRuntime extends TypedEventEmitter {
1697
1697
  else if (lastAck === undefined) {
1698
1698
  summaryContext = {
1699
1699
  proposalHandle: undefined,
1700
- ackHandle: (_b = this.context.getLoadedFromVersion()) === null || _b === void 0 ? void 0 : _b.id,
1700
+ ackHandle: (_a = this.context.getLoadedFromVersion()) === null || _a === void 0 ? void 0 : _a.id,
1701
1701
  referenceSequenceNumber: summaryRefSeqNum,
1702
1702
  };
1703
1703
  }
@@ -1743,7 +1743,7 @@ export class ContainerRuntime extends TypedEventEmitter {
1743
1743
  // Cleanup wip summary in case of failure
1744
1744
  this.summarizerNode.clearSummary();
1745
1745
  // ! This needs to happen before we resume inbound queues to ensure heuristics are tracked correctly
1746
- (_d = (_c = this._summarizer) === null || _c === void 0 ? void 0 : _c.recordSummaryAttempt) === null || _d === void 0 ? void 0 : _d.call(_c, summaryRefSeqNum);
1746
+ (_c = (_b = this._summarizer) === null || _b === void 0 ? void 0 : _b.recordSummaryAttempt) === null || _c === void 0 ? void 0 : _c.call(_b, summaryRefSeqNum);
1747
1747
  // Restart the delta manager
1748
1748
  this.deltaManager.inbound.resume();
1749
1749
  if (shouldPauseInboundSignal) {