@fluidframework/container-runtime 2.101.1 → 2.103.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/container-runtime.test-files.tar +0 -0
- package/dist/batchTracker.d.ts +1 -1
- package/dist/batchTracker.d.ts.map +1 -1
- package/dist/batchTracker.js +1 -1
- package/dist/batchTracker.js.map +1 -1
- package/dist/blobManager/blobManagerSnapSum.d.ts +2 -2
- package/dist/blobManager/blobManagerSnapSum.d.ts.map +1 -1
- package/dist/blobManager/blobManagerSnapSum.js.map +1 -1
- package/dist/connectionTelemetry.js.map +1 -1
- package/dist/containerRuntime.d.ts +16 -5
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +160 -18
- package/dist/containerRuntime.js.map +1 -1
- package/dist/dataStore.d.ts +2 -2
- package/dist/dataStore.d.ts.map +1 -1
- package/dist/dataStore.js.map +1 -1
- package/dist/dataStoreContext.d.ts.map +1 -1
- package/dist/dataStoreContext.js +1 -4
- package/dist/dataStoreContext.js.map +1 -1
- package/dist/dataStoreContexts.d.ts.map +1 -1
- package/dist/dataStoreContexts.js.map +1 -1
- package/dist/deltaScheduler.d.ts +2 -2
- package/dist/deltaScheduler.d.ts.map +1 -1
- package/dist/deltaScheduler.js.map +1 -1
- package/dist/gc/garbageCollection.d.ts +2 -2
- package/dist/gc/garbageCollection.d.ts.map +1 -1
- package/dist/gc/garbageCollection.js +12 -5
- package/dist/gc/garbageCollection.js.map +1 -1
- package/dist/gc/gcDefinitions.d.ts +3 -3
- package/dist/gc/gcDefinitions.d.ts.map +1 -1
- package/dist/gc/gcDefinitions.js.map +1 -1
- package/dist/gc/gcTelemetry.d.ts +3 -3
- package/dist/gc/gcTelemetry.d.ts.map +1 -1
- package/dist/gc/gcTelemetry.js.map +1 -1
- package/dist/inboundBatchAggregator.d.ts +2 -2
- package/dist/inboundBatchAggregator.d.ts.map +1 -1
- package/dist/inboundBatchAggregator.js.map +1 -1
- package/dist/opLifecycle/duplicateBatchDetector.d.ts +39 -3
- package/dist/opLifecycle/duplicateBatchDetector.d.ts.map +1 -1
- package/dist/opLifecycle/duplicateBatchDetector.js +57 -15
- package/dist/opLifecycle/duplicateBatchDetector.js.map +1 -1
- package/dist/opLifecycle/opCompressor.d.ts.map +1 -1
- package/dist/opLifecycle/opCompressor.js.map +1 -1
- package/dist/opLifecycle/opDecompressor.d.ts.map +1 -1
- package/dist/opLifecycle/opDecompressor.js.map +1 -1
- package/dist/opLifecycle/opGroupingManager.d.ts.map +1 -1
- package/dist/opLifecycle/opGroupingManager.js.map +1 -1
- package/dist/opLifecycle/opSplitter.d.ts.map +1 -1
- package/dist/opLifecycle/opSplitter.js.map +1 -1
- package/dist/opLifecycle/outbox.d.ts.map +1 -1
- package/dist/opLifecycle/outbox.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/pendingStateManager.d.ts +48 -1
- package/dist/pendingStateManager.d.ts.map +1 -1
- package/dist/pendingStateManager.js +54 -1
- package/dist/pendingStateManager.js.map +1 -1
- package/dist/runtimeLayerCompatState.d.ts +2 -2
- package/dist/signalTelemetryProcessing.d.ts +2 -2
- package/dist/signalTelemetryProcessing.d.ts.map +1 -1
- package/dist/signalTelemetryProcessing.js.map +1 -1
- package/dist/summary/documentSchema.d.ts +2 -2
- package/dist/summary/documentSchema.d.ts.map +1 -1
- package/dist/summary/documentSchema.js +35 -3
- package/dist/summary/documentSchema.js.map +1 -1
- package/dist/summary/orderedClientElection.d.ts +2 -2
- package/dist/summary/orderedClientElection.d.ts.map +1 -1
- package/dist/summary/orderedClientElection.js.map +1 -1
- package/dist/summary/summarizerClientElection.d.ts +2 -2
- package/dist/summary/summarizerClientElection.d.ts.map +1 -1
- package/dist/summary/summarizerClientElection.js.map +1 -1
- package/dist/summary/summarizerNode/summarizerNode.d.ts +3 -3
- package/dist/summary/summarizerNode/summarizerNode.d.ts.map +1 -1
- package/dist/summary/summarizerNode/summarizerNode.js.map +1 -1
- package/dist/summary/summarizerNode/summarizerNodeUtils.d.ts +2 -2
- package/dist/summary/summarizerNode/summarizerNodeUtils.d.ts.map +1 -1
- package/dist/summary/summarizerNode/summarizerNodeUtils.js.map +1 -1
- package/dist/summary/summarizerTypes.d.ts +3 -3
- package/dist/summary/summarizerTypes.d.ts.map +1 -1
- package/dist/summary/summarizerTypes.js.map +1 -1
- package/dist/summary/summaryCollection.d.ts.map +1 -1
- package/dist/summary/summaryCollection.js.map +1 -1
- package/dist/summary/summaryDelayLoadedModule/runningSummarizer.d.ts +2 -2
- package/dist/summary/summaryDelayLoadedModule/runningSummarizer.d.ts.map +1 -1
- package/dist/summary/summaryDelayLoadedModule/runningSummarizer.js.map +1 -1
- package/dist/summary/summaryDelayLoadedModule/summarizer.d.ts +2 -2
- package/dist/summary/summaryDelayLoadedModule/summarizer.d.ts.map +1 -1
- package/dist/summary/summaryDelayLoadedModule/summarizer.js.map +1 -1
- package/dist/summary/summaryDelayLoadedModule/summarizerHeuristics.d.ts +2 -2
- package/dist/summary/summaryDelayLoadedModule/summarizerHeuristics.d.ts.map +1 -1
- package/dist/summary/summaryDelayLoadedModule/summarizerHeuristics.js.map +1 -1
- package/dist/summary/summaryDelayLoadedModule/summaryGenerator.d.ts +2 -2
- package/dist/summary/summaryDelayLoadedModule/summaryGenerator.d.ts.map +1 -1
- package/dist/summary/summaryDelayLoadedModule/summaryGenerator.js.map +1 -1
- package/dist/summary/summaryManager.d.ts.map +1 -1
- package/dist/summary/summaryManager.js.map +1 -1
- package/lib/batchTracker.d.ts +1 -1
- package/lib/batchTracker.d.ts.map +1 -1
- package/lib/batchTracker.js +1 -1
- package/lib/batchTracker.js.map +1 -1
- package/lib/blobManager/blobManagerSnapSum.d.ts +2 -2
- package/lib/blobManager/blobManagerSnapSum.d.ts.map +1 -1
- package/lib/blobManager/blobManagerSnapSum.js.map +1 -1
- package/lib/connectionTelemetry.js.map +1 -1
- package/lib/containerRuntime.d.ts +16 -5
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +160 -18
- package/lib/containerRuntime.js.map +1 -1
- package/lib/dataStore.d.ts +2 -2
- package/lib/dataStore.d.ts.map +1 -1
- package/lib/dataStore.js.map +1 -1
- package/lib/dataStoreContext.d.ts.map +1 -1
- package/lib/dataStoreContext.js +2 -5
- package/lib/dataStoreContext.js.map +1 -1
- package/lib/dataStoreContexts.d.ts.map +1 -1
- package/lib/dataStoreContexts.js.map +1 -1
- package/lib/deltaScheduler.d.ts +2 -2
- package/lib/deltaScheduler.d.ts.map +1 -1
- package/lib/deltaScheduler.js +1 -1
- package/lib/deltaScheduler.js.map +1 -1
- package/lib/gc/garbageCollection.d.ts +2 -2
- package/lib/gc/garbageCollection.d.ts.map +1 -1
- package/lib/gc/garbageCollection.js +12 -5
- package/lib/gc/garbageCollection.js.map +1 -1
- package/lib/gc/gcDefinitions.d.ts +3 -3
- package/lib/gc/gcDefinitions.d.ts.map +1 -1
- package/lib/gc/gcDefinitions.js.map +1 -1
- package/lib/gc/gcTelemetry.d.ts +3 -3
- package/lib/gc/gcTelemetry.d.ts.map +1 -1
- package/lib/gc/gcTelemetry.js.map +1 -1
- package/lib/inboundBatchAggregator.d.ts +2 -2
- package/lib/inboundBatchAggregator.d.ts.map +1 -1
- package/lib/inboundBatchAggregator.js.map +1 -1
- package/lib/opLifecycle/duplicateBatchDetector.d.ts +39 -3
- package/lib/opLifecycle/duplicateBatchDetector.d.ts.map +1 -1
- package/lib/opLifecycle/duplicateBatchDetector.js +57 -15
- package/lib/opLifecycle/duplicateBatchDetector.js.map +1 -1
- package/lib/opLifecycle/opCompressor.d.ts.map +1 -1
- package/lib/opLifecycle/opCompressor.js.map +1 -1
- package/lib/opLifecycle/opDecompressor.d.ts.map +1 -1
- package/lib/opLifecycle/opDecompressor.js.map +1 -1
- package/lib/opLifecycle/opGroupingManager.d.ts.map +1 -1
- package/lib/opLifecycle/opGroupingManager.js.map +1 -1
- package/lib/opLifecycle/opSplitter.d.ts.map +1 -1
- package/lib/opLifecycle/opSplitter.js.map +1 -1
- package/lib/opLifecycle/outbox.d.ts.map +1 -1
- package/lib/opLifecycle/outbox.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/pendingStateManager.d.ts +48 -1
- package/lib/pendingStateManager.d.ts.map +1 -1
- package/lib/pendingStateManager.js +54 -1
- package/lib/pendingStateManager.js.map +1 -1
- package/lib/runtimeLayerCompatState.d.ts +2 -2
- package/lib/signalTelemetryProcessing.d.ts +2 -2
- package/lib/signalTelemetryProcessing.d.ts.map +1 -1
- package/lib/signalTelemetryProcessing.js.map +1 -1
- package/lib/summary/documentSchema.d.ts +2 -2
- package/lib/summary/documentSchema.d.ts.map +1 -1
- package/lib/summary/documentSchema.js +35 -3
- package/lib/summary/documentSchema.js.map +1 -1
- package/lib/summary/orderedClientElection.d.ts +2 -2
- package/lib/summary/orderedClientElection.d.ts.map +1 -1
- package/lib/summary/orderedClientElection.js.map +1 -1
- package/lib/summary/summarizerClientElection.d.ts +2 -2
- package/lib/summary/summarizerClientElection.d.ts.map +1 -1
- package/lib/summary/summarizerClientElection.js.map +1 -1
- package/lib/summary/summarizerNode/summarizerNode.d.ts +3 -3
- package/lib/summary/summarizerNode/summarizerNode.d.ts.map +1 -1
- package/lib/summary/summarizerNode/summarizerNode.js.map +1 -1
- package/lib/summary/summarizerNode/summarizerNodeUtils.d.ts +2 -2
- package/lib/summary/summarizerNode/summarizerNodeUtils.d.ts.map +1 -1
- package/lib/summary/summarizerNode/summarizerNodeUtils.js.map +1 -1
- package/lib/summary/summarizerTypes.d.ts +3 -3
- package/lib/summary/summarizerTypes.d.ts.map +1 -1
- package/lib/summary/summarizerTypes.js.map +1 -1
- package/lib/summary/summaryCollection.d.ts.map +1 -1
- package/lib/summary/summaryCollection.js.map +1 -1
- package/lib/summary/summaryDelayLoadedModule/runningSummarizer.d.ts +2 -2
- package/lib/summary/summaryDelayLoadedModule/runningSummarizer.d.ts.map +1 -1
- package/lib/summary/summaryDelayLoadedModule/runningSummarizer.js.map +1 -1
- package/lib/summary/summaryDelayLoadedModule/summarizer.d.ts +2 -2
- package/lib/summary/summaryDelayLoadedModule/summarizer.d.ts.map +1 -1
- package/lib/summary/summaryDelayLoadedModule/summarizer.js.map +1 -1
- package/lib/summary/summaryDelayLoadedModule/summarizerHeuristics.d.ts +2 -2
- package/lib/summary/summaryDelayLoadedModule/summarizerHeuristics.d.ts.map +1 -1
- package/lib/summary/summaryDelayLoadedModule/summarizerHeuristics.js.map +1 -1
- package/lib/summary/summaryDelayLoadedModule/summaryGenerator.d.ts +2 -2
- package/lib/summary/summaryDelayLoadedModule/summaryGenerator.d.ts.map +1 -1
- package/lib/summary/summaryDelayLoadedModule/summaryGenerator.js.map +1 -1
- package/lib/summary/summaryManager.d.ts.map +1 -1
- package/lib/summary/summaryManager.js.map +1 -1
- package/package.json +18 -18
- package/src/batchTracker.ts +3 -3
- package/src/blobManager/blobManagerSnapSum.ts +2 -2
- package/src/connectionTelemetry.ts +3 -3
- package/src/containerRuntime.ts +188 -25
- package/src/dataStore.ts +3 -3
- package/src/dataStoreContext.ts +2 -4
- package/src/dataStoreContexts.ts +2 -2
- package/src/deltaScheduler.ts +2 -5
- package/src/gc/garbageCollection.ts +16 -9
- package/src/gc/gcDefinitions.ts +3 -3
- package/src/gc/gcTelemetry.ts +3 -3
- package/src/inboundBatchAggregator.ts +2 -2
- package/src/opLifecycle/duplicateBatchDetector.ts +103 -23
- package/src/opLifecycle/opCompressor.ts +2 -2
- package/src/opLifecycle/opDecompressor.ts +2 -2
- package/src/opLifecycle/opGroupingManager.ts +2 -2
- package/src/opLifecycle/opSplitter.ts +2 -2
- package/src/opLifecycle/outbox.ts +2 -2
- package/src/packageVersion.ts +1 -1
- package/src/pendingStateManager.ts +80 -2
- package/src/signalTelemetryProcessing.ts +2 -2
- package/src/summary/documentSchema.ts +58 -5
- package/src/summary/orderedClientElection.ts +3 -3
- package/src/summary/summarizerClientElection.ts +2 -2
- package/src/summary/summarizerNode/summarizerNode.ts +3 -3
- package/src/summary/summarizerNode/summarizerNodeUtils.ts +2 -2
- package/src/summary/summarizerTypes.ts +3 -3
- package/src/summary/summaryCollection.ts +2 -2
- package/src/summary/summaryDelayLoadedModule/runningSummarizer.ts +2 -4
- package/src/summary/summaryDelayLoadedModule/summarizer.ts +3 -3
- package/src/summary/summaryDelayLoadedModule/summarizerHeuristics.ts +2 -2
- package/src/summary/summaryDelayLoadedModule/summaryGenerator.ts +2 -2
- package/src/summary/summaryManager.ts +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pendingStateManager.js","sourceRoot":"","sources":["../src/pendingStateManager.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 { IDisposable, ITelemetryBaseLogger } from \"@fluidframework/core-interfaces\";\nimport { assert, Lazy } from \"@fluidframework/core-utils/internal\";\nimport {\n\ttype ITelemetryLoggerExt,\n\tDataProcessingError,\n\tLoggingError,\n\textractSafePropertiesFromMessage,\n\tcreateChildLogger,\n} from \"@fluidframework/telemetry-utils/internal\";\nimport Deque from \"double-ended-queue\";\nimport { v4 as uuid } from \"uuid\";\n\nimport { isContainerMessageDirtyable } from \"./containerRuntime.js\";\nimport type {\n\tInboundContainerRuntimeMessage,\n\tInboundSequencedContainerRuntimeMessage,\n\tLocalContainerRuntimeMessage,\n} from \"./messageTypes.js\";\nimport { asBatchMetadata, asEmptyBatchLocalOpMetadata } from \"./metadata.js\";\nimport {\n\ttype EmptyGroupedBatch,\n\ttype LocalBatchMessage,\n\tgetEffectiveBatchId,\n\ttype BatchStartInfo,\n\ttype InboundMessageResult,\n\tserializeOp,\n\ttype LocalEmptyBatchPlaceholder,\n\ttype BatchResubmitInfo,\n} from \"./opLifecycle/index.js\";\n\n/**\n * This represents a message that has been submitted and is added to the pending queue when `submit` is called on the\n * ContainerRuntime. This message has either not been ack'd by the server or has not been submitted to the server yet.\n *\n * @remarks This is the current serialization format for pending local state when a Container is serialized.\n */\nexport interface IPendingMessage {\n\ttype: \"message\";\n\treferenceSequenceNumber: number;\n\t/**\n\t * Serialized copy of runtimeOp\n\t */\n\tcontent: string;\n\t/**\n\t * The original runtime op that was submitted to the ContainerRuntime\n\t * Unless this pending message came from stashed content, in which case this is undefined at first and then deserialized from the contents string\n\t */\n\truntimeOp: LocalContainerRuntimeMessage | EmptyGroupedBatch | undefined; // Undefined for initial messages before parsing\n\t/**\n\t * Local Op Metadata that was passed to the ContainerRuntime when the op was submitted.\n\t * This contains state needed when processing the ack, or to resubmit or rollback the op.\n\t */\n\tlocalOpMetadata: unknown;\n\t/**\n\t * Metadata that was passed to the ContainerRuntime when the op was submitted.\n\t * This is rarely used, and may be inspected by the service (as opposed to op contents which is opaque)\n\t */\n\topMetadata: Record<string, unknown> | undefined;\n\t/**\n\t * Populated upon processing the op's ack, before moving the pending message to savedOps.\n\t */\n\tsequenceNumber?: number;\n\t/**\n\t * Info about the batch this pending message belongs to, for validation and for computing the batchId on reconnect\n\t * We don't include batchId itself to avoid redundancy, because that's stamped on opMetadata above\n\t */\n\tbatchInfo: {\n\t\t/**\n\t\t * The Batch's original clientId, from when it was first flushed to be submitted.\n\t\t * Or, a random uuid if it was never submitted (and batchStartCsn will be -1)\n\t\t */\n\t\tclientId: string;\n\t\t/**\n\t\t * The Batch's original clientSequenceNumber, from when it was first flushed to be submitted\n\t\t * Or, -1 if it was never submitted (and clientId will be a random uuid)\n\t\t */\n\t\tbatchStartCsn: number;\n\t\t/**\n\t\t * length of the batch (how many runtime messages here)\n\t\t */\n\t\tlength: number;\n\t\t/**\n\t\t * If true, this batch is staged and should not actually be submitted on replayPendingStates.\n\t\t */\n\t\tstaged: boolean;\n\t};\n}\n\ntype Patch<T, U> = U & Omit<T, keyof U>;\n\n/**\n * First version of the type (pre-dates batchInfo)\n */\ntype IPendingMessageV0 = Patch<IPendingMessage, { batchInfo?: undefined }>;\n\n/**\n * Union of all supported schemas for when applying stashed ops\n *\n * @remarks When the format changes, this type should update to reflect all possible schemas.\n */\ntype IPendingMessageFromStash = IPendingMessageV0 | IPendingMessage;\n\nexport interface IPendingLocalState {\n\t/**\n\t * list of pending states, including ops and batch information\n\t */\n\tpendingStates: IPendingMessage[];\n}\n\n/**\n * Info needed to replay/resubmit a pending message\n */\nexport type PendingMessageResubmitData = Pick<\n\tIPendingMessage,\n\t\"runtimeOp\" | \"localOpMetadata\" | \"opMetadata\"\n> & {\n\t// Required (it's only missing on IPendingMessage for empty batch, which will be resubmitted as an empty array)\n\truntimeOp: LocalContainerRuntimeMessage;\n};\n\nexport interface PendingBatchResubmitMetadata extends BatchResubmitInfo {\n\t/**\n\t * Whether changes in this batch should be squashed when resubmitting.\n\t */\n\tsquash: boolean;\n}\n\nexport interface IRuntimeStateHandler {\n\tconnected(): boolean;\n\tclientId(): string | undefined;\n\tapplyStashedOp(serializedOp: string): Promise<unknown>;\n\treSubmitBatch(\n\t\tbatch: PendingMessageResubmitData[],\n\t\tmetadata: PendingBatchResubmitMetadata,\n\t): void;\n\tisActiveConnection: () => boolean;\n\tisAttached: () => boolean;\n}\n\nfunction isEmptyBatchPendingMessage(message: IPendingMessageFromStash): boolean {\n\tconst content = JSON.parse(message.content) as Partial<EmptyGroupedBatch>;\n\treturn content.type === \"groupedBatch\" && content.contents?.length === 0;\n}\n\nfunction buildPendingMessageContent(message: InboundSequencedContainerRuntimeMessage): string {\n\t// IMPORTANT: Order matters here, this must match the order of the properties used\n\t// when submitting the message.\n\tconst { type, contents }: InboundContainerRuntimeMessage = message;\n\t// Any properties that are not defined, won't be emitted by stringify.\n\treturn JSON.stringify({ type, contents });\n}\n\nfunction typesOfKeys<T extends object>(obj: T): Record<keyof T, string> {\n\t// TODO: Fix this violation and remove the disable\n\t// eslint-disable-next-line unicorn/no-array-reduce\n\treturn Object.keys(obj).reduce((acc, key) => {\n\t\tacc[key] = typeof obj[key];\n\t\treturn acc;\n\t}, {}) as Record<keyof T, string>;\n}\n\nfunction scrubAndStringify(\n\tmessage: InboundContainerRuntimeMessage | LocalContainerRuntimeMessage,\n): string {\n\t// Scrub the whole object in case there are unexpected keys\n\tconst scrubbed: Record<string, unknown> = typesOfKeys(message);\n\n\t// For these known/expected keys, we can either drill into the object (for contents)\n\t// or just use the value as-is (since it's not personal info)\n\tscrubbed.contents =\n\t\ttypeof message.contents === \"object\" && message.contents !== null\n\t\t\t? typesOfKeys(message.contents)\n\t\t\t: undefined;\n\tscrubbed.type = message.type;\n\n\treturn JSON.stringify(scrubbed);\n}\n\n/**\n * Finds and returns the index where the strings diverge, and the character at that index in each string (or undefined if not applicable)\n */\nfunction findFirstRawCharacterMismatched(\n\ta: string,\n\tb: string,\n): [index: number, charA?: string, charB?: string] {\n\tconst minLength = Math.min(a.length, b.length);\n\tfor (let i = 0; i < minLength; i++) {\n\t\tif (a[i] !== b[i]) {\n\t\t\treturn [i, a[i], b[i]];\n\t\t}\n\t}\n\n\t// Since we didn't return in the loop, the shorter string must be a prefix of the other.\n\t// If they're the same length, return -1 to indicate they're identical.\n\t// Otherwise, the next character of the longer one is where they differ. No need to return that next character.\n\treturn a.length === b.length\n\t\t? [-1, undefined, undefined]\n\t\t: [minLength, a[minLength], b[minLength]];\n}\n\n/**\n * Finds and returns the index where the strings diverge, and the character at that index in each string (or undefined if not applicable)\n * It scrubs non-ASCII characters since they convey more meaning (privacy consideration)\n */\nexport function findFirstCharacterMismatched(\n\ta: string,\n\tb: string,\n): [index: number, charA?: string, charB?: string] {\n\tconst [index, rawCharA, rawCharB] = findFirstRawCharacterMismatched(a, b);\n\n\tconst charA = (rawCharA?.codePointAt(0) ?? 0) <= 0x7f ? rawCharA : \"[non-ASCII]\";\n\tconst charB = (rawCharB?.codePointAt(0) ?? 0) <= 0x7f ? rawCharB : \"[non-ASCII]\";\n\n\treturn [index, charA, charB];\n}\n\n/**\n * Returns a shallow copy of the given message with the non-serializable properties removed.\n * Note that the runtimeOp's data has already been serialized in the content property.\n */\nfunction toSerializableForm(\n\tmessage: IPendingMessage,\n): IPendingMessage & { runtimeOp: undefined; localOpMetadata: undefined } {\n\treturn {\n\t\t...message,\n\t\tlocalOpMetadata: undefined,\n\t\truntimeOp: undefined,\n\t};\n}\n\ninterface ReplayPendingStateOptions {\n\t/**\n\t * If true, only replay staged batches, clearing the \"staged\" flag.\n\t * This is used when we are exiting staging mode and want to rebase and submit the staged batches without resubmitting pre-staged messages.\n\t * Default: false\n\t */\n\tcommittingStagedBatches: boolean;\n\t/**\n\t * @param squash - If true, edits should be squashed when resubmitting.\n\t * Default: false\n\t */\n\tsquash: boolean;\n}\n\nconst defaultReplayPendingStatesOptions: ReplayPendingStateOptions = {\n\tcommittingStagedBatches: false,\n\tsquash: false,\n};\n\n/**\n * PendingStateManager is responsible for maintaining the messages that have not been sent or have not yet been\n * acknowledged by the server. It also maintains the batch information for both automatically and manually flushed\n * batches along with the messages.\n * When the Container reconnects, it replays the pending states, which includes manual flushing\n * of messages and triggering resubmission of unacked ops.\n *\n * It verifies that all the ops are acked, are received in the right order and batch information is correct.\n */\nexport class PendingStateManager implements IDisposable {\n\t/**\n\t * Messages that will need to be resubmitted if not ack'd before the next reconnection\n\t */\n\tprivate readonly pendingMessages = new Deque<IPendingMessage>();\n\t/**\n\t * Messages stashed from a previous container, now being rehydrated. Need to be resubmitted.\n\t */\n\tprivate readonly initialMessages = new Deque<IPendingMessageFromStash>();\n\n\t/**\n\t * Sequenced local ops that are saved when stashing since pending ops may depend on them\n\t */\n\tprivate savedOps: IPendingMessage[] = [];\n\n\t// eslint-disable-next-line unicorn/consistent-function-scoping -- Property is defined once; no need to extract inner lambda\n\tprivate readonly disposeOnce = new Lazy<void>(() => {\n\t\tthis.initialMessages.clear();\n\t\tthis.pendingMessages.clear();\n\t});\n\n\t/**\n\t * Used to ensure we don't replay ops on the same connection twice\n\t */\n\tprivate clientIdFromLastReplay: string | undefined;\n\n\t/**\n\t * The pending messages count. Includes `pendingMessages` and `initialMessages` to keep in sync with\n\t * 'hasPendingMessages'.\n\t */\n\tpublic get pendingMessagesCount(): number {\n\t\treturn this.pendingMessages.length + this.initialMessages.length;\n\t}\n\n\t/**\n\t * Checks the pending messages to see if any of them represent user changes (aka \"dirtyable\" messages)\n\t */\n\tpublic hasPendingUserChanges(): boolean {\n\t\tfor (let i = 0; i < this.pendingMessages.length; i++) {\n\t\t\tconst element = this.pendingMessages.get(i);\n\t\t\tif (\n\t\t\t\telement !== undefined &&\n\t\t\t\thasTypicalRuntimeOp(element) && // Empty batches don't count towards user changes\n\t\t\t\tisContainerMessageDirtyable(element.runtimeOp)\n\t\t\t) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\t// Consider any initial messages to be user changes\n\t\t// (it's an approximation since we would have to parse them to know for sure)\n\t\treturn this.initialMessages.length > 0;\n\t}\n\n\t/**\n\t * The minimumPendingMessageSequenceNumber is the minimum of the first pending message and the first initial message.\n\t *\n\t * We need this so that we can properly keep local data and maintain the correct sequence window.\n\t */\n\tpublic get minimumPendingMessageSequenceNumber(): number | undefined {\n\t\treturn this.pendingMessages.peekFront()?.referenceSequenceNumber;\n\t}\n\n\t/**\n\t * Called to check if there are any pending messages in the pending message queue.\n\t * @returns A boolean indicating whether there are messages or not.\n\t */\n\tpublic hasPendingMessages(): boolean {\n\t\treturn this.pendingMessagesCount !== 0;\n\t}\n\n\tpublic getLocalState(snapshotSequenceNumber?: number): {\n\t\tpending: IPendingLocalState;\n\t} {\n\t\tassert(\n\t\t\tthis.initialMessages.isEmpty(),\n\t\t\t0x2e9 /* \"Must call getLocalState() after applying initial states\" */,\n\t\t);\n\t\t// Using snapshot sequence number to filter ops older than our latest snapshot.\n\t\t// Such ops should not be declared in pending/stashed state. Snapshot seq num will not\n\t\t// be available when the container is not attached. Therefore, no filtering is needed.\n\t\tconst newSavedOps = [...this.savedOps].filter((message) => {\n\t\t\tassert(\n\t\t\t\tmessage.sequenceNumber !== undefined,\n\t\t\t\t0x97c /* saved op should already have a sequence number */,\n\t\t\t);\n\t\t\treturn message.sequenceNumber > (snapshotSequenceNumber ?? 0);\n\t\t});\n\t\tfor (const message of this.pendingMessages.toArray()) {\n\t\t\tif (\n\t\t\t\tsnapshotSequenceNumber !== undefined &&\n\t\t\t\tmessage.referenceSequenceNumber < snapshotSequenceNumber\n\t\t\t) {\n\t\t\t\tthrow new LoggingError(\"trying to stash ops older than our latest snapshot\");\n\t\t\t}\n\t\t}\n\t\treturn {\n\t\t\tpending: {\n\t\t\t\tpendingStates: [\n\t\t\t\t\t...newSavedOps,\n\t\t\t\t\t...this.pendingMessages.toArray().map((message) => toSerializableForm(message)),\n\t\t\t\t],\n\t\t\t},\n\t\t};\n\t}\n\n\tprivate readonly logger: ITelemetryLoggerExt;\n\n\tconstructor(\n\t\tprivate readonly stateHandler: IRuntimeStateHandler,\n\t\tstashedLocalState: IPendingLocalState | undefined,\n\t\tlogger: ITelemetryBaseLogger,\n\t) {\n\t\tthis.logger = createChildLogger({ logger });\n\t\tif (stashedLocalState?.pendingStates) {\n\t\t\tthis.initialMessages.push(...stashedLocalState.pendingStates);\n\t\t}\n\t}\n\n\tpublic get disposed(): boolean {\n\t\treturn this.disposeOnce.evaluated;\n\t}\n\tpublic readonly dispose = (): void => this.disposeOnce.value;\n\n\t/**\n\t * We've flushed an empty batch, and need to track it locally until the corresponding\n\t * ack is processed, to properly track batch IDs\n\t */\n\tpublic onFlushEmptyBatch(\n\t\tplaceholder: LocalEmptyBatchPlaceholder,\n\t\tclientSequenceNumber: number | undefined,\n\t\tstaged: boolean,\n\t): void {\n\t\tthis.onFlushBatch([placeholder], clientSequenceNumber, staged);\n\t}\n\n\t/**\n\t * The given batch has been flushed, and needs to be tracked locally until the corresponding\n\t * acks are processed, to ensure it is successfully sent.\n\t * @param batch - The batch that was flushed\n\t * @param clientSequenceNumber - The CSN of the first message in the batch,\n\t * or undefined if the batch was not yet sent (e.g. by the time we flushed we lost the connection)\n\t * @param staged - Indicates whether batch is staged (not to be submitted while runtime is in Staging Mode)\n\t */\n\tpublic onFlushBatch(\n\t\tbatch: LocalBatchMessage[] | [LocalEmptyBatchPlaceholder],\n\t\tclientSequenceNumber: number | undefined,\n\t\tstaged: boolean,\n\t): void {\n\t\t// clientId and batchStartCsn are used for generating the batchId so we can detect container forks\n\t\t// where this batch was submitted by two different clients rehydrating from the same local state.\n\t\t// In the typical case where the batch was actually sent, use the clientId and clientSequenceNumber.\n\t\t// In the case where the batch was not sent, use a random uuid for clientId, and -1 for clientSequenceNumber to indicate this case.\n\t\t// This will guarantee uniqueness of the batchId, and is a suitable fallback since clientId/CSN is only needed if the batch was actually sent/sequenced.\n\t\tconst batchWasSent = clientSequenceNumber !== undefined;\n\t\tif (batchWasSent) {\n\t\t\tassert(!staged, 0xb84 /* Staged batches should not have been submitted */);\n\t\t}\n\t\tconst [clientId, batchStartCsn] = batchWasSent\n\t\t\t? [this.stateHandler.clientId(), clientSequenceNumber]\n\t\t\t: [uuid(), -1]; // -1 will indicate not a real clientId/CSN pair\n\t\tassert(\n\t\t\tclientId !== undefined,\n\t\t\t0xa33 /* clientId (from stateHandler) could only be undefined if we've never connected, but we have a CSN so we know that's not the case */,\n\t\t);\n\t\tconst batchInfo = { clientId, batchStartCsn, length: batch.length, staged };\n\t\tfor (const message of batch) {\n\t\t\tconst {\n\t\t\t\truntimeOp,\n\t\t\t\treferenceSequenceNumber,\n\t\t\t\tlocalOpMetadata,\n\t\t\t\tmetadata: opMetadata,\n\t\t\t} = message;\n\t\t\tconst pendingMessage: IPendingMessage = {\n\t\t\t\ttype: \"message\",\n\t\t\t\treferenceSequenceNumber,\n\t\t\t\tcontent: serializeOp(runtimeOp).content,\n\t\t\t\truntimeOp,\n\t\t\t\tlocalOpMetadata,\n\t\t\t\topMetadata,\n\t\t\t\tbatchInfo,\n\t\t\t};\n\t\t\tthis.pendingMessages.push(pendingMessage);\n\t\t}\n\t}\n\n\t/**\n\t * Applies stashed ops at their reference sequence number so they are ready to be ACKed or resubmitted\n\t * @param seqNum - Sequence number at which to apply ops. Will apply all ops if seqNum is undefined.\n\t */\n\tpublic async applyStashedOpsAt(seqNum?: number): Promise<void> {\n\t\t// apply stashed ops at sequence number\n\t\twhile (!this.initialMessages.isEmpty()) {\n\t\t\tif (seqNum !== undefined) {\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n\t\t\t\tconst peekMessage = this.initialMessages.peekFront()!;\n\t\t\t\tif (peekMessage.referenceSequenceNumber > seqNum) {\n\t\t\t\t\tbreak; // nothing left to do at this sequence number\n\t\t\t\t}\n\t\t\t\tif (peekMessage.referenceSequenceNumber < seqNum) {\n\t\t\t\t\tthrow new Error(\"loaded from snapshot too recent to apply stashed ops\");\n\t\t\t\t}\n\t\t\t}\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n\t\t\tconst nextMessage = this.initialMessages.shift()!;\n\t\t\t// Nothing to apply if the message is an empty batch.\n\t\t\t// We still need to track it for resubmission.\n\t\t\ttry {\n\t\t\t\tif (isEmptyBatchPendingMessage(nextMessage)) {\n\t\t\t\t\tnextMessage.localOpMetadata = {\n\t\t\t\t\t\temptyBatch: true,\n\t\t\t\t\t} satisfies LocalEmptyBatchPlaceholder[\"localOpMetadata\"]; // equivalent to applyStashedOp for empty batch\n\t\t\t\t\tpatchbatchInfo(nextMessage); // Back compat\n\t\t\t\t\tthis.pendingMessages.push(nextMessage);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t// applyStashedOp will cause the DDS to behave as if it has sent the op but not actually send it\n\t\t\t\tconst localOpMetadata = await this.stateHandler.applyStashedOp(nextMessage.content);\n\t\t\t\tif (this.stateHandler.isAttached()) {\n\t\t\t\t\tnextMessage.localOpMetadata = localOpMetadata;\n\t\t\t\t\t// NOTE: This runtimeOp has been roundtripped through string, which is technically lossy.\n\t\t\t\t\t// e.g. At this point, handles are in their encoded form.\n\t\t\t\t\tnextMessage.runtimeOp = JSON.parse(\n\t\t\t\t\t\tnextMessage.content,\n\t\t\t\t\t) as LocalContainerRuntimeMessage;\n\t\t\t\t\t// then we push onto pendingMessages which will cause PendingStateManager to resubmit when we connect\n\t\t\t\t\tpatchbatchInfo(nextMessage); // Back compat\n\t\t\t\t\tthis.pendingMessages.push(nextMessage);\n\t\t\t\t} else {\n\t\t\t\t\tif (localOpMetadata !== undefined) {\n\t\t\t\t\t\tthrow new Error(\"Local Op Metadata must be undefined when not attached\");\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tthrow DataProcessingError.wrapIfUnrecognized(error, \"applyStashedOp\", nextMessage);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Compares the batch ID of the incoming batch with the pending batch ID for this client.\n\t * They should not match, as that would indicate a forked container.\n\t * @param remoteBatchStart - BatchStartInfo for an incoming batch *NOT* submitted by this client\n\t * @returns whether the batch IDs match\n\t */\n\tprivate remoteBatchMatchesPendingBatch(remoteBatchStart: BatchStartInfo): boolean {\n\t\tconst pendingMessage = this.pendingMessages.peekFront();\n\t\tif (pendingMessage === undefined) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// We must compare the effective batch IDs, since one of these ops\n\t\t// may have been the original, not resubmitted, so wouldn't have its batch ID stamped yet.\n\t\tconst pendingBatchId = getEffectiveBatchId(pendingMessage);\n\t\tconst inboundBatchId = getEffectiveBatchId(remoteBatchStart);\n\n\t\treturn pendingBatchId === inboundBatchId;\n\t}\n\n\t/**\n\t * Processes an inbound message or batch of messages - May be local or remote.\n\t *\n\t * @param inbound - The inbound message(s) to process, with extra info (e.g. about the start of a batch). Could be local or remote.\n\t * @param local - true if we submitted these messages and expect corresponding pending messages\n\t * @returns The inbound messages with localOpMetadata \"zipped\" in.\n\t *\n\t * @throws a DataProcessingError in either of these cases:\n\t * - The pending message content doesn't match the incoming message content for any message here\n\t * - The batch IDs *do match* but it's not local (indicates Container forking).\n\t */\n\tpublic processInboundMessages(\n\t\tinbound: InboundMessageResult,\n\t\tlocal: boolean,\n\t): {\n\t\tmessage: InboundSequencedContainerRuntimeMessage;\n\t\tlocalOpMetadata?: unknown;\n\t}[] {\n\t\tif (local) {\n\t\t\treturn this.processPendingLocalMessages(inbound);\n\t\t}\n\n\t\t// An inbound remote batch should not match the pending batch ID for this client.\n\t\t// That would indicate the container forked (two instances trying to submit the same local state)\n\t\tif (\"batchStart\" in inbound && this.remoteBatchMatchesPendingBatch(inbound.batchStart)) {\n\t\t\tthrow DataProcessingError.create(\n\t\t\t\t\"Forked Container Error! Matching batchIds but mismatched clientId\",\n\t\t\t\t\"PendingStateManager.processInboundMessages\",\n\t\t\t\tinbound.batchStart.keyMessage,\n\t\t\t);\n\t\t}\n\n\t\t// No localOpMetadata for remote messages\n\t\tconst messages = inbound.type === \"fullBatch\" ? inbound.messages : [inbound.nextMessage];\n\t\treturn messages.map((message) => ({ message }));\n\t}\n\n\t/**\n\t * Processes the incoming message(s) from the server that were submitted by this client.\n\t * It verifies that messages are received in the right order and that any batch information is correct.\n\t * @param inbound - The inbound message(s) (originating from this client) to correlate with the pending local state\n\t * @throws DataProcessingError if the pending message content doesn't match the incoming message content for any message here\n\t * @returns The inbound messages with localOpMetadata \"zipped\" in.\n\t */\n\tprivate processPendingLocalMessages(inbound: InboundMessageResult): {\n\t\tmessage: InboundSequencedContainerRuntimeMessage;\n\t\tlocalOpMetadata: unknown;\n\t}[] {\n\t\tif (\"batchStart\" in inbound) {\n\t\t\tthis.onLocalBatchBegin(inbound.batchStart, inbound.length);\n\t\t}\n\n\t\t// Empty batch\n\t\tif (inbound.length === 0) {\n\t\t\tconst localOpMetadata = this.processNextPendingMessage(\n\t\t\t\tinbound.batchStart.keyMessage.sequenceNumber,\n\t\t\t);\n\t\t\tassert(\n\t\t\t\tasEmptyBatchLocalOpMetadata(localOpMetadata)?.emptyBatch === true,\n\t\t\t\t0xa20 /* Expected empty batch marker */,\n\t\t\t);\n\t\t\treturn [];\n\t\t}\n\n\t\tconst messages = inbound.type === \"fullBatch\" ? inbound.messages : [inbound.nextMessage];\n\n\t\treturn messages.map((message) => ({\n\t\t\tmessage,\n\t\t\tlocalOpMetadata: this.processNextPendingMessage(message.sequenceNumber, message),\n\t\t}));\n\t}\n\n\t/**\n\t * Processes the pending local copy of message that's been ack'd by the server.\n\t * @param sequenceNumber - The sequenceNumber from the server corresponding to the next pending message.\n\t * @param message - [optional] The entire incoming message, for comparing contents with the pending message for extra validation.\n\t * @throws DataProcessingError if the pending message content doesn't match the incoming message content.\n\t * @returns The localOpMetadata of the next pending message, to be sent to whoever submitted the original message.\n\t */\n\tprivate processNextPendingMessage(\n\t\tsequenceNumber: number,\n\t\tmessage?: InboundSequencedContainerRuntimeMessage,\n\t): unknown {\n\t\tconst pendingMessage = this.pendingMessages.peekFront();\n\t\tassert(\n\t\t\tpendingMessage !== undefined,\n\t\t\t0x169 /* \"No pending message found for this remote message\" */,\n\t\t);\n\n\t\tpendingMessage.sequenceNumber = sequenceNumber;\n\t\tthis.savedOps.push(toSerializableForm(pendingMessage));\n\n\t\tthis.pendingMessages.shift();\n\n\t\t// message is undefined in the Empty Batch case,\n\t\t// because we don't have an incoming message to compare and pendingMessage is just a placeholder anyway.\n\t\tif (message !== undefined) {\n\t\t\tconst messageContent = buildPendingMessageContent(message);\n\n\t\t\t// Stringified content should match\n\t\t\t// If it doesn't, collect as much info about the difference as possible (privacy-wise) and log it\n\t\t\tif (pendingMessage.content !== messageContent) {\n\t\t\t\tconst [pendingLength, incomingLength] = [\n\t\t\t\t\tpendingMessage.content.length,\n\t\t\t\t\tmessageContent.length,\n\t\t\t\t];\n\t\t\t\tconst [mismatchStartIndex, pendingChar, incomingChar] = findFirstCharacterMismatched(\n\t\t\t\t\tpendingMessage.content,\n\t\t\t\t\tmessageContent,\n\t\t\t\t);\n\n\t\t\t\tconst pendingContentObj = JSON.parse(\n\t\t\t\t\tpendingMessage.content,\n\t\t\t\t) as LocalContainerRuntimeMessage;\n\t\t\t\tconst incomingContentObj = JSON.parse(\n\t\t\t\t\tmessageContent,\n\t\t\t\t) as InboundContainerRuntimeMessage;\n\n\t\t\t\t// Compare inner contents object, since that both should be { type, contents }\n\t\t\t\tconst contentsMatch =\n\t\t\t\t\tpendingContentObj.contents === incomingContentObj.contents ||\n\t\t\t\t\t(pendingContentObj.contents !== undefined &&\n\t\t\t\t\t\tincomingContentObj.contents !== undefined &&\n\t\t\t\t\t\tJSON.stringify(pendingContentObj.contents) ===\n\t\t\t\t\t\t\tJSON.stringify(incomingContentObj.contents));\n\n\t\t\t\tthis.logger.sendErrorEvent({\n\t\t\t\t\teventName: \"unexpectedAckReceived\",\n\t\t\t\t\tdetails: {\n\t\t\t\t\t\tpendingContentScrubbed: scrubAndStringify(pendingContentObj),\n\t\t\t\t\t\tincomingContentScrubbed: scrubAndStringify(incomingContentObj),\n\t\t\t\t\t\tcontentsMatch,\n\t\t\t\t\t\tpendingLength,\n\t\t\t\t\t\tincomingLength,\n\t\t\t\t\t\tmismatchStartIndex,\n\t\t\t\t\t\tpendingChar,\n\t\t\t\t\t\tincomingChar,\n\t\t\t\t\t},\n\t\t\t\t});\n\n\t\t\t\tthrow DataProcessingError.create(\n\t\t\t\t\t\"pending local message content mismatch\",\n\t\t\t\t\t\"unexpectedAckReceived\",\n\t\t\t\t\tmessage,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\treturn pendingMessage.localOpMetadata;\n\t}\n\n\t/**\n\t * Check if the incoming batch matches the batch info for the next pending message.\n\t */\n\tprivate onLocalBatchBegin(batchStart: BatchStartInfo, batchLength?: number): void {\n\t\t// Get the next message from the pending queue. Verify a message exists.\n\t\tconst pendingMessage = this.pendingMessages.peekFront();\n\t\tassert(\n\t\t\tpendingMessage !== undefined,\n\t\t\t0xa21 /* No pending message found as we start processing this remote batch */,\n\t\t);\n\t\tassert(\n\t\t\t!pendingMessage.batchInfo.staged,\n\t\t\t0xb85 /* Pending state mismatch, ack came in but next pending message is staged */,\n\t\t);\n\n\t\t// If this batch became empty on resubmit, batch.messages will be empty (but keyMessage is always set)\n\t\t// and the next pending message should be an empty batch marker.\n\t\t// More Info: We must submit empty batches and track them in case a different fork\n\t\t// of this container also submitted the same batch (and it may not be empty for that fork).\n\t\tconst firstMessage = batchStart.keyMessage;\n\t\t// -1 length is for back compat, undefined length means we actually don't know it\n\t\tconst skipLengthCheck =\n\t\t\tpendingMessage.batchInfo.length === -1 || batchLength === undefined;\n\t\tconst expectedPendingBatchLength =\n\t\t\tbatchLength === 0\n\t\t\t\t? 1 // For an empty batch, expect a singleton array with the empty batch marker\n\t\t\t\t: batchLength; // Otherwise, the lengths should actually match\n\n\t\t// Note: We don't need to use getEffectiveBatchId here, just check the explicit stamped batchID\n\t\t// That logic is needed only when comparing across potential container forks.\n\t\t// Furthermore, we also are comparing the batch IDs constituent data - clientId (it's local) and batchStartCsn.\n\t\tconst pendingBatchId = asBatchMetadata(pendingMessage.opMetadata)?.batchId;\n\t\tconst inboundBatchId = batchStart.batchId;\n\n\t\t// We expect the incoming batch to be of the same length, starting at the same clientSequenceNumber,\n\t\t// as the batch we originally submitted. The batchIds should match as well, if set (or neither should be set)\n\t\t// We have another later check to compare the message contents, which we'd expect to fail if this check does,\n\t\t// so we don't throw here, merely log. In a later release this check may replace that one since it's cheaper.\n\t\tif (\n\t\t\tpendingMessage.batchInfo.batchStartCsn !== batchStart.batchStartCsn ||\n\t\t\t(!skipLengthCheck && pendingMessage.batchInfo.length !== expectedPendingBatchLength) ||\n\t\t\tpendingBatchId !== inboundBatchId\n\t\t) {\n\t\t\tthis.logger?.sendErrorEvent({\n\t\t\t\teventName: \"BatchInfoMismatch\",\n\t\t\t\tdetails: {\n\t\t\t\t\tpendingBatchCsn: pendingMessage.batchInfo.batchStartCsn,\n\t\t\t\t\tbatchStartCsn: batchStart.batchStartCsn,\n\t\t\t\t\tpendingBatchLength: pendingMessage.batchInfo.length,\n\t\t\t\t\texpectedPendingBatchLength,\n\t\t\t\t\tbatchLength,\n\t\t\t\t\tpendingBatchId,\n\t\t\t\t\tinboundBatchId,\n\t\t\t\t\tpendingMessageBatchMetadata: asBatchMetadata(pendingMessage.opMetadata)?.batch,\n\t\t\t\t\tmessageBatchMetadata: asBatchMetadata(firstMessage?.metadata)?.batch,\n\t\t\t\t},\n\t\t\t\tmessageDetails: extractSafePropertiesFromMessage(firstMessage),\n\t\t\t});\n\t\t}\n\t}\n\n\t/**\n\t * Called when the Container's connection state changes. If the Container gets connected, it replays all the pending\n\t * states in its queue. This includes triggering resubmission of unacked ops.\n\t * ! Note: successfully resubmitting an op that has been successfully sequenced is not possible due to checks in the ConnectionStateHandler (Loader layer)\n\t *\n\t * @returns The unique batch infos for all batches that were replayed.\n\t */\n\tpublic replayPendingStates(\n\t\toptions?: ReplayPendingStateOptions,\n\t): IPendingMessage[\"batchInfo\"][] {\n\t\tconst { committingStagedBatches, squash } = {\n\t\t\t...defaultReplayPendingStatesOptions,\n\t\t\t...options,\n\t\t};\n\t\tassert(\n\t\t\tthis.stateHandler.connected() || committingStagedBatches === true,\n\t\t\t0x172 /* \"The connection state is not consistent with the runtime\" */,\n\t\t);\n\n\t\t// Staged batches have not yet been submitted so check doesn't apply\n\t\tif (!committingStagedBatches) {\n\t\t\t// This assert suggests we are about to send same ops twice, which will result in data loss.\n\t\t\tassert(\n\t\t\t\tthis.clientIdFromLastReplay !== this.stateHandler.clientId(),\n\t\t\t\t0x173 /* \"replayPendingStates called twice for same clientId!\" */,\n\t\t\t);\n\t\t}\n\t\tthis.clientIdFromLastReplay = this.stateHandler.clientId();\n\n\t\tassert(\n\t\t\tthis.initialMessages.isEmpty(),\n\t\t\t0x174 /* \"initial states should be empty before replaying pending\" */,\n\t\t);\n\n\t\tconst initialPendingMessagesCount = this.pendingMessages.length;\n\t\tlet remainingPendingMessagesCount = this.pendingMessages.length;\n\t\tconst replayedBatchSet = new Set<IPendingMessage[\"batchInfo\"]>();\n\n\t\tlet seenStagedBatch = false;\n\n\t\t// Process exactly `pendingMessagesCount` items in the queue as it represents the number of messages that were\n\t\t// pending when we connected. This is important because the `reSubmitFn` might add more items in the queue\n\t\t// which must not be replayed.\n\t\twhile (remainingPendingMessagesCount > 0) {\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n\t\t\tlet pendingMessage = this.pendingMessages.shift()!;\n\t\t\tremainingPendingMessagesCount--;\n\n\t\t\t// Re-queue pre-staging messages - we are only to replay staged batches\n\t\t\tif (committingStagedBatches) {\n\t\t\t\tif (!pendingMessage.batchInfo.staged) {\n\t\t\t\t\tassert(!seenStagedBatch, 0xb86 /* Staged batch was followed by non-staged batch */);\n\t\t\t\t\tthis.pendingMessages.push(pendingMessage);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tseenStagedBatch = true;\n\t\t\t\tpendingMessage.batchInfo.staged = false; // Clear staged flag so we can submit\n\t\t\t}\n\n\t\t\tconst batchMetadataFlag = asBatchMetadata(pendingMessage.opMetadata)?.batch;\n\t\t\tassert(batchMetadataFlag !== false, 0x41b /* We cannot process batches in chunks */);\n\n\t\t\t// The next message starts a batch (possibly single-message), and we'll need its batchId.\n\t\t\tconst batchId = getEffectiveBatchId(pendingMessage);\n\n\t\t\tconst staged = pendingMessage.batchInfo.staged;\n\n\t\t\tif (asEmptyBatchLocalOpMetadata(pendingMessage.localOpMetadata)?.emptyBatch === true) {\n\t\t\t\t// Resubmit no messages, with the batchId. Will result in another empty batch marker.\n\t\t\t\tthis.stateHandler.reSubmitBatch([], { batchId, staged, squash });\n\t\t\t\treplayedBatchSet.add(pendingMessage.batchInfo);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tassert(\n\t\t\t\thasTypicalRuntimeOp(pendingMessage),\n\t\t\t\t0xb87 /* runtimeOp is only undefined for empty batches */,\n\t\t\t);\n\n\t\t\t/**\n\t\t\t * We must preserve the distinct batches on resubmit.\n\t\t\t * Note: It is not possible for the PendingStateManager to receive a partially acked batch. It will\n\t\t\t * either receive the whole batch ack or nothing at all. See {@link InboundBatchAggregator} for how this works.\n\t\t\t */\n\t\t\tif (batchMetadataFlag === undefined) {\n\t\t\t\t// Single-message batch\n\n\t\t\t\tthis.stateHandler.reSubmitBatch(\n\t\t\t\t\t[\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\truntimeOp: pendingMessage.runtimeOp,\n\t\t\t\t\t\t\tlocalOpMetadata: pendingMessage.localOpMetadata,\n\t\t\t\t\t\t\topMetadata: pendingMessage.opMetadata,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\t{ batchId, staged, squash },\n\t\t\t\t);\n\t\t\t\treplayedBatchSet.add(pendingMessage.batchInfo);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t// else: batchMetadataFlag === true (It's a typical multi-message batch)\n\n\t\t\tassert(\n\t\t\t\tremainingPendingMessagesCount > 0,\n\t\t\t\t0x554 /* Last pending message cannot be a batch begin */,\n\t\t\t);\n\n\t\t\tconst batch: PendingMessageResubmitData[] = [];\n\n\t\t\t// check is >= because batch end may be last pending message\n\t\t\twhile (remainingPendingMessagesCount >= 0) {\n\t\t\t\tassert(\n\t\t\t\t\thasTypicalRuntimeOp(pendingMessage),\n\t\t\t\t\t0xb88 /* runtimeOp is only undefined for empty batches */,\n\t\t\t\t);\n\t\t\t\tbatch.push({\n\t\t\t\t\truntimeOp: pendingMessage.runtimeOp,\n\t\t\t\t\tlocalOpMetadata: pendingMessage.localOpMetadata,\n\t\t\t\t\topMetadata: pendingMessage.opMetadata,\n\t\t\t\t});\n\n\t\t\t\t// End of the batch\n\t\t\t\tif (pendingMessage.opMetadata?.batch === false) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tassert(remainingPendingMessagesCount > 0, 0x555 /* No batch end found */);\n\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n\t\t\t\tpendingMessage = this.pendingMessages.shift()!;\n\t\t\t\tremainingPendingMessagesCount--;\n\t\t\t\tassert(\n\t\t\t\t\tpendingMessage.opMetadata?.batch !== true,\n\t\t\t\t\t0x556 /* Batch start needs a corresponding batch end */,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tthis.stateHandler.reSubmitBatch(batch, { batchId, staged, squash });\n\t\t\treplayedBatchSet.add(pendingMessage.batchInfo);\n\t\t}\n\n\t\tif (!committingStagedBatches) {\n\t\t\t// pending ops should no longer depend on previous sequenced local ops after resubmitting all pending messages (does not apply if only replaying the staged messages)\n\t\t\tthis.savedOps = [];\n\t\t}\n\n\t\t// We replayPendingStates on read connections too - we expect these to get nack'd though, and to then reconnect\n\t\t// on a write connection and replay again. This filters out the replay that happens on the read connection so\n\t\t// we only see the replays on write connections (that have a chance to go through).\n\t\tif (this.stateHandler.isActiveConnection()) {\n\t\t\tthis.logger?.sendTelemetryEvent({\n\t\t\t\teventName: \"PendingStatesReplayed\",\n\t\t\t\tcount: initialPendingMessagesCount,\n\t\t\t\tclientId: this.stateHandler.clientId(),\n\t\t\t});\n\t\t}\n\n\t\treturn [...replayedBatchSet];\n\t}\n\n\t/**\n\t * Pops all staged batches, invoking the callback on each constituent op in order (LIFO)\n\t */\n\tpublic popStagedBatches(\n\t\tcallback: (\n\t\t\t// callback will only be given staged messages with a valid runtime op (i.e. not empty batch and not an initial message with only serialized content)\n\t\t\tstagedMessage: IPendingMessage & { runtimeOp: LocalContainerRuntimeMessage },\n\t\t) => void,\n\t): IPendingMessage[\"batchInfo\"][] {\n\t\tconst batchSet = new Set<IPendingMessage[\"batchInfo\"]>();\n\t\twhile (!this.pendingMessages.isEmpty()) {\n\t\t\tconst stagedMessage = this.pendingMessages.peekBack();\n\t\t\tif (stagedMessage?.batchInfo.staged === true) {\n\t\t\t\tthis.pendingMessages.pop();\n\t\t\t\tbatchSet.add(stagedMessage.batchInfo);\n\n\t\t\t\tif (hasTypicalRuntimeOp(stagedMessage)) {\n\t\t\t\t\tcallback(stagedMessage);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tbreak; // no more staged messages\n\t\t\t}\n\t\t}\n\t\tassert(\n\t\t\tthis.pendingMessages.toArray().every((m) => m.batchInfo.staged !== true),\n\t\t\t0xb89 /* Shouldn't be any more staged messages */,\n\t\t);\n\t\treturn [...batchSet];\n\t}\n}\n\n/**\n * For back-compat if trying to apply stashed ops that pre-date batchInfo\n */\nfunction patchbatchInfo(\n\tmessage: IPendingMessageFromStash,\n): asserts message is IPendingMessage {\n\tconst batchInfo: IPendingMessageFromStash[\"batchInfo\"] = message.batchInfo;\n\tif (batchInfo === undefined) {\n\t\t// Using uuid guarantees uniqueness, retaining existing behavior\n\t\tmessage.batchInfo = { clientId: uuid(), batchStartCsn: -1, length: -1, staged: false };\n\t}\n}\n\n/**\n * This filters out messages that are not \"typical\" runtime ops, i.e. empty batches or initial messages (which only have serialized content).\n */\nfunction hasTypicalRuntimeOp(\n\tmessage: IPendingMessage,\n): message is IPendingMessage & { runtimeOp: LocalContainerRuntimeMessage } {\n\treturn message.runtimeOp !== undefined && message.runtimeOp.type !== \"groupedBatch\";\n}\n"]}
|
|
1
|
+
{"version":3,"file":"pendingStateManager.js","sourceRoot":"","sources":["../src/pendingStateManager.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 { IDisposable, ITelemetryBaseLogger } from \"@fluidframework/core-interfaces\";\nimport { assert, Lazy } from \"@fluidframework/core-utils/internal\";\nimport {\n\ttype TelemetryLoggerExt,\n\tDataProcessingError,\n\tLoggingError,\n\textractSafePropertiesFromMessage,\n\tcreateChildLogger,\n} from \"@fluidframework/telemetry-utils/internal\";\nimport Deque from \"double-ended-queue\";\nimport { v4 as uuid } from \"uuid\";\n\nimport { isContainerMessageDirtyable } from \"./containerRuntime.js\";\nimport type {\n\tInboundContainerRuntimeMessage,\n\tInboundSequencedContainerRuntimeMessage,\n\tLocalContainerRuntimeMessage,\n} from \"./messageTypes.js\";\nimport { asBatchMetadata, asEmptyBatchLocalOpMetadata } from \"./metadata.js\";\nimport {\n\ttype EmptyGroupedBatch,\n\ttype LocalBatchMessage,\n\tgetEffectiveBatchId,\n\ttype BatchStartInfo,\n\ttype InboundMessageResult,\n\tserializeOp,\n\ttype LocalEmptyBatchPlaceholder,\n\ttype BatchResubmitInfo,\n} from \"./opLifecycle/index.js\";\n\n/**\n * This represents a message that has been submitted and is added to the pending queue when `submit` is called on the\n * ContainerRuntime. This message has either not been ack'd by the server or has not been submitted to the server yet.\n *\n * @remarks This is the current serialization format for pending local state when a Container is serialized.\n */\nexport interface IPendingMessage {\n\ttype: \"message\";\n\treferenceSequenceNumber: number;\n\t/**\n\t * Serialized copy of runtimeOp\n\t */\n\tcontent: string;\n\t/**\n\t * The original runtime op that was submitted to the ContainerRuntime\n\t * Unless this pending message came from stashed content, in which case this is undefined at first and then deserialized from the contents string\n\t */\n\truntimeOp: LocalContainerRuntimeMessage | EmptyGroupedBatch | undefined; // Undefined for initial messages before parsing\n\t/**\n\t * Local Op Metadata that was passed to the ContainerRuntime when the op was submitted.\n\t * This contains state needed when processing the ack, or to resubmit or rollback the op.\n\t */\n\tlocalOpMetadata: unknown;\n\t/**\n\t * Metadata that was passed to the ContainerRuntime when the op was submitted.\n\t * This is rarely used, and may be inspected by the service (as opposed to op contents which is opaque)\n\t */\n\topMetadata: Record<string, unknown> | undefined;\n\t/**\n\t * Populated upon processing the op's ack, before moving the pending message to savedOps.\n\t */\n\tsequenceNumber?: number;\n\t/**\n\t * Info about the batch this pending message belongs to, for validation and for computing the batchId on reconnect\n\t * We don't include batchId itself to avoid redundancy, because that's stamped on opMetadata above\n\t */\n\tbatchInfo: {\n\t\t/**\n\t\t * The Batch's original clientId, from when it was first flushed to be submitted.\n\t\t * Or, a random uuid if it was never submitted (and batchStartCsn will be -1)\n\t\t */\n\t\tclientId: string;\n\t\t/**\n\t\t * The Batch's original clientSequenceNumber, from when it was first flushed to be submitted\n\t\t * Or, -1 if it was never submitted (and clientId will be a random uuid)\n\t\t */\n\t\tbatchStartCsn: number;\n\t\t/**\n\t\t * length of the batch (how many runtime messages here)\n\t\t */\n\t\tlength: number;\n\t\t/**\n\t\t * If true, this batch is staged and should not actually be submitted on replayPendingStates.\n\t\t */\n\t\tstaged: boolean;\n\t};\n}\n\ntype Patch<T, U> = U & Omit<T, keyof U>;\n\n/**\n * First version of the type (pre-dates batchInfo)\n */\ntype IPendingMessageV0 = Patch<IPendingMessage, { batchInfo?: undefined }>;\n\n/**\n * Union of all supported schemas for when applying stashed ops\n *\n * @remarks When the format changes, this type should update to reflect all possible schemas.\n */\ntype IPendingMessageFromStash = IPendingMessageV0 | IPendingMessage;\n\nexport interface IPendingLocalState {\n\t/**\n\t * list of pending states, including ops and batch information\n\t */\n\tpendingStates: IPendingMessage[];\n}\n\n/**\n * Info needed to replay/resubmit a pending message\n */\nexport type PendingMessageResubmitData = Pick<\n\tIPendingMessage,\n\t\"runtimeOp\" | \"localOpMetadata\" | \"opMetadata\"\n> & {\n\t// Required (it's only missing on IPendingMessage for empty batch, which will be resubmitted as an empty array)\n\truntimeOp: LocalContainerRuntimeMessage;\n};\n\nexport interface PendingBatchResubmitMetadata extends BatchResubmitInfo {\n\t/**\n\t * Whether changes in this batch should be squashed when resubmitting.\n\t */\n\tsquash: boolean;\n}\n\nexport interface IRuntimeStateHandler {\n\tconnected(): boolean;\n\tclientId(): string | undefined;\n\tapplyStashedOp(serializedOp: string): Promise<unknown>;\n\treSubmitBatch(\n\t\tbatch: PendingMessageResubmitData[],\n\t\tmetadata: PendingBatchResubmitMetadata,\n\t): void;\n\tisActiveConnection: () => boolean;\n\tisAttached: () => boolean;\n}\n\n/**\n * Optional hooks invoked at the close of the stashed-op apply lifecycle.\n *\n * `onAfterStashedOpsApplied` fires synchronously the first time\n * `initialMessages` drains during `applyStashedOpsAt`, immediately after\n * `isApplyingStashedOps` flips to `false`. Fires at most once per PSM\n * lifetime. If an apply throws, control never reaches the close site and\n * the hook is not invoked — load is fatal in that case.\n *\n * No corresponding open hook is exposed. The apply window is opened eagerly\n * in the PSM constructor, but at that point `ContainerRuntime` has not yet\n * wired up the downstream observers (`channelCollection` is undefined), so a\n * fanout fired from the constructor would be a no-op. Consumers that care\n * about the open transition can read `isApplyingStashedOps` directly.\n */\nexport interface PendingStateManagerHooks {\n\tonAfterStashedOpsApplied?: () => void;\n}\n\nfunction isEmptyBatchPendingMessage(message: IPendingMessageFromStash): boolean {\n\tconst content = JSON.parse(message.content) as Partial<EmptyGroupedBatch>;\n\treturn content.type === \"groupedBatch\" && content.contents?.length === 0;\n}\n\nfunction buildPendingMessageContent(message: InboundSequencedContainerRuntimeMessage): string {\n\t// IMPORTANT: Order matters here, this must match the order of the properties used\n\t// when submitting the message.\n\tconst { type, contents }: InboundContainerRuntimeMessage = message;\n\t// Any properties that are not defined, won't be emitted by stringify.\n\treturn JSON.stringify({ type, contents });\n}\n\nfunction typesOfKeys<T extends object>(obj: T): Record<keyof T, string> {\n\t// TODO: Fix this violation and remove the disable\n\t// eslint-disable-next-line unicorn/no-array-reduce\n\treturn Object.keys(obj).reduce((acc, key) => {\n\t\tacc[key] = typeof obj[key];\n\t\treturn acc;\n\t}, {}) as Record<keyof T, string>;\n}\n\nfunction scrubAndStringify(\n\tmessage: InboundContainerRuntimeMessage | LocalContainerRuntimeMessage,\n): string {\n\t// Scrub the whole object in case there are unexpected keys\n\tconst scrubbed: Record<string, unknown> = typesOfKeys(message);\n\n\t// For these known/expected keys, we can either drill into the object (for contents)\n\t// or just use the value as-is (since it's not personal info)\n\tscrubbed.contents =\n\t\ttypeof message.contents === \"object\" && message.contents !== null\n\t\t\t? typesOfKeys(message.contents)\n\t\t\t: undefined;\n\tscrubbed.type = message.type;\n\n\treturn JSON.stringify(scrubbed);\n}\n\n/**\n * Finds and returns the index where the strings diverge, and the character at that index in each string (or undefined if not applicable)\n */\nfunction findFirstRawCharacterMismatched(\n\ta: string,\n\tb: string,\n): [index: number, charA?: string, charB?: string] {\n\tconst minLength = Math.min(a.length, b.length);\n\tfor (let i = 0; i < minLength; i++) {\n\t\tif (a[i] !== b[i]) {\n\t\t\treturn [i, a[i], b[i]];\n\t\t}\n\t}\n\n\t// Since we didn't return in the loop, the shorter string must be a prefix of the other.\n\t// If they're the same length, return -1 to indicate they're identical.\n\t// Otherwise, the next character of the longer one is where they differ. No need to return that next character.\n\treturn a.length === b.length\n\t\t? [-1, undefined, undefined]\n\t\t: [minLength, a[minLength], b[minLength]];\n}\n\n/**\n * Finds and returns the index where the strings diverge, and the character at that index in each string (or undefined if not applicable)\n * It scrubs non-ASCII characters since they convey more meaning (privacy consideration)\n */\nexport function findFirstCharacterMismatched(\n\ta: string,\n\tb: string,\n): [index: number, charA?: string, charB?: string] {\n\tconst [index, rawCharA, rawCharB] = findFirstRawCharacterMismatched(a, b);\n\n\tconst charA = (rawCharA?.codePointAt(0) ?? 0) <= 0x7f ? rawCharA : \"[non-ASCII]\";\n\tconst charB = (rawCharB?.codePointAt(0) ?? 0) <= 0x7f ? rawCharB : \"[non-ASCII]\";\n\n\treturn [index, charA, charB];\n}\n\n/**\n * Returns a shallow copy of the given message with the non-serializable properties removed.\n * Note that the runtimeOp's data has already been serialized in the content property.\n */\nfunction toSerializableForm(\n\tmessage: IPendingMessage,\n): IPendingMessage & { runtimeOp: undefined; localOpMetadata: undefined } {\n\treturn {\n\t\t...message,\n\t\tlocalOpMetadata: undefined,\n\t\truntimeOp: undefined,\n\t};\n}\n\ninterface ReplayPendingStateOptions {\n\t/**\n\t * If true, only replay staged batches, clearing the \"staged\" flag.\n\t * This is used when we are exiting staging mode and want to rebase and submit the staged batches without resubmitting pre-staged messages.\n\t * Default: false\n\t */\n\tcommittingStagedBatches: boolean;\n\t/**\n\t * @param squash - If true, edits should be squashed when resubmitting.\n\t * Default: false\n\t */\n\tsquash: boolean;\n}\n\nconst defaultReplayPendingStatesOptions: ReplayPendingStateOptions = {\n\tcommittingStagedBatches: false,\n\tsquash: false,\n};\n\n/**\n * PendingStateManager is responsible for maintaining the messages that have not been sent or have not yet been\n * acknowledged by the server. It also maintains the batch information for both automatically and manually flushed\n * batches along with the messages.\n * When the Container reconnects, it replays the pending states, which includes manual flushing\n * of messages and triggering resubmission of unacked ops.\n *\n * It verifies that all the ops are acked, are received in the right order and batch information is correct.\n */\nexport class PendingStateManager implements IDisposable {\n\t/**\n\t * Messages that will need to be resubmitted if not ack'd before the next reconnection\n\t */\n\tprivate readonly pendingMessages = new Deque<IPendingMessage>();\n\t/**\n\t * Messages stashed from a previous container, now being rehydrated. Need to be resubmitted.\n\t */\n\tprivate readonly initialMessages = new Deque<IPendingMessageFromStash>();\n\n\t/**\n\t * Sequenced local ops that are saved when stashing since pending ops may depend on them\n\t */\n\tprivate savedOps: IPendingMessage[] = [];\n\n\t// eslint-disable-next-line unicorn/consistent-function-scoping -- Property is defined once; no need to extract inner lambda\n\tprivate readonly disposeOnce = new Lazy<void>(() => {\n\t\tthis.initialMessages.clear();\n\t\tthis.pendingMessages.clear();\n\t});\n\n\t/**\n\t * Used to ensure we don't replay ops on the same connection twice\n\t */\n\tprivate clientIdFromLastReplay: string | undefined;\n\n\t/**\n\t * The pending messages count. Includes `pendingMessages` and `initialMessages` to keep in sync with\n\t * 'hasPendingMessages'.\n\t */\n\tpublic get pendingMessagesCount(): number {\n\t\treturn this.pendingMessages.length + this.initialMessages.length;\n\t}\n\n\t/**\n\t * Checks the pending messages to see if any of them represent user changes (aka \"dirtyable\" messages)\n\t */\n\tpublic hasPendingUserChanges(): boolean {\n\t\tfor (let i = 0; i < this.pendingMessages.length; i++) {\n\t\t\tconst element = this.pendingMessages.get(i);\n\t\t\tif (\n\t\t\t\telement !== undefined &&\n\t\t\t\thasTypicalRuntimeOp(element) && // Empty batches don't count towards user changes\n\t\t\t\tisContainerMessageDirtyable(element.runtimeOp)\n\t\t\t) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\t// Consider any initial messages to be user changes\n\t\t// (it's an approximation since we would have to parse them to know for sure)\n\t\treturn this.initialMessages.length > 0;\n\t}\n\n\t/**\n\t * The minimumPendingMessageSequenceNumber is the minimum of the first pending message and the first initial message.\n\t *\n\t * We need this so that we can properly keep local data and maintain the correct sequence window.\n\t */\n\tpublic get minimumPendingMessageSequenceNumber(): number | undefined {\n\t\treturn this.pendingMessages.peekFront()?.referenceSequenceNumber;\n\t}\n\n\t/**\n\t * Called to check if there are any pending messages in the pending message queue.\n\t * @returns A boolean indicating whether there are messages or not.\n\t */\n\tpublic hasPendingMessages(): boolean {\n\t\treturn this.pendingMessagesCount !== 0;\n\t}\n\n\tpublic getLocalState(snapshotSequenceNumber?: number): {\n\t\tpending: IPendingLocalState;\n\t} {\n\t\tassert(\n\t\t\tthis.initialMessages.isEmpty(),\n\t\t\t0x2e9 /* \"Must call getLocalState() after applying initial states\" */,\n\t\t);\n\t\t// Using snapshot sequence number to filter ops older than our latest snapshot.\n\t\t// Such ops should not be declared in pending/stashed state. Snapshot seq num will not\n\t\t// be available when the container is not attached. Therefore, no filtering is needed.\n\t\tconst newSavedOps = [...this.savedOps].filter((message) => {\n\t\t\tassert(\n\t\t\t\tmessage.sequenceNumber !== undefined,\n\t\t\t\t0x97c /* saved op should already have a sequence number */,\n\t\t\t);\n\t\t\treturn message.sequenceNumber > (snapshotSequenceNumber ?? 0);\n\t\t});\n\t\tfor (const message of this.pendingMessages.toArray()) {\n\t\t\tif (\n\t\t\t\tsnapshotSequenceNumber !== undefined &&\n\t\t\t\tmessage.referenceSequenceNumber < snapshotSequenceNumber\n\t\t\t) {\n\t\t\t\tthrow new LoggingError(\"trying to stash ops older than our latest snapshot\");\n\t\t\t}\n\t\t}\n\t\treturn {\n\t\t\tpending: {\n\t\t\t\tpendingStates: [\n\t\t\t\t\t...newSavedOps,\n\t\t\t\t\t...this.pendingMessages.toArray().map((message) => toSerializableForm(message)),\n\t\t\t\t],\n\t\t\t},\n\t\t};\n\t}\n\n\tprivate readonly logger: TelemetryLoggerExt;\n\n\t/**\n\t * One-way lifecycle of the stashed-op apply window: `ended` → `applying` → `ended`.\n\t *\n\t * Default is `ended` — no stashed state means there's nothing to apply, so the window is\n\t * closed before it ever opens. `ended` → `applying` happens in the constructor when\n\t * stashed state is present (i.e. `initialMessages` is non-empty at construction). The\n\t * open is eager so the runtime is readonly from the moment any DDS could possibly\n\t * observe it. `applying` → `ended` happens the first time {@link applyStashedOpsAt}\n\t * drains `initialMessages`. After that, local edits are safe — they queue FIFO behind\n\t * any remaining `pendingMessages`, preserving server-side ordering.\n\t *\n\t * The window never reopens. After the close, subsequent `applyStashedOpsAt` calls (e.g.\n\t * from late `notifyOpReplay`s) early-return at the empty guard.\n\t *\n\t * `pendingMessages` state is intentionally NOT part of the close condition. Those\n\t * entries are drained transparently by {@link replayPendingStates} on connect via\n\t * resubmit (each pop is matched by a fresh push), so the queue size is conserved across\n\t * resubmit and DDSes can't distinguish a resubmit-ack from a normal ack. Holding the\n\t * window open through resubmit would force resubmits to run while the runtime is\n\t * readonly, which is the inverse of what we want (\"never resubmit during apply stashed\n\t * ops\").\n\t *\n\t * An apply error leaves the lifecycle at `applying` because the queue isn't drained.\n\t * That's fine: an error here is fatal for the load, the container is unusable, and\n\t * there's no state to restore.\n\t */\n\tprivate _applyLifecycle: \"applying\" | \"ended\" = \"ended\";\n\tpublic get isApplyingStashedOps(): boolean {\n\t\treturn this._applyLifecycle === \"applying\";\n\t}\n\n\tprivate readonly hooks: PendingStateManagerHooks;\n\n\tconstructor(\n\t\tprivate readonly stateHandler: IRuntimeStateHandler,\n\t\tstashedLocalState: IPendingLocalState | undefined,\n\t\tlogger: ITelemetryBaseLogger,\n\t\thooks: PendingStateManagerHooks = {},\n\t) {\n\t\tthis.logger = createChildLogger({ logger });\n\t\tthis.hooks = hooks;\n\t\tif (stashedLocalState?.pendingStates) {\n\t\t\tthis.initialMessages.push(...stashedLocalState.pendingStates);\n\t\t}\n\t\t// Open the apply window eagerly if there is any stashed work. The\n\t\t// runtime is readonly while `isApplyingStashedOps` is true (see\n\t\t// `ContainerRuntime.isReadOnly`); compliant DDSes consult `readOnly`\n\t\t// at realize time and skip submits. No fanout fires here — downstream\n\t\t// observers (`channelCollection`) are not yet constructed at this\n\t\t// point in the runtime constructor, and the first real readonly read\n\t\t// happens after the constructor returns.\n\t\tif (!this.initialMessages.isEmpty()) {\n\t\t\tthis._applyLifecycle = \"applying\";\n\t\t}\n\t}\n\n\tpublic get disposed(): boolean {\n\t\treturn this.disposeOnce.evaluated;\n\t}\n\tpublic readonly dispose = (): void => this.disposeOnce.value;\n\n\t/**\n\t * We've flushed an empty batch, and need to track it locally until the corresponding\n\t * ack is processed, to properly track batch IDs\n\t */\n\tpublic onFlushEmptyBatch(\n\t\tplaceholder: LocalEmptyBatchPlaceholder,\n\t\tclientSequenceNumber: number | undefined,\n\t\tstaged: boolean,\n\t): void {\n\t\tthis.onFlushBatch([placeholder], clientSequenceNumber, staged);\n\t}\n\n\t/**\n\t * The given batch has been flushed, and needs to be tracked locally until the corresponding\n\t * acks are processed, to ensure it is successfully sent.\n\t * @param batch - The batch that was flushed\n\t * @param clientSequenceNumber - The CSN of the first message in the batch,\n\t * or undefined if the batch was not yet sent (e.g. by the time we flushed we lost the connection)\n\t * @param staged - Indicates whether batch is staged (not to be submitted while runtime is in Staging Mode)\n\t */\n\tpublic onFlushBatch(\n\t\tbatch: LocalBatchMessage[] | [LocalEmptyBatchPlaceholder],\n\t\tclientSequenceNumber: number | undefined,\n\t\tstaged: boolean,\n\t): void {\n\t\t// clientId and batchStartCsn are used for generating the batchId so we can detect container forks\n\t\t// where this batch was submitted by two different clients rehydrating from the same local state.\n\t\t// In the typical case where the batch was actually sent, use the clientId and clientSequenceNumber.\n\t\t// In the case where the batch was not sent, use a random uuid for clientId, and -1 for clientSequenceNumber to indicate this case.\n\t\t// This will guarantee uniqueness of the batchId, and is a suitable fallback since clientId/CSN is only needed if the batch was actually sent/sequenced.\n\t\tconst batchWasSent = clientSequenceNumber !== undefined;\n\t\tif (batchWasSent) {\n\t\t\tassert(!staged, 0xb84 /* Staged batches should not have been submitted */);\n\t\t}\n\t\tconst [clientId, batchStartCsn] = batchWasSent\n\t\t\t? [this.stateHandler.clientId(), clientSequenceNumber]\n\t\t\t: [uuid(), -1]; // -1 will indicate not a real clientId/CSN pair\n\t\tassert(\n\t\t\tclientId !== undefined,\n\t\t\t0xa33 /* clientId (from stateHandler) could only be undefined if we've never connected, but we have a CSN so we know that's not the case */,\n\t\t);\n\t\tconst batchInfo = { clientId, batchStartCsn, length: batch.length, staged };\n\t\tfor (const message of batch) {\n\t\t\tconst {\n\t\t\t\truntimeOp,\n\t\t\t\treferenceSequenceNumber,\n\t\t\t\tlocalOpMetadata,\n\t\t\t\tmetadata: opMetadata,\n\t\t\t} = message;\n\t\t\tconst pendingMessage: IPendingMessage = {\n\t\t\t\ttype: \"message\",\n\t\t\t\treferenceSequenceNumber,\n\t\t\t\tcontent: serializeOp(runtimeOp).content,\n\t\t\t\truntimeOp,\n\t\t\t\tlocalOpMetadata,\n\t\t\t\topMetadata,\n\t\t\t\tbatchInfo,\n\t\t\t};\n\t\t\tthis.pendingMessages.push(pendingMessage);\n\t\t}\n\t}\n\n\t/**\n\t * Applies stashed ops at their reference sequence number so they are ready to be ACKed or resubmitted\n\t * @param seqNum - Sequence number at which to apply ops. Will apply all ops if seqNum is undefined.\n\t */\n\tpublic async applyStashedOpsAt(seqNum?: number): Promise<void> {\n\t\tif (this.initialMessages.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\n\t\t// apply stashed ops at sequence number\n\t\twhile (!this.initialMessages.isEmpty()) {\n\t\t\tif (seqNum !== undefined) {\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n\t\t\t\tconst peekMessage = this.initialMessages.peekFront()!;\n\t\t\t\tif (peekMessage.referenceSequenceNumber > seqNum) {\n\t\t\t\t\tbreak; // nothing left to do at this sequence number\n\t\t\t\t}\n\t\t\t\tif (peekMessage.referenceSequenceNumber < seqNum) {\n\t\t\t\t\tthrow new Error(\"loaded from snapshot too recent to apply stashed ops\");\n\t\t\t\t}\n\t\t\t}\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n\t\t\tconst nextMessage = this.initialMessages.shift()!;\n\t\t\t// Nothing to apply if the message is an empty batch.\n\t\t\t// We still need to track it for resubmission.\n\t\t\ttry {\n\t\t\t\tif (isEmptyBatchPendingMessage(nextMessage)) {\n\t\t\t\t\tnextMessage.localOpMetadata = {\n\t\t\t\t\t\temptyBatch: true,\n\t\t\t\t\t} satisfies LocalEmptyBatchPlaceholder[\"localOpMetadata\"]; // equivalent to applyStashedOp for empty batch\n\t\t\t\t\tpatchbatchInfo(nextMessage); // Back compat\n\t\t\t\t\tthis.pendingMessages.push(nextMessage);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t// applyStashedOp will cause the DDS to behave as if it has sent the op but not actually send it\n\t\t\t\tconst localOpMetadata = await this.stateHandler.applyStashedOp(nextMessage.content);\n\t\t\t\tif (this.stateHandler.isAttached()) {\n\t\t\t\t\tnextMessage.localOpMetadata = localOpMetadata;\n\t\t\t\t\t// NOTE: This runtimeOp has been roundtripped through string, which is technically lossy.\n\t\t\t\t\t// e.g. At this point, handles are in their encoded form.\n\t\t\t\t\tnextMessage.runtimeOp = JSON.parse(\n\t\t\t\t\t\tnextMessage.content,\n\t\t\t\t\t) as LocalContainerRuntimeMessage;\n\t\t\t\t\t// then we push onto pendingMessages which will cause PendingStateManager to resubmit when we connect\n\t\t\t\t\tpatchbatchInfo(nextMessage); // Back compat\n\t\t\t\t\tthis.pendingMessages.push(nextMessage);\n\t\t\t\t} else {\n\t\t\t\t\tif (localOpMetadata !== undefined) {\n\t\t\t\t\t\tthrow new Error(\"Local Op Metadata must be undefined when not attached\");\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tthrow DataProcessingError.wrapIfUnrecognized(error, \"applyStashedOp\", nextMessage);\n\t\t\t}\n\t\t}\n\n\t\t// The apply window was opened eagerly in the constructor when there\n\t\t// was any stashed work. We close it on full successful drain only.\n\t\t// If an apply throws above, control never reaches here and the\n\t\t// lifecycle stays at \"applying\" — the load is fatal so there's no\n\t\t// recoverable state.\n\t\tif (this._applyLifecycle === \"applying\" && this.initialMessages.isEmpty()) {\n\t\t\tthis._applyLifecycle = \"ended\";\n\t\t\tthis.hooks.onAfterStashedOpsApplied?.();\n\t\t}\n\t}\n\n\t/**\n\t * Compares the batch ID of the incoming batch with the pending batch ID for this client.\n\t * They should not match, as that would indicate a forked container.\n\t * @param remoteBatchStart - BatchStartInfo for an incoming batch *NOT* submitted by this client\n\t * @returns whether the batch IDs match\n\t */\n\tprivate remoteBatchMatchesPendingBatch(remoteBatchStart: BatchStartInfo): boolean {\n\t\tconst pendingMessage = this.pendingMessages.peekFront();\n\t\tif (pendingMessage === undefined) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// We must compare the effective batch IDs, since one of these ops\n\t\t// may have been the original, not resubmitted, so wouldn't have its batch ID stamped yet.\n\t\tconst pendingBatchId = getEffectiveBatchId(pendingMessage);\n\t\tconst inboundBatchId = getEffectiveBatchId(remoteBatchStart);\n\n\t\treturn pendingBatchId === inboundBatchId;\n\t}\n\n\t/**\n\t * Processes an inbound message or batch of messages - May be local or remote.\n\t *\n\t * @param inbound - The inbound message(s) to process, with extra info (e.g. about the start of a batch). Could be local or remote.\n\t * @param local - true if we submitted these messages and expect corresponding pending messages\n\t * @returns The inbound messages with localOpMetadata \"zipped\" in.\n\t *\n\t * @throws a DataProcessingError in either of these cases:\n\t * - The pending message content doesn't match the incoming message content for any message here\n\t * - The batch IDs *do match* but it's not local (indicates Container forking).\n\t */\n\tpublic processInboundMessages(\n\t\tinbound: InboundMessageResult,\n\t\tlocal: boolean,\n\t): {\n\t\tmessage: InboundSequencedContainerRuntimeMessage;\n\t\tlocalOpMetadata?: unknown;\n\t}[] {\n\t\tif (local) {\n\t\t\treturn this.processPendingLocalMessages(inbound);\n\t\t}\n\n\t\t// An inbound remote batch should not match the pending batch ID for this client.\n\t\t// That would indicate the container forked (two instances trying to submit the same local state)\n\t\tif (\"batchStart\" in inbound && this.remoteBatchMatchesPendingBatch(inbound.batchStart)) {\n\t\t\tthrow DataProcessingError.create(\n\t\t\t\t\"Forked Container Error! Matching batchIds but mismatched clientId\",\n\t\t\t\t\"PendingStateManager.processInboundMessages\",\n\t\t\t\tinbound.batchStart.keyMessage,\n\t\t\t);\n\t\t}\n\n\t\t// No localOpMetadata for remote messages\n\t\tconst messages = inbound.type === \"fullBatch\" ? inbound.messages : [inbound.nextMessage];\n\t\treturn messages.map((message) => ({ message }));\n\t}\n\n\t/**\n\t * Processes the incoming message(s) from the server that were submitted by this client.\n\t * It verifies that messages are received in the right order and that any batch information is correct.\n\t * @param inbound - The inbound message(s) (originating from this client) to correlate with the pending local state\n\t * @throws DataProcessingError if the pending message content doesn't match the incoming message content for any message here\n\t * @returns The inbound messages with localOpMetadata \"zipped\" in.\n\t */\n\tprivate processPendingLocalMessages(inbound: InboundMessageResult): {\n\t\tmessage: InboundSequencedContainerRuntimeMessage;\n\t\tlocalOpMetadata: unknown;\n\t}[] {\n\t\tif (\"batchStart\" in inbound) {\n\t\t\tthis.onLocalBatchBegin(inbound.batchStart, inbound.length);\n\t\t}\n\n\t\t// Empty batch\n\t\tif (inbound.length === 0) {\n\t\t\tconst localOpMetadata = this.processNextPendingMessage(\n\t\t\t\tinbound.batchStart.keyMessage.sequenceNumber,\n\t\t\t);\n\t\t\tassert(\n\t\t\t\tasEmptyBatchLocalOpMetadata(localOpMetadata)?.emptyBatch === true,\n\t\t\t\t0xa20 /* Expected empty batch marker */,\n\t\t\t);\n\t\t\treturn [];\n\t\t}\n\n\t\tconst messages = inbound.type === \"fullBatch\" ? inbound.messages : [inbound.nextMessage];\n\n\t\treturn messages.map((message) => ({\n\t\t\tmessage,\n\t\t\tlocalOpMetadata: this.processNextPendingMessage(message.sequenceNumber, message),\n\t\t}));\n\t}\n\n\t/**\n\t * Processes the pending local copy of message that's been ack'd by the server.\n\t * @param sequenceNumber - The sequenceNumber from the server corresponding to the next pending message.\n\t * @param message - [optional] The entire incoming message, for comparing contents with the pending message for extra validation.\n\t * @throws DataProcessingError if the pending message content doesn't match the incoming message content.\n\t * @returns The localOpMetadata of the next pending message, to be sent to whoever submitted the original message.\n\t */\n\tprivate processNextPendingMessage(\n\t\tsequenceNumber: number,\n\t\tmessage?: InboundSequencedContainerRuntimeMessage,\n\t): unknown {\n\t\tconst pendingMessage = this.pendingMessages.peekFront();\n\t\tassert(\n\t\t\tpendingMessage !== undefined,\n\t\t\t0x169 /* \"No pending message found for this remote message\" */,\n\t\t);\n\n\t\tpendingMessage.sequenceNumber = sequenceNumber;\n\t\tthis.savedOps.push(toSerializableForm(pendingMessage));\n\n\t\tthis.pendingMessages.shift();\n\n\t\t// message is undefined in the Empty Batch case,\n\t\t// because we don't have an incoming message to compare and pendingMessage is just a placeholder anyway.\n\t\tif (message !== undefined) {\n\t\t\tconst messageContent = buildPendingMessageContent(message);\n\n\t\t\t// Stringified content should match\n\t\t\t// If it doesn't, collect as much info about the difference as possible (privacy-wise) and log it\n\t\t\tif (pendingMessage.content !== messageContent) {\n\t\t\t\tconst [pendingLength, incomingLength] = [\n\t\t\t\t\tpendingMessage.content.length,\n\t\t\t\t\tmessageContent.length,\n\t\t\t\t];\n\t\t\t\tconst [mismatchStartIndex, pendingChar, incomingChar] = findFirstCharacterMismatched(\n\t\t\t\t\tpendingMessage.content,\n\t\t\t\t\tmessageContent,\n\t\t\t\t);\n\n\t\t\t\tconst pendingContentObj = JSON.parse(\n\t\t\t\t\tpendingMessage.content,\n\t\t\t\t) as LocalContainerRuntimeMessage;\n\t\t\t\tconst incomingContentObj = JSON.parse(\n\t\t\t\t\tmessageContent,\n\t\t\t\t) as InboundContainerRuntimeMessage;\n\n\t\t\t\t// Compare inner contents object, since that both should be { type, contents }\n\t\t\t\tconst contentsMatch =\n\t\t\t\t\tpendingContentObj.contents === incomingContentObj.contents ||\n\t\t\t\t\t(pendingContentObj.contents !== undefined &&\n\t\t\t\t\t\tincomingContentObj.contents !== undefined &&\n\t\t\t\t\t\tJSON.stringify(pendingContentObj.contents) ===\n\t\t\t\t\t\t\tJSON.stringify(incomingContentObj.contents));\n\n\t\t\t\tthis.logger.sendErrorEvent({\n\t\t\t\t\teventName: \"unexpectedAckReceived\",\n\t\t\t\t\tdetails: {\n\t\t\t\t\t\tpendingContentScrubbed: scrubAndStringify(pendingContentObj),\n\t\t\t\t\t\tincomingContentScrubbed: scrubAndStringify(incomingContentObj),\n\t\t\t\t\t\tcontentsMatch,\n\t\t\t\t\t\tpendingLength,\n\t\t\t\t\t\tincomingLength,\n\t\t\t\t\t\tmismatchStartIndex,\n\t\t\t\t\t\tpendingChar,\n\t\t\t\t\t\tincomingChar,\n\t\t\t\t\t},\n\t\t\t\t});\n\n\t\t\t\tthrow DataProcessingError.create(\n\t\t\t\t\t\"pending local message content mismatch\",\n\t\t\t\t\t\"unexpectedAckReceived\",\n\t\t\t\t\tmessage,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\treturn pendingMessage.localOpMetadata;\n\t}\n\n\t/**\n\t * Check if the incoming batch matches the batch info for the next pending message.\n\t */\n\tprivate onLocalBatchBegin(batchStart: BatchStartInfo, batchLength?: number): void {\n\t\t// Get the next message from the pending queue. Verify a message exists.\n\t\tconst pendingMessage = this.pendingMessages.peekFront();\n\t\tassert(\n\t\t\tpendingMessage !== undefined,\n\t\t\t0xa21 /* No pending message found as we start processing this remote batch */,\n\t\t);\n\t\tassert(\n\t\t\t!pendingMessage.batchInfo.staged,\n\t\t\t0xb85 /* Pending state mismatch, ack came in but next pending message is staged */,\n\t\t);\n\n\t\t// If this batch became empty on resubmit, batch.messages will be empty (but keyMessage is always set)\n\t\t// and the next pending message should be an empty batch marker.\n\t\t// More Info: We must submit empty batches and track them in case a different fork\n\t\t// of this container also submitted the same batch (and it may not be empty for that fork).\n\t\tconst firstMessage = batchStart.keyMessage;\n\t\t// -1 length is for back compat, undefined length means we actually don't know it\n\t\tconst skipLengthCheck =\n\t\t\tpendingMessage.batchInfo.length === -1 || batchLength === undefined;\n\t\tconst expectedPendingBatchLength =\n\t\t\tbatchLength === 0\n\t\t\t\t? 1 // For an empty batch, expect a singleton array with the empty batch marker\n\t\t\t\t: batchLength; // Otherwise, the lengths should actually match\n\n\t\t// Note: We don't need to use getEffectiveBatchId here, just check the explicit stamped batchID\n\t\t// That logic is needed only when comparing across potential container forks.\n\t\t// Furthermore, we also are comparing the batch IDs constituent data - clientId (it's local) and batchStartCsn.\n\t\tconst pendingBatchId = asBatchMetadata(pendingMessage.opMetadata)?.batchId;\n\t\tconst inboundBatchId = batchStart.batchId;\n\n\t\t// We expect the incoming batch to be of the same length, starting at the same clientSequenceNumber,\n\t\t// as the batch we originally submitted. The batchIds should match as well, if set (or neither should be set)\n\t\t// We have another later check to compare the message contents, which we'd expect to fail if this check does,\n\t\t// so we don't throw here, merely log. In a later release this check may replace that one since it's cheaper.\n\t\tif (\n\t\t\tpendingMessage.batchInfo.batchStartCsn !== batchStart.batchStartCsn ||\n\t\t\t(!skipLengthCheck && pendingMessage.batchInfo.length !== expectedPendingBatchLength) ||\n\t\t\tpendingBatchId !== inboundBatchId\n\t\t) {\n\t\t\tthis.logger?.sendErrorEvent({\n\t\t\t\teventName: \"BatchInfoMismatch\",\n\t\t\t\tdetails: {\n\t\t\t\t\tpendingBatchCsn: pendingMessage.batchInfo.batchStartCsn,\n\t\t\t\t\tbatchStartCsn: batchStart.batchStartCsn,\n\t\t\t\t\tpendingBatchLength: pendingMessage.batchInfo.length,\n\t\t\t\t\texpectedPendingBatchLength,\n\t\t\t\t\tbatchLength,\n\t\t\t\t\tpendingBatchId,\n\t\t\t\t\tinboundBatchId,\n\t\t\t\t\tpendingMessageBatchMetadata: asBatchMetadata(pendingMessage.opMetadata)?.batch,\n\t\t\t\t\tmessageBatchMetadata: asBatchMetadata(firstMessage?.metadata)?.batch,\n\t\t\t\t},\n\t\t\t\tmessageDetails: extractSafePropertiesFromMessage(firstMessage),\n\t\t\t});\n\t\t}\n\t}\n\n\t/**\n\t * Called when the Container's connection state changes. If the Container gets connected, it replays all the pending\n\t * states in its queue. This includes triggering resubmission of unacked ops.\n\t * ! Note: successfully resubmitting an op that has been successfully sequenced is not possible due to checks in the ConnectionStateHandler (Loader layer)\n\t *\n\t * @returns The unique batch infos for all batches that were replayed.\n\t */\n\tpublic replayPendingStates(\n\t\toptions?: ReplayPendingStateOptions,\n\t): IPendingMessage[\"batchInfo\"][] {\n\t\tconst { committingStagedBatches, squash } = {\n\t\t\t...defaultReplayPendingStatesOptions,\n\t\t\t...options,\n\t\t};\n\t\tassert(\n\t\t\tthis.stateHandler.connected() || committingStagedBatches === true,\n\t\t\t0x172 /* \"The connection state is not consistent with the runtime\" */,\n\t\t);\n\n\t\t// Staged batches have not yet been submitted so check doesn't apply\n\t\tif (!committingStagedBatches) {\n\t\t\t// This assert suggests we are about to send same ops twice, which will result in data loss.\n\t\t\tassert(\n\t\t\t\tthis.clientIdFromLastReplay !== this.stateHandler.clientId(),\n\t\t\t\t0x173 /* \"replayPendingStates called twice for same clientId!\" */,\n\t\t\t);\n\t\t}\n\t\tthis.clientIdFromLastReplay = this.stateHandler.clientId();\n\n\t\tassert(\n\t\t\tthis.initialMessages.isEmpty(),\n\t\t\t0x174 /* \"initial states should be empty before replaying pending\" */,\n\t\t);\n\n\t\tconst initialPendingMessagesCount = this.pendingMessages.length;\n\t\tlet remainingPendingMessagesCount = this.pendingMessages.length;\n\t\tconst replayedBatchSet = new Set<IPendingMessage[\"batchInfo\"]>();\n\n\t\tlet seenStagedBatch = false;\n\n\t\t// Process exactly `pendingMessagesCount` items in the queue as it represents the number of messages that were\n\t\t// pending when we connected. This is important because the `reSubmitFn` might add more items in the queue\n\t\t// which must not be replayed.\n\t\twhile (remainingPendingMessagesCount > 0) {\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n\t\t\tlet pendingMessage = this.pendingMessages.shift()!;\n\t\t\tremainingPendingMessagesCount--;\n\n\t\t\t// Re-queue pre-staging messages - we are only to replay staged batches\n\t\t\tif (committingStagedBatches) {\n\t\t\t\tif (!pendingMessage.batchInfo.staged) {\n\t\t\t\t\tassert(!seenStagedBatch, 0xb86 /* Staged batch was followed by non-staged batch */);\n\t\t\t\t\tthis.pendingMessages.push(pendingMessage);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tseenStagedBatch = true;\n\t\t\t\tpendingMessage.batchInfo.staged = false; // Clear staged flag so we can submit\n\t\t\t}\n\n\t\t\tconst batchMetadataFlag = asBatchMetadata(pendingMessage.opMetadata)?.batch;\n\t\t\tassert(batchMetadataFlag !== false, 0x41b /* We cannot process batches in chunks */);\n\n\t\t\t// The next message starts a batch (possibly single-message), and we'll need its batchId.\n\t\t\tconst batchId = getEffectiveBatchId(pendingMessage);\n\n\t\t\tconst staged = pendingMessage.batchInfo.staged;\n\n\t\t\tif (asEmptyBatchLocalOpMetadata(pendingMessage.localOpMetadata)?.emptyBatch === true) {\n\t\t\t\t// Resubmit no messages, with the batchId. Will result in another empty batch marker.\n\t\t\t\tthis.stateHandler.reSubmitBatch([], { batchId, staged, squash });\n\t\t\t\treplayedBatchSet.add(pendingMessage.batchInfo);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tassert(\n\t\t\t\thasTypicalRuntimeOp(pendingMessage),\n\t\t\t\t0xb87 /* runtimeOp is only undefined for empty batches */,\n\t\t\t);\n\n\t\t\t/**\n\t\t\t * We must preserve the distinct batches on resubmit.\n\t\t\t * Note: It is not possible for the PendingStateManager to receive a partially acked batch. It will\n\t\t\t * either receive the whole batch ack or nothing at all. See {@link InboundBatchAggregator} for how this works.\n\t\t\t */\n\t\t\tif (batchMetadataFlag === undefined) {\n\t\t\t\t// Single-message batch\n\n\t\t\t\tthis.stateHandler.reSubmitBatch(\n\t\t\t\t\t[\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\truntimeOp: pendingMessage.runtimeOp,\n\t\t\t\t\t\t\tlocalOpMetadata: pendingMessage.localOpMetadata,\n\t\t\t\t\t\t\topMetadata: pendingMessage.opMetadata,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\t{ batchId, staged, squash },\n\t\t\t\t);\n\t\t\t\treplayedBatchSet.add(pendingMessage.batchInfo);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t// else: batchMetadataFlag === true (It's a typical multi-message batch)\n\n\t\t\tassert(\n\t\t\t\tremainingPendingMessagesCount > 0,\n\t\t\t\t0x554 /* Last pending message cannot be a batch begin */,\n\t\t\t);\n\n\t\t\tconst batch: PendingMessageResubmitData[] = [];\n\n\t\t\t// check is >= because batch end may be last pending message\n\t\t\twhile (remainingPendingMessagesCount >= 0) {\n\t\t\t\tassert(\n\t\t\t\t\thasTypicalRuntimeOp(pendingMessage),\n\t\t\t\t\t0xb88 /* runtimeOp is only undefined for empty batches */,\n\t\t\t\t);\n\t\t\t\tbatch.push({\n\t\t\t\t\truntimeOp: pendingMessage.runtimeOp,\n\t\t\t\t\tlocalOpMetadata: pendingMessage.localOpMetadata,\n\t\t\t\t\topMetadata: pendingMessage.opMetadata,\n\t\t\t\t});\n\n\t\t\t\t// End of the batch\n\t\t\t\tif (pendingMessage.opMetadata?.batch === false) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tassert(remainingPendingMessagesCount > 0, 0x555 /* No batch end found */);\n\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n\t\t\t\tpendingMessage = this.pendingMessages.shift()!;\n\t\t\t\tremainingPendingMessagesCount--;\n\t\t\t\tassert(\n\t\t\t\t\tpendingMessage.opMetadata?.batch !== true,\n\t\t\t\t\t0x556 /* Batch start needs a corresponding batch end */,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tthis.stateHandler.reSubmitBatch(batch, { batchId, staged, squash });\n\t\t\treplayedBatchSet.add(pendingMessage.batchInfo);\n\t\t}\n\n\t\tif (!committingStagedBatches) {\n\t\t\t// pending ops should no longer depend on previous sequenced local ops after resubmitting all pending messages (does not apply if only replaying the staged messages)\n\t\t\tthis.savedOps = [];\n\t\t}\n\n\t\t// We replayPendingStates on read connections too - we expect these to get nack'd though, and to then reconnect\n\t\t// on a write connection and replay again. This filters out the replay that happens on the read connection so\n\t\t// we only see the replays on write connections (that have a chance to go through).\n\t\tif (this.stateHandler.isActiveConnection()) {\n\t\t\tthis.logger?.sendTelemetryEvent({\n\t\t\t\teventName: \"PendingStatesReplayed\",\n\t\t\t\tcount: initialPendingMessagesCount,\n\t\t\t\tclientId: this.stateHandler.clientId(),\n\t\t\t});\n\t\t}\n\n\t\treturn [...replayedBatchSet];\n\t}\n\n\t/**\n\t * Pops all staged batches, invoking the callback on each constituent op in order (LIFO)\n\t */\n\tpublic popStagedBatches(\n\t\tcallback: (\n\t\t\t// callback will only be given staged messages with a valid runtime op (i.e. not empty batch and not an initial message with only serialized content)\n\t\t\tstagedMessage: IPendingMessage & { runtimeOp: LocalContainerRuntimeMessage },\n\t\t) => void,\n\t): IPendingMessage[\"batchInfo\"][] {\n\t\tconst batchSet = new Set<IPendingMessage[\"batchInfo\"]>();\n\t\twhile (!this.pendingMessages.isEmpty()) {\n\t\t\tconst stagedMessage = this.pendingMessages.peekBack();\n\t\t\tif (stagedMessage?.batchInfo.staged === true) {\n\t\t\t\tthis.pendingMessages.pop();\n\t\t\t\tbatchSet.add(stagedMessage.batchInfo);\n\n\t\t\t\tif (hasTypicalRuntimeOp(stagedMessage)) {\n\t\t\t\t\tcallback(stagedMessage);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tbreak; // no more staged messages\n\t\t\t}\n\t\t}\n\t\tassert(\n\t\t\tthis.pendingMessages.toArray().every((m) => m.batchInfo.staged !== true),\n\t\t\t0xb89 /* Shouldn't be any more staged messages */,\n\t\t);\n\t\treturn [...batchSet];\n\t}\n}\n\n/**\n * For back-compat if trying to apply stashed ops that pre-date batchInfo\n */\nfunction patchbatchInfo(\n\tmessage: IPendingMessageFromStash,\n): asserts message is IPendingMessage {\n\tconst batchInfo: IPendingMessageFromStash[\"batchInfo\"] = message.batchInfo;\n\tif (batchInfo === undefined) {\n\t\t// Using uuid guarantees uniqueness, retaining existing behavior\n\t\tmessage.batchInfo = { clientId: uuid(), batchStartCsn: -1, length: -1, staged: false };\n\t}\n}\n\n/**\n * This filters out messages that are not \"typical\" runtime ops, i.e. empty batches or initial messages (which only have serialized content).\n */\nfunction hasTypicalRuntimeOp(\n\tmessage: IPendingMessage,\n): message is IPendingMessage & { runtimeOp: LocalContainerRuntimeMessage } {\n\treturn message.runtimeOp !== undefined && message.runtimeOp.type !== \"groupedBatch\";\n}\n"]}
|
|
@@ -17,11 +17,11 @@ export declare const runtimeCoreCompatDetails: {
|
|
|
17
17
|
/**
|
|
18
18
|
* The package version of the Runtime layer.
|
|
19
19
|
*/
|
|
20
|
-
readonly pkgVersion: "2.
|
|
20
|
+
readonly pkgVersion: "2.103.0";
|
|
21
21
|
/**
|
|
22
22
|
* The current generation of the Runtime layer.
|
|
23
23
|
*/
|
|
24
|
-
readonly generation:
|
|
24
|
+
readonly generation: 7;
|
|
25
25
|
};
|
|
26
26
|
/**
|
|
27
27
|
* Runtime's compatibility details that is exposed to the Loader layer.
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
import type { ISignalEnvelope } from "@fluidframework/core-interfaces/internal";
|
|
6
|
-
import type {
|
|
6
|
+
import type { TelemetryLoggerExt } from "@fluidframework/telemetry-utils/internal";
|
|
7
7
|
export declare class SignalTelemetryManager {
|
|
8
8
|
private readonly signalTracking;
|
|
9
9
|
/**
|
|
@@ -21,7 +21,7 @@ export declare class SignalTelemetryManager {
|
|
|
21
21
|
* @param logger - The telemetry logger to use for emitting telemetry events.
|
|
22
22
|
* @param consecutiveReconnects - The number of consecutive reconnects that have occurred. Only used for logging.
|
|
23
23
|
*/
|
|
24
|
-
trackReceivedSignal(envelope: ISignalEnvelope, logger:
|
|
24
|
+
trackReceivedSignal(envelope: ISignalEnvelope, logger: TelemetryLoggerExt, consecutiveReconnects: number): void;
|
|
25
25
|
/**
|
|
26
26
|
* Updates tracking state for broadcast signals based on the provided signal envelope, and updates the
|
|
27
27
|
* envelope with additional information that the signal needs to have stamped on it.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"signalTelemetryProcessing.d.ts","sourceRoot":"","sources":["../src/signalTelemetryProcessing.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0CAA0C,CAAC;AAChF,OAAO,KAAK,EACX,
|
|
1
|
+
{"version":3,"file":"signalTelemetryProcessing.d.ts","sourceRoot":"","sources":["../src/signalTelemetryProcessing.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0CAA0C,CAAC;AAChF,OAAO,KAAK,EACX,kBAAkB,EAElB,MAAM,0CAA0C,CAAC;AAsDlD,qBAAa,sBAAsB;IAClC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAS7B;IAEF;;;OAGG;IACH,OAAO,CAAC,6BAA6B,CAAa;IAElD;;OAEG;IACI,aAAa,IAAI,IAAI;IAW5B;;;;;OAKG;IACI,mBAAmB,CACzB,QAAQ,EAAE,eAAe,EACzB,MAAM,EAAE,kBAAkB,EAC1B,qBAAqB,EAAE,MAAM,GAC3B,IAAI;IA2FP;;;;;OAKG;IACI,sCAAsC,CAAC,QAAQ,EAAE,eAAe,GAAG,IAAI;CA8B9E"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"signalTelemetryProcessing.js","sourceRoot":"","sources":["../src/signalTelemetryProcessing.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 { ISignalEnvelope } from \"@fluidframework/core-interfaces/internal\";\nimport type {\n\tITelemetryLoggerExt,\n\tTelemetryEventPropertyTypeExt,\n} from \"@fluidframework/telemetry-utils/internal\";\n\nconst defaultTelemetrySignalSampleCount = 100;\n\n/**\n * Set of stats/values used to keep track of telemetry related to signals.\n */\ninterface ISignalTelemetryTracking {\n\t/**\n\t * Accumulates the total number of broadcast signals sent during the current signal latency measurement window.\n\t * This value represents the total number of signals sent since the latency measurement began and is used\n\t * logged in telemetry when the latency measurement completes.\n\t */\n\ttotalSignalsSentInLatencyWindow: number;\n\n\t/**\n\t * Counts the number of broadcast signals sent since the last latency measurement was initiated.\n\t * This counter increments with each broadcast signal sent. When a new latency measurement starts,\n\t * this counter is added to `totalSignalsSentInLatencyWindow` and then reset to zero.\n\t */\n\tsignalsSentSinceLastLatencyMeasurement: number;\n\n\t/**\n\t * Number of signals that were expected but not received.\n\t */\n\tsignalsLost: number;\n\n\t/**\n\t * Number of signals received out of order/non-sequentially.\n\t */\n\tsignalsOutOfOrder: number;\n\n\t/**\n\t * Timestamp before submitting the signal we will trace.\n\t */\n\tsignalTimestamp: number;\n\n\t/**\n\t * Signal we will trace for roundtrip latency.\n\t */\n\troundTripSignalSequenceNumber: number | undefined;\n\n\t/**\n\t * Next expected signal sequence number to be received.\n\t */\n\ttrackingSignalSequenceNumber: number | undefined;\n\n\t/**\n\t * Inclusive lower bound of signal monitoring window.\n\t * Used by the logic that checks if signals are received out of order.\n\t */\n\tminimumTrackingSignalSequenceNumber: number | undefined;\n}\n\nexport class SignalTelemetryManager {\n\tprivate readonly signalTracking: ISignalTelemetryTracking = {\n\t\ttotalSignalsSentInLatencyWindow: 0,\n\t\tsignalsLost: 0,\n\t\tsignalsOutOfOrder: 0,\n\t\tsignalsSentSinceLastLatencyMeasurement: 0,\n\t\tsignalTimestamp: 0,\n\t\troundTripSignalSequenceNumber: undefined,\n\t\ttrackingSignalSequenceNumber: undefined,\n\t\tminimumTrackingSignalSequenceNumber: undefined,\n\t};\n\n\t/**\n\t * Identifier to track broadcast signals being submitted in order to\n\t * allow collection of data around the roundtrip of signal messages.\n\t */\n\tprivate broadcastSignalSequenceNumber: number = 0;\n\n\t/**\n\t * Resets the signal tracking state in the {@link SignalTelemetryManager}.\n\t */\n\tpublic resetTracking(): void {\n\t\tthis.signalTracking.signalsLost = 0;\n\t\tthis.signalTracking.signalsOutOfOrder = 0;\n\t\tthis.signalTracking.signalTimestamp = 0;\n\t\tthis.signalTracking.signalsSentSinceLastLatencyMeasurement = 0;\n\t\tthis.signalTracking.totalSignalsSentInLatencyWindow = 0;\n\t\tthis.signalTracking.roundTripSignalSequenceNumber = undefined;\n\t\tthis.signalTracking.trackingSignalSequenceNumber = undefined;\n\t\tthis.signalTracking.minimumTrackingSignalSequenceNumber = undefined;\n\t}\n\n\t/**\n\t * Perform telemetry-related processing of incoming signals.\n\t * @param envelope - The signal envelope to process.\n\t * @param logger - The telemetry logger to use for emitting telemetry events.\n\t * @param consecutiveReconnects - The number of consecutive reconnects that have occurred. Only used for logging.\n\t */\n\tpublic trackReceivedSignal(\n\t\tenvelope: ISignalEnvelope,\n\t\tlogger: ITelemetryLoggerExt,\n\t\tconsecutiveReconnects: number,\n\t): void {\n\t\tconst {\n\t\t\tclientBroadcastSignalSequenceNumber,\n\t\t\tcontents: envelopeContents,\n\t\t\taddress: envelopeAddress,\n\t\t} = envelope;\n\n\t\tif (clientBroadcastSignalSequenceNumber === undefined) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\t// If no tracking window has been set, nothing to do\n\t\tif (\n\t\t\tthis.signalTracking.trackingSignalSequenceNumber === undefined ||\n\t\t\tthis.signalTracking.minimumTrackingSignalSequenceNumber === undefined\n\t\t) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tif (\n\t\t\tclientBroadcastSignalSequenceNumber >= this.signalTracking.trackingSignalSequenceNumber\n\t\t) {\n\t\t\t// Calculate the number of signals lost and log the event.\n\t\t\tconst signalsLost =\n\t\t\t\tclientBroadcastSignalSequenceNumber - this.signalTracking.trackingSignalSequenceNumber;\n\t\t\tif (signalsLost > 0) {\n\t\t\t\tthis.signalTracking.signalsLost += signalsLost;\n\t\t\t\tlogger.sendErrorEvent({\n\t\t\t\t\teventName: \"SignalLost\",\n\t\t\t\t\tdetails: {\n\t\t\t\t\t\tsignalsLost, // Number of lost signals detected.\n\t\t\t\t\t\texpectedSequenceNumber: this.signalTracking.trackingSignalSequenceNumber, // The next expected signal sequence number.\n\t\t\t\t\t\tclientBroadcastSignalSequenceNumber, // Actual signal sequence number received.\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t}\n\t\t\t// Update the tracking signal sequence number to the next expected signal in the sequence.\n\t\t\tthis.signalTracking.trackingSignalSequenceNumber =\n\t\t\t\tclientBroadcastSignalSequenceNumber + 1;\n\t\t} else if (\n\t\t\t// Check if this is a signal in range of interest.\n\t\t\tclientBroadcastSignalSequenceNumber >=\n\t\t\tthis.signalTracking.minimumTrackingSignalSequenceNumber\n\t\t) {\n\t\t\tthis.signalTracking.signalsOutOfOrder++;\n\t\t\tconst details: TelemetryEventPropertyTypeExt = {\n\t\t\t\texpectedSequenceNumber: this.signalTracking.trackingSignalSequenceNumber, // The next expected signal sequence number.\n\t\t\t\tclientBroadcastSignalSequenceNumber, // Sequence number of the out of order signal.\n\t\t\t};\n\t\t\t// Only log `contents.type` when address is for container to avoid chance that contents type is customer data.\n\t\t\tif (envelopeAddress === undefined) {\n\t\t\t\tdetails.contentsType = envelopeContents.type; // Type of signal that was received out of order.\n\t\t\t}\n\t\t\tlogger.sendTelemetryEvent({\n\t\t\t\teventName: \"SignalOutOfOrder\",\n\t\t\t\tdetails,\n\t\t\t});\n\t\t}\n\n\t\tif (\n\t\t\tthis.signalTracking.roundTripSignalSequenceNumber !== undefined &&\n\t\t\tclientBroadcastSignalSequenceNumber >= this.signalTracking.roundTripSignalSequenceNumber\n\t\t) {\n\t\t\tif (\n\t\t\t\tclientBroadcastSignalSequenceNumber ===\n\t\t\t\tthis.signalTracking.roundTripSignalSequenceNumber\n\t\t\t) {\n\t\t\t\t// Latency tracked signal has been received.\n\t\t\t\t// We now emit telemetry with the roundtrip duration of the tracked signal.\n\t\t\t\t// The telemetry event also includes metrics for broadcast signals (sent, lost, and out of order),\n\t\t\t\t// and these metrics are reset after emitting the event.\n\t\t\t\tconst duration = Date.now() - this.signalTracking.signalTimestamp;\n\t\t\t\tlogger.sendPerformanceEvent({\n\t\t\t\t\teventName: \"SignalLatency\",\n\t\t\t\t\tdetails: {\n\t\t\t\t\t\tduration, // Roundtrip duration of the tracked signal in milliseconds.\n\t\t\t\t\t\tsent: this.signalTracking.totalSignalsSentInLatencyWindow, // Signals sent since the last logged SignalLatency event.\n\t\t\t\t\t\tlost: this.signalTracking.signalsLost, // Signals lost since the last logged SignalLatency event.\n\t\t\t\t\t\toutOfOrder: this.signalTracking.signalsOutOfOrder, // Out of order signals since the last logged SignalLatency event.\n\t\t\t\t\t\treconnectCount: consecutiveReconnects, // Container reconnect count.\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t\tthis.signalTracking.signalsLost = 0;\n\t\t\t\tthis.signalTracking.signalsOutOfOrder = 0;\n\t\t\t\tthis.signalTracking.signalTimestamp = 0;\n\t\t\t\tthis.signalTracking.totalSignalsSentInLatencyWindow = 0;\n\t\t\t}\n\t\t\tthis.signalTracking.roundTripSignalSequenceNumber = undefined;\n\t\t}\n\t}\n\n\t/**\n\t * Updates tracking state for broadcast signals based on the provided signal envelope, and updates the\n\t * envelope with additional information that the signal needs to have stamped on it.\n\t * @remarks Do not call this for non-broadcast signals.\n\t * @param envelope - The signal envelope to process.\n\t */\n\tpublic applyTrackingToBroadcastSignalEnvelope(envelope: ISignalEnvelope): void {\n\t\tconst broadcastSignalSequenceNumber = ++this.broadcastSignalSequenceNumber;\n\n\t\t// Stamp with the broadcast signal sequence number.\n\t\tenvelope.clientBroadcastSignalSequenceNumber = broadcastSignalSequenceNumber;\n\n\t\tthis.signalTracking.signalsSentSinceLastLatencyMeasurement++;\n\n\t\t// If we don't have a signal monitoring window yet,\n\t\t// initialize tracking to expect the next signal sent by the connected client.\n\t\tif (\n\t\t\tthis.signalTracking.minimumTrackingSignalSequenceNumber === undefined ||\n\t\t\tthis.signalTracking.trackingSignalSequenceNumber === undefined\n\t\t) {\n\t\t\tthis.signalTracking.minimumTrackingSignalSequenceNumber = broadcastSignalSequenceNumber;\n\t\t\tthis.signalTracking.trackingSignalSequenceNumber = broadcastSignalSequenceNumber;\n\t\t}\n\n\t\t// Start tracking roundtrip for a new signal only if we are not tracking one already (and sampling logic is met)\n\t\tif (\n\t\t\tthis.signalTracking.roundTripSignalSequenceNumber === undefined &&\n\t\t\tbroadcastSignalSequenceNumber % defaultTelemetrySignalSampleCount === 1\n\t\t) {\n\t\t\tthis.signalTracking.signalTimestamp = Date.now();\n\t\t\tthis.signalTracking.roundTripSignalSequenceNumber = broadcastSignalSequenceNumber;\n\t\t\tthis.signalTracking.totalSignalsSentInLatencyWindow +=\n\t\t\t\tthis.signalTracking.signalsSentSinceLastLatencyMeasurement;\n\t\t\tthis.signalTracking.signalsSentSinceLastLatencyMeasurement = 0;\n\t\t}\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"signalTelemetryProcessing.js","sourceRoot":"","sources":["../src/signalTelemetryProcessing.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 { ISignalEnvelope } from \"@fluidframework/core-interfaces/internal\";\nimport type {\n\tTelemetryLoggerExt,\n\tTelemetryEventPropertyTypeExt,\n} from \"@fluidframework/telemetry-utils/internal\";\n\nconst defaultTelemetrySignalSampleCount = 100;\n\n/**\n * Set of stats/values used to keep track of telemetry related to signals.\n */\ninterface ISignalTelemetryTracking {\n\t/**\n\t * Accumulates the total number of broadcast signals sent during the current signal latency measurement window.\n\t * This value represents the total number of signals sent since the latency measurement began and is used\n\t * logged in telemetry when the latency measurement completes.\n\t */\n\ttotalSignalsSentInLatencyWindow: number;\n\n\t/**\n\t * Counts the number of broadcast signals sent since the last latency measurement was initiated.\n\t * This counter increments with each broadcast signal sent. When a new latency measurement starts,\n\t * this counter is added to `totalSignalsSentInLatencyWindow` and then reset to zero.\n\t */\n\tsignalsSentSinceLastLatencyMeasurement: number;\n\n\t/**\n\t * Number of signals that were expected but not received.\n\t */\n\tsignalsLost: number;\n\n\t/**\n\t * Number of signals received out of order/non-sequentially.\n\t */\n\tsignalsOutOfOrder: number;\n\n\t/**\n\t * Timestamp before submitting the signal we will trace.\n\t */\n\tsignalTimestamp: number;\n\n\t/**\n\t * Signal we will trace for roundtrip latency.\n\t */\n\troundTripSignalSequenceNumber: number | undefined;\n\n\t/**\n\t * Next expected signal sequence number to be received.\n\t */\n\ttrackingSignalSequenceNumber: number | undefined;\n\n\t/**\n\t * Inclusive lower bound of signal monitoring window.\n\t * Used by the logic that checks if signals are received out of order.\n\t */\n\tminimumTrackingSignalSequenceNumber: number | undefined;\n}\n\nexport class SignalTelemetryManager {\n\tprivate readonly signalTracking: ISignalTelemetryTracking = {\n\t\ttotalSignalsSentInLatencyWindow: 0,\n\t\tsignalsLost: 0,\n\t\tsignalsOutOfOrder: 0,\n\t\tsignalsSentSinceLastLatencyMeasurement: 0,\n\t\tsignalTimestamp: 0,\n\t\troundTripSignalSequenceNumber: undefined,\n\t\ttrackingSignalSequenceNumber: undefined,\n\t\tminimumTrackingSignalSequenceNumber: undefined,\n\t};\n\n\t/**\n\t * Identifier to track broadcast signals being submitted in order to\n\t * allow collection of data around the roundtrip of signal messages.\n\t */\n\tprivate broadcastSignalSequenceNumber: number = 0;\n\n\t/**\n\t * Resets the signal tracking state in the {@link SignalTelemetryManager}.\n\t */\n\tpublic resetTracking(): void {\n\t\tthis.signalTracking.signalsLost = 0;\n\t\tthis.signalTracking.signalsOutOfOrder = 0;\n\t\tthis.signalTracking.signalTimestamp = 0;\n\t\tthis.signalTracking.signalsSentSinceLastLatencyMeasurement = 0;\n\t\tthis.signalTracking.totalSignalsSentInLatencyWindow = 0;\n\t\tthis.signalTracking.roundTripSignalSequenceNumber = undefined;\n\t\tthis.signalTracking.trackingSignalSequenceNumber = undefined;\n\t\tthis.signalTracking.minimumTrackingSignalSequenceNumber = undefined;\n\t}\n\n\t/**\n\t * Perform telemetry-related processing of incoming signals.\n\t * @param envelope - The signal envelope to process.\n\t * @param logger - The telemetry logger to use for emitting telemetry events.\n\t * @param consecutiveReconnects - The number of consecutive reconnects that have occurred. Only used for logging.\n\t */\n\tpublic trackReceivedSignal(\n\t\tenvelope: ISignalEnvelope,\n\t\tlogger: TelemetryLoggerExt,\n\t\tconsecutiveReconnects: number,\n\t): void {\n\t\tconst {\n\t\t\tclientBroadcastSignalSequenceNumber,\n\t\t\tcontents: envelopeContents,\n\t\t\taddress: envelopeAddress,\n\t\t} = envelope;\n\n\t\tif (clientBroadcastSignalSequenceNumber === undefined) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\t// If no tracking window has been set, nothing to do\n\t\tif (\n\t\t\tthis.signalTracking.trackingSignalSequenceNumber === undefined ||\n\t\t\tthis.signalTracking.minimumTrackingSignalSequenceNumber === undefined\n\t\t) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tif (\n\t\t\tclientBroadcastSignalSequenceNumber >= this.signalTracking.trackingSignalSequenceNumber\n\t\t) {\n\t\t\t// Calculate the number of signals lost and log the event.\n\t\t\tconst signalsLost =\n\t\t\t\tclientBroadcastSignalSequenceNumber - this.signalTracking.trackingSignalSequenceNumber;\n\t\t\tif (signalsLost > 0) {\n\t\t\t\tthis.signalTracking.signalsLost += signalsLost;\n\t\t\t\tlogger.sendErrorEvent({\n\t\t\t\t\teventName: \"SignalLost\",\n\t\t\t\t\tdetails: {\n\t\t\t\t\t\tsignalsLost, // Number of lost signals detected.\n\t\t\t\t\t\texpectedSequenceNumber: this.signalTracking.trackingSignalSequenceNumber, // The next expected signal sequence number.\n\t\t\t\t\t\tclientBroadcastSignalSequenceNumber, // Actual signal sequence number received.\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t}\n\t\t\t// Update the tracking signal sequence number to the next expected signal in the sequence.\n\t\t\tthis.signalTracking.trackingSignalSequenceNumber =\n\t\t\t\tclientBroadcastSignalSequenceNumber + 1;\n\t\t} else if (\n\t\t\t// Check if this is a signal in range of interest.\n\t\t\tclientBroadcastSignalSequenceNumber >=\n\t\t\tthis.signalTracking.minimumTrackingSignalSequenceNumber\n\t\t) {\n\t\t\tthis.signalTracking.signalsOutOfOrder++;\n\t\t\tconst details: TelemetryEventPropertyTypeExt = {\n\t\t\t\texpectedSequenceNumber: this.signalTracking.trackingSignalSequenceNumber, // The next expected signal sequence number.\n\t\t\t\tclientBroadcastSignalSequenceNumber, // Sequence number of the out of order signal.\n\t\t\t};\n\t\t\t// Only log `contents.type` when address is for container to avoid chance that contents type is customer data.\n\t\t\tif (envelopeAddress === undefined) {\n\t\t\t\tdetails.contentsType = envelopeContents.type; // Type of signal that was received out of order.\n\t\t\t}\n\t\t\tlogger.sendTelemetryEvent({\n\t\t\t\teventName: \"SignalOutOfOrder\",\n\t\t\t\tdetails,\n\t\t\t});\n\t\t}\n\n\t\tif (\n\t\t\tthis.signalTracking.roundTripSignalSequenceNumber !== undefined &&\n\t\t\tclientBroadcastSignalSequenceNumber >= this.signalTracking.roundTripSignalSequenceNumber\n\t\t) {\n\t\t\tif (\n\t\t\t\tclientBroadcastSignalSequenceNumber ===\n\t\t\t\tthis.signalTracking.roundTripSignalSequenceNumber\n\t\t\t) {\n\t\t\t\t// Latency tracked signal has been received.\n\t\t\t\t// We now emit telemetry with the roundtrip duration of the tracked signal.\n\t\t\t\t// The telemetry event also includes metrics for broadcast signals (sent, lost, and out of order),\n\t\t\t\t// and these metrics are reset after emitting the event.\n\t\t\t\tconst duration = Date.now() - this.signalTracking.signalTimestamp;\n\t\t\t\tlogger.sendPerformanceEvent({\n\t\t\t\t\teventName: \"SignalLatency\",\n\t\t\t\t\tdetails: {\n\t\t\t\t\t\tduration, // Roundtrip duration of the tracked signal in milliseconds.\n\t\t\t\t\t\tsent: this.signalTracking.totalSignalsSentInLatencyWindow, // Signals sent since the last logged SignalLatency event.\n\t\t\t\t\t\tlost: this.signalTracking.signalsLost, // Signals lost since the last logged SignalLatency event.\n\t\t\t\t\t\toutOfOrder: this.signalTracking.signalsOutOfOrder, // Out of order signals since the last logged SignalLatency event.\n\t\t\t\t\t\treconnectCount: consecutiveReconnects, // Container reconnect count.\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t\tthis.signalTracking.signalsLost = 0;\n\t\t\t\tthis.signalTracking.signalsOutOfOrder = 0;\n\t\t\t\tthis.signalTracking.signalTimestamp = 0;\n\t\t\t\tthis.signalTracking.totalSignalsSentInLatencyWindow = 0;\n\t\t\t}\n\t\t\tthis.signalTracking.roundTripSignalSequenceNumber = undefined;\n\t\t}\n\t}\n\n\t/**\n\t * Updates tracking state for broadcast signals based on the provided signal envelope, and updates the\n\t * envelope with additional information that the signal needs to have stamped on it.\n\t * @remarks Do not call this for non-broadcast signals.\n\t * @param envelope - The signal envelope to process.\n\t */\n\tpublic applyTrackingToBroadcastSignalEnvelope(envelope: ISignalEnvelope): void {\n\t\tconst broadcastSignalSequenceNumber = ++this.broadcastSignalSequenceNumber;\n\n\t\t// Stamp with the broadcast signal sequence number.\n\t\tenvelope.clientBroadcastSignalSequenceNumber = broadcastSignalSequenceNumber;\n\n\t\tthis.signalTracking.signalsSentSinceLastLatencyMeasurement++;\n\n\t\t// If we don't have a signal monitoring window yet,\n\t\t// initialize tracking to expect the next signal sent by the connected client.\n\t\tif (\n\t\t\tthis.signalTracking.minimumTrackingSignalSequenceNumber === undefined ||\n\t\t\tthis.signalTracking.trackingSignalSequenceNumber === undefined\n\t\t) {\n\t\t\tthis.signalTracking.minimumTrackingSignalSequenceNumber = broadcastSignalSequenceNumber;\n\t\t\tthis.signalTracking.trackingSignalSequenceNumber = broadcastSignalSequenceNumber;\n\t\t}\n\n\t\t// Start tracking roundtrip for a new signal only if we are not tracking one already (and sampling logic is met)\n\t\tif (\n\t\t\tthis.signalTracking.roundTripSignalSequenceNumber === undefined &&\n\t\t\tbroadcastSignalSequenceNumber % defaultTelemetrySignalSampleCount === 1\n\t\t) {\n\t\t\tthis.signalTracking.signalTimestamp = Date.now();\n\t\t\tthis.signalTracking.roundTripSignalSequenceNumber = broadcastSignalSequenceNumber;\n\t\t\tthis.signalTracking.totalSignalsSentInLatencyWindow +=\n\t\t\t\tthis.signalTracking.signalsSentSinceLastLatencyMeasurement;\n\t\t\tthis.signalTracking.signalsSentSinceLastLatencyMeasurement = 0;\n\t\t}\n\t}\n}\n"]}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
import type { SemanticVersion } from "@fluidframework/runtime-utils/internal";
|
|
6
|
-
import type {
|
|
6
|
+
import type { TelemetryLoggerExt } from "@fluidframework/telemetry-utils/internal";
|
|
7
7
|
/**
|
|
8
8
|
* Descripe allowed type for properties in document schema.
|
|
9
9
|
* Please note that for all property types we should use undefined to indicate that particular capability is off.
|
|
@@ -256,7 +256,7 @@ export declare class DocumentsSchemaController {
|
|
|
256
256
|
* @param logger - telemetry logger from the runtime
|
|
257
257
|
* @param disableSchemaUpgrade - when true, the controller will never send or accept DocumentSchemaChange ops
|
|
258
258
|
*/
|
|
259
|
-
constructor(existing: boolean, snapshotSequenceNumber: number, documentMetadataSchema: IDocumentSchema | undefined, features: IDocumentSchemaFeatures, onSchemaChange: (schema: IDocumentSchemaCurrent) => void, info: IDocumentSchemaInfo, logger:
|
|
259
|
+
constructor(existing: boolean, snapshotSequenceNumber: number, documentMetadataSchema: IDocumentSchema | undefined, features: IDocumentSchemaFeatures, onSchemaChange: (schema: IDocumentSchemaCurrent) => void, info: IDocumentSchemaInfo, logger: TelemetryLoggerExt, disableSchemaUpgrade: boolean);
|
|
260
260
|
summarizeDocumentSchema(refSeq: number): IDocumentSchema | IDocumentSchemaCurrent | undefined;
|
|
261
261
|
/**
|
|
262
262
|
* Called by Container runtime whenever it is about to send some op.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"documentSchema.d.ts","sourceRoot":"","sources":["../../src/summary/documentSchema.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wCAAwC,CAAC;AAC9E,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"documentSchema.d.ts","sourceRoot":"","sources":["../../src/summary/documentSchema.ts"],"names":[],"mappings":"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"}
|
|
@@ -91,6 +91,37 @@ const documentSchemaSupportedConfigs = {
|
|
|
91
91
|
createBlobPayloadPending: new TrueOrUndefined(),
|
|
92
92
|
disallowedVersions: new CheckVersions(),
|
|
93
93
|
};
|
|
94
|
+
/**
|
|
95
|
+
* Retired runtime features. A retired feature is one this runtime version no longer toggles
|
|
96
|
+
* via {@link IDocumentSchemaFeatures}, but that older documents may still carry in their
|
|
97
|
+
* persisted schema. Each entry bundles the property handler with the hardcoded value for the feature.
|
|
98
|
+
*
|
|
99
|
+
* Retired features participate in the normal merge / schema-change-op flow exactly like
|
|
100
|
+
* non-retired features — the only difference is that their values are hardcoded.
|
|
101
|
+
*/
|
|
102
|
+
const retiredDocumentSchemaFeatures = {
|
|
103
|
+
// Note: There are currently no retired retired features. To retire a feature, remove it from IDocumentSchemaFeatures
|
|
104
|
+
// and documentSchemaSupportedConfigs and add an entry here, e.g.:
|
|
105
|
+
// featureFoo: { handler: new TrueOrUndefined(), value: true },
|
|
106
|
+
};
|
|
107
|
+
/**
|
|
108
|
+
* Looks up the validator/merger for a given runtime property name across both supported and
|
|
109
|
+
* retired configs. Returns `undefined` if the property is unknown to this runtime.
|
|
110
|
+
*/
|
|
111
|
+
function getRuntimeConfigHandler(name) {
|
|
112
|
+
return (documentSchemaSupportedConfigs[name] ??
|
|
113
|
+
retiredDocumentSchemaFeatures[name]?.handler);
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Builds the `{ key: value }` for retired features (for building the desired schema).
|
|
117
|
+
*/
|
|
118
|
+
function retiredFeatureValues() {
|
|
119
|
+
const result = {};
|
|
120
|
+
for (const [key, entry] of Object.entries(retiredDocumentSchemaFeatures)) {
|
|
121
|
+
result[key] = entry.value;
|
|
122
|
+
}
|
|
123
|
+
return result;
|
|
124
|
+
}
|
|
94
125
|
/**
|
|
95
126
|
* Checks if a given schema is compatible with current code, i.e. if current code can understand all the features of that schema.
|
|
96
127
|
* If schema is not compatible with current code, it throws an exception.
|
|
@@ -123,7 +154,7 @@ function checkRuntimeCompatibility(documentSchema, schemaName) {
|
|
|
123
154
|
}
|
|
124
155
|
else {
|
|
125
156
|
for (const [name, value] of Object.entries(documentSchema.runtime)) {
|
|
126
|
-
const validator =
|
|
157
|
+
const validator = getRuntimeConfigHandler(name);
|
|
127
158
|
if (!(validator?.validate(value) ?? false)) {
|
|
128
159
|
unknownProperty = `runtime/${name}`;
|
|
129
160
|
}
|
|
@@ -148,7 +179,7 @@ function and(persistedSchema, providedSchema) {
|
|
|
148
179
|
...Object.keys(persistedSchema.runtime),
|
|
149
180
|
...Object.keys(providedSchema.runtime),
|
|
150
181
|
])) {
|
|
151
|
-
runtime[key] =
|
|
182
|
+
runtime[key] = getRuntimeConfigHandler(key).and(persistedSchema.runtime[key], providedSchema.runtime[key]);
|
|
152
183
|
}
|
|
153
184
|
// We keep the persisted minVersionForCollab if present, even if the provided minVersionForCollab
|
|
154
185
|
// is higher.
|
|
@@ -166,7 +197,7 @@ function or(persistedSchema, providedSchema) {
|
|
|
166
197
|
...Object.keys(persistedSchema.runtime),
|
|
167
198
|
...Object.keys(providedSchema.runtime),
|
|
168
199
|
])) {
|
|
169
|
-
runtime[key] =
|
|
200
|
+
runtime[key] = getRuntimeConfigHandler(key).or(persistedSchema.runtime[key], providedSchema.runtime[key]);
|
|
170
201
|
}
|
|
171
202
|
// We take the greater of the persisted/provided minVersionForCollab
|
|
172
203
|
const minVersionForCollab = persistedSchema.info === undefined
|
|
@@ -334,6 +365,7 @@ class DocumentsSchemaController {
|
|
|
334
365
|
opGroupingEnabled: boolToProp(features.opGroupingEnabled),
|
|
335
366
|
createBlobPayloadPending: features.createBlobPayloadPending,
|
|
336
367
|
disallowedVersions: arrayToProp(features.disallowedVersions),
|
|
368
|
+
...retiredFeatureValues(),
|
|
337
369
|
},
|
|
338
370
|
};
|
|
339
371
|
// Schema coming from document metadata (snapshot we loaded from), or if no document exists
|