@fluidframework/container-runtime 2.111.0 → 2.113.0-411909

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 (48) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/container-runtime.test-files.tar +0 -0
  3. package/dist/blobManager/blobManager.d.ts +6 -3
  4. package/dist/blobManager/blobManager.d.ts.map +1 -1
  5. package/dist/blobManager/blobManager.js +19 -5
  6. package/dist/blobManager/blobManager.js.map +1 -1
  7. package/dist/containerRuntime.d.ts +31 -0
  8. package/dist/containerRuntime.d.ts.map +1 -1
  9. package/dist/containerRuntime.js +69 -10
  10. package/dist/containerRuntime.js.map +1 -1
  11. package/dist/packageVersion.d.ts +1 -1
  12. package/dist/packageVersion.d.ts.map +1 -1
  13. package/dist/packageVersion.js +1 -1
  14. package/dist/packageVersion.js.map +1 -1
  15. package/dist/pendingStateManager.d.ts +7 -0
  16. package/dist/pendingStateManager.d.ts.map +1 -1
  17. package/dist/pendingStateManager.js +15 -0
  18. package/dist/pendingStateManager.js.map +1 -1
  19. package/dist/runtimeLayerCompatState.d.ts +1 -1
  20. package/dist/summary/summarizerNode/summarizerNode.d.ts.map +1 -1
  21. package/dist/summary/summarizerNode/summarizerNode.js +6 -5
  22. package/dist/summary/summarizerNode/summarizerNode.js.map +1 -1
  23. package/lib/blobManager/blobManager.d.ts +6 -3
  24. package/lib/blobManager/blobManager.d.ts.map +1 -1
  25. package/lib/blobManager/blobManager.js +19 -5
  26. package/lib/blobManager/blobManager.js.map +1 -1
  27. package/lib/containerRuntime.d.ts +31 -0
  28. package/lib/containerRuntime.d.ts.map +1 -1
  29. package/lib/containerRuntime.js +69 -10
  30. package/lib/containerRuntime.js.map +1 -1
  31. package/lib/packageVersion.d.ts +1 -1
  32. package/lib/packageVersion.d.ts.map +1 -1
  33. package/lib/packageVersion.js +1 -1
  34. package/lib/packageVersion.js.map +1 -1
  35. package/lib/pendingStateManager.d.ts +7 -0
  36. package/lib/pendingStateManager.d.ts.map +1 -1
  37. package/lib/pendingStateManager.js +15 -0
  38. package/lib/pendingStateManager.js.map +1 -1
  39. package/lib/runtimeLayerCompatState.d.ts +1 -1
  40. package/lib/summary/summarizerNode/summarizerNode.d.ts.map +1 -1
  41. package/lib/summary/summarizerNode/summarizerNode.js +6 -5
  42. package/lib/summary/summarizerNode/summarizerNode.js.map +1 -1
  43. package/package.json +18 -18
  44. package/src/blobManager/blobManager.ts +20 -4
  45. package/src/containerRuntime.ts +78 -11
  46. package/src/packageVersion.ts +1 -1
  47. package/src/pendingStateManager.ts +16 -0
  48. package/src/summary/summarizerNode/summarizerNode.ts +9 -7
@@ -1 +1 @@
1
- {"version":3,"file":"summarizerNode.js","sourceRoot":"","sources":["../../../src/summary/summarizerNode/summarizerNode.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport type { ITelemetryBaseLogger } from \"@fluidframework/core-interfaces\";\nimport { assert, unreachableCase } from \"@fluidframework/core-utils/internal\";\nimport { SummaryType } from \"@fluidframework/driver-definitions\";\nimport type {\n\tISnapshotTree,\n\tISequencedDocumentMessage,\n} from \"@fluidframework/driver-definitions/internal\";\nimport {\n\tchannelsTreeName,\n\ttype IExperimentalIncrementalSummaryContext,\n\ttype ITelemetryContext,\n\ttype CreateChildSummarizerNodeParam,\n\tCreateSummarizerNodeSource,\n\ttype ISummarizeResult,\n\ttype ISummarizerNode,\n\ttype ISummarizerNodeConfig,\n\ttype SummarizeInternalFn,\n} from \"@fluidframework/runtime-definitions/internal\";\nimport { mergeStats } from \"@fluidframework/runtime-utils/internal\";\nimport {\n\tLoggingError,\n\tPerformanceEvent,\n\tTelemetryDataTag,\n\tcreateChildLogger,\n\ttagCodeArtifacts,\n} from \"@fluidframework/telemetry-utils/internal\";\nimport type {\n\tITelemetryErrorEventExt,\n\tTelemetryLoggerExt,\n} from \"@fluidframework/telemetry-utils/internal\";\n\nimport type {\n\tICreateChildDetails,\n\tIRefreshSummaryResult,\n\tIStartSummaryResult,\n\tISummarizerNodeRootContract,\n\tValidateSummaryResult,\n\tPendingSummaryInfo,\n} from \"./summarizerNodeUtils.js\";\n\nexport interface IRootSummarizerNode extends ISummarizerNode, ISummarizerNodeRootContract {}\n\n/**\n * Encapsulates the summarizing work and state of an individual tree node in the\n * summary tree. It tracks changes and allows for optimizations when unchanged, or\n * can allow for fallback summaries to be generated when an error is encountered.\n * Usage is for the root node to call startSummary first to begin tracking a WIP\n * (work in progress) summary. Then all nodes will call summarize to summaries their\n * individual parts. Once completed and uploaded to storage, the root node will call\n * completeSummary or clearSummary to clear the WIP summary tracking state if something\n * went wrong. The SummarizerNodes will track all pending summaries that have been\n * recorded by the completeSummary call. When one of them is acked, the root node should\n * call refreshLatestSummary to inform the tree of SummarizerNodes of the new baseline\n * latest successful summary.\n */\nexport class SummarizerNode implements IRootSummarizerNode {\n\t/**\n\t * The reference sequence number of the most recent acked summary.\n\t * Returns 0 if there is not yet an acked summary.\n\t */\n\tpublic get referenceSequenceNumber(): number {\n\t\treturn this._lastSummaryReferenceSequenceNumber ?? 0;\n\t}\n\n\t/**\n\t * returns the handle of the last successful summary of this summarizerNode in string format\n\t * (this getter is primarily only used in the test code)\n\t */\n\tpublic get summaryHandleId(): string {\n\t\treturn this._summaryHandleId;\n\t}\n\n\tprotected readonly children = new Map<string, SummarizerNode>();\n\t/**\n\t * Key value pair of summaries submitted by this client which are not yet acked.\n\t * Key is the proposalHandle and value is the summary op's referece sequence number.\n\t */\n\tprotected readonly pendingSummaries = new Map<string, PendingSummaryInfo>();\n\tprotected wipReferenceSequenceNumber: number | undefined;\n\t/**\n\t * True if the current node was summarized during the current summary process\n\t * This flag is used to identify scenarios where summarize was not called on a node.\n\t * For example, this node was created after its parent was already summarized due to out-of-order realization via application code.\n\t */\n\tprivate wipSummarizeCalled: boolean = false;\n\tprivate wipSkipRecursion = false;\n\n\tprotected readonly logger: TelemetryLoggerExt;\n\n\t/**\n\t * Do not call constructor directly.\n\t * Use createRootSummarizerNode to create root node, or createChild to create child nodes.\n\t */\n\tpublic constructor(\n\t\tbaseLogger: ITelemetryBaseLogger,\n\t\tprivate readonly summarizeInternalFn: SummarizeInternalFn,\n\t\tconfig: ISummarizerNodeConfig,\n\t\t/**\n\t\t * Encoded handle or path to the node\n\t\t */\n\t\tprivate readonly _summaryHandleId: string,\n\t\tprivate _changeSequenceNumber: number,\n\t\t/**\n\t\t * Summary reference sequence number, i.e. last sequence number seen when last successful summary was created\n\t\t */\n\t\tprivate _lastSummaryReferenceSequenceNumber?: number,\n\t\tprotected wipSummaryLogger?: ITelemetryBaseLogger,\n\t\t/**\n\t\t * A unique id of this node to be logged when sending telemetry.\n\t\t */\n\t\tprotected telemetryNodeId?: string,\n\t) {\n\t\tthis.canReuseHandle = config.canReuseHandle ?? true;\n\t\t// All logs posted by the summarizer node should include the telemetryNodeId.\n\t\tthis.logger = createChildLogger({\n\t\t\tlogger: baseLogger,\n\t\t\tproperties: {\n\t\t\t\tall: tagCodeArtifacts({ id: this.telemetryNodeId }),\n\t\t\t},\n\t\t});\n\t}\n\n\t/**\n\t * In order to produce a summary with a summarizer node, the summarizer node system must be notified a summary has\n\t * started. This is done by calling startSummary. This will track the reference sequence number of the summary and\n\t * run some validation checks to ensure the summary is correct.\n\t * @param referenceSequenceNumber - the number of ops processed up to this point\n\t * @param summaryLogger - the logger to use for the summary\n\t * @param latestSummaryRefSeqNum - the reference sequence number of the latest summary. Another way to think about\n\t * it is the reference sequence number of the previous summary.\n\t * @returns the number of nodes in the tree, the number of nodes that are invalid, and the different types of\n\t * sequence number mismatches\n\t */\n\tpublic startSummary(\n\t\treferenceSequenceNumber: number,\n\t\tsummaryLogger: ITelemetryBaseLogger,\n\t\tlatestSummaryRefSeqNum: number,\n\t): IStartSummaryResult {\n\t\tassert(\n\t\t\tthis.wipSummaryLogger === undefined,\n\t\t\t0x19f /* \"wipSummaryLogger should not be set yet in startSummary\" */,\n\t\t);\n\t\tassert(\n\t\t\tthis.wipReferenceSequenceNumber === undefined,\n\t\t\t0x1a0 /* \"Already tracking a summary\" */,\n\t\t);\n\n\t\tlet nodes = 1; // number of summarizerNodes at the start of the summary\n\t\tlet invalidNodes = 0;\n\t\tconst sequenceNumberMismatchKeySet = new Set<string>();\n\t\tconst nodeLatestSummaryRefSeqNum = this._lastSummaryReferenceSequenceNumber;\n\t\tif (\n\t\t\tnodeLatestSummaryRefSeqNum !== undefined &&\n\t\t\tlatestSummaryRefSeqNum !== nodeLatestSummaryRefSeqNum\n\t\t) {\n\t\t\tinvalidNodes++;\n\t\t\tsequenceNumberMismatchKeySet.add(\n\t\t\t\t`${latestSummaryRefSeqNum}-${nodeLatestSummaryRefSeqNum}`,\n\t\t\t);\n\t\t}\n\n\t\tthis.wipSummaryLogger = summaryLogger;\n\n\t\tfor (const child of this.children.values()) {\n\t\t\tconst childStartSummaryResult = child.startSummary(\n\t\t\t\treferenceSequenceNumber,\n\t\t\t\tthis.wipSummaryLogger,\n\t\t\t\tlatestSummaryRefSeqNum,\n\t\t\t);\n\t\t\tnodes += childStartSummaryResult.nodes;\n\t\t\tinvalidNodes += childStartSummaryResult.invalidNodes;\n\t\t\tfor (const invalidSequenceNumber of childStartSummaryResult.mismatchNumbers) {\n\t\t\t\tsequenceNumberMismatchKeySet.add(invalidSequenceNumber);\n\t\t\t}\n\t\t}\n\t\tthis.wipReferenceSequenceNumber = referenceSequenceNumber;\n\t\treturn {\n\t\t\tnodes,\n\t\t\tinvalidNodes,\n\t\t\tmismatchNumbers: sequenceNumberMismatchKeySet,\n\t\t};\n\t}\n\n\tpublic async summarize(\n\t\tfullTree: boolean,\n\t\ttrackState: boolean = true,\n\t\ttelemetryContext?: ITelemetryContext,\n\t): Promise<ISummarizeResult> {\n\t\t// If trackState is false, call summarize internal directly and don't track any state.\n\t\tif (!trackState) {\n\t\t\treturn this.summarizeInternalFn(fullTree, trackState, telemetryContext);\n\t\t}\n\n\t\t// Set to wipSummarizeCalled true to represent that current node was included in the summary process.\n\t\tthis.wipSummarizeCalled = true;\n\n\t\t// Try to reuse the tree if unchanged\n\t\tif (\n\t\t\tthis.canReuseHandle &&\n\t\t\t!fullTree &&\n\t\t\t!this.hasChanged() &&\n\t\t\tthis._lastSummaryReferenceSequenceNumber !== undefined\n\t\t) {\n\t\t\tthis.wipSkipRecursion = true;\n\t\t\tconst stats = mergeStats();\n\t\t\tstats.handleNodeCount++;\n\t\t\treturn {\n\t\t\t\tsummary: {\n\t\t\t\t\ttype: SummaryType.Handle,\n\t\t\t\t\thandle: this.summaryHandleId,\n\t\t\t\t\thandleType: SummaryType.Tree,\n\t\t\t\t},\n\t\t\t\tstats,\n\t\t\t};\n\t\t}\n\n\t\tlet incrementalSummaryContext: IExperimentalIncrementalSummaryContext | undefined;\n\t\tif (!fullTree) {\n\t\t\tassert(\n\t\t\t\tthis.wipReferenceSequenceNumber !== undefined,\n\t\t\t\t0x5df /* Summarize should not be called when not tracking the summary */,\n\t\t\t);\n\t\t\tincrementalSummaryContext =\n\t\t\t\tthis._lastSummaryReferenceSequenceNumber === undefined\n\t\t\t\t\t? undefined\n\t\t\t\t\t: {\n\t\t\t\t\t\t\tsummarySequenceNumber: this.wipReferenceSequenceNumber,\n\t\t\t\t\t\t\tlatestSummarySequenceNumber: this._lastSummaryReferenceSequenceNumber,\n\t\t\t\t\t\t\t// TODO: remove summaryPath.\n\t\t\t\t\t\t\tsummaryPath: this.summaryHandleId,\n\t\t\t\t\t\t};\n\t\t}\n\n\t\tconst result = await this.summarizeInternalFn(\n\t\t\tfullTree,\n\t\t\ttrackState,\n\t\t\ttelemetryContext,\n\t\t\tincrementalSummaryContext,\n\t\t);\n\n\t\treturn { summary: result.summary, stats: result.stats };\n\t}\n\n\t/**\n\t * Validates that the in-progress summary is correct, i.e., summarize should have run for all non-skipped\n\t * nodes. This will only be called for the root summarizer node and is called by it recursively on all child nodes.\n\t *\n\t * @returns ValidateSummaryResult which contains a boolean success indicating whether the validation was successful.\n\t * In case of failure, additional information is returned indicating type of failure and where it was.\n\t */\n\tpublic validateSummary(): ValidateSummaryResult {\n\t\treturn this.validateSummaryCore(false /* parentSkipRecursion */);\n\t}\n\n\t/**\n\t * Validates that the in-progress summary is correct for all nodes, i.e., summarize should have run for all\n\t * non-skipped nodes.\n\t * @param parentSkipRecursion - true if the parent of this node skipped recursing the child nodes when summarizing.\n\t * In that case, the children will not have work-in-progress state.\n\t *\n\t * @returns ValidateSummaryResult which contains a boolean success indicating whether the validation was successful.\n\t * In case of failure, additional information is returned indicating type of failure and where it was.\n\t */\n\tprotected validateSummaryCore(parentSkipRecursion: boolean): ValidateSummaryResult {\n\t\tif (this.wasSummarizeMissed(parentSkipRecursion)) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\treason: \"NodeDidNotSummarize\",\n\t\t\t\tid: {\n\t\t\t\t\ttag: TelemetryDataTag.CodeArtifact,\n\t\t\t\t\tvalue: this.telemetryNodeId,\n\t\t\t\t},\n\t\t\t\t// These errors are usually transient and should go away when summarize is retried.\n\t\t\t\tretryAfterSeconds: 1,\n\t\t\t};\n\t\t}\n\t\tif (parentSkipRecursion) {\n\t\t\treturn { success: true };\n\t\t}\n\n\t\tfor (const child of this.children.values()) {\n\t\t\tconst result = child.validateSummaryCore(this.wipSkipRecursion || parentSkipRecursion);\n\t\t\t// If any child fails, return the failure.\n\t\t\tif (!result.success) {\n\t\t\t\treturn result;\n\t\t\t}\n\t\t}\n\t\treturn { success: true };\n\t}\n\n\tprivate wasSummarizeMissed(parentSkipRecursion: boolean): boolean {\n\t\tassert(\n\t\t\tthis.wipSummaryLogger !== undefined,\n\t\t\t0x6fc /* wipSummaryLogger should have been set in startSummary or ctor */,\n\t\t);\n\t\tassert(this.wipReferenceSequenceNumber !== undefined, 0x6fd /* Not tracking a summary */);\n\n\t\t// If the parent node skipped recursion, it did not call summarize on this node. So, summarize was not missed\n\t\t// but was intentionally not called.\n\t\t// Otherwise, summarize should have been called on this node and wipSummarizeCalled must be set.\n\t\tif (parentSkipRecursion || this.wipSummarizeCalled) {\n\t\t\treturn false;\n\t\t}\n\n\t\t/**\n\t\t * The absence of wip local path indicates that summarize was not called for this node. Return failure.\n\t\t * This can happen if:\n\t\t * 1. A child node was created after summarize was already called on the parent. For example, a data store\n\t\t * is realized (loaded) after summarize was called on it creating summarizer nodes for its DDSes. In this case,\n\t\t * parentSkipRecursion will be true and the if block above would handle it.\n\t\t * 2. A new node was created but summarize was never called on it. This can mean that the summary that is\n\t\t * generated may not have the data from this node. We should not continue, log and throw an error. This\n\t\t * will help us identify these cases and take appropriate action.\n\t\t *\n\t\t * This happens due to scenarios such as data store created during summarize. Such errors should go away when\n\t\t * summarize is attempted again.\n\t\t */\n\t\treturn true;\n\t}\n\n\t/**\n\t * Called after summary has been uploaded to the server. Add the work-in-progress state to the pending summary\n\t * queue. We track this until we get an ack from the server for this summary.\n\t * @param proposalHandle - The handle of the summary that was uploaded to the server.\n\t */\n\tpublic completeSummary(proposalHandle: string): void {\n\t\tthis.completeSummaryCore(proposalHandle, false /* parentSkipRecursion */);\n\t}\n\n\t/**\n\t * Recursive implementation for completeSummary, with additional internal-only parameters.\n\t * @param proposalHandle - The handle of the summary that was uploaded to the server.\n\t * @param parentPath - The path of the parent node which is used to build the path of this node.\n\t * @param parentSkipRecursion - true if the parent of this node skipped recursing the child nodes when summarizing.\n\t * In that case, the children will not have work-in-progress state.\n\t * @param validate - true to validate that the in-progress summary is correct for all nodes.\n\t */\n\tprotected completeSummaryCore(proposalHandle: string, parentSkipRecursion: boolean): void {\n\t\tassert(\n\t\t\tthis.wipReferenceSequenceNumber !== undefined,\n\t\t\t0x1a4 /* \"Not tracking a summary\" */,\n\t\t);\n\t\tif (parentSkipRecursion && this._lastSummaryReferenceSequenceNumber === undefined) {\n\t\t\t// This case the child is added after the latest non-failure summary.\n\t\t\t// This node and all children should consider themselves as still not\n\t\t\t// having a successful summary yet.\n\t\t\t// We cannot \"reuse\" this node if unchanged since that summary, because\n\t\t\t// handles will be unable to point to that node. It never made it to the\n\t\t\t// tree itself, and only exists as an attach op in the _outstandingOps.\n\t\t\tthis.clearSummary();\n\t\t\treturn;\n\t\t}\n\n\t\tfor (const child of this.children.values()) {\n\t\t\tchild.completeSummaryCore(proposalHandle, this.wipSkipRecursion || parentSkipRecursion);\n\t\t}\n\t\t// Note that this overwrites existing pending summary with\n\t\t// the same proposalHandle. If proposalHandle is something like\n\t\t// a hash or unique identifier, this should be fine. If storage\n\t\t// can return the same proposalHandle for a different summary,\n\t\t// this should still be okay, because we should be proposing the\n\t\t// newer one later which would have to overwrite the previous one.\n\t\tthis.pendingSummaries.set(proposalHandle, {\n\t\t\treferenceSequenceNumber: this.wipReferenceSequenceNumber,\n\t\t});\n\t\tthis.clearSummary();\n\t}\n\n\tpublic clearSummary(): void {\n\t\tthis.wipReferenceSequenceNumber = undefined;\n\t\tthis.wipSummarizeCalled = false;\n\t\tthis.wipSkipRecursion = false;\n\t\tthis.wipSummaryLogger = undefined;\n\t\tfor (const child of this.children.values()) {\n\t\t\tchild.clearSummary();\n\t\t}\n\t}\n\n\t/**\n\t * Refreshes the latest summary tracked by this node. If we have a pending summary for the given proposal handle,\n\t * it becomes the latest summary. If the current summary is already ahead, we skip the update.\n\t * If the current summary is behind, then we do not refresh.\n\t * @param proposalHandle - Handle of the generated / uploaded summary.\n\t * @param summaryRefSeq - Reference sequence of the acked summary\n\t * @returns true if the summary is tracked by this node, false otherwise.\n\t */\n\tpublic async refreshLatestSummary(\n\t\tproposalHandle: string,\n\t\tsummaryRefSeq: number,\n\t): Promise<IRefreshSummaryResult> {\n\t\tconst eventProps: {\n\t\t\tproposalHandle: string | undefined;\n\t\t\tsummaryRefSeq: number;\n\t\t\treferenceSequenceNumber: number;\n\t\t\tisSummaryTracked?: boolean;\n\t\t\tpendingSummaryFound?: boolean;\n\t\t} = {\n\t\t\tproposalHandle,\n\t\t\tsummaryRefSeq,\n\t\t\treferenceSequenceNumber: this.referenceSequenceNumber,\n\t\t};\n\t\treturn PerformanceEvent.timedExecAsync(\n\t\t\tthis.logger,\n\t\t\t{\n\t\t\t\teventName: \"refreshLatestSummary\",\n\t\t\t\t...eventProps,\n\t\t\t},\n\t\t\tasync (event) => {\n\t\t\t\t// Refresh latest summary should not happen while a summary is in progress. If it does, it can result\n\t\t\t\t// in inconsistent state, so, we should not continue;\n\t\t\t\tif (this.isSummaryInProgress()) {\n\t\t\t\t\tthrow new LoggingError(\"UnexpectedRefreshDuringSummarize\", {\n\t\t\t\t\t\tinProgressSummaryRefSeq: this.wipReferenceSequenceNumber,\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\tlet isSummaryTracked = false;\n\t\t\t\tlet isSummaryNewer = false;\n\n\t\t\t\tif (summaryRefSeq > this.referenceSequenceNumber) {\n\t\t\t\t\tisSummaryNewer = true;\n\t\t\t\t}\n\n\t\t\t\t// If the acked summary is found in the pendingSummaries, it means the summary was created and tracked by the current client\n\t\t\t\t// so set the isSummaryTracked to true.\n\t\t\t\tconst pendingSummary = this.pendingSummaries.get(proposalHandle);\n\t\t\t\tif (pendingSummary?.referenceSequenceNumber !== undefined) {\n\t\t\t\t\tisSummaryTracked = true;\n\t\t\t\t\t// update the pendingSummariesMap for the root and all child summarizerNodes\n\t\t\t\t\tthis.refreshLatestSummaryFromPending(\n\t\t\t\t\t\tproposalHandle,\n\t\t\t\t\t\tpendingSummary.referenceSequenceNumber,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tevent.end({ ...eventProps, isSummaryNewer, pendingSummaryFound: isSummaryTracked });\n\t\t\t\treturn { isSummaryTracked, isSummaryNewer };\n\t\t\t},\n\t\t\t{ start: true, end: true, cancel: \"error\" },\n\t\t);\n\t}\n\t/**\n\t * Called when we get an ack from the server for a summary we've just sent. Updates the reference state of this node\n\t * from the state in the pending summary queue.\n\t * @param proposalHandle - Handle for the current proposal.\n\t * @param referenceSequenceNumber - Reference sequence number of sent summary.\n\t */\n\tprotected refreshLatestSummaryFromPending(\n\t\tproposalHandle: string,\n\t\treferenceSequenceNumber: number,\n\t): void {\n\t\tconst pendingSummary = this.pendingSummaries.get(proposalHandle);\n\t\tif (pendingSummary === undefined) {\n\t\t\t// This should only happen if parent skipped recursion AND no prior summary existed.\n\t\t\tassert(\n\t\t\t\tthis._lastSummaryReferenceSequenceNumber === undefined,\n\t\t\t\t0x1a6 /* \"Not found pending summary, but this node has previously completed a summary\" */,\n\t\t\t);\n\t\t\treturn;\n\t\t} else {\n\t\t\tassert(\n\t\t\t\treferenceSequenceNumber === pendingSummary.referenceSequenceNumber,\n\t\t\t\t0x1a7 /* Pending summary reference sequence number should be consistent */,\n\t\t\t);\n\n\t\t\t// Clear earlier pending summaries\n\t\t\tthis.pendingSummaries.delete(proposalHandle);\n\t\t}\n\n\t\t// Delete all summaries whose reference sequence number is smaller than the one just acked.\n\t\tfor (const [key, summary] of this.pendingSummaries) {\n\t\t\tif (summary.referenceSequenceNumber < referenceSequenceNumber) {\n\t\t\t\tthis.pendingSummaries.delete(key);\n\t\t\t}\n\t\t}\n\t\t// Update the latest successful summary reference number\n\t\tthis._lastSummaryReferenceSequenceNumber = pendingSummary.referenceSequenceNumber;\n\t\t// Propagate update to all child nodes\n\t\tfor (const child of this.children.values()) {\n\t\t\tchild.refreshLatestSummaryFromPending(proposalHandle, referenceSequenceNumber);\n\t\t}\n\t}\n\n\tpublic updateBaseSummaryState(snapshot: ISnapshotTree): void {\n\t\t// Function deprecated. Empty declaration is kept around to compat failures.\n\t}\n\n\tpublic recordChange(op: ISequencedDocumentMessage): void {\n\t\tthis.invalidate(op.sequenceNumber);\n\t}\n\n\tpublic invalidate(sequenceNumber: number): void {\n\t\tif (sequenceNumber > this._changeSequenceNumber) {\n\t\t\tthis._changeSequenceNumber = sequenceNumber;\n\t\t}\n\t}\n\n\t/**\n\t * True if a change has been recorded with sequence number exceeding\n\t * the latest successfully acked summary reference sequence number.\n\t * False implies that the previous summary can be reused.\n\t */\n\tprotected hasChanged(): boolean {\n\t\treturn this._changeSequenceNumber > this.referenceSequenceNumber;\n\t}\n\n\tprotected readonly canReuseHandle: boolean;\n\n\tpublic createChild(\n\t\t/**\n\t\t * Summarize function\n\t\t */\n\t\tsummarizeInternalFn: SummarizeInternalFn,\n\t\t/**\n\t\t * Initial id or path part of this node\n\t\t */\n\t\tid: string,\n\t\t/**\n\t\t * Information needed to create the node.\n\t\t * If it is from a base summary, it will assert that a summary has been seen.\n\t\t * Attach information if it is created from an attach op.\n\t\t */\n\t\tcreateParam: CreateChildSummarizerNodeParam,\n\t\tconfig: ISummarizerNodeConfig = {},\n\t): ISummarizerNode {\n\t\tassert(!this.children.has(id), 0x1ab /* \"Create SummarizerNode child already exists\" */);\n\n\t\tconst createDetails: ICreateChildDetails = this.getCreateDetailsForChild(id, createParam);\n\t\tconst child = new SummarizerNode(\n\t\t\tthis.logger,\n\t\t\tsummarizeInternalFn,\n\t\t\tconfig,\n\t\t\tcreateDetails.summaryHandleId,\n\t\t\tcreateDetails.changeSequenceNumber,\n\t\t\tcreateDetails.lastSummaryReferenceSequenceNumber,\n\t\t\tthis.wipSummaryLogger,\n\t\t\tcreateDetails.telemetryNodeId,\n\t\t);\n\n\t\t// There may be additional state that has to be updated in this child. For example, if a summary is being\n\t\t// tracked, the child's summary tracking state needs to be updated too. Same goes for pendingSummaries we might\n\t\t// have outstanding on the parent in case we realize nodes in between Summary Op and Summary Ack.\n\t\tthis.maybeUpdateChildState(child, id);\n\n\t\tthis.children.set(id, child);\n\t\treturn child;\n\t}\n\n\tpublic getChild(id: string): ISummarizerNode | undefined {\n\t\treturn this.children.get(id);\n\t}\n\n\t/**\n\t * Returns the details needed to create a child node.\n\t * @param id - Initial id or path part of the child node.\n\t * @param createParam - Information needed to create the node.\n\t * @returns the details needed to create the child node.\n\t */\n\tprotected getCreateDetailsForChild(\n\t\tid: string,\n\t\tcreateParam: CreateChildSummarizerNodeParam,\n\t): ICreateChildDetails {\n\t\tlet childLastSummaryReferenceSequenceNumber: number | undefined;\n\t\tlet changeSequenceNumber: number;\n\n\t\tconst parentLastSummaryReferenceSequenceNumber = this._lastSummaryReferenceSequenceNumber;\n\t\tswitch (createParam.type) {\n\t\t\tcase CreateSummarizerNodeSource.FromAttach: {\n\t\t\t\tif (\n\t\t\t\t\tparentLastSummaryReferenceSequenceNumber !== undefined &&\n\t\t\t\t\tcreateParam.sequenceNumber <= parentLastSummaryReferenceSequenceNumber\n\t\t\t\t) {\n\t\t\t\t\t// Prioritize latest summary if it was after this node was attached.\n\t\t\t\t\tchildLastSummaryReferenceSequenceNumber = parentLastSummaryReferenceSequenceNumber;\n\t\t\t\t}\n\t\t\t\tchangeSequenceNumber = createParam.sequenceNumber;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase CreateSummarizerNodeSource.FromSummary:\n\t\t\tcase CreateSummarizerNodeSource.Local: {\n\t\t\t\tchildLastSummaryReferenceSequenceNumber = parentLastSummaryReferenceSequenceNumber;\n\t\t\t\tchangeSequenceNumber = parentLastSummaryReferenceSequenceNumber ?? -1;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tdefault: {\n\t\t\t\tconst type = (createParam as unknown as CreateChildSummarizerNodeParam).type;\n\t\t\t\tunreachableCase(createParam, `Unexpected CreateSummarizerNodeSource: ${type}`);\n\t\t\t}\n\t\t}\n\n\t\tconst childTelemetryNodeId = `${this.telemetryNodeId ?? \"\"}/${id}`;\n\t\t// parentHandleId/.channels/childId\n\t\tconst childSummaryHandleId = `${this._summaryHandleId}/${channelsTreeName}/${id}`;\n\n\t\treturn {\n\t\t\tchangeSequenceNumber,\n\t\t\ttelemetryNodeId: childTelemetryNodeId,\n\t\t\tsummaryHandleId: childSummaryHandleId,\n\t\t\tlastSummaryReferenceSequenceNumber: childLastSummaryReferenceSequenceNumber,\n\t\t};\n\t}\n\n\t/**\n\t * Updates the state of the child if required. For example, if a summary is currently being tracked, the child's\n\t * summary tracking state needs to be updated too.\n\t * Also, in case a child node gets realized in between Summary Op and Summary Ack, let's initialize the child's\n\t * pending summary as well.\n\t * @param child - The child node whose state is to be updated.\n\t * @param id - Initial id or path part of this node\n\t *\n\t */\n\tprotected maybeUpdateChildState(child: SummarizerNode, id: string): void {\n\t\t// If a summary is in progress, this child was created after the summary started. So, we need to update the\n\t\t// child's summary state as well.\n\t\tif (this.isSummaryInProgress()) {\n\t\t\tchild.wipReferenceSequenceNumber = this.wipReferenceSequenceNumber;\n\t\t}\n\t\t// In case we have pending summaries on the parent, let's initialize it on the child.\n\t\tif (child._lastSummaryReferenceSequenceNumber !== undefined) {\n\t\t\tfor (const [proposedHandle, pendingSummaryInfo] of this.pendingSummaries.entries()) {\n\t\t\t\tchild.addPendingSummary(proposedHandle, pendingSummaryInfo);\n\t\t\t}\n\t\t}\n\t}\n\n\tprotected addPendingSummary(key: string, pendingSummaryInfo: PendingSummaryInfo): void {\n\t\tthis.pendingSummaries.set(key, pendingSummaryInfo);\n\t}\n\n\t/**\n\t * Tells whether summary tracking is in progress. True if \"startSummary\" API is called before summarize.\n\t */\n\tpublic isSummaryInProgress(): boolean {\n\t\treturn this.wipReferenceSequenceNumber !== undefined;\n\t}\n\n\t/**\n\t * Creates and throws an error due to unexpected conditions.\n\t */\n\tprotected throwUnexpectedError(eventProps: ITelemetryErrorEventExt): never {\n\t\tconst error = new LoggingError(eventProps.eventName, {\n\t\t\t...eventProps,\n\t\t\treferenceSequenceNumber: this.wipReferenceSequenceNumber,\n\t\t\t...tagCodeArtifacts({\n\t\t\t\tid: this.telemetryNodeId,\n\t\t\t}),\n\t\t});\n\t\tthis.logger.sendErrorEvent(eventProps, error);\n\t\tthrow error;\n\t}\n}\n\n/**\n * Creates a root summarizer node.\n * @param logger - Logger to use within SummarizerNode\n * @param summarizeInternalFn - Function to generate summary\n * @param changeSequenceNumber - Sequence number of latest change to new node/subtree\n * @param referenceSequenceNumber - Reference sequence number of last acked summary,\n * or undefined if not loaded from summary\n * @param config - Configure behavior of summarizer node\n */\nexport const createRootSummarizerNode = (\n\tlogger: TelemetryLoggerExt,\n\tsummarizeInternalFn: SummarizeInternalFn,\n\tchangeSequenceNumber: number,\n\treferenceSequenceNumber: number | undefined,\n\tconfig: ISummarizerNodeConfig = {},\n): IRootSummarizerNode =>\n\tnew SummarizerNode(\n\t\tlogger,\n\t\tsummarizeInternalFn,\n\t\tconfig,\n\t\t\"\" /* summaryHandleId */,\n\t\tchangeSequenceNumber,\n\t\treferenceSequenceNumber,\n\t\tundefined /* wipSummaryLogger */,\n\t\t\"\" /* telemetryNodeId */,\n\t);\n"]}
1
+ {"version":3,"file":"summarizerNode.js","sourceRoot":"","sources":["../../../src/summary/summarizerNode/summarizerNode.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,qCAAqC,CAAC;AAC9E,OAAO,EAAE,WAAW,EAAE,MAAM,oCAAoC,CAAC;AAKjE,OAAO,EACN,gBAAgB,EAIhB,0BAA0B,GAK1B,MAAM,8CAA8C,CAAC;AACtD,OAAO,EAAE,UAAU,EAAE,MAAM,wCAAwC,CAAC;AACpE,OAAO,EACN,YAAY,EACZ,gBAAgB,EAChB,gBAAgB,EAChB,iBAAiB,EACjB,gBAAgB,GAChB,MAAM,0CAA0C,CAAC;AAiBlD;;;;;;;;;;;;GAYG;AACH,MAAM,OAAO,cAAc;IAC1B;;;OAGG;IACH,IAAW,uBAAuB;QACjC,OAAO,IAAI,CAAC,mCAAmC,IAAI,CAAC,CAAC;IACtD,CAAC;IAED;;;OAGG;IACH,IAAW,eAAe;QACzB,OAAO,IAAI,CAAC,gBAAgB,CAAC;IAC9B,CAAC;IAmBD;;;OAGG;IACH,YACC,UAAgC,EACf,mBAAwC,EACzD,MAA6B;IAC7B;;OAEG;IACc,gBAAwB,EACjC,qBAA6B;IACrC;;OAEG;IACK,mCAA4C,EAC1C,gBAAuC;IACjD;;OAEG;IACO,eAAwB;QAfjB,wBAAmB,GAAnB,mBAAmB,CAAqB;QAKxC,qBAAgB,GAAhB,gBAAgB,CAAQ;QACjC,0BAAqB,GAArB,qBAAqB,CAAQ;QAI7B,wCAAmC,GAAnC,mCAAmC,CAAS;QAC1C,qBAAgB,GAAhB,gBAAgB,CAAuB;QAIvC,oBAAe,GAAf,eAAe,CAAS;QAtChB,aAAQ,GAAG,IAAI,GAAG,EAA0B,CAAC;QAChE;;;WAGG;QACgB,qBAAgB,GAAG,IAAI,GAAG,EAA8B,CAAC;QAE5E;;;;WAIG;QACK,uBAAkB,GAAY,KAAK,CAAC;QACpC,qBAAgB,GAAG,KAAK,CAAC;QA2BhC,IAAI,CAAC,cAAc,GAAG,MAAM,CAAC,cAAc,IAAI,IAAI,CAAC;QACpD,6EAA6E;QAC7E,IAAI,CAAC,MAAM,GAAG,iBAAiB,CAAC;YAC/B,MAAM,EAAE,UAAU;YAClB,UAAU,EAAE;gBACX,GAAG,EAAE,gBAAgB,CAAC,EAAE,EAAE,EAAE,IAAI,CAAC,eAAe,EAAE,CAAC;aACnD;SACD,CAAC,CAAC;IACJ,CAAC;IAED;;;;;;;;;;OAUG;IACI,YAAY,CAClB,uBAA+B,EAC/B,aAAmC,EACnC,sBAA8B;QAE9B,MAAM,CACL,IAAI,CAAC,gBAAgB,KAAK,SAAS,EACnC,KAAK,CAAC,8DAA8D,CACpE,CAAC;QACF,MAAM,CACL,IAAI,CAAC,0BAA0B,KAAK,SAAS,EAC7C,KAAK,CAAC,kCAAkC,CACxC,CAAC;QAEF,IAAI,KAAK,GAAG,CAAC,CAAC,CAAC,wDAAwD;QACvE,IAAI,YAAY,GAAG,CAAC,CAAC;QACrB,MAAM,4BAA4B,GAAG,IAAI,GAAG,EAAU,CAAC;QACvD,MAAM,0BAA0B,GAAG,IAAI,CAAC,mCAAmC,CAAC;QAC5E,IACC,0BAA0B,KAAK,SAAS;YACxC,sBAAsB,KAAK,0BAA0B,EACpD,CAAC;YACF,YAAY,EAAE,CAAC;YACf,4BAA4B,CAAC,GAAG,CAC/B,GAAG,sBAAsB,IAAI,0BAA0B,EAAE,CACzD,CAAC;QACH,CAAC;QAED,IAAI,CAAC,gBAAgB,GAAG,aAAa,CAAC;QAEtC,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC;YAC5C,MAAM,uBAAuB,GAAG,KAAK,CAAC,YAAY,CACjD,uBAAuB,EACvB,IAAI,CAAC,gBAAgB,EACrB,sBAAsB,CACtB,CAAC;YACF,KAAK,IAAI,uBAAuB,CAAC,KAAK,CAAC;YACvC,YAAY,IAAI,uBAAuB,CAAC,YAAY,CAAC;YACrD,KAAK,MAAM,qBAAqB,IAAI,uBAAuB,CAAC,eAAe,EAAE,CAAC;gBAC7E,4BAA4B,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC;YACzD,CAAC;QACF,CAAC;QACD,IAAI,CAAC,0BAA0B,GAAG,uBAAuB,CAAC;QAC1D,OAAO;YACN,KAAK;YACL,YAAY;YACZ,eAAe,EAAE,4BAA4B;SAC7C,CAAC;IACH,CAAC;IAEM,KAAK,CAAC,SAAS,CACrB,QAAiB,EACjB,aAAsB,IAAI,EAC1B,gBAAoC;QAEpC,sFAAsF;QACtF,IAAI,CAAC,UAAU,EAAE,CAAC;YACjB,OAAO,IAAI,CAAC,mBAAmB,CAAC,QAAQ,EAAE,UAAU,EAAE,gBAAgB,CAAC,CAAC;QACzE,CAAC;QAED,qGAAqG;QACrG,IAAI,CAAC,kBAAkB,GAAG,IAAI,CAAC;QAE/B,qCAAqC;QACrC,IACC,IAAI,CAAC,cAAc;YACnB,CAAC,QAAQ;YACT,CAAC,IAAI,CAAC,UAAU,EAAE;YAClB,IAAI,CAAC,mCAAmC,KAAK,SAAS,EACrD,CAAC;YACF,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAC;YAC7B,MAAM,KAAK,GAAG,UAAU,EAAE,CAAC;YAC3B,KAAK,CAAC,eAAe,EAAE,CAAC;YACxB,OAAO;gBACN,OAAO,EAAE;oBACR,IAAI,EAAE,WAAW,CAAC,MAAM;oBACxB,MAAM,EAAE,IAAI,CAAC,eAAe;oBAC5B,UAAU,EAAE,WAAW,CAAC,IAAI;iBAC5B;gBACD,KAAK;aACL,CAAC;QACH,CAAC;QAED,IAAI,yBAA6E,CAAC;QAClF,IAAI,CAAC,QAAQ,EAAE,CAAC;YACf,MAAM,CACL,IAAI,CAAC,0BAA0B,KAAK,SAAS,EAC7C,KAAK,CAAC,kEAAkE,CACxE,CAAC;YACF,yBAAyB;gBACxB,IAAI,CAAC,mCAAmC,KAAK,SAAS;oBACrD,CAAC,CAAC,SAAS;oBACX,CAAC,CAAC;wBACA,qBAAqB,EAAE,IAAI,CAAC,0BAA0B;wBACtD,2BAA2B,EAAE,IAAI,CAAC,mCAAmC;wBACrE,4BAA4B;wBAC5B,WAAW,EAAE,IAAI,CAAC,eAAe;qBACjC,CAAC;QACN,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,mBAAmB,CAC5C,QAAQ,EACR,UAAU,EACV,gBAAgB,EAChB,yBAAyB,CACzB,CAAC;QAEF,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC;IACzD,CAAC;IAED;;;;;;OAMG;IACI,eAAe;QACrB,OAAO,IAAI,CAAC,mBAAmB,CAAC,KAAK,CAAC,yBAAyB,CAAC,CAAC;IAClE,CAAC;IAED;;;;;;;;OAQG;IACO,mBAAmB,CAAC,mBAA4B;QACzD,IAAI,IAAI,CAAC,kBAAkB,CAAC,mBAAmB,CAAC,EAAE,CAAC;YAClD,OAAO;gBACN,OAAO,EAAE,KAAK;gBACd,MAAM,EAAE,qBAAqB;gBAC7B,EAAE,EAAE;oBACH,GAAG,EAAE,gBAAgB,CAAC,YAAY;oBAClC,KAAK,EAAE,IAAI,CAAC,eAAe;iBAC3B;gBACD,mFAAmF;gBACnF,iBAAiB,EAAE,CAAC;aACpB,CAAC;QACH,CAAC;QACD,IAAI,mBAAmB,EAAE,CAAC;YACzB,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;QAC1B,CAAC;QAED,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC;YAC5C,MAAM,MAAM,GAAG,KAAK,CAAC,mBAAmB,CAAC,IAAI,CAAC,gBAAgB,IAAI,mBAAmB,CAAC,CAAC;YACvF,0CAA0C;YAC1C,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;gBACrB,OAAO,MAAM,CAAC;YACf,CAAC;QACF,CAAC;QACD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IAC1B,CAAC;IAEO,kBAAkB,CAAC,mBAA4B;QACtD,MAAM,CACL,IAAI,CAAC,gBAAgB,KAAK,SAAS,EACnC,KAAK,CAAC,mEAAmE,CACzE,CAAC;QACF,MAAM,CAAC,IAAI,CAAC,0BAA0B,KAAK,SAAS,EAAE,KAAK,CAAC,4BAA4B,CAAC,CAAC;QAE1F,6GAA6G;QAC7G,oCAAoC;QACpC,gGAAgG;QAChG,IAAI,mBAAmB,IAAI,IAAI,CAAC,kBAAkB,EAAE,CAAC;YACpD,OAAO,KAAK,CAAC;QACd,CAAC;QAED;;;;;;;;;;;;WAYG;QACH,OAAO,IAAI,CAAC;IACb,CAAC;IAED;;;;OAIG;IACI,eAAe,CAAC,cAAsB;QAC5C,IAAI,CAAC,mBAAmB,CAAC,cAAc,EAAE,KAAK,CAAC,yBAAyB,CAAC,CAAC;IAC3E,CAAC;IAED;;;;;;;OAOG;IACO,mBAAmB,CAAC,cAAsB,EAAE,mBAA4B;QACjF,MAAM,CACL,IAAI,CAAC,0BAA0B,KAAK,SAAS,EAC7C,KAAK,CAAC,8BAA8B,CACpC,CAAC;QACF,IAAI,mBAAmB,IAAI,IAAI,CAAC,mCAAmC,KAAK,SAAS,EAAE,CAAC;YACnF,qEAAqE;YACrE,qEAAqE;YACrE,mCAAmC;YACnC,uEAAuE;YACvE,wEAAwE;YACxE,uEAAuE;YACvE,IAAI,CAAC,YAAY,EAAE,CAAC;YACpB,OAAO;QACR,CAAC;QAED,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC;YAC5C,KAAK,CAAC,mBAAmB,CAAC,cAAc,EAAE,IAAI,CAAC,gBAAgB,IAAI,mBAAmB,CAAC,CAAC;QACzF,CAAC;QACD,0DAA0D;QAC1D,+DAA+D;QAC/D,+DAA+D;QAC/D,8DAA8D;QAC9D,gEAAgE;QAChE,kEAAkE;QAClE,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,cAAc,EAAE;YACzC,uBAAuB,EAAE,IAAI,CAAC,0BAA0B;SACxD,CAAC,CAAC;QACH,IAAI,CAAC,YAAY,EAAE,CAAC;IACrB,CAAC;IAEM,YAAY;QAClB,IAAI,CAAC,0BAA0B,GAAG,SAAS,CAAC;QAC5C,IAAI,CAAC,kBAAkB,GAAG,KAAK,CAAC;QAChC,IAAI,CAAC,gBAAgB,GAAG,KAAK,CAAC;QAC9B,IAAI,CAAC,gBAAgB,GAAG,SAAS,CAAC;QAClC,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC;YAC5C,KAAK,CAAC,YAAY,EAAE,CAAC;QACtB,CAAC;IACF,CAAC;IAED;;;;;;;OAOG;IACI,KAAK,CAAC,oBAAoB,CAChC,cAAsB,EACtB,aAAqB;QAErB,MAAM,UAAU,GAMZ;YACH,cAAc;YACd,aAAa;YACb,uBAAuB,EAAE,IAAI,CAAC,uBAAuB;SACrD,CAAC;QACF,OAAO,gBAAgB,CAAC,cAAc,CACrC,IAAI,CAAC,MAAM,EACX;YACC,SAAS,EAAE,sBAAsB;YACjC,GAAG,UAAU;SACb,EACD,KAAK,EAAE,KAAK,EAAE,EAAE;YACf,qGAAqG;YACrG,qDAAqD;YACrD,IAAI,IAAI,CAAC,mBAAmB,EAAE,EAAE,CAAC;gBAChC,MAAM,IAAI,YAAY,CAAC,kCAAkC,EAAE;oBAC1D,uBAAuB,EAAE,IAAI,CAAC,0BAA0B;iBACxD,CAAC,CAAC;YACJ,CAAC;YAED,IAAI,gBAAgB,GAAG,KAAK,CAAC;YAC7B,IAAI,cAAc,GAAG,KAAK,CAAC;YAE3B,IAAI,aAAa,GAAG,IAAI,CAAC,uBAAuB,EAAE,CAAC;gBAClD,cAAc,GAAG,IAAI,CAAC;YACvB,CAAC;YAED,4HAA4H;YAC5H,uCAAuC;YACvC,MAAM,cAAc,GAAG,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;YACjE,IAAI,cAAc,EAAE,uBAAuB,KAAK,SAAS,EAAE,CAAC;gBAC3D,gBAAgB,GAAG,IAAI,CAAC;gBACxB,4EAA4E;gBAC5E,IAAI,CAAC,+BAA+B,CACnC,cAAc,EACd,cAAc,CAAC,uBAAuB,CACtC,CAAC;YACH,CAAC;YACD,KAAK,CAAC,GAAG,CAAC,EAAE,GAAG,UAAU,EAAE,cAAc,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,CAAC,CAAC;YACpF,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,CAAC;QAC7C,CAAC,EACD,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAC3C,CAAC;IACH,CAAC;IACD;;;;;OAKG;IACO,+BAA+B,CACxC,cAAsB,EACtB,uBAA+B;QAE/B,MAAM,cAAc,GAAG,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;QACjE,IAAI,cAAc,KAAK,SAAS,EAAE,CAAC;YAClC,oFAAoF;YACpF,MAAM,CACL,IAAI,CAAC,mCAAmC,KAAK,SAAS,EACtD,KAAK,CAAC,mFAAmF,CACzF,CAAC;YACF,OAAO;QACR,CAAC;aAAM,CAAC;YACP,MAAM,CACL,uBAAuB,KAAK,cAAc,CAAC,uBAAuB,EAClE,KAAK,CAAC,oEAAoE,CAC1E,CAAC;YAEF,kCAAkC;YAClC,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC;QAC9C,CAAC;QAED,2FAA2F;QAC3F,KAAK,MAAM,CAAC,GAAG,EAAE,OAAO,CAAC,IAAI,IAAI,CAAC,gBAAgB,EAAE,CAAC;YACpD,IAAI,OAAO,CAAC,uBAAuB,GAAG,uBAAuB,EAAE,CAAC;gBAC/D,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACnC,CAAC;QACF,CAAC;QACD,wDAAwD;QACxD,IAAI,CAAC,mCAAmC,GAAG,cAAc,CAAC,uBAAuB,CAAC;QAClF,sCAAsC;QACtC,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC;YAC5C,KAAK,CAAC,+BAA+B,CAAC,cAAc,EAAE,uBAAuB,CAAC,CAAC;QAChF,CAAC;IACF,CAAC;IAEM,sBAAsB,CAAC,QAAuB;QACpD,4EAA4E;IAC7E,CAAC;IAEM,YAAY,CAAC,EAA6B;QAChD,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC,cAAc,CAAC,CAAC;IACpC,CAAC;IAEM,UAAU,CAAC,cAAsB;QACvC,IAAI,cAAc,GAAG,IAAI,CAAC,qBAAqB,EAAE,CAAC;YACjD,IAAI,CAAC,qBAAqB,GAAG,cAAc,CAAC;QAC7C,CAAC;IACF,CAAC;IAED;;;;OAIG;IACO,UAAU;QACnB,OAAO,IAAI,CAAC,qBAAqB,GAAG,IAAI,CAAC,uBAAuB,CAAC;IAClE,CAAC;IAIM,WAAW;IACjB;;OAEG;IACH,mBAAwC;IACxC;;OAEG;IACH,EAAU;IACV;;;;OAIG;IACH,WAA2C,EAC3C,SAAgC,EAAE;QAElC,MAAM,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,KAAK,CAAC,kDAAkD,CAAC,CAAC;QAEzF,MAAM,aAAa,GAAwB,IAAI,CAAC,wBAAwB,CAAC,EAAE,EAAE,WAAW,CAAC,CAAC;QAC1F,MAAM,KAAK,GAAG,IAAI,cAAc,CAC/B,IAAI,CAAC,MAAM,EACX,mBAAmB,EACnB,MAAM,EACN,aAAa,CAAC,eAAe,EAC7B,aAAa,CAAC,oBAAoB,EAClC,aAAa,CAAC,kCAAkC,EAChD,IAAI,CAAC,gBAAgB,EACrB,aAAa,CAAC,eAAe,CAC7B,CAAC;QAEF,yGAAyG;QACzG,+GAA+G;QAC/G,iGAAiG;QACjG,IAAI,CAAC,qBAAqB,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QAEtC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;QAC7B,OAAO,KAAK,CAAC;IACd,CAAC;IAEM,QAAQ,CAAC,EAAU;QACzB,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAC9B,CAAC;IAED;;;;;OAKG;IACO,wBAAwB,CACjC,EAAU,EACV,WAA2C;QAE3C,IAAI,uCAA2D,CAAC;QAChE,IAAI,oBAA4B,CAAC;QAEjC,MAAM,wCAAwC,GAAG,IAAI,CAAC,mCAAmC,CAAC;QAC1F,QAAQ,WAAW,CAAC,IAAI,EAAE,CAAC;YAC1B,KAAK,0BAA0B,CAAC,UAAU,CAAC,CAAC,CAAC;gBAC5C,iGAAiG;gBACjG,uGAAuG;gBACvG,uGAAuG;gBACvG,sDAAsD;gBACtD,MAAM,CACL,wCAAwC,KAAK,SAAS;oBACrD,WAAW,CAAC,cAAc,GAAG,wCAAwC,EACtE,mGAAmG,CACnG,CAAC;gBACF,oBAAoB,GAAG,WAAW,CAAC,cAAc,CAAC;gBAClD,MAAM;YACP,CAAC;YACD,KAAK,0BAA0B,CAAC,WAAW,CAAC;YAC5C,KAAK,0BAA0B,CAAC,KAAK,CAAC,CAAC,CAAC;gBACvC,uCAAuC,GAAG,wCAAwC,CAAC;gBACnF,oBAAoB,GAAG,wCAAwC,IAAI,CAAC,CAAC,CAAC;gBACtE,MAAM;YACP,CAAC;YACD,OAAO,CAAC,CAAC,CAAC;gBACT,MAAM,IAAI,GAAI,WAAyD,CAAC,IAAI,CAAC;gBAC7E,eAAe,CAAC,WAAW,EAAE,0CAA0C,IAAI,EAAE,CAAC,CAAC;YAChF,CAAC;QACF,CAAC;QAED,MAAM,oBAAoB,GAAG,GAAG,IAAI,CAAC,eAAe,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC;QACnE,mCAAmC;QACnC,MAAM,oBAAoB,GAAG,GAAG,IAAI,CAAC,gBAAgB,IAAI,gBAAgB,IAAI,EAAE,EAAE,CAAC;QAElF,OAAO;YACN,oBAAoB;YACpB,eAAe,EAAE,oBAAoB;YACrC,eAAe,EAAE,oBAAoB;YACrC,kCAAkC,EAAE,uCAAuC;SAC3E,CAAC;IACH,CAAC;IAED;;;;;;;;OAQG;IACO,qBAAqB,CAAC,KAAqB,EAAE,EAAU;QAChE,2GAA2G;QAC3G,iCAAiC;QACjC,IAAI,IAAI,CAAC,mBAAmB,EAAE,EAAE,CAAC;YAChC,KAAK,CAAC,0BAA0B,GAAG,IAAI,CAAC,0BAA0B,CAAC;QACpE,CAAC;QACD,qFAAqF;QACrF,IAAI,KAAK,CAAC,mCAAmC,KAAK,SAAS,EAAE,CAAC;YAC7D,KAAK,MAAM,CAAC,cAAc,EAAE,kBAAkB,CAAC,IAAI,IAAI,CAAC,gBAAgB,CAAC,OAAO,EAAE,EAAE,CAAC;gBACpF,KAAK,CAAC,iBAAiB,CAAC,cAAc,EAAE,kBAAkB,CAAC,CAAC;YAC7D,CAAC;QACF,CAAC;IACF,CAAC;IAES,iBAAiB,CAAC,GAAW,EAAE,kBAAsC;QAC9E,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,GAAG,EAAE,kBAAkB,CAAC,CAAC;IACpD,CAAC;IAED;;OAEG;IACI,mBAAmB;QACzB,OAAO,IAAI,CAAC,0BAA0B,KAAK,SAAS,CAAC;IACtD,CAAC;IAED;;OAEG;IACO,oBAAoB,CAAC,UAAmC;QACjE,MAAM,KAAK,GAAG,IAAI,YAAY,CAAC,UAAU,CAAC,SAAS,EAAE;YACpD,GAAG,UAAU;YACb,uBAAuB,EAAE,IAAI,CAAC,0BAA0B;YACxD,GAAG,gBAAgB,CAAC;gBACnB,EAAE,EAAE,IAAI,CAAC,eAAe;aACxB,CAAC;SACF,CAAC,CAAC;QACH,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;QAC9C,MAAM,KAAK,CAAC;IACb,CAAC;CACD;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,CACvC,MAA0B,EAC1B,mBAAwC,EACxC,oBAA4B,EAC5B,uBAA2C,EAC3C,SAAgC,EAAE,EACZ,EAAE,CACxB,IAAI,cAAc,CACjB,MAAM,EACN,mBAAmB,EACnB,MAAM,EACN,EAAE,CAAC,qBAAqB,EACxB,oBAAoB,EACpB,uBAAuB,EACvB,SAAS,CAAC,sBAAsB,EAChC,EAAE,CAAC,qBAAqB,CACxB,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport type { ITelemetryBaseLogger } from \"@fluidframework/core-interfaces\";\nimport { assert, unreachableCase } from \"@fluidframework/core-utils/internal\";\nimport { SummaryType } from \"@fluidframework/driver-definitions\";\nimport type {\n\tISnapshotTree,\n\tISequencedDocumentMessage,\n} from \"@fluidframework/driver-definitions/internal\";\nimport {\n\tchannelsTreeName,\n\ttype IExperimentalIncrementalSummaryContext,\n\ttype ITelemetryContext,\n\ttype CreateChildSummarizerNodeParam,\n\tCreateSummarizerNodeSource,\n\ttype ISummarizeResult,\n\ttype ISummarizerNode,\n\ttype ISummarizerNodeConfig,\n\ttype SummarizeInternalFn,\n} from \"@fluidframework/runtime-definitions/internal\";\nimport { mergeStats } from \"@fluidframework/runtime-utils/internal\";\nimport {\n\tLoggingError,\n\tPerformanceEvent,\n\tTelemetryDataTag,\n\tcreateChildLogger,\n\ttagCodeArtifacts,\n} from \"@fluidframework/telemetry-utils/internal\";\nimport type {\n\tITelemetryErrorEventExt,\n\tTelemetryLoggerExt,\n} from \"@fluidframework/telemetry-utils/internal\";\n\nimport type {\n\tICreateChildDetails,\n\tIRefreshSummaryResult,\n\tIStartSummaryResult,\n\tISummarizerNodeRootContract,\n\tValidateSummaryResult,\n\tPendingSummaryInfo,\n} from \"./summarizerNodeUtils.js\";\n\nexport interface IRootSummarizerNode extends ISummarizerNode, ISummarizerNodeRootContract {}\n\n/**\n * Encapsulates the summarizing work and state of an individual tree node in the\n * summary tree. It tracks changes and allows for optimizations when unchanged, or\n * can allow for fallback summaries to be generated when an error is encountered.\n * Usage is for the root node to call startSummary first to begin tracking a WIP\n * (work in progress) summary. Then all nodes will call summarize to summaries their\n * individual parts. Once completed and uploaded to storage, the root node will call\n * completeSummary or clearSummary to clear the WIP summary tracking state if something\n * went wrong. The SummarizerNodes will track all pending summaries that have been\n * recorded by the completeSummary call. When one of them is acked, the root node should\n * call refreshLatestSummary to inform the tree of SummarizerNodes of the new baseline\n * latest successful summary.\n */\nexport class SummarizerNode implements IRootSummarizerNode {\n\t/**\n\t * The reference sequence number of the most recent acked summary.\n\t * Returns 0 if there is not yet an acked summary.\n\t */\n\tpublic get referenceSequenceNumber(): number {\n\t\treturn this._lastSummaryReferenceSequenceNumber ?? 0;\n\t}\n\n\t/**\n\t * returns the handle of the last successful summary of this summarizerNode in string format\n\t * (this getter is primarily only used in the test code)\n\t */\n\tpublic get summaryHandleId(): string {\n\t\treturn this._summaryHandleId;\n\t}\n\n\tprotected readonly children = new Map<string, SummarizerNode>();\n\t/**\n\t * Key value pair of summaries submitted by this client which are not yet acked.\n\t * Key is the proposalHandle and value is the summary op's referece sequence number.\n\t */\n\tprotected readonly pendingSummaries = new Map<string, PendingSummaryInfo>();\n\tprotected wipReferenceSequenceNumber: number | undefined;\n\t/**\n\t * True if the current node was summarized during the current summary process\n\t * This flag is used to identify scenarios where summarize was not called on a node.\n\t * For example, this node was created after its parent was already summarized due to out-of-order realization via application code.\n\t */\n\tprivate wipSummarizeCalled: boolean = false;\n\tprivate wipSkipRecursion = false;\n\n\tprotected readonly logger: TelemetryLoggerExt;\n\n\t/**\n\t * Do not call constructor directly.\n\t * Use createRootSummarizerNode to create root node, or createChild to create child nodes.\n\t */\n\tpublic constructor(\n\t\tbaseLogger: ITelemetryBaseLogger,\n\t\tprivate readonly summarizeInternalFn: SummarizeInternalFn,\n\t\tconfig: ISummarizerNodeConfig,\n\t\t/**\n\t\t * Encoded handle or path to the node\n\t\t */\n\t\tprivate readonly _summaryHandleId: string,\n\t\tprivate _changeSequenceNumber: number,\n\t\t/**\n\t\t * Summary reference sequence number, i.e. last sequence number seen when last successful summary was created\n\t\t */\n\t\tprivate _lastSummaryReferenceSequenceNumber?: number,\n\t\tprotected wipSummaryLogger?: ITelemetryBaseLogger,\n\t\t/**\n\t\t * A unique id of this node to be logged when sending telemetry.\n\t\t */\n\t\tprotected telemetryNodeId?: string,\n\t) {\n\t\tthis.canReuseHandle = config.canReuseHandle ?? true;\n\t\t// All logs posted by the summarizer node should include the telemetryNodeId.\n\t\tthis.logger = createChildLogger({\n\t\t\tlogger: baseLogger,\n\t\t\tproperties: {\n\t\t\t\tall: tagCodeArtifacts({ id: this.telemetryNodeId }),\n\t\t\t},\n\t\t});\n\t}\n\n\t/**\n\t * In order to produce a summary with a summarizer node, the summarizer node system must be notified a summary has\n\t * started. This is done by calling startSummary. This will track the reference sequence number of the summary and\n\t * run some validation checks to ensure the summary is correct.\n\t * @param referenceSequenceNumber - the number of ops processed up to this point\n\t * @param summaryLogger - the logger to use for the summary\n\t * @param latestSummaryRefSeqNum - the reference sequence number of the latest summary. Another way to think about\n\t * it is the reference sequence number of the previous summary.\n\t * @returns the number of nodes in the tree, the number of nodes that are invalid, and the different types of\n\t * sequence number mismatches\n\t */\n\tpublic startSummary(\n\t\treferenceSequenceNumber: number,\n\t\tsummaryLogger: ITelemetryBaseLogger,\n\t\tlatestSummaryRefSeqNum: number,\n\t): IStartSummaryResult {\n\t\tassert(\n\t\t\tthis.wipSummaryLogger === undefined,\n\t\t\t0x19f /* \"wipSummaryLogger should not be set yet in startSummary\" */,\n\t\t);\n\t\tassert(\n\t\t\tthis.wipReferenceSequenceNumber === undefined,\n\t\t\t0x1a0 /* \"Already tracking a summary\" */,\n\t\t);\n\n\t\tlet nodes = 1; // number of summarizerNodes at the start of the summary\n\t\tlet invalidNodes = 0;\n\t\tconst sequenceNumberMismatchKeySet = new Set<string>();\n\t\tconst nodeLatestSummaryRefSeqNum = this._lastSummaryReferenceSequenceNumber;\n\t\tif (\n\t\t\tnodeLatestSummaryRefSeqNum !== undefined &&\n\t\t\tlatestSummaryRefSeqNum !== nodeLatestSummaryRefSeqNum\n\t\t) {\n\t\t\tinvalidNodes++;\n\t\t\tsequenceNumberMismatchKeySet.add(\n\t\t\t\t`${latestSummaryRefSeqNum}-${nodeLatestSummaryRefSeqNum}`,\n\t\t\t);\n\t\t}\n\n\t\tthis.wipSummaryLogger = summaryLogger;\n\n\t\tfor (const child of this.children.values()) {\n\t\t\tconst childStartSummaryResult = child.startSummary(\n\t\t\t\treferenceSequenceNumber,\n\t\t\t\tthis.wipSummaryLogger,\n\t\t\t\tlatestSummaryRefSeqNum,\n\t\t\t);\n\t\t\tnodes += childStartSummaryResult.nodes;\n\t\t\tinvalidNodes += childStartSummaryResult.invalidNodes;\n\t\t\tfor (const invalidSequenceNumber of childStartSummaryResult.mismatchNumbers) {\n\t\t\t\tsequenceNumberMismatchKeySet.add(invalidSequenceNumber);\n\t\t\t}\n\t\t}\n\t\tthis.wipReferenceSequenceNumber = referenceSequenceNumber;\n\t\treturn {\n\t\t\tnodes,\n\t\t\tinvalidNodes,\n\t\t\tmismatchNumbers: sequenceNumberMismatchKeySet,\n\t\t};\n\t}\n\n\tpublic async summarize(\n\t\tfullTree: boolean,\n\t\ttrackState: boolean = true,\n\t\ttelemetryContext?: ITelemetryContext,\n\t): Promise<ISummarizeResult> {\n\t\t// If trackState is false, call summarize internal directly and don't track any state.\n\t\tif (!trackState) {\n\t\t\treturn this.summarizeInternalFn(fullTree, trackState, telemetryContext);\n\t\t}\n\n\t\t// Set to wipSummarizeCalled true to represent that current node was included in the summary process.\n\t\tthis.wipSummarizeCalled = true;\n\n\t\t// Try to reuse the tree if unchanged\n\t\tif (\n\t\t\tthis.canReuseHandle &&\n\t\t\t!fullTree &&\n\t\t\t!this.hasChanged() &&\n\t\t\tthis._lastSummaryReferenceSequenceNumber !== undefined\n\t\t) {\n\t\t\tthis.wipSkipRecursion = true;\n\t\t\tconst stats = mergeStats();\n\t\t\tstats.handleNodeCount++;\n\t\t\treturn {\n\t\t\t\tsummary: {\n\t\t\t\t\ttype: SummaryType.Handle,\n\t\t\t\t\thandle: this.summaryHandleId,\n\t\t\t\t\thandleType: SummaryType.Tree,\n\t\t\t\t},\n\t\t\t\tstats,\n\t\t\t};\n\t\t}\n\n\t\tlet incrementalSummaryContext: IExperimentalIncrementalSummaryContext | undefined;\n\t\tif (!fullTree) {\n\t\t\tassert(\n\t\t\t\tthis.wipReferenceSequenceNumber !== undefined,\n\t\t\t\t0x5df /* Summarize should not be called when not tracking the summary */,\n\t\t\t);\n\t\t\tincrementalSummaryContext =\n\t\t\t\tthis._lastSummaryReferenceSequenceNumber === undefined\n\t\t\t\t\t? undefined\n\t\t\t\t\t: {\n\t\t\t\t\t\t\tsummarySequenceNumber: this.wipReferenceSequenceNumber,\n\t\t\t\t\t\t\tlatestSummarySequenceNumber: this._lastSummaryReferenceSequenceNumber,\n\t\t\t\t\t\t\t// TODO: remove summaryPath.\n\t\t\t\t\t\t\tsummaryPath: this.summaryHandleId,\n\t\t\t\t\t\t};\n\t\t}\n\n\t\tconst result = await this.summarizeInternalFn(\n\t\t\tfullTree,\n\t\t\ttrackState,\n\t\t\ttelemetryContext,\n\t\t\tincrementalSummaryContext,\n\t\t);\n\n\t\treturn { summary: result.summary, stats: result.stats };\n\t}\n\n\t/**\n\t * Validates that the in-progress summary is correct, i.e., summarize should have run for all non-skipped\n\t * nodes. This will only be called for the root summarizer node and is called by it recursively on all child nodes.\n\t *\n\t * @returns ValidateSummaryResult which contains a boolean success indicating whether the validation was successful.\n\t * In case of failure, additional information is returned indicating type of failure and where it was.\n\t */\n\tpublic validateSummary(): ValidateSummaryResult {\n\t\treturn this.validateSummaryCore(false /* parentSkipRecursion */);\n\t}\n\n\t/**\n\t * Validates that the in-progress summary is correct for all nodes, i.e., summarize should have run for all\n\t * non-skipped nodes.\n\t * @param parentSkipRecursion - true if the parent of this node skipped recursing the child nodes when summarizing.\n\t * In that case, the children will not have work-in-progress state.\n\t *\n\t * @returns ValidateSummaryResult which contains a boolean success indicating whether the validation was successful.\n\t * In case of failure, additional information is returned indicating type of failure and where it was.\n\t */\n\tprotected validateSummaryCore(parentSkipRecursion: boolean): ValidateSummaryResult {\n\t\tif (this.wasSummarizeMissed(parentSkipRecursion)) {\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\treason: \"NodeDidNotSummarize\",\n\t\t\t\tid: {\n\t\t\t\t\ttag: TelemetryDataTag.CodeArtifact,\n\t\t\t\t\tvalue: this.telemetryNodeId,\n\t\t\t\t},\n\t\t\t\t// These errors are usually transient and should go away when summarize is retried.\n\t\t\t\tretryAfterSeconds: 1,\n\t\t\t};\n\t\t}\n\t\tif (parentSkipRecursion) {\n\t\t\treturn { success: true };\n\t\t}\n\n\t\tfor (const child of this.children.values()) {\n\t\t\tconst result = child.validateSummaryCore(this.wipSkipRecursion || parentSkipRecursion);\n\t\t\t// If any child fails, return the failure.\n\t\t\tif (!result.success) {\n\t\t\t\treturn result;\n\t\t\t}\n\t\t}\n\t\treturn { success: true };\n\t}\n\n\tprivate wasSummarizeMissed(parentSkipRecursion: boolean): boolean {\n\t\tassert(\n\t\t\tthis.wipSummaryLogger !== undefined,\n\t\t\t0x6fc /* wipSummaryLogger should have been set in startSummary or ctor */,\n\t\t);\n\t\tassert(this.wipReferenceSequenceNumber !== undefined, 0x6fd /* Not tracking a summary */);\n\n\t\t// If the parent node skipped recursion, it did not call summarize on this node. So, summarize was not missed\n\t\t// but was intentionally not called.\n\t\t// Otherwise, summarize should have been called on this node and wipSummarizeCalled must be set.\n\t\tif (parentSkipRecursion || this.wipSummarizeCalled) {\n\t\t\treturn false;\n\t\t}\n\n\t\t/**\n\t\t * The absence of wip local path indicates that summarize was not called for this node. Return failure.\n\t\t * This can happen if:\n\t\t * 1. A child node was created after summarize was already called on the parent. For example, a data store\n\t\t * is realized (loaded) after summarize was called on it creating summarizer nodes for its DDSes. In this case,\n\t\t * parentSkipRecursion will be true and the if block above would handle it.\n\t\t * 2. A new node was created but summarize was never called on it. This can mean that the summary that is\n\t\t * generated may not have the data from this node. We should not continue, log and throw an error. This\n\t\t * will help us identify these cases and take appropriate action.\n\t\t *\n\t\t * This happens due to scenarios such as data store created during summarize. Such errors should go away when\n\t\t * summarize is attempted again.\n\t\t */\n\t\treturn true;\n\t}\n\n\t/**\n\t * Called after summary has been uploaded to the server. Add the work-in-progress state to the pending summary\n\t * queue. We track this until we get an ack from the server for this summary.\n\t * @param proposalHandle - The handle of the summary that was uploaded to the server.\n\t */\n\tpublic completeSummary(proposalHandle: string): void {\n\t\tthis.completeSummaryCore(proposalHandle, false /* parentSkipRecursion */);\n\t}\n\n\t/**\n\t * Recursive implementation for completeSummary, with additional internal-only parameters.\n\t * @param proposalHandle - The handle of the summary that was uploaded to the server.\n\t * @param parentPath - The path of the parent node which is used to build the path of this node.\n\t * @param parentSkipRecursion - true if the parent of this node skipped recursing the child nodes when summarizing.\n\t * In that case, the children will not have work-in-progress state.\n\t * @param validate - true to validate that the in-progress summary is correct for all nodes.\n\t */\n\tprotected completeSummaryCore(proposalHandle: string, parentSkipRecursion: boolean): void {\n\t\tassert(\n\t\t\tthis.wipReferenceSequenceNumber !== undefined,\n\t\t\t0x1a4 /* \"Not tracking a summary\" */,\n\t\t);\n\t\tif (parentSkipRecursion && this._lastSummaryReferenceSequenceNumber === undefined) {\n\t\t\t// This case the child is added after the latest non-failure summary.\n\t\t\t// This node and all children should consider themselves as still not\n\t\t\t// having a successful summary yet.\n\t\t\t// We cannot \"reuse\" this node if unchanged since that summary, because\n\t\t\t// handles will be unable to point to that node. It never made it to the\n\t\t\t// tree itself, and only exists as an attach op in the _outstandingOps.\n\t\t\tthis.clearSummary();\n\t\t\treturn;\n\t\t}\n\n\t\tfor (const child of this.children.values()) {\n\t\t\tchild.completeSummaryCore(proposalHandle, this.wipSkipRecursion || parentSkipRecursion);\n\t\t}\n\t\t// Note that this overwrites existing pending summary with\n\t\t// the same proposalHandle. If proposalHandle is something like\n\t\t// a hash or unique identifier, this should be fine. If storage\n\t\t// can return the same proposalHandle for a different summary,\n\t\t// this should still be okay, because we should be proposing the\n\t\t// newer one later which would have to overwrite the previous one.\n\t\tthis.pendingSummaries.set(proposalHandle, {\n\t\t\treferenceSequenceNumber: this.wipReferenceSequenceNumber,\n\t\t});\n\t\tthis.clearSummary();\n\t}\n\n\tpublic clearSummary(): void {\n\t\tthis.wipReferenceSequenceNumber = undefined;\n\t\tthis.wipSummarizeCalled = false;\n\t\tthis.wipSkipRecursion = false;\n\t\tthis.wipSummaryLogger = undefined;\n\t\tfor (const child of this.children.values()) {\n\t\t\tchild.clearSummary();\n\t\t}\n\t}\n\n\t/**\n\t * Refreshes the latest summary tracked by this node. If we have a pending summary for the given proposal handle,\n\t * it becomes the latest summary. If the current summary is already ahead, we skip the update.\n\t * If the current summary is behind, then we do not refresh.\n\t * @param proposalHandle - Handle of the generated / uploaded summary.\n\t * @param summaryRefSeq - Reference sequence of the acked summary\n\t * @returns true if the summary is tracked by this node, false otherwise.\n\t */\n\tpublic async refreshLatestSummary(\n\t\tproposalHandle: string,\n\t\tsummaryRefSeq: number,\n\t): Promise<IRefreshSummaryResult> {\n\t\tconst eventProps: {\n\t\t\tproposalHandle: string | undefined;\n\t\t\tsummaryRefSeq: number;\n\t\t\treferenceSequenceNumber: number;\n\t\t\tisSummaryTracked?: boolean;\n\t\t\tpendingSummaryFound?: boolean;\n\t\t} = {\n\t\t\tproposalHandle,\n\t\t\tsummaryRefSeq,\n\t\t\treferenceSequenceNumber: this.referenceSequenceNumber,\n\t\t};\n\t\treturn PerformanceEvent.timedExecAsync(\n\t\t\tthis.logger,\n\t\t\t{\n\t\t\t\teventName: \"refreshLatestSummary\",\n\t\t\t\t...eventProps,\n\t\t\t},\n\t\t\tasync (event) => {\n\t\t\t\t// Refresh latest summary should not happen while a summary is in progress. If it does, it can result\n\t\t\t\t// in inconsistent state, so, we should not continue;\n\t\t\t\tif (this.isSummaryInProgress()) {\n\t\t\t\t\tthrow new LoggingError(\"UnexpectedRefreshDuringSummarize\", {\n\t\t\t\t\t\tinProgressSummaryRefSeq: this.wipReferenceSequenceNumber,\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\tlet isSummaryTracked = false;\n\t\t\t\tlet isSummaryNewer = false;\n\n\t\t\t\tif (summaryRefSeq > this.referenceSequenceNumber) {\n\t\t\t\t\tisSummaryNewer = true;\n\t\t\t\t}\n\n\t\t\t\t// If the acked summary is found in the pendingSummaries, it means the summary was created and tracked by the current client\n\t\t\t\t// so set the isSummaryTracked to true.\n\t\t\t\tconst pendingSummary = this.pendingSummaries.get(proposalHandle);\n\t\t\t\tif (pendingSummary?.referenceSequenceNumber !== undefined) {\n\t\t\t\t\tisSummaryTracked = true;\n\t\t\t\t\t// update the pendingSummariesMap for the root and all child summarizerNodes\n\t\t\t\t\tthis.refreshLatestSummaryFromPending(\n\t\t\t\t\t\tproposalHandle,\n\t\t\t\t\t\tpendingSummary.referenceSequenceNumber,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tevent.end({ ...eventProps, isSummaryNewer, pendingSummaryFound: isSummaryTracked });\n\t\t\t\treturn { isSummaryTracked, isSummaryNewer };\n\t\t\t},\n\t\t\t{ start: true, end: true, cancel: \"error\" },\n\t\t);\n\t}\n\t/**\n\t * Called when we get an ack from the server for a summary we've just sent. Updates the reference state of this node\n\t * from the state in the pending summary queue.\n\t * @param proposalHandle - Handle for the current proposal.\n\t * @param referenceSequenceNumber - Reference sequence number of sent summary.\n\t */\n\tprotected refreshLatestSummaryFromPending(\n\t\tproposalHandle: string,\n\t\treferenceSequenceNumber: number,\n\t): void {\n\t\tconst pendingSummary = this.pendingSummaries.get(proposalHandle);\n\t\tif (pendingSummary === undefined) {\n\t\t\t// This should only happen if parent skipped recursion AND no prior summary existed.\n\t\t\tassert(\n\t\t\t\tthis._lastSummaryReferenceSequenceNumber === undefined,\n\t\t\t\t0x1a6 /* \"Not found pending summary, but this node has previously completed a summary\" */,\n\t\t\t);\n\t\t\treturn;\n\t\t} else {\n\t\t\tassert(\n\t\t\t\treferenceSequenceNumber === pendingSummary.referenceSequenceNumber,\n\t\t\t\t0x1a7 /* Pending summary reference sequence number should be consistent */,\n\t\t\t);\n\n\t\t\t// Clear earlier pending summaries\n\t\t\tthis.pendingSummaries.delete(proposalHandle);\n\t\t}\n\n\t\t// Delete all summaries whose reference sequence number is smaller than the one just acked.\n\t\tfor (const [key, summary] of this.pendingSummaries) {\n\t\t\tif (summary.referenceSequenceNumber < referenceSequenceNumber) {\n\t\t\t\tthis.pendingSummaries.delete(key);\n\t\t\t}\n\t\t}\n\t\t// Update the latest successful summary reference number\n\t\tthis._lastSummaryReferenceSequenceNumber = pendingSummary.referenceSequenceNumber;\n\t\t// Propagate update to all child nodes\n\t\tfor (const child of this.children.values()) {\n\t\t\tchild.refreshLatestSummaryFromPending(proposalHandle, referenceSequenceNumber);\n\t\t}\n\t}\n\n\tpublic updateBaseSummaryState(snapshot: ISnapshotTree): void {\n\t\t// Function deprecated. Empty declaration is kept around to compat failures.\n\t}\n\n\tpublic recordChange(op: ISequencedDocumentMessage): void {\n\t\tthis.invalidate(op.sequenceNumber);\n\t}\n\n\tpublic invalidate(sequenceNumber: number): void {\n\t\tif (sequenceNumber > this._changeSequenceNumber) {\n\t\t\tthis._changeSequenceNumber = sequenceNumber;\n\t\t}\n\t}\n\n\t/**\n\t * True if a change has been recorded with sequence number exceeding\n\t * the latest successfully acked summary reference sequence number.\n\t * False implies that the previous summary can be reused.\n\t */\n\tprotected hasChanged(): boolean {\n\t\treturn this._changeSequenceNumber > this.referenceSequenceNumber;\n\t}\n\n\tprotected readonly canReuseHandle: boolean;\n\n\tpublic createChild(\n\t\t/**\n\t\t * Summarize function\n\t\t */\n\t\tsummarizeInternalFn: SummarizeInternalFn,\n\t\t/**\n\t\t * Initial id or path part of this node\n\t\t */\n\t\tid: string,\n\t\t/**\n\t\t * Information needed to create the node.\n\t\t * If it is from a base summary, it will assert that a summary has been seen.\n\t\t * Attach information if it is created from an attach op.\n\t\t */\n\t\tcreateParam: CreateChildSummarizerNodeParam,\n\t\tconfig: ISummarizerNodeConfig = {},\n\t): ISummarizerNode {\n\t\tassert(!this.children.has(id), 0x1ab /* \"Create SummarizerNode child already exists\" */);\n\n\t\tconst createDetails: ICreateChildDetails = this.getCreateDetailsForChild(id, createParam);\n\t\tconst child = new SummarizerNode(\n\t\t\tthis.logger,\n\t\t\tsummarizeInternalFn,\n\t\t\tconfig,\n\t\t\tcreateDetails.summaryHandleId,\n\t\t\tcreateDetails.changeSequenceNumber,\n\t\t\tcreateDetails.lastSummaryReferenceSequenceNumber,\n\t\t\tthis.wipSummaryLogger,\n\t\t\tcreateDetails.telemetryNodeId,\n\t\t);\n\n\t\t// There may be additional state that has to be updated in this child. For example, if a summary is being\n\t\t// tracked, the child's summary tracking state needs to be updated too. Same goes for pendingSummaries we might\n\t\t// have outstanding on the parent in case we realize nodes in between Summary Op and Summary Ack.\n\t\tthis.maybeUpdateChildState(child, id);\n\n\t\tthis.children.set(id, child);\n\t\treturn child;\n\t}\n\n\tpublic getChild(id: string): ISummarizerNode | undefined {\n\t\treturn this.children.get(id);\n\t}\n\n\t/**\n\t * Returns the details needed to create a child node.\n\t * @param id - Initial id or path part of the child node.\n\t * @param createParam - Information needed to create the node.\n\t * @returns the details needed to create the child node.\n\t */\n\tprotected getCreateDetailsForChild(\n\t\tid: string,\n\t\tcreateParam: CreateChildSummarizerNodeParam,\n\t): ICreateChildDetails {\n\t\tlet childLastSummaryReferenceSequenceNumber: number | undefined;\n\t\tlet changeSequenceNumber: number;\n\n\t\tconst parentLastSummaryReferenceSequenceNumber = this._lastSummaryReferenceSequenceNumber;\n\t\tswitch (createParam.type) {\n\t\t\tcase CreateSummarizerNodeSource.FromAttach: {\n\t\t\t\t// `parentLastSummaryReferenceSequenceNumber` is the latest sequence number processed in the last\n\t\t\t\t// successful summary. So, for `createParam.sequenceNumber <= parentLastSummaryReferenceSequenceNumber`\n\t\t\t\t// to be true, the attach message would have to have been sequenced before the last summary and somehow\n\t\t\t\t// not be part of that summary, which is not possible.\n\t\t\t\tassert(\n\t\t\t\t\tparentLastSummaryReferenceSequenceNumber === undefined ||\n\t\t\t\t\t\tcreateParam.sequenceNumber > parentLastSummaryReferenceSequenceNumber,\n\t\t\t\t\t\"Attach sequence number should be greater than the parent's last summary reference sequence number\",\n\t\t\t\t);\n\t\t\t\tchangeSequenceNumber = createParam.sequenceNumber;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase CreateSummarizerNodeSource.FromSummary:\n\t\t\tcase CreateSummarizerNodeSource.Local: {\n\t\t\t\tchildLastSummaryReferenceSequenceNumber = parentLastSummaryReferenceSequenceNumber;\n\t\t\t\tchangeSequenceNumber = parentLastSummaryReferenceSequenceNumber ?? -1;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tdefault: {\n\t\t\t\tconst type = (createParam as unknown as CreateChildSummarizerNodeParam).type;\n\t\t\t\tunreachableCase(createParam, `Unexpected CreateSummarizerNodeSource: ${type}`);\n\t\t\t}\n\t\t}\n\n\t\tconst childTelemetryNodeId = `${this.telemetryNodeId ?? \"\"}/${id}`;\n\t\t// parentHandleId/.channels/childId\n\t\tconst childSummaryHandleId = `${this._summaryHandleId}/${channelsTreeName}/${id}`;\n\n\t\treturn {\n\t\t\tchangeSequenceNumber,\n\t\t\ttelemetryNodeId: childTelemetryNodeId,\n\t\t\tsummaryHandleId: childSummaryHandleId,\n\t\t\tlastSummaryReferenceSequenceNumber: childLastSummaryReferenceSequenceNumber,\n\t\t};\n\t}\n\n\t/**\n\t * Updates the state of the child if required. For example, if a summary is currently being tracked, the child's\n\t * summary tracking state needs to be updated too.\n\t * Also, in case a child node gets realized in between Summary Op and Summary Ack, let's initialize the child's\n\t * pending summary as well.\n\t * @param child - The child node whose state is to be updated.\n\t * @param id - Initial id or path part of this node\n\t *\n\t */\n\tprotected maybeUpdateChildState(child: SummarizerNode, id: string): void {\n\t\t// If a summary is in progress, this child was created after the summary started. So, we need to update the\n\t\t// child's summary state as well.\n\t\tif (this.isSummaryInProgress()) {\n\t\t\tchild.wipReferenceSequenceNumber = this.wipReferenceSequenceNumber;\n\t\t}\n\t\t// In case we have pending summaries on the parent, let's initialize it on the child.\n\t\tif (child._lastSummaryReferenceSequenceNumber !== undefined) {\n\t\t\tfor (const [proposedHandle, pendingSummaryInfo] of this.pendingSummaries.entries()) {\n\t\t\t\tchild.addPendingSummary(proposedHandle, pendingSummaryInfo);\n\t\t\t}\n\t\t}\n\t}\n\n\tprotected addPendingSummary(key: string, pendingSummaryInfo: PendingSummaryInfo): void {\n\t\tthis.pendingSummaries.set(key, pendingSummaryInfo);\n\t}\n\n\t/**\n\t * Tells whether summary tracking is in progress. True if \"startSummary\" API is called before summarize.\n\t */\n\tpublic isSummaryInProgress(): boolean {\n\t\treturn this.wipReferenceSequenceNumber !== undefined;\n\t}\n\n\t/**\n\t * Creates and throws an error due to unexpected conditions.\n\t */\n\tprotected throwUnexpectedError(eventProps: ITelemetryErrorEventExt): never {\n\t\tconst error = new LoggingError(eventProps.eventName, {\n\t\t\t...eventProps,\n\t\t\treferenceSequenceNumber: this.wipReferenceSequenceNumber,\n\t\t\t...tagCodeArtifacts({\n\t\t\t\tid: this.telemetryNodeId,\n\t\t\t}),\n\t\t});\n\t\tthis.logger.sendErrorEvent(eventProps, error);\n\t\tthrow error;\n\t}\n}\n\n/**\n * Creates a root summarizer node.\n * @param logger - Logger to use within SummarizerNode\n * @param summarizeInternalFn - Function to generate summary\n * @param changeSequenceNumber - Sequence number of latest change to new node/subtree\n * @param referenceSequenceNumber - Reference sequence number of last acked summary,\n * or undefined if not loaded from summary\n * @param config - Configure behavior of summarizer node\n */\nexport const createRootSummarizerNode = (\n\tlogger: TelemetryLoggerExt,\n\tsummarizeInternalFn: SummarizeInternalFn,\n\tchangeSequenceNumber: number,\n\treferenceSequenceNumber: number | undefined,\n\tconfig: ISummarizerNodeConfig = {},\n): IRootSummarizerNode =>\n\tnew SummarizerNode(\n\t\tlogger,\n\t\tsummarizeInternalFn,\n\t\tconfig,\n\t\t\"\" /* summaryHandleId */,\n\t\tchangeSequenceNumber,\n\t\treferenceSequenceNumber,\n\t\tundefined /* wipSummaryLogger */,\n\t\t\"\" /* telemetryNodeId */,\n\t);\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fluidframework/container-runtime",
3
- "version": "2.111.0",
3
+ "version": "2.113.0-411909",
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.111.0",
123
- "@fluidframework/container-definitions": "~2.111.0",
124
- "@fluidframework/container-runtime-definitions": "~2.111.0",
125
- "@fluidframework/core-interfaces": "~2.111.0",
126
- "@fluidframework/core-utils": "~2.111.0",
127
- "@fluidframework/datastore": "~2.111.0",
128
- "@fluidframework/driver-definitions": "~2.111.0",
129
- "@fluidframework/driver-utils": "~2.111.0",
130
- "@fluidframework/id-compressor": "~2.111.0",
131
- "@fluidframework/runtime-definitions": "~2.111.0",
132
- "@fluidframework/runtime-utils": "~2.111.0",
133
- "@fluidframework/telemetry-utils": "~2.111.0",
122
+ "@fluid-internal/client-utils": "2.113.0-411909",
123
+ "@fluidframework/container-definitions": "2.113.0-411909",
124
+ "@fluidframework/container-runtime-definitions": "2.113.0-411909",
125
+ "@fluidframework/core-interfaces": "2.113.0-411909",
126
+ "@fluidframework/core-utils": "2.113.0-411909",
127
+ "@fluidframework/datastore": "2.113.0-411909",
128
+ "@fluidframework/driver-definitions": "2.113.0-411909",
129
+ "@fluidframework/driver-utils": "2.113.0-411909",
130
+ "@fluidframework/id-compressor": "2.113.0-411909",
131
+ "@fluidframework/runtime-definitions": "2.113.0-411909",
132
+ "@fluidframework/runtime-utils": "2.113.0-411909",
133
+ "@fluidframework/telemetry-utils": "2.113.0-411909",
134
134
  "@tylerbu/sorted-btree-es6": "^2.1.1",
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.18.2",
142
142
  "@biomejs/biome": "~2.4.5",
143
- "@fluid-internal/mocha-test-setup": "~2.111.0",
144
- "@fluid-private/stochastic-test-utils": "~2.111.0",
145
- "@fluid-private/test-pairwise-generator": "~2.111.0",
143
+ "@fluid-internal/mocha-test-setup": "2.113.0-411909",
144
+ "@fluid-private/stochastic-test-utils": "2.113.0-411909",
145
+ "@fluid-private/test-pairwise-generator": "2.113.0-411909",
146
146
  "@fluid-tools/benchmark": "^0.59.0",
147
147
  "@fluid-tools/build-cli": "^0.65.0",
148
148
  "@fluidframework/build-common": "^2.0.3",
149
149
  "@fluidframework/build-tools": "^0.65.0",
150
- "@fluidframework/container-runtime-previous": "npm:@fluidframework/container-runtime@2.110.0",
150
+ "@fluidframework/container-runtime-previous": "npm:@fluidframework/container-runtime@2.111.0",
151
151
  "@fluidframework/eslint-config-fluid": "^13.0.0",
152
- "@fluidframework/test-runtime-utils": "~2.111.0",
152
+ "@fluidframework/test-runtime-utils": "2.113.0-411909",
153
153
  "@microsoft/api-extractor": "7.58.1",
154
154
  "@types/double-ended-queue": "^2.1.0",
155
155
  "@types/lz4js": "^0.2.0",
@@ -66,6 +66,7 @@ export class BlobHandle
66
66
  IFluidHandleInternalPayloadPending<ArrayBufferLike>
67
67
  {
68
68
  private attached: boolean = false;
69
+ private payloadSharingStarted: boolean = false;
69
70
 
70
71
  public get isAttached(): boolean {
71
72
  return this.routeContext.isAttached && this.attached;
@@ -100,7 +101,8 @@ export class BlobHandle
100
101
  // TODO: just take the blob rather than a get function?
101
102
  public get: () => Promise<ArrayBufferLike>,
102
103
  public readonly payloadPending: boolean,
103
- private readonly onAttachGraph?: () => void,
104
+ private readonly sharePayloadCallback?: () => void,
105
+ private readonly attachGraphCallback?: () => void,
104
106
  ) {
105
107
  super();
106
108
  this._payloadState = payloadPending ? "pending" : "shared";
@@ -117,10 +119,19 @@ export class BlobHandle
117
119
  this._events?.emit("payloadShareFailed", error);
118
120
  };
119
121
 
122
+ public sharePayload(): void {
123
+ if (this.payloadState === "shared" || this.payloadSharingStarted) {
124
+ return;
125
+ }
126
+ this.payloadSharingStarted = true;
127
+ this.sharePayloadCallback?.();
128
+ }
129
+
120
130
  public attachGraph(): void {
121
131
  if (!this.attached) {
122
132
  this.attached = true;
123
- this.onAttachGraph?.();
133
+ this.attachGraphCallback?.();
134
+ this.sharePayload();
124
135
  }
125
136
  }
126
137
  }
@@ -254,7 +265,7 @@ export class BlobManager {
254
265
  private readonly localBlobCache: Map<string, LocalBlobRecord> = new Map();
255
266
  /**
256
267
  * Blobs with an attached handle that have not finished blob-attaching are the set we need to provide from
257
- * getPendingState(). This stores their local IDs, and then we can look them up against the localBlobCache.
268
+ * getPendingState(). This stores their local IDs, and then we can look them up against the localBlobCache.
258
269
  */
259
270
  private readonly pendingBlobsWithAttachedHandles: Set<string> = new Set();
260
271
  /**
@@ -515,7 +526,6 @@ export class BlobManager {
515
526
  async () => blob,
516
527
  true, // payloadPending
517
528
  () => {
518
- this.pendingBlobsWithAttachedHandles.add(localId);
519
529
  const uploadAndAttachP = this.uploadAndAttach(localId, signal);
520
530
  uploadAndAttachP.then(blobHandle.notifyShared).catch((error) => {
521
531
  // TODO: notifyShared won't fail directly, but it emits an event to the customer.
@@ -523,6 +533,12 @@ export class BlobManager {
523
533
  });
524
534
  uploadAndAttachP.catch(blobHandle.notifyFailed);
525
535
  },
536
+ () => {
537
+ const localBlobRecord = this.localBlobCache.get(localId);
538
+ if (localBlobRecord !== undefined && localBlobRecord.state !== "attached") {
539
+ this.pendingBlobsWithAttachedHandles.add(localId);
540
+ }
541
+ },
526
542
  );
527
543
 
528
544
  return blobHandle;
@@ -1523,6 +1523,7 @@ export class ContainerRuntime
1523
1523
 
1524
1524
  private lastEmittedDirty: boolean;
1525
1525
  private emitDirtyDocumentEvent = true;
1526
+ private lastEmittedHasStagedChanges: boolean;
1526
1527
  private readonly useDeltaManagerOpsProxy: boolean;
1527
1528
  private readonly closeSummarizerDelayMs: number;
1528
1529
 
@@ -2196,6 +2197,9 @@ export class ContainerRuntime
2196
2197
  this.lastEmittedDirty = this.computeCurrentDirtyState();
2197
2198
  context.updateDirtyContainerState(this.lastEmittedDirty);
2198
2199
 
2200
+ // We haven't emitted hasStagedChangesChanged yet, but this is the baseline so we know to emit when it changes
2201
+ this.lastEmittedHasStagedChanges = this.computeCurrentHasStagedChanges();
2202
+
2199
2203
  // Reference Sequence Number may have just changed, and it must be consistent across a batch,
2200
2204
  // so we should flush now to clear the way for the next ops.
2201
2205
  // NOTE: This will be redundant whenever CR.process was called for the op (since we flush there too) -
@@ -3244,13 +3248,14 @@ export class ContainerRuntime
3244
3248
  "Duplicate batch - The same batch was sequenced twice",
3245
3249
  { batchId: batchStart.batchId },
3246
3250
  );
3247
-
3251
+ const batchIdExplicit = batchStart.batchId !== undefined;
3252
+ const otherBatchIdExplicit = result.otherBatchInfo?.batchIdExplicit ?? false;
3248
3253
  this.mc.logger.sendTelemetryEvent(
3249
3254
  {
3250
3255
  eventName: "DuplicateBatch",
3251
3256
  details: {
3252
3257
  batchId: batchStart.batchId,
3253
- batchIdExplicit: batchStart.batchId !== undefined,
3258
+ batchIdExplicit,
3254
3259
  clientId: batchStart.clientId,
3255
3260
  batchStartCsn: batchStart.batchStartCsn,
3256
3261
  size: inboundResult.length,
@@ -3261,7 +3266,7 @@ export class ContainerRuntime
3261
3266
  // loaded from a summary snapshot rather than seen at runtime.
3262
3267
  otherClientId: result.otherBatchInfo?.clientId,
3263
3268
  otherBatchStartCsn: result.otherBatchInfo?.batchStartCsn,
3264
- otherBatchIdExplicit: result.otherBatchInfo?.batchIdExplicit,
3269
+ otherBatchIdExplicit,
3265
3270
  otherFromSnapshot: result.otherBatchInfo === undefined,
3266
3271
  ...extractSafePropertiesFromMessage(batchStart.keyMessage),
3267
3272
  // For grouped batches, `keyMessage` is one of the sub-messages produced by
@@ -3274,10 +3279,12 @@ export class ContainerRuntime
3274
3279
  },
3275
3280
  error,
3276
3281
  );
3277
- // Due to a live incident where we had a bug in the service that caused duplicate batches to be sent to clients, we want to log when we detect a duplicate batch, but we don't want to throw an error
3278
- // as it could hit the same service bug. We need to monitor below event to catch legitimate container forking scenarios and reenable throwing the data corruption error once the service bug is fixed and we stop seeing duplicate batches in the wild
3279
- // or once we are able to identify batch duplication reason (forking vs service bug).
3280
- // throw error;
3282
+ // Only throw the error if either the current batch or the other batch has an explicit batchId since that indicates a meaningful duplication scenario
3283
+ // coming from our batch readings rather than a server outage scenario.
3284
+ const shouldThrowOnDuplicate = batchIdExplicit || otherBatchIdExplicit;
3285
+ if (shouldThrowOnDuplicate) {
3286
+ throw error;
3287
+ }
3281
3288
  }
3282
3289
  }
3283
3290
 
@@ -3717,6 +3724,12 @@ export class ContainerRuntime
3717
3724
  this.closeFn(error2);
3718
3725
  throw error2;
3719
3726
  }
3727
+
3728
+ // Flushing moves any staged batch from the Outbox into the PendingStateManager. Since
3729
+ // computeCurrentHasStagedChanges() now also considers a non-empty Outbox in Staging Mode, the
3730
+ // externally-visible value shouldn't change here, but re-checking is cheap and keeps this
3731
+ // method robust to future changes in how the two are tracked.
3732
+ this.updateHasStagedChangesState();
3720
3733
  }
3721
3734
 
3722
3735
  /**
@@ -3899,6 +3912,7 @@ export class ContainerRuntime
3899
3912
  },
3900
3913
  );
3901
3914
  this.updateDocumentDirtyState();
3915
+ this.updateHasStagedChangesState();
3902
3916
  return batchInfos;
3903
3917
  }, "discard"),
3904
3918
  commitChanges: (options) => {
@@ -3906,10 +3920,12 @@ export class ContainerRuntime
3906
3920
  exitStagingMode(() => {
3907
3921
  // Replay all staged batches in typical FIFO order.
3908
3922
  // We'll be out of staging mode so they'll be sent to the service finally.
3909
- return this.pendingStateManager.replayPendingStates({
3923
+ const batchInfos = this.pendingStateManager.replayPendingStates({
3910
3924
  committingStagedBatches: true,
3911
3925
  squash,
3912
3926
  });
3927
+ this.updateHasStagedChangesState();
3928
+ return batchInfos;
3913
3929
  }, "commit");
3914
3930
  },
3915
3931
  };
@@ -4036,6 +4052,38 @@ export class ContainerRuntime
4036
4052
  );
4037
4053
  }
4038
4054
 
4055
+ /**
4056
+ * Returns true if there are any staged changes, i.e. changes submitted while in Staging Mode
4057
+ * (see {@link @fluidframework/runtime-definitions#IContainerRuntimeBaseInternal.enterStagingMode})
4058
+ * that have not yet been discarded or committed.
4059
+ *
4060
+ * @remarks This is distinct from {@link ContainerRuntime.isDirty}: a container may be dirty due to
4061
+ * ordinary unacknowledged local changes without having any staged changes.
4062
+ */
4063
+ public get hasStagedChanges(): boolean {
4064
+ // Rather than recomputing this in the moment, just regurgitate the last emitted state.
4065
+ return this.lastEmittedHasStagedChanges;
4066
+ }
4067
+
4068
+ /**
4069
+ * Returns true if there are currently any staged (not yet discarded or committed) changes pending.
4070
+ *
4071
+ * @remarks While in Staging Mode, newly submitted ops sit in the Outbox until the next flush before
4072
+ * they're moved to the PendingStateManager (where `pendingStateManager.hasStagedChanges()` looks).
4073
+ * So we also check the Outbox here, otherwise there would be a window between submit and flush where
4074
+ * staged changes exist but this would incorrectly report false.
4075
+ *
4076
+ * @remarks We don't care about the type of ops in the Outbox here (unlike dirty state), just whether
4077
+ * there's anything queued at all, for consistency with how `pendingStateManager.hasStagedChanges()`
4078
+ * doesn't discriminate by op type either.
4079
+ */
4080
+ private computeCurrentHasStagedChanges(): boolean {
4081
+ return (
4082
+ this.pendingStateManager.hasStagedChanges() ||
4083
+ (this.inStagingMode && !this.outbox.isEmpty)
4084
+ );
4085
+ }
4086
+
4039
4087
  /**
4040
4088
  * Submits the signal to be sent to other clients.
4041
4089
  * @param type - Type of the signal.
@@ -4871,13 +4919,31 @@ export class ContainerRuntime
4871
4919
  private updateDocumentDirtyState(): void {
4872
4920
  const dirty: boolean = this.computeCurrentDirtyState();
4873
4921
 
4874
- if (this.lastEmittedDirty === dirty) {
4922
+ if (this.lastEmittedDirty !== dirty) {
4923
+ this.lastEmittedDirty = dirty;
4924
+ if (this.emitDirtyDocumentEvent) {
4925
+ this.emit(dirty ? "dirty" : "saved");
4926
+ }
4927
+ }
4928
+ }
4929
+
4930
+ /**
4931
+ * Emit "hasStagedChangesChanged" if the current staged-changes state differs from what was last emitted.
4932
+ * This must be called explicitly at each place staged changes can be added or removed (submit, flush,
4933
+ * discardChanges, commitChanges) -- unlike {@link ContainerRuntime.updateDocumentDirtyState}, it is not
4934
+ * safe to call this unconditionally alongside dirty tracking, since most dirty-state transitions (e.g.
4935
+ * acking ops, reconnecting) can't affect staged changes.
4936
+ */
4937
+ private updateHasStagedChangesState(): void {
4938
+ const hasStagedChanges: boolean = this.computeCurrentHasStagedChanges();
4939
+
4940
+ if (this.lastEmittedHasStagedChanges === hasStagedChanges) {
4875
4941
  return;
4876
4942
  }
4877
4943
 
4878
- this.lastEmittedDirty = dirty;
4944
+ this.lastEmittedHasStagedChanges = hasStagedChanges;
4879
4945
  if (this.emitDirtyDocumentEvent) {
4880
- this.emit(dirty ? "dirty" : "saved");
4946
+ this.emit("hasStagedChangesChanged", hasStagedChanges);
4881
4947
  }
4882
4948
  }
4883
4949
 
@@ -5056,6 +5122,7 @@ export class ContainerRuntime
5056
5122
  }
5057
5123
 
5058
5124
  this.updateDocumentDirtyState();
5125
+ this.updateHasStagedChangesState();
5059
5126
  }
5060
5127
 
5061
5128
  private scheduleFlush(): void {
@@ -6,4 +6,4 @@
6
6
  */
7
7
 
8
8
  export const pkgName = "@fluidframework/container-runtime";
9
- export const pkgVersion = "2.111.0";
9
+ export const pkgVersion = "2.113.0-411909";
@@ -333,6 +333,22 @@ export class PendingStateManager implements IDisposable {
333
333
  return this.initialMessages.length > 0;
334
334
  }
335
335
 
336
+ /**
337
+ * Checks the pending messages to see if any of them are staged (submitted while in Staging Mode).
338
+ * Unlike {@link PendingStateManager.hasPendingUserChanges}, this does not filter by "dirtyable" messages:
339
+ * anything submitted while in Staging Mode counts as a staged change that must be committed or discarded,
340
+ * regardless of whether it would otherwise count towards dirty tracking.
341
+ */
342
+ public hasStagedChanges(): boolean {
343
+ for (let i = 0; i < this.pendingMessages.length; i++) {
344
+ const element = this.pendingMessages.get(i);
345
+ if (element?.batchInfo.staged === true) {
346
+ return true;
347
+ }
348
+ }
349
+ return false;
350
+ }
351
+
336
352
  /**
337
353
  * The minimumPendingMessageSequenceNumber is the minimum of the first pending message and the first initial message.
338
354
  *
@@ -570,13 +570,15 @@ export class SummarizerNode implements IRootSummarizerNode {
570
570
  const parentLastSummaryReferenceSequenceNumber = this._lastSummaryReferenceSequenceNumber;
571
571
  switch (createParam.type) {
572
572
  case CreateSummarizerNodeSource.FromAttach: {
573
- if (
574
- parentLastSummaryReferenceSequenceNumber !== undefined &&
575
- createParam.sequenceNumber <= parentLastSummaryReferenceSequenceNumber
576
- ) {
577
- // Prioritize latest summary if it was after this node was attached.
578
- childLastSummaryReferenceSequenceNumber = parentLastSummaryReferenceSequenceNumber;
579
- }
573
+ // `parentLastSummaryReferenceSequenceNumber` is the latest sequence number processed in the last
574
+ // successful summary. So, for `createParam.sequenceNumber <= parentLastSummaryReferenceSequenceNumber`
575
+ // to be true, the attach message would have to have been sequenced before the last summary and somehow
576
+ // not be part of that summary, which is not possible.
577
+ assert(
578
+ parentLastSummaryReferenceSequenceNumber === undefined ||
579
+ createParam.sequenceNumber > parentLastSummaryReferenceSequenceNumber,
580
+ "Attach sequence number should be greater than the parent's last summary reference sequence number",
581
+ );
580
582
  changeSequenceNumber = createParam.sequenceNumber;
581
583
  break;
582
584
  }