@fluidframework/container-runtime 2.2.0 → 2.3.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 +15 -0
- package/api-report/container-runtime.legacy.alpha.api.md +15 -0
- package/container-runtime.test-files.tar +0 -0
- package/dist/channelCollection.d.ts +1 -1
- package/dist/channelCollection.d.ts.map +1 -1
- package/dist/channelCollection.js +1 -16
- package/dist/channelCollection.js.map +1 -1
- package/dist/connectionTelemetry.d.ts +27 -3
- package/dist/connectionTelemetry.d.ts.map +1 -1
- package/dist/connectionTelemetry.js.map +1 -1
- package/dist/containerRuntime.d.ts +68 -13
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +262 -180
- package/dist/containerRuntime.js.map +1 -1
- package/dist/deltaManagerProxies.d.ts.map +1 -1
- package/dist/deltaManagerProxies.js +11 -4
- package/dist/deltaManagerProxies.js.map +1 -1
- package/dist/gc/garbageCollection.d.ts.map +1 -1
- package/dist/gc/garbageCollection.js +0 -2
- package/dist/gc/garbageCollection.js.map +1 -1
- package/dist/gc/gcHelpers.d.ts.map +1 -1
- package/dist/gc/gcHelpers.js +0 -8
- package/dist/gc/gcHelpers.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/legacy.d.ts +3 -1
- package/dist/messageTypes.d.ts +0 -9
- package/dist/messageTypes.d.ts.map +1 -1
- package/dist/messageTypes.js.map +1 -1
- package/dist/opLifecycle/batchManager.d.ts +9 -0
- package/dist/opLifecycle/batchManager.d.ts.map +1 -1
- package/dist/opLifecycle/batchManager.js +19 -6
- package/dist/opLifecycle/batchManager.js.map +1 -1
- package/dist/opLifecycle/duplicateBatchDetector.d.ts +32 -0
- package/dist/opLifecycle/duplicateBatchDetector.d.ts.map +1 -0
- package/dist/opLifecycle/duplicateBatchDetector.js +68 -0
- package/dist/opLifecycle/duplicateBatchDetector.js.map +1 -0
- package/dist/opLifecycle/index.d.ts +3 -2
- package/dist/opLifecycle/index.d.ts.map +1 -1
- package/dist/opLifecycle/index.js +4 -1
- package/dist/opLifecycle/index.js.map +1 -1
- package/dist/opLifecycle/opCompressor.d.ts.map +1 -1
- package/dist/opLifecycle/opCompressor.js +0 -4
- package/dist/opLifecycle/opCompressor.js.map +1 -1
- package/dist/opLifecycle/opGroupingManager.d.ts.map +1 -1
- package/dist/opLifecycle/opGroupingManager.js +0 -4
- package/dist/opLifecycle/opGroupingManager.js.map +1 -1
- package/dist/opLifecycle/opSplitter.d.ts.map +1 -1
- package/dist/opLifecycle/opSplitter.js +1 -6
- package/dist/opLifecycle/opSplitter.js.map +1 -1
- package/dist/opLifecycle/outbox.d.ts.map +1 -1
- package/dist/opLifecycle/outbox.js +1 -4
- package/dist/opLifecycle/outbox.js.map +1 -1
- package/dist/opLifecycle/remoteMessageProcessor.d.ts +37 -17
- package/dist/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
- package/dist/opLifecycle/remoteMessageProcessor.js +47 -37
- package/dist/opLifecycle/remoteMessageProcessor.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 +27 -17
- package/dist/pendingStateManager.d.ts.map +1 -1
- package/dist/pendingStateManager.js +85 -56
- package/dist/pendingStateManager.js.map +1 -1
- package/dist/scheduleManager.d.ts +2 -4
- package/dist/scheduleManager.d.ts.map +1 -1
- package/dist/scheduleManager.js +6 -37
- package/dist/scheduleManager.js.map +1 -1
- package/dist/summary/summarizerNode/summarizerNodeUtils.d.ts.map +1 -1
- package/dist/summary/summarizerNode/summarizerNodeUtils.js +0 -2
- package/dist/summary/summarizerNode/summarizerNodeUtils.js.map +1 -1
- package/dist/summary/summaryCollection.d.ts.map +1 -1
- package/dist/summary/summaryCollection.js +5 -7
- package/dist/summary/summaryCollection.js.map +1 -1
- package/dist/summary/summaryFormat.d.ts.map +1 -1
- package/dist/summary/summaryFormat.js +1 -4
- package/dist/summary/summaryFormat.js.map +1 -1
- package/lib/channelCollection.d.ts +1 -1
- package/lib/channelCollection.d.ts.map +1 -1
- package/lib/channelCollection.js +1 -16
- package/lib/channelCollection.js.map +1 -1
- package/lib/connectionTelemetry.d.ts +27 -3
- package/lib/connectionTelemetry.d.ts.map +1 -1
- package/lib/connectionTelemetry.js.map +1 -1
- package/lib/containerRuntime.d.ts +68 -13
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +262 -181
- package/lib/containerRuntime.js.map +1 -1
- package/lib/deltaManagerProxies.d.ts.map +1 -1
- package/lib/deltaManagerProxies.js +11 -4
- package/lib/deltaManagerProxies.js.map +1 -1
- package/lib/gc/garbageCollection.d.ts.map +1 -1
- package/lib/gc/garbageCollection.js +0 -2
- package/lib/gc/garbageCollection.js.map +1 -1
- package/lib/gc/gcHelpers.d.ts.map +1 -1
- package/lib/gc/gcHelpers.js +0 -8
- package/lib/gc/gcHelpers.js.map +1 -1
- package/lib/index.d.ts +1 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -1
- package/lib/index.js.map +1 -1
- package/lib/legacy.d.ts +3 -1
- package/lib/messageTypes.d.ts +0 -9
- package/lib/messageTypes.d.ts.map +1 -1
- package/lib/messageTypes.js.map +1 -1
- package/lib/opLifecycle/batchManager.d.ts +9 -0
- package/lib/opLifecycle/batchManager.d.ts.map +1 -1
- package/lib/opLifecycle/batchManager.js +17 -5
- package/lib/opLifecycle/batchManager.js.map +1 -1
- package/lib/opLifecycle/duplicateBatchDetector.d.ts +32 -0
- package/lib/opLifecycle/duplicateBatchDetector.d.ts.map +1 -0
- package/lib/opLifecycle/duplicateBatchDetector.js +64 -0
- package/lib/opLifecycle/duplicateBatchDetector.js.map +1 -0
- package/lib/opLifecycle/index.d.ts +3 -2
- package/lib/opLifecycle/index.d.ts.map +1 -1
- package/lib/opLifecycle/index.js +2 -1
- package/lib/opLifecycle/index.js.map +1 -1
- package/lib/opLifecycle/opCompressor.d.ts.map +1 -1
- package/lib/opLifecycle/opCompressor.js +0 -4
- package/lib/opLifecycle/opCompressor.js.map +1 -1
- package/lib/opLifecycle/opGroupingManager.d.ts.map +1 -1
- package/lib/opLifecycle/opGroupingManager.js +0 -4
- package/lib/opLifecycle/opGroupingManager.js.map +1 -1
- package/lib/opLifecycle/opSplitter.d.ts.map +1 -1
- package/lib/opLifecycle/opSplitter.js +1 -6
- package/lib/opLifecycle/opSplitter.js.map +1 -1
- package/lib/opLifecycle/outbox.d.ts.map +1 -1
- package/lib/opLifecycle/outbox.js +1 -4
- package/lib/opLifecycle/outbox.js.map +1 -1
- package/lib/opLifecycle/remoteMessageProcessor.d.ts +37 -17
- package/lib/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
- package/lib/opLifecycle/remoteMessageProcessor.js +47 -37
- package/lib/opLifecycle/remoteMessageProcessor.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 +27 -17
- package/lib/pendingStateManager.d.ts.map +1 -1
- package/lib/pendingStateManager.js +85 -56
- package/lib/pendingStateManager.js.map +1 -1
- package/lib/scheduleManager.d.ts +2 -4
- package/lib/scheduleManager.d.ts.map +1 -1
- package/lib/scheduleManager.js +6 -37
- package/lib/scheduleManager.js.map +1 -1
- package/lib/summary/summarizerNode/summarizerNodeUtils.d.ts.map +1 -1
- package/lib/summary/summarizerNode/summarizerNodeUtils.js +0 -2
- package/lib/summary/summarizerNode/summarizerNodeUtils.js.map +1 -1
- package/lib/summary/summaryCollection.d.ts.map +1 -1
- package/lib/summary/summaryCollection.js +5 -7
- package/lib/summary/summaryCollection.js.map +1 -1
- package/lib/summary/summaryFormat.d.ts.map +1 -1
- package/lib/summary/summaryFormat.js +1 -4
- package/lib/summary/summaryFormat.js.map +1 -1
- package/lib/tsdoc-metadata.json +1 -1
- package/package.json +49 -27
- package/src/channelCollection.ts +7 -21
- package/src/connectionTelemetry.ts +33 -3
- package/src/containerRuntime.ts +382 -233
- package/src/deltaManagerProxies.ts +11 -4
- package/src/gc/garbageCollection.ts +1 -3
- package/src/gc/gcHelpers.ts +4 -12
- package/src/index.ts +2 -0
- package/src/messageTypes.ts +0 -10
- package/src/opLifecycle/batchManager.ts +29 -7
- package/src/opLifecycle/duplicateBatchDetector.ts +78 -0
- package/src/opLifecycle/index.ts +4 -1
- package/src/opLifecycle/opCompressor.ts +2 -6
- package/src/opLifecycle/opGroupingManager.ts +2 -6
- package/src/opLifecycle/opSplitter.ts +2 -6
- package/src/opLifecycle/outbox.ts +1 -3
- package/src/opLifecycle/remoteMessageProcessor.ts +87 -59
- package/src/packageVersion.ts +1 -1
- package/src/pendingStateManager.ts +114 -66
- package/src/scheduleManager.ts +8 -47
- package/src/summary/summarizerNode/summarizerNodeUtils.ts +1 -3
- package/src/summary/summaryCollection.ts +7 -9
- package/src/summary/summaryFormat.ts +1 -3
- package/src/summary/summaryFormats.md +11 -9
- package/tsconfig.json +1 -0
|
@@ -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 { IDisposable } from \"@fluidframework/core-interfaces\";\nimport { assert, Lazy } from \"@fluidframework/core-utils/internal\";\nimport {\n\tITelemetryLoggerExt,\n\tDataProcessingError,\n\tLoggingError,\n\textractSafePropertiesFromMessage,\n} from \"@fluidframework/telemetry-utils/internal\";\nimport Deque from \"double-ended-queue\";\nimport { v4 as uuid } from \"uuid\";\n\nimport {\n\ttype InboundContainerRuntimeMessage,\n\ttype InboundSequencedContainerRuntimeMessage,\n\ttype LocalContainerRuntimeMessage,\n} from \"./messageTypes.js\";\nimport { asBatchMetadata, asEmptyBatchLocalOpMetadata } from \"./metadata.js\";\nimport { BatchId, BatchMessage, generateBatchId, InboundBatch } 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\tcontent: string;\n\tlocalOpMetadata: unknown;\n\topMetadata: Record<string, unknown> | undefined;\n\tsequenceNumber?: number;\n\t/** Info about the batch this pending message belongs to, for validation and for computing the batchId on reconnect */\n\tbatchInfo: {\n\t\t/** The Batch's original clientId, from when it was first flushed to be submitted */\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 *\t@remarks A negative value means it was not yet submitted when queued here (e.g. disconnected right before flush fired)\n\t\t */\n\t\tbatchStartCsn: number;\n\t\t/** length of the batch (how many runtime messages here) */\n\t\tlength: number;\n\t};\n}\n\ntype Patch<T, U> = U & Omit<T, keyof U>;\n\n/** First version of the type (pre-dates batchInfo) */\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/** Info needed to replay/resubmit a pending message */\nexport type PendingMessageResubmitData = Pick<\n\tIPendingMessage,\n\t\"content\" | \"localOpMetadata\" | \"opMetadata\"\n>;\n\nexport interface IRuntimeStateHandler {\n\tconnected(): boolean;\n\tclientId(): string | undefined;\n\tapplyStashedOp(content: string): Promise<unknown>;\n\treSubmitBatch(batch: PendingMessageResubmitData[], batchId: BatchId): void;\n\tisActiveConnection: () => boolean;\n\tisAttached: () => boolean;\n}\n\nfunction isEmptyBatchPendingMessage(message: IPendingMessageFromStash): boolean {\n\tconst content = JSON.parse(message.content);\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, compatDetails }: InboundContainerRuntimeMessage = message;\n\t// Any properties that are not defined, won't be emitted by stringify.\n\treturn JSON.stringify({ type, contents, compatDetails });\n}\n\nfunction typesOfKeys<T extends object>(obj: T): Record<keyof T, string> {\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 in (for contents)\n\t// or just use the value as-is (since it's not personal info)\n\tscrubbed.contents = message.contents && typesOfKeys(message.contents);\n\tscrubbed.compatDetails = message.compatDetails;\n\tscrubbed.type = message.type;\n\n\treturn JSON.stringify(scrubbed);\n}\n\nfunction withoutLocalOpMetadata(message: IPendingMessage): IPendingMessage {\n\treturn {\n\t\t...message,\n\t\tlocalOpMetadata: undefined,\n\t};\n}\n\n/**\n * Get the effective batch ID for a pending message.\n * If the batch ID is already present in the message's op metadata, return it.\n * Otherwise, generate a new batch ID using the client ID and batch start CSN.\n * @param pendingMessage - The pending message\n * @returns The effective batch ID\n */\nfunction getEffectiveBatchId(pendingMessage: IPendingMessage): string {\n\treturn (\n\t\tasBatchMetadata(pendingMessage.opMetadata)?.batchId ??\n\t\tgenerateBatchId(pendingMessage.batchInfo.clientId, pendingMessage.batchInfo.batchStartCsn)\n\t);\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/** Messages that will need to be resubmitted if not ack'd before the next reconnection */\n\tprivate readonly pendingMessages = new Deque<IPendingMessage>();\n\t/** Messages stashed from a previous container, now being rehydrated. Need to be resubmitted. */\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/** Used to stand in for batchStartCsn for messages that weren't submitted (so no CSN) */\n\tprivate negativeCounter: number = -1;\n\n\tprivate readonly disposeOnce = new Lazy<void>(() => {\n\t\tthis.initialMessages.clear();\n\t\tthis.pendingMessages.clear();\n\t});\n\n\t/** Used to ensure we don't replay ops on the same connection twice */\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 * 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): IPendingLocalState {\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\tthis.pendingMessages.toArray().forEach((message) => {\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\tpendingStates: [\n\t\t\t\t...newSavedOps,\n\t\t\t\t...this.pendingMessages.toArray().map(withoutLocalOpMetadata),\n\t\t\t],\n\t\t};\n\t}\n\n\tconstructor(\n\t\tprivate readonly stateHandler: IRuntimeStateHandler,\n\t\tstashedLocalState: IPendingLocalState | undefined,\n\t\tprivate readonly logger: ITelemetryLoggerExt,\n\t) {\n\t\tif (stashedLocalState?.pendingStates) {\n\t\t\tthis.initialMessages.push(...stashedLocalState.pendingStates);\n\t\t}\n\t}\n\n\tpublic get disposed() {\n\t\treturn this.disposeOnce.evaluated;\n\t}\n\tpublic readonly dispose = () => this.disposeOnce.value;\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 */\n\tpublic onFlushBatch(batch: BatchMessage[], clientSequenceNumber: number | undefined) {\n\t\t// If we're connected this is the client of the current connection,\n\t\t// otherwise it's the clientId that just disconnected\n\t\t// It's only undefined if we've NEVER connected. This is a tight corner case and we can\n\t\t// simply make up a unique ID in this case.\n\t\tconst clientId = this.stateHandler.clientId() ?? uuid();\n\n\t\t// If the batch was not yet sent, we need to assign a unique batchStartCsn\n\t\t// Use a negative number to distinguish these from real CSNs\n\t\tconst batchStartCsn = clientSequenceNumber ?? this.negativeCounter--;\n\n\t\tfor (const message of batch) {\n\t\t\tconst {\n\t\t\t\tcontents: content = \"\",\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,\n\t\t\t\tlocalOpMetadata,\n\t\t\t\topMetadata,\n\t\t\t\t// Note: We only will read this off the first message, but put it on all for simplicity\n\t\t\t\tbatchInfo: { clientId, batchStartCsn, length: batch.length },\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) {\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 = { emptyBatch: true }; // 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\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} else {\n\t\t\t\t\tnextMessage.localOpMetadata = localOpMetadata;\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}\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 * Processes an inbound batch of messages - May be local or remote.\n\t *\n\t * @param batch - The inbound batch of messages to process. Could be local or remote.\n\t * @param local - true if we submitted this batch and expect corresponding pending messages\n\t * @returns The inbound batch's messages with localOpMetadata \"zipped\" in.\n\t *\n\t * @remarks Closes the container if:\n\t * - The batchStartCsn doesn't match for local batches\n\t */\n\tpublic processInboundBatch(\n\t\tbatch: InboundBatch,\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.processPendingLocalBatch(batch);\n\t\t}\n\n\t\t// No localOpMetadata for remote messages\n\t\treturn batch.messages.map((message) => ({ message }));\n\t}\n\n\t/**\n\t * Processes the incoming batch from the server that was submitted by this client.\n\t * It verifies that messages are received in the right order and that the batch information is correct.\n\t * @param batch - The inbound batch (originating from this client) to correlate with the pending local state\n\t * @returns The inbound batch's messages with localOpMetadata \"zipped\" in.\n\t */\n\tprivate processPendingLocalBatch(batch: InboundBatch): {\n\t\tmessage: InboundSequencedContainerRuntimeMessage;\n\t\tlocalOpMetadata: unknown;\n\t}[] {\n\t\tthis.onLocalBatchBegin(batch);\n\n\t\t// Empty batch\n\t\tif (batch.messages.length === 0) {\n\t\t\tassert(\n\t\t\t\tbatch.emptyBatchSequenceNumber !== undefined,\n\t\t\t\t0x9fb /* Expected sequence number for empty batch */,\n\t\t\t);\n\t\t\tconst localOpMetadata = this.processNextPendingMessage(batch.emptyBatchSequenceNumber);\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\treturn batch.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(withoutLocalOpMetadata(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\tif (pendingMessage.content !== messageContent) {\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\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},\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(batch: InboundBatch) {\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\n\t\t// Note: This could be undefined if this batch became empty on resubmit.\n\t\t// In this case the next pending message is an empty batch marker.\n\t\t// Empty batches became empty on Resubmit, and submit them and track them in case\n\t\t// a different fork of this container also submitted the same batch (and it may not be empty for that fork).\n\t\tconst firstMessage = batch.messages.length > 0 ? batch.messages[0] : undefined;\n\t\tconst expectedPendingBatchLength = batch.messages.length === 0 ? 1 : batch.messages.length;\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.\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.\n\t\tif (\n\t\t\tpendingMessage.batchInfo.batchStartCsn !== batch.batchStartCsn ||\n\t\t\t(pendingMessage.batchInfo.length >= 0 && // -1 length is back compat and isn't suitable for this check\n\t\t\t\tpendingMessage.batchInfo.length !== expectedPendingBatchLength)\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: batch.batchStartCsn,\n\t\t\t\t\tpendingBatchLength: pendingMessage.batchInfo.length,\n\t\t\t\t\tbatchLength: batch.messages.length,\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: firstMessage && 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\tpublic replayPendingStates() {\n\t\tassert(\n\t\t\tthis.stateHandler.connected(),\n\t\t\t0x172 /* \"The connection state is not consistent with the runtime\" */,\n\t\t);\n\n\t\t// This assert suggests we are about to send same ops twice, which will result in data loss.\n\t\tassert(\n\t\t\tthis.clientIdFromLastReplay !== this.stateHandler.clientId(),\n\t\t\t0x173 /* \"replayPendingStates called twice for same clientId!\" */,\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\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\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\t\t\t// Resubmit no messages, with the batchId. Will result in another empty batch marker.\n\t\t\tif (asEmptyBatchLocalOpMetadata(pendingMessage.localOpMetadata)?.emptyBatch === true) {\n\t\t\t\tthis.stateHandler.reSubmitBatch([], batchId);\n\t\t\t\tcontinue;\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 ScheduleManager 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\tcontent: pendingMessage.content,\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\tbatchId,\n\t\t\t\t);\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\tbatch.push({\n\t\t\t\t\tcontent: pendingMessage.content,\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\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);\n\t\t}\n\n\t\t// pending ops should no longer depend on previous sequenced local ops after resubmit\n\t\tthis.savedOps = [];\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\t}\n}\n\n/** For back-compat if trying to apply stashed ops that pre-date batchInfo */\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 };\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"pendingStateManager.js","sourceRoot":"","sources":["../src/pendingStateManager.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,qCAAqC,CAAC;AACnE,OAAO,EAEN,mBAAmB,EACnB,YAAY,EACZ,gCAAgC,GAChC,MAAM,0CAA0C,CAAC;AAClD,OAAO,KAAK,MAAM,oBAAoB,CAAC;AACvC,OAAO,EAAE,EAAE,IAAI,IAAI,EAAE,MAAM,MAAM,CAAC;AAOlC,OAAO,EAAE,eAAe,EAAE,2BAA2B,EAAE,MAAM,eAAe,CAAC;AAC7E,OAAO,EAGN,mBAAmB,GAGnB,MAAM,wBAAwB,CAAC;AAkEhC,SAAS,0BAA0B,CAAC,OAAiC;IACpE,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IAC5C,OAAO,OAAO,CAAC,IAAI,KAAK,cAAc,IAAI,OAAO,CAAC,QAAQ,EAAE,MAAM,KAAK,CAAC,CAAC;AAC1E,CAAC;AAED,SAAS,0BAA0B,CAAC,OAAgD;IACnF,kFAAkF;IAClF,+BAA+B;IAC/B,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,aAAa,EAAE,GAAmC,OAAO,CAAC;IAClF,sEAAsE;IACtE,OAAO,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,aAAa,EAAE,CAAC,CAAC;AAC1D,CAAC;AAED,SAAS,WAAW,CAAmB,GAAM;IAC5C,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;QAC3C,GAAG,CAAC,GAAG,CAAC,GAAG,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC;QAC3B,OAAO,GAAG,CAAC;IACZ,CAAC,EAAE,EAAE,CAA4B,CAAC;AACnC,CAAC;AAED,SAAS,iBAAiB,CACzB,OAAsE;IAEtE,2DAA2D;IAC3D,MAAM,QAAQ,GAA4B,WAAW,CAAC,OAAO,CAAC,CAAC;IAE/D,uEAAuE;IACvE,6DAA6D;IAC7D,QAAQ,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,WAAW,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IACtE,QAAQ,CAAC,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC;IAC/C,QAAQ,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAE7B,OAAO,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;AACjC,CAAC;AAED,SAAS,sBAAsB,CAAC,OAAwB;IACvD,OAAO;QACN,GAAG,OAAO;QACV,eAAe,EAAE,SAAS;KAC1B,CAAC;AACH,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,OAAO,mBAAmB;IAmB/B;;;OAGG;IACH,IAAW,oBAAoB;QAC9B,OAAO,IAAI,CAAC,eAAe,CAAC,MAAM,GAAG,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC;IAClE,CAAC;IAED;;;;OAIG;IACH,IAAW,mCAAmC;QAC7C,OAAO,IAAI,CAAC,eAAe,CAAC,SAAS,EAAE,EAAE,uBAAuB,CAAC;IAClE,CAAC;IAED;;;OAGG;IACI,kBAAkB;QACxB,OAAO,IAAI,CAAC,oBAAoB,KAAK,CAAC,CAAC;IACxC,CAAC;IAEM,aAAa,CAAC,sBAA+B;QACnD,MAAM,CACL,IAAI,CAAC,eAAe,CAAC,OAAO,EAAE,EAC9B,KAAK,CAAC,+DAA+D,CACrE,CAAC;QACF,+EAA+E;QAC/E,sFAAsF;QACtF,sFAAsF;QACtF,MAAM,WAAW,GAAG,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE;YACzD,MAAM,CACL,OAAO,CAAC,cAAc,KAAK,SAAS,EACpC,KAAK,CAAC,oDAAoD,CAC1D,CAAC;YACF,OAAO,OAAO,CAAC,cAAc,GAAG,CAAC,sBAAsB,IAAI,CAAC,CAAC,CAAC;QAC/D,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,eAAe,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;YAClD,IACC,sBAAsB,KAAK,SAAS;gBACpC,OAAO,CAAC,uBAAuB,GAAG,sBAAsB,EACvD,CAAC;gBACF,MAAM,IAAI,YAAY,CAAC,oDAAoD,CAAC,CAAC;YAC9E,CAAC;QACF,CAAC,CAAC,CAAC;QACH,OAAO;YACN,aAAa,EAAE;gBACd,GAAG,WAAW;gBACd,GAAG,IAAI,CAAC,eAAe,CAAC,OAAO,EAAE,CAAC,GAAG,CAAC,sBAAsB,CAAC;aAC7D;SACD,CAAC;IACH,CAAC;IAED,YACkB,YAAkC,EACnD,iBAAiD,EAChC,MAA2B;QAF3B,iBAAY,GAAZ,YAAY,CAAsB;QAElC,WAAM,GAAN,MAAM,CAAqB;QA7E7C,0FAA0F;QACzE,oBAAe,GAAG,IAAI,KAAK,EAAmB,CAAC;QAChE,gGAAgG;QAC/E,oBAAe,GAAG,IAAI,KAAK,EAA4B,CAAC;QAEzE;;WAEG;QACK,aAAQ,GAAsB,EAAE,CAAC;QAExB,gBAAW,GAAG,IAAI,IAAI,CAAO,GAAG,EAAE;YAClD,IAAI,CAAC,eAAe,CAAC,KAAK,EAAE,CAAC;YAC7B,IAAI,CAAC,eAAe,CAAC,KAAK,EAAE,CAAC;QAC9B,CAAC,CAAC,CAAC;QA0Ea,YAAO,GAAG,GAAG,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC;QARtD,IAAI,iBAAiB,EAAE,aAAa,EAAE,CAAC;YACtC,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,GAAG,iBAAiB,CAAC,aAAa,CAAC,CAAC;QAC/D,CAAC;IACF,CAAC;IAED,IAAW,QAAQ;QAClB,OAAO,IAAI,CAAC,WAAW,CAAC,SAAS,CAAC;IACnC,CAAC;IAGD;;;;;;OAMG;IACI,YAAY,CAAC,KAAqB,EAAE,oBAAwC;QAClF,kGAAkG;QAClG,iGAAiG;QACjG,oGAAoG;QACpG,mIAAmI;QACnI,wJAAwJ;QACxJ,MAAM,YAAY,GAAG,oBAAoB,KAAK,SAAS,CAAC;QACxD,MAAM,CAAC,QAAQ,EAAE,aAAa,CAAC,GAAG,YAAY;YAC7C,CAAC,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,QAAQ,EAAE,EAAE,oBAAoB,CAAC;YACtD,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,gDAAgD;QACjE,MAAM,CACL,QAAQ,KAAK,SAAS,EACtB,KAAK,CAAC,qIAAqI,CAC3I,CAAC;QAEF,KAAK,MAAM,OAAO,IAAI,KAAK,EAAE,CAAC;YAC7B,MAAM,EACL,QAAQ,EAAE,OAAO,GAAG,EAAE,EACtB,uBAAuB,EACvB,eAAe,EACf,QAAQ,EAAE,UAAU,GACpB,GAAG,OAAO,CAAC;YACZ,MAAM,cAAc,GAAoB;gBACvC,IAAI,EAAE,SAAS;gBACf,uBAAuB;gBACvB,OAAO;gBACP,eAAe;gBACf,UAAU;gBACV,uFAAuF;gBACvF,SAAS,EAAE,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE;aAC5D,CAAC;YACF,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QAC3C,CAAC;IACF,CAAC;IAED;;;OAGG;IACI,KAAK,CAAC,iBAAiB,CAAC,MAAe;QAC7C,uCAAuC;QACvC,OAAO,CAAC,IAAI,CAAC,eAAe,CAAC,OAAO,EAAE,EAAE,CAAC;YACxC,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;gBAC1B,oEAAoE;gBACpE,MAAM,WAAW,GAAG,IAAI,CAAC,eAAe,CAAC,SAAS,EAAG,CAAC;gBACtD,IAAI,WAAW,CAAC,uBAAuB,GAAG,MAAM,EAAE,CAAC;oBAClD,MAAM,CAAC,6CAA6C;gBACrD,CAAC;gBACD,IAAI,WAAW,CAAC,uBAAuB,GAAG,MAAM,EAAE,CAAC;oBAClD,MAAM,IAAI,KAAK,CAAC,sDAAsD,CAAC,CAAC;gBACzE,CAAC;YACF,CAAC;YACD,oEAAoE;YACpE,MAAM,WAAW,GAAG,IAAI,CAAC,eAAe,CAAC,KAAK,EAAG,CAAC;YAClD,qDAAqD;YACrD,8CAA8C;YAC9C,IAAI,CAAC;gBACJ,IAAI,0BAA0B,CAAC,WAAW,CAAC,EAAE,CAAC;oBAC7C,WAAW,CAAC,eAAe,GAAG,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC,+CAA+C;oBACnG,cAAc,CAAC,WAAW,CAAC,CAAC,CAAC,cAAc;oBAC3C,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;oBACvC,SAAS;gBACV,CAAC;gBACD,gGAAgG;gBAChG,MAAM,eAAe,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,cAAc,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;gBACpF,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,UAAU,EAAE,EAAE,CAAC;oBACrC,IAAI,eAAe,KAAK,SAAS,EAAE,CAAC;wBACnC,MAAM,IAAI,KAAK,CAAC,uDAAuD,CAAC,CAAC;oBAC1E,CAAC;gBACF,CAAC;qBAAM,CAAC;oBACP,WAAW,CAAC,eAAe,GAAG,eAAe,CAAC;oBAC9C,qGAAqG;oBACrG,cAAc,CAAC,WAAW,CAAC,CAAC,CAAC,cAAc;oBAC3C,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;gBACxC,CAAC;YACF,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBAChB,MAAM,mBAAmB,CAAC,kBAAkB,CAAC,KAAK,EAAE,gBAAgB,EAAE,WAAW,CAAC,CAAC;YACpF,CAAC;QACF,CAAC;IACF,CAAC;IAED;;;;;OAKG;IACK,8BAA8B,CAAC,gBAAgC;QACtE,gEAAgE;QAChE,MAAM,cAAc,GAAG,IAAI,CAAC,eAAe,CAAC,SAAS,EAAE,CAAC;QACxD,IAAI,cAAc,KAAK,SAAS,EAAE,CAAC;YAClC,OAAO,KAAK,CAAC;QACd,CAAC;QAED,kEAAkE;QAClE,0FAA0F;QAC1F,MAAM,cAAc,GAAG,mBAAmB,CAAC,cAAc,CAAC,CAAC;QAC3D,MAAM,cAAc,GAAG,mBAAmB,CAAC,gBAAgB,CAAC,CAAC;QAE7D,OAAO,cAAc,KAAK,cAAc,CAAC;IAC1C,CAAC;IAED;;;;;;;;;;OAUG;IACI,sBAAsB,CAC5B,OAA6B,EAC7B,KAAc;QAKd,IAAI,KAAK,EAAE,CAAC;YACX,OAAO,IAAI,CAAC,2BAA2B,CAAC,OAAO,CAAC,CAAC;QAClD,CAAC;QAED,iFAAiF;QACjF,iGAAiG;QACjG,IAAI,YAAY,IAAI,OAAO,IAAI,IAAI,CAAC,8BAA8B,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC;YACxF,MAAM,mBAAmB,CAAC,MAAM,CAC/B,mEAAmE,EACnE,4CAA4C,EAC5C,OAAO,CAAC,UAAU,CAAC,UAAU,CAC7B,CAAC;QACH,CAAC;QAED,yCAAyC;QACzC,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,KAAK,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;QACzF,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC;IACjD,CAAC;IAED;;;;;;OAMG;IACK,2BAA2B,CAAC,OAA6B;QAIhE,IAAI,YAAY,IAAI,OAAO,EAAE,CAAC;YAC7B,IAAI,CAAC,iBAAiB,CAAC,OAAO,CAAC,UAAU,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;QAC5D,CAAC;QAED,cAAc;QACd,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC1B,MAAM,eAAe,GAAG,IAAI,CAAC,yBAAyB,CACrD,OAAO,CAAC,UAAU,CAAC,UAAU,CAAC,cAAc,CAC5C,CAAC;YACF,MAAM,CACL,2BAA2B,CAAC,eAAe,CAAC,EAAE,UAAU,KAAK,IAAI,EACjE,KAAK,CAAC,iCAAiC,CACvC,CAAC;YACF,OAAO,EAAE,CAAC;QACX,CAAC;QAED,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,KAAK,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;QAEzF,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;YACjC,OAAO;YACP,eAAe,EAAE,IAAI,CAAC,yBAAyB,CAAC,OAAO,CAAC,cAAc,EAAE,OAAO,CAAC;SAChF,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;;;;;OAMG;IACK,yBAAyB,CAChC,cAAsB,EACtB,OAAiD;QAEjD,MAAM,cAAc,GAAG,IAAI,CAAC,eAAe,CAAC,SAAS,EAAE,CAAC;QACxD,MAAM,CACL,cAAc,KAAK,SAAS,EAC5B,KAAK,CAAC,wDAAwD,CAC9D,CAAC;QAEF,cAAc,CAAC,cAAc,GAAG,cAAc,CAAC;QAC/C,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,sBAAsB,CAAC,cAAc,CAAC,CAAC,CAAC;QAE3D,IAAI,CAAC,eAAe,CAAC,KAAK,EAAE,CAAC;QAE7B,gDAAgD;QAChD,wGAAwG;QACxG,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;YAC3B,MAAM,cAAc,GAAG,0BAA0B,CAAC,OAAO,CAAC,CAAC;YAE3D,mCAAmC;YACnC,IAAI,cAAc,CAAC,OAAO,KAAK,cAAc,EAAE,CAAC;gBAC/C,MAAM,iBAAiB,GAAG,IAAI,CAAC,KAAK,CACnC,cAAc,CAAC,OAAO,CACU,CAAC;gBAClC,MAAM,kBAAkB,GAAG,IAAI,CAAC,KAAK,CACpC,cAAc,CACoB,CAAC;gBAEpC,MAAM,aAAa,GAClB,iBAAiB,CAAC,QAAQ,KAAK,kBAAkB,CAAC,QAAQ;oBAC1D,CAAC,iBAAiB,CAAC,QAAQ,KAAK,SAAS;wBACxC,kBAAkB,CAAC,QAAQ,KAAK,SAAS;wBACzC,IAAI,CAAC,SAAS,CAAC,iBAAiB,CAAC,QAAQ,CAAC;4BACzC,IAAI,CAAC,SAAS,CAAC,kBAAkB,CAAC,QAAQ,CAAC,CAAC,CAAC;gBAEhD,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC;oBAC1B,SAAS,EAAE,uBAAuB;oBAClC,OAAO,EAAE;wBACR,sBAAsB,EAAE,iBAAiB,CAAC,iBAAiB,CAAC;wBAC5D,uBAAuB,EAAE,iBAAiB,CAAC,kBAAkB,CAAC;wBAC9D,aAAa;qBACb;iBACD,CAAC,CAAC;gBAEH,MAAM,mBAAmB,CAAC,MAAM,CAC/B,wCAAwC,EACxC,uBAAuB,EACvB,OAAO,CACP,CAAC;YACH,CAAC;QACF,CAAC;QAED,OAAO,cAAc,CAAC,eAAe,CAAC;IACvC,CAAC;IAED;;OAEG;IACK,iBAAiB,CAAC,UAA0B,EAAE,WAAoB;QACzE,wEAAwE;QACxE,MAAM,cAAc,GAAG,IAAI,CAAC,eAAe,CAAC,SAAS,EAAE,CAAC;QACxD,MAAM,CACL,cAAc,KAAK,SAAS,EAC5B,KAAK,CAAC,uEAAuE,CAC7E,CAAC;QAEF,mGAAmG;QACnG,gEAAgE;QAChE,kFAAkF;QAClF,2FAA2F;QAC3F,MAAM,YAAY,GAAG,UAAU,CAAC,UAAU,CAAC;QAC3C,iFAAiF;QACjF,MAAM,eAAe,GACpB,cAAc,CAAC,SAAS,CAAC,MAAM,KAAK,CAAC,CAAC,IAAI,WAAW,KAAK,SAAS,CAAC;QACrE,MAAM,0BAA0B,GAC/B,WAAW,KAAK,CAAC;YAChB,CAAC,CAAC,CAAC,CAAC,2EAA2E;YAC/E,CAAC,CAAC,WAAW,CAAC,CAAC,+CAA+C;QAEhE,+FAA+F;QAC/F,6EAA6E;QAC7E,+GAA+G;QAC/G,MAAM,cAAc,GAAG,eAAe,CAAC,cAAc,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC;QAC3E,MAAM,cAAc,GAAG,UAAU,CAAC,OAAO,CAAC;QAE1C,oGAAoG;QACpG,6GAA6G;QAC7G,6GAA6G;QAC7G,8GAA8G;QAC9G,IACC,cAAc,CAAC,SAAS,CAAC,aAAa,KAAK,UAAU,CAAC,aAAa;YACnE,CAAC,CAAC,eAAe,IAAI,cAAc,CAAC,SAAS,CAAC,MAAM,KAAK,0BAA0B,CAAC;YACpF,cAAc,KAAK,cAAc,EAChC,CAAC;YACF,IAAI,CAAC,MAAM,EAAE,cAAc,CAAC;gBAC3B,SAAS,EAAE,mBAAmB;gBAC9B,OAAO,EAAE;oBACR,eAAe,EAAE,cAAc,CAAC,SAAS,CAAC,aAAa;oBACvD,aAAa,EAAE,UAAU,CAAC,aAAa;oBACvC,kBAAkB,EAAE,cAAc,CAAC,SAAS,CAAC,MAAM;oBACnD,0BAA0B;oBAC1B,WAAW;oBACX,cAAc;oBACd,cAAc;oBACd,2BAA2B,EAAE,eAAe,CAAC,cAAc,CAAC,UAAU,CAAC,EAAE,KAAK;oBAC9E,oBAAoB,EAAE,eAAe,CAAC,YAAY,EAAE,QAAQ,CAAC,EAAE,KAAK;iBACpE;gBACD,cAAc,EAAE,YAAY,IAAI,gCAAgC,CAAC,YAAY,CAAC;aAC9E,CAAC,CAAC;QACJ,CAAC;IACF,CAAC;IAED;;;;OAIG;IACI,mBAAmB;QACzB,MAAM,CACL,IAAI,CAAC,YAAY,CAAC,SAAS,EAAE,EAC7B,KAAK,CAAC,+DAA+D,CACrE,CAAC;QAEF,4FAA4F;QAC5F,MAAM,CACL,IAAI,CAAC,sBAAsB,KAAK,IAAI,CAAC,YAAY,CAAC,QAAQ,EAAE,EAC5D,KAAK,CAAC,2DAA2D,CACjE,CAAC;QACF,IAAI,CAAC,sBAAsB,GAAG,IAAI,CAAC,YAAY,CAAC,QAAQ,EAAE,CAAC;QAE3D,MAAM,CACL,IAAI,CAAC,eAAe,CAAC,OAAO,EAAE,EAC9B,KAAK,CAAC,+DAA+D,CACrE,CAAC;QAEF,MAAM,2BAA2B,GAAG,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC;QAChE,IAAI,6BAA6B,GAAG,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC;QAEhE,8GAA8G;QAC9G,0GAA0G;QAC1G,8BAA8B;QAC9B,OAAO,6BAA6B,GAAG,CAAC,EAAE,CAAC;YAC1C,oEAAoE;YACpE,IAAI,cAAc,GAAG,IAAI,CAAC,eAAe,CAAC,KAAK,EAAG,CAAC;YACnD,6BAA6B,EAAE,CAAC;YAEhC,MAAM,iBAAiB,GAAG,eAAe,CAAC,cAAc,CAAC,UAAU,CAAC,EAAE,KAAK,CAAC;YAC5E,MAAM,CAAC,iBAAiB,KAAK,KAAK,EAAE,KAAK,CAAC,yCAAyC,CAAC,CAAC;YAErF,yFAAyF;YACzF,MAAM,OAAO,GAAG,mBAAmB,CAAC,cAAc,CAAC,CAAC;YAEpD,IAAI,2BAA2B,CAAC,cAAc,CAAC,eAAe,CAAC,EAAE,UAAU,KAAK,IAAI,EAAE,CAAC;gBACtF,qFAAqF;gBACrF,IAAI,CAAC,YAAY,CAAC,aAAa,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;gBAC7C,SAAS;YACV,CAAC;YAED;;;;eAIG;YACH,IAAI,iBAAiB,KAAK,SAAS,EAAE,CAAC;gBACrC,uBAAuB;gBAEvB,IAAI,CAAC,YAAY,CAAC,aAAa,CAC9B;oBACC;wBACC,OAAO,EAAE,cAAc,CAAC,OAAO;wBAC/B,eAAe,EAAE,cAAc,CAAC,eAAe;wBAC/C,UAAU,EAAE,cAAc,CAAC,UAAU;qBACrC;iBACD,EACD,OAAO,CACP,CAAC;gBACF,SAAS;YACV,CAAC;YACD,yEAAyE;YAEzE,MAAM,CACL,6BAA6B,GAAG,CAAC,EACjC,KAAK,CAAC,kDAAkD,CACxD,CAAC;YAEF,MAAM,KAAK,GAAiC,EAAE,CAAC;YAE/C,4DAA4D;YAC5D,OAAO,6BAA6B,IAAI,CAAC,EAAE,CAAC;gBAC3C,KAAK,CAAC,IAAI,CAAC;oBACV,OAAO,EAAE,cAAc,CAAC,OAAO;oBAC/B,eAAe,EAAE,cAAc,CAAC,eAAe;oBAC/C,UAAU,EAAE,cAAc,CAAC,UAAU;iBACrC,CAAC,CAAC;gBAEH,IAAI,cAAc,CAAC,UAAU,EAAE,KAAK,KAAK,KAAK,EAAE,CAAC;oBAChD,MAAM;gBACP,CAAC;gBACD,MAAM,CAAC,6BAA6B,GAAG,CAAC,EAAE,KAAK,CAAC,wBAAwB,CAAC,CAAC;gBAE1E,oEAAoE;gBACpE,cAAc,GAAG,IAAI,CAAC,eAAe,CAAC,KAAK,EAAG,CAAC;gBAC/C,6BAA6B,EAAE,CAAC;gBAChC,MAAM,CACL,cAAc,CAAC,UAAU,EAAE,KAAK,KAAK,IAAI,EACzC,KAAK,CAAC,iDAAiD,CACvD,CAAC;YACH,CAAC;YAED,IAAI,CAAC,YAAY,CAAC,aAAa,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;QACjD,CAAC;QAED,qFAAqF;QACrF,IAAI,CAAC,QAAQ,GAAG,EAAE,CAAC;QAEnB,+GAA+G;QAC/G,6GAA6G;QAC7G,mFAAmF;QACnF,IAAI,IAAI,CAAC,YAAY,CAAC,kBAAkB,EAAE,EAAE,CAAC;YAC5C,IAAI,CAAC,MAAM,EAAE,kBAAkB,CAAC;gBAC/B,SAAS,EAAE,uBAAuB;gBAClC,KAAK,EAAE,2BAA2B;gBAClC,QAAQ,EAAE,IAAI,CAAC,YAAY,CAAC,QAAQ,EAAE;aACtC,CAAC,CAAC;QACJ,CAAC;IACF,CAAC;CACD;AAED,6EAA6E;AAC7E,SAAS,cAAc,CACtB,OAAiC;IAEjC,MAAM,SAAS,GAA0C,OAAO,CAAC,SAAS,CAAC;IAC3E,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;QAC7B,gEAAgE;QAChE,OAAO,CAAC,SAAS,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,aAAa,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,EAAE,CAAC;IACzE,CAAC;AACF,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { IDisposable } from \"@fluidframework/core-interfaces\";\nimport { assert, Lazy } from \"@fluidframework/core-utils/internal\";\nimport {\n\tITelemetryLoggerExt,\n\tDataProcessingError,\n\tLoggingError,\n\textractSafePropertiesFromMessage,\n} from \"@fluidframework/telemetry-utils/internal\";\nimport Deque from \"double-ended-queue\";\nimport { v4 as uuid } from \"uuid\";\n\nimport {\n\ttype InboundContainerRuntimeMessage,\n\ttype InboundSequencedContainerRuntimeMessage,\n\ttype LocalContainerRuntimeMessage,\n} from \"./messageTypes.js\";\nimport { asBatchMetadata, asEmptyBatchLocalOpMetadata } from \"./metadata.js\";\nimport {\n\tBatchId,\n\tBatchMessage,\n\tgetEffectiveBatchId,\n\tBatchStartInfo,\n\tInboundMessageResult,\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\tcontent: string;\n\tlocalOpMetadata: unknown;\n\topMetadata: Record<string, unknown> | undefined;\n\tsequenceNumber?: number;\n\t/** Info about the batch this pending message belongs to, for validation and for computing the batchId on reconnect */\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/** length of the batch (how many runtime messages here) */\n\t\tlength: number;\n\t};\n}\n\ntype Patch<T, U> = U & Omit<T, keyof U>;\n\n/** First version of the type (pre-dates batchInfo) */\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/** Info needed to replay/resubmit a pending message */\nexport type PendingMessageResubmitData = Pick<\n\tIPendingMessage,\n\t\"content\" | \"localOpMetadata\" | \"opMetadata\"\n>;\n\nexport interface IRuntimeStateHandler {\n\tconnected(): boolean;\n\tclientId(): string | undefined;\n\tapplyStashedOp(content: string): Promise<unknown>;\n\treSubmitBatch(batch: PendingMessageResubmitData[], batchId: BatchId): void;\n\tisActiveConnection: () => boolean;\n\tisAttached: () => boolean;\n}\n\nfunction isEmptyBatchPendingMessage(message: IPendingMessageFromStash): boolean {\n\tconst content = JSON.parse(message.content);\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, compatDetails }: InboundContainerRuntimeMessage = message;\n\t// Any properties that are not defined, won't be emitted by stringify.\n\treturn JSON.stringify({ type, contents, compatDetails });\n}\n\nfunction typesOfKeys<T extends object>(obj: T): Record<keyof T, string> {\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 in (for contents)\n\t// or just use the value as-is (since it's not personal info)\n\tscrubbed.contents = message.contents && typesOfKeys(message.contents);\n\tscrubbed.compatDetails = message.compatDetails;\n\tscrubbed.type = message.type;\n\n\treturn JSON.stringify(scrubbed);\n}\n\nfunction withoutLocalOpMetadata(message: IPendingMessage): IPendingMessage {\n\treturn {\n\t\t...message,\n\t\tlocalOpMetadata: undefined,\n\t};\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/** Messages that will need to be resubmitted if not ack'd before the next reconnection */\n\tprivate readonly pendingMessages = new Deque<IPendingMessage>();\n\t/** Messages stashed from a previous container, now being rehydrated. Need to be resubmitted. */\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\tprivate readonly disposeOnce = new Lazy<void>(() => {\n\t\tthis.initialMessages.clear();\n\t\tthis.pendingMessages.clear();\n\t});\n\n\t/** Used to ensure we don't replay ops on the same connection twice */\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 * 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): IPendingLocalState {\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\tthis.pendingMessages.toArray().forEach((message) => {\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\tpendingStates: [\n\t\t\t\t...newSavedOps,\n\t\t\t\t...this.pendingMessages.toArray().map(withoutLocalOpMetadata),\n\t\t\t],\n\t\t};\n\t}\n\n\tconstructor(\n\t\tprivate readonly stateHandler: IRuntimeStateHandler,\n\t\tstashedLocalState: IPendingLocalState | undefined,\n\t\tprivate readonly logger: ITelemetryLoggerExt,\n\t) {\n\t\tif (stashedLocalState?.pendingStates) {\n\t\t\tthis.initialMessages.push(...stashedLocalState.pendingStates);\n\t\t}\n\t}\n\n\tpublic get disposed() {\n\t\treturn this.disposeOnce.evaluated;\n\t}\n\tpublic readonly dispose = () => this.disposeOnce.value;\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 */\n\tpublic onFlushBatch(batch: BatchMessage[], clientSequenceNumber: number | undefined) {\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\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\n\t\tfor (const message of batch) {\n\t\t\tconst {\n\t\t\t\tcontents: content = \"\",\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,\n\t\t\t\tlocalOpMetadata,\n\t\t\t\topMetadata,\n\t\t\t\t// Note: We only will read this off the first message, but put it on all for simplicity\n\t\t\t\tbatchInfo: { clientId, batchStartCsn, length: batch.length },\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) {\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 = { emptyBatch: true }; // 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\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} else {\n\t\t\t\t\tnextMessage.localOpMetadata = localOpMetadata;\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}\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\t// We may have no pending changes - if so, no match, no problem.\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(withoutLocalOpMetadata(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\tif (pendingMessage.content !== messageContent) {\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\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},\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) {\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\n\t\t// If this batch became empty on resubmit, batch.messages will be empty (so firstMessage undefined)\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: firstMessage && 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\tpublic replayPendingStates() {\n\t\tassert(\n\t\t\tthis.stateHandler.connected(),\n\t\t\t0x172 /* \"The connection state is not consistent with the runtime\" */,\n\t\t);\n\n\t\t// This assert suggests we are about to send same ops twice, which will result in data loss.\n\t\tassert(\n\t\t\tthis.clientIdFromLastReplay !== this.stateHandler.clientId(),\n\t\t\t0x173 /* \"replayPendingStates called twice for same clientId!\" */,\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\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\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\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);\n\t\t\t\tcontinue;\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 ScheduleManager 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\tcontent: pendingMessage.content,\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\tbatchId,\n\t\t\t\t);\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\tbatch.push({\n\t\t\t\t\tcontent: pendingMessage.content,\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\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);\n\t\t}\n\n\t\t// pending ops should no longer depend on previous sequenced local ops after resubmit\n\t\tthis.savedOps = [];\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\t}\n}\n\n/** For back-compat if trying to apply stashed ops that pre-date batchInfo */\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 };\n\t}\n}\n"]}
|
package/lib/scheduleManager.d.ts
CHANGED
|
@@ -21,10 +21,8 @@ export declare class ScheduleManager {
|
|
|
21
21
|
readonly getClientId: () => string | undefined;
|
|
22
22
|
private readonly logger;
|
|
23
23
|
private readonly deltaScheduler;
|
|
24
|
-
private batchClientId;
|
|
25
|
-
private hitError;
|
|
26
24
|
constructor(deltaManager: IDeltaManager<ISequencedDocumentMessage, IDocumentMessage>, emitter: EventEmitter, getClientId: () => string | undefined, logger: ITelemetryLoggerExt);
|
|
27
|
-
|
|
28
|
-
|
|
25
|
+
batchBegin(message: ISequencedDocumentMessage): void;
|
|
26
|
+
batchEnd(error: any | undefined, message: ISequencedDocumentMessage): void;
|
|
29
27
|
}
|
|
30
28
|
//# sourceMappingURL=scheduleManager.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scheduleManager.d.ts","sourceRoot":"","sources":["../src/scheduleManager.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAEjE,OAAO,EAAE,aAAa,EAAE,MAAM,gDAAgD,CAAC;AAE/E,OAAO,EACN,gBAAgB,EAChB,yBAAyB,EACzB,MAAM,6CAA6C,CAAC;AAErD,OAAO,EACN,mBAAmB,EAKnB,MAAM,0CAA0C,CAAC;AAYlD;;;;;;;;GAQG;AACH,qBAAa,eAAe;
|
|
1
|
+
{"version":3,"file":"scheduleManager.d.ts","sourceRoot":"","sources":["../src/scheduleManager.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAEjE,OAAO,EAAE,aAAa,EAAE,MAAM,gDAAgD,CAAC;AAE/E,OAAO,EACN,gBAAgB,EAChB,yBAAyB,EACzB,MAAM,6CAA6C,CAAC;AAErD,OAAO,EACN,mBAAmB,EAKnB,MAAM,0CAA0C,CAAC;AAYlD;;;;;;;;GAQG;AACH,qBAAa,eAAe;IAI1B,OAAO,CAAC,QAAQ,CAAC,YAAY;IAC7B,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,QAAQ,CAAC,WAAW,EAAE,MAAM,MAAM,GAAG,SAAS;IAC9C,OAAO,CAAC,QAAQ,CAAC,MAAM;IANxB,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAiB;gBAG9B,YAAY,EAAE,aAAa,CAAC,yBAAyB,EAAE,gBAAgB,CAAC,EACxE,OAAO,EAAE,YAAY,EAC7B,WAAW,EAAE,MAAM,MAAM,GAAG,SAAS,EAC7B,MAAM,EAAE,mBAAmB;IAStC,UAAU,CAAC,OAAO,EAAE,yBAAyB;IAK7C,QAAQ,CAAC,KAAK,EAAE,GAAG,GAAG,SAAS,EAAE,OAAO,EAAE,yBAAyB;CAI1E"}
|
package/lib/scheduleManager.js
CHANGED
|
@@ -23,43 +23,16 @@ export class ScheduleManager {
|
|
|
23
23
|
this.emitter = emitter;
|
|
24
24
|
this.getClientId = getClientId;
|
|
25
25
|
this.logger = logger;
|
|
26
|
-
this.hitError = false;
|
|
27
26
|
this.deltaScheduler = new DeltaScheduler(this.deltaManager, createChildLogger({ logger: this.logger, namespace: "DeltaScheduler" }));
|
|
28
27
|
void new ScheduleManagerCore(deltaManager, getClientId, logger);
|
|
29
28
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
// This could be the beginning of a new batch or an individual message.
|
|
34
|
-
this.emitter.emit("batchBegin", message);
|
|
35
|
-
this.deltaScheduler.batchBegin(message);
|
|
36
|
-
const batch = message?.metadata?.batch;
|
|
37
|
-
// TODO: Verify whether this should be able to handle server-generated ops (with null clientId)
|
|
38
|
-
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
|
39
|
-
this.batchClientId = batch ? message.clientId : undefined;
|
|
40
|
-
}
|
|
29
|
+
batchBegin(message) {
|
|
30
|
+
this.emitter.emit("batchBegin", message);
|
|
31
|
+
this.deltaScheduler.batchBegin(message);
|
|
41
32
|
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
if (error) {
|
|
46
|
-
// We assume here that loader will close container and stop processing all future ops.
|
|
47
|
-
// This is implicit dependency. If this flow changes, this code might no longer be correct.
|
|
48
|
-
this.hitError = true;
|
|
49
|
-
this.batchClientId = undefined;
|
|
50
|
-
this.emitter.emit("batchEnd", error, message);
|
|
51
|
-
this.deltaScheduler.batchEnd(message);
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
54
|
-
const batch = message?.metadata?.batch;
|
|
55
|
-
// If no batchClientId has been set then we're in an individual batch. Else, if we get
|
|
56
|
-
// batch end metadata, this is end of the current batch.
|
|
57
|
-
if (this.batchClientId === undefined || batch === false) {
|
|
58
|
-
this.batchClientId = undefined;
|
|
59
|
-
this.emitter.emit("batchEnd", undefined, message);
|
|
60
|
-
this.deltaScheduler.batchEnd(message);
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
33
|
+
batchEnd(error, message) {
|
|
34
|
+
this.emitter.emit("batchEnd", error, message);
|
|
35
|
+
this.deltaScheduler.batchEnd(message);
|
|
63
36
|
}
|
|
64
37
|
}
|
|
65
38
|
/**
|
|
@@ -80,8 +53,6 @@ class ScheduleManagerCore {
|
|
|
80
53
|
return;
|
|
81
54
|
}
|
|
82
55
|
// First message will have the batch flag set to true if doing a batched send
|
|
83
|
-
// Non null asserting because of the length check above
|
|
84
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
85
56
|
const firstMessageMetadata = messages[0].metadata;
|
|
86
57
|
if (!firstMessageMetadata?.batch) {
|
|
87
58
|
return;
|
|
@@ -92,8 +63,6 @@ class ScheduleManagerCore {
|
|
|
92
63
|
return;
|
|
93
64
|
}
|
|
94
65
|
// Set the batch flag to false on the last message to indicate the end of the send batch
|
|
95
|
-
// Non null asserting here because of the length check at the start of the function
|
|
96
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
97
66
|
const lastMessage = messages[messages.length - 1];
|
|
98
67
|
// TODO: It's not clear if this shallow clone is required, as opposed to just setting "batch" to false.
|
|
99
68
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scheduleManager.js","sourceRoot":"","sources":["../src/scheduleManager.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport type { EventEmitter } from \"@fluid-internal/client-utils\";\nimport { performance } from \"@fluid-internal/client-utils\";\nimport { IDeltaManager } from \"@fluidframework/container-definitions/internal\";\nimport { assert } from \"@fluidframework/core-utils/internal\";\nimport {\n\tIDocumentMessage,\n\tISequencedDocumentMessage,\n} from \"@fluidframework/driver-definitions/internal\";\nimport { isRuntimeMessage } from \"@fluidframework/driver-utils/internal\";\nimport {\n\tITelemetryLoggerExt,\n\tDataCorruptionError,\n\tDataProcessingError,\n\tcreateChildLogger,\n\textractSafePropertiesFromMessage,\n} from \"@fluidframework/telemetry-utils/internal\";\n\nimport { DeltaScheduler } from \"./deltaScheduler.js\";\nimport { IBatchMetadata } from \"./metadata.js\";\nimport { pkgVersion } from \"./packageVersion.js\";\n\ntype IRuntimeMessageMetadata =\n\t| undefined\n\t| {\n\t\t\tbatch?: boolean;\n\t };\n\n/**\n * This class has the following responsibilities:\n *\n * 1. It tracks batches as we process ops and raises \"batchBegin\" and \"batchEnd\" events.\n * As part of it, it validates batch correctness (i.e. no system ops in the middle of batch)\n *\n * 2. It creates instance of ScheduleManagerCore that ensures we never start processing ops from batch\n * unless all ops of the batch are in.\n */\nexport class ScheduleManager {\n\tprivate readonly deltaScheduler: DeltaScheduler;\n\tprivate batchClientId: string | undefined;\n\tprivate hitError = false;\n\n\tconstructor(\n\t\tprivate readonly deltaManager: IDeltaManager<ISequencedDocumentMessage, IDocumentMessage>,\n\t\tprivate readonly emitter: EventEmitter,\n\t\treadonly getClientId: () => string | undefined,\n\t\tprivate readonly logger: ITelemetryLoggerExt,\n\t) {\n\t\tthis.deltaScheduler = new DeltaScheduler(\n\t\t\tthis.deltaManager,\n\t\t\tcreateChildLogger({ logger: this.logger, namespace: \"DeltaScheduler\" }),\n\t\t);\n\t\tvoid new ScheduleManagerCore(deltaManager, getClientId, logger);\n\t}\n\n\tpublic beforeOpProcessing(message: ISequencedDocumentMessage) {\n\t\tif (this.batchClientId !== message.clientId) {\n\t\t\tassert(\n\t\t\t\tthis.batchClientId === undefined,\n\t\t\t\t0x2a2 /* \"Batch is interrupted by other client op. Should be caught by trackPending()\" */,\n\t\t\t);\n\n\t\t\t// This could be the beginning of a new batch or an individual message.\n\t\t\tthis.emitter.emit(\"batchBegin\", message);\n\t\t\tthis.deltaScheduler.batchBegin(message);\n\n\t\t\tconst batch = (message?.metadata as IRuntimeMessageMetadata)?.batch;\n\t\t\t// TODO: Verify whether this should be able to handle server-generated ops (with null clientId)\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion\n\t\t\tthis.batchClientId = batch ? (message.clientId as string) : undefined;\n\t\t}\n\t}\n\n\tpublic afterOpProcessing(error: any | undefined, message: ISequencedDocumentMessage) {\n\t\t// If this is no longer true, we need to revisit what we do where we set this.hitError.\n\t\tassert(!this.hitError, 0x2a3 /* \"container should be closed on any error\" */);\n\n\t\tif (error) {\n\t\t\t// We assume here that loader will close container and stop processing all future ops.\n\t\t\t// This is implicit dependency. If this flow changes, this code might no longer be correct.\n\t\t\tthis.hitError = true;\n\t\t\tthis.batchClientId = undefined;\n\t\t\tthis.emitter.emit(\"batchEnd\", error, message);\n\t\t\tthis.deltaScheduler.batchEnd(message);\n\t\t\treturn;\n\t\t}\n\n\t\tconst batch = (message?.metadata as IRuntimeMessageMetadata)?.batch;\n\t\t// If no batchClientId has been set then we're in an individual batch. Else, if we get\n\t\t// batch end metadata, this is end of the current batch.\n\t\tif (this.batchClientId === undefined || batch === false) {\n\t\t\tthis.batchClientId = undefined;\n\t\t\tthis.emitter.emit(\"batchEnd\", undefined, message);\n\t\t\tthis.deltaScheduler.batchEnd(message);\n\t\t\treturn;\n\t\t}\n\t}\n}\n\n/**\n * This class controls pausing and resuming of inbound queue to ensure that we never\n * start processing ops in a batch IF we do not have all ops in the batch.\n */\nclass ScheduleManagerCore {\n\tprivate pauseSequenceNumber: number | undefined;\n\tprivate currentBatchClientId: string | undefined;\n\tprivate localPaused = false;\n\tprivate timePaused = 0;\n\tprivate batchCount = 0;\n\n\tconstructor(\n\t\tprivate readonly deltaManager: IDeltaManager<ISequencedDocumentMessage, IDocumentMessage>,\n\t\tprivate readonly getClientId: () => string | undefined,\n\t\tprivate readonly logger: ITelemetryLoggerExt,\n\t) {\n\t\t// Listen for delta manager sends and add batch metadata to messages\n\t\tthis.deltaManager.on(\"prepareSend\", (messages: IDocumentMessage[]) => {\n\t\t\tif (messages.length === 0) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// First message will have the batch flag set to true if doing a batched send\n\t\t\t// Non null asserting because of the length check above\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n\t\t\tconst firstMessageMetadata = messages[0]!.metadata as IRuntimeMessageMetadata;\n\t\t\tif (!firstMessageMetadata?.batch) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// If the batch contains only a single op, clear the batch flag.\n\t\t\tif (messages.length === 1) {\n\t\t\t\tdelete firstMessageMetadata.batch;\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Set the batch flag to false on the last message to indicate the end of the send batch\n\t\t\t// Non null asserting here because of the length check at the start of the function\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n\t\t\tconst lastMessage = messages[messages.length - 1]!;\n\t\t\t// TODO: It's not clear if this shallow clone is required, as opposed to just setting \"batch\" to false.\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion\n\t\t\tlastMessage.metadata = { ...(lastMessage.metadata as any), batch: false };\n\t\t});\n\n\t\t// Listen for updates and peek at the inbound\n\t\tthis.deltaManager.inbound.on(\"push\", (message: ISequencedDocumentMessage) => {\n\t\t\tthis.trackPending(message);\n\t\t});\n\n\t\t// Start with baseline - empty inbound queue.\n\t\tassert(!this.localPaused, 0x293 /* \"initial state\" */);\n\n\t\tconst allPending = this.deltaManager.inbound.toArray();\n\t\tfor (const pending of allPending) {\n\t\t\tthis.trackPending(pending);\n\t\t}\n\n\t\t// We are intentionally directly listening to the \"op\" to inspect system ops as well.\n\t\t// If we do not observe system ops, we are likely to hit 0x296 assert when system ops\n\t\t// precedes start of incomplete batch.\n\t\tthis.deltaManager.on(\"op\", (message) => this.afterOpProcessing(message));\n\t}\n\n\t/**\n\t * The only public function in this class - called when we processed an op,\n\t * to make decision if op processing should be paused or not after that.\n\t */\n\tpublic afterOpProcessing(message: ISequencedDocumentMessage) {\n\t\tassert(\n\t\t\t!this.localPaused,\n\t\t\t0x294 /* \"can't have op processing paused if we are processing an op\" */,\n\t\t);\n\n\t\t// If the inbound queue is ever empty, nothing to do!\n\t\tif (this.deltaManager.inbound.length === 0) {\n\t\t\tassert(\n\t\t\t\tthis.pauseSequenceNumber === undefined,\n\t\t\t\t0x295 /* \"there should be no pending batch if we have no ops\" */,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\t// The queue is\n\t\t// 1. paused only when the next message to be processed is the beginning of a batch. Done in two places:\n\t\t// - here (processing ops until reaching start of incomplete batch)\n\t\t// - in trackPending(), when queue was empty and start of batch showed up.\n\t\t// 2. resumed when batch end comes in (in trackPending())\n\n\t\t// do we have incomplete batch to worry about?\n\t\tif (this.pauseSequenceNumber !== undefined) {\n\t\t\tif (message.sequenceNumber >= this.pauseSequenceNumber) {\n\t\t\t\tthrow DataProcessingError.create(\n\t\t\t\t\t// Former assert 0x296\n\t\t\t\t\t\"Incomplete batch\",\n\t\t\t\t\t\"ScheduleManager\",\n\t\t\t\t\tmessage,\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: message.type,\n\t\t\t\t\t\tcontentType: typeof message.contents,\n\t\t\t\t\t\tbatch: (message.metadata as IBatchMetadata | undefined)?.batch,\n\t\t\t\t\t\tcompression: message.compression,\n\t\t\t\t\t\tpauseSeqNum: this.pauseSequenceNumber,\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// If the next op is the start of incomplete batch, then we can't process it until it's fully in - pause!\n\t\t\tif (message.sequenceNumber + 1 === this.pauseSequenceNumber) {\n\t\t\t\tthis.pauseQueue();\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate pauseQueue() {\n\t\tassert(!this.localPaused, 0x297 /* \"always called from resumed state\" */);\n\t\tthis.localPaused = true;\n\t\tthis.timePaused = performance.now();\n\t\t// eslint-disable-next-line @typescript-eslint/no-floating-promises\n\t\tthis.deltaManager.inbound.pause();\n\t}\n\n\tprivate resumeQueue(startBatch: number, messageEndBatch: ISequencedDocumentMessage) {\n\t\tconst endBatch = messageEndBatch.sequenceNumber;\n\t\tconst duration = this.localPaused ? performance.now() - this.timePaused : undefined;\n\n\t\tthis.batchCount++;\n\t\tif (this.batchCount % 1000 === 1) {\n\t\t\tthis.logger.sendTelemetryEvent({\n\t\t\t\teventName: \"BatchStats\",\n\t\t\t\tsequenceNumber: endBatch,\n\t\t\t\tlength: endBatch - startBatch + 1,\n\t\t\t\tmsnDistance: endBatch - messageEndBatch.minimumSequenceNumber,\n\t\t\t\tduration,\n\t\t\t\tbatchCount: this.batchCount,\n\t\t\t\tinterrupted: this.localPaused,\n\t\t\t});\n\t\t}\n\n\t\t// Return early if no change in value\n\t\tif (!this.localPaused) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis.localPaused = false;\n\n\t\tthis.deltaManager.inbound.resume();\n\t}\n\n\t/**\n\t * Called for each incoming op (i.e. inbound \"push\" notification)\n\t */\n\tprivate trackPending(message: ISequencedDocumentMessage) {\n\t\tassert(\n\t\t\tthis.deltaManager.inbound.length !== 0,\n\t\t\t0x298 /* \"we have something in the queue that generates this event\" */,\n\t\t);\n\n\t\tassert(\n\t\t\t(this.currentBatchClientId === undefined) === (this.pauseSequenceNumber === undefined),\n\t\t\t0x299 /* \"non-synchronized state\" */,\n\t\t);\n\n\t\tconst metadata = message.metadata as IRuntimeMessageMetadata;\n\t\t// batchMetadata will be true for the message that starts a batch, false for the one that ends it, and\n\t\t// undefined for all other messages.\n\t\tconst batchMetadata = metadata?.batch;\n\n\t\t// Protocol messages are never part of a runtime batch of messages\n\t\tif (!isRuntimeMessage(message)) {\n\t\t\t// Protocol messages should never show up in the middle of the batch!\n\t\t\tif (this.currentBatchClientId !== undefined) {\n\t\t\t\tthrow DataProcessingError.create(\n\t\t\t\t\t\"Received a system message during batch processing\", // Formerly known as assert 0x29a\n\t\t\t\t\t\"trackPending\",\n\t\t\t\t\tmessage,\n\t\t\t\t\t{\n\t\t\t\t\t\truntimeVersion: pkgVersion,\n\t\t\t\t\t\tbatchClientId:\n\t\t\t\t\t\t\t// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing\n\t\t\t\t\t\t\tthis.currentBatchClientId === null ? \"null\" : this.currentBatchClientId,\n\t\t\t\t\t\tpauseSequenceNumber: this.pauseSequenceNumber,\n\t\t\t\t\t\tlocalBatch: this.currentBatchClientId === this.getClientId(),\n\t\t\t\t\t\tmessageType: message.type,\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tassert(batchMetadata === undefined, 0x29b /* \"system op in a batch?\" */);\n\t\t\tassert(\n\t\t\t\t!this.localPaused,\n\t\t\t\t0x29c /* \"we should be processing ops when there is no active batch\" */,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\tif (this.currentBatchClientId === undefined && batchMetadata === undefined) {\n\t\t\tassert(\n\t\t\t\t!this.localPaused,\n\t\t\t\t0x29d /* \"we should be processing ops when there is no active batch\" */,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\t// If we got here, the message is part of a batch. Either starting, in progress, or ending.\n\n\t\t// If this is not the start of the batch, error out if the message was sent by a client other than the one that\n\t\t// started the current batch (it should not be possible for ops from other clients to get interleaved with a batch).\n\t\tif (\n\t\t\tthis.currentBatchClientId !== undefined &&\n\t\t\tthis.currentBatchClientId !== message.clientId\n\t\t) {\n\t\t\tthrow new DataCorruptionError(\"OpBatchIncomplete\", {\n\t\t\t\truntimeVersion: pkgVersion,\n\t\t\t\tbatchClientId:\n\t\t\t\t\t// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing\n\t\t\t\t\tthis.currentBatchClientId === null ? \"null\" : this.currentBatchClientId,\n\t\t\t\tpauseSequenceNumber: this.pauseSequenceNumber,\n\t\t\t\tlocalBatch: this.currentBatchClientId === this.getClientId(),\n\t\t\t\tlocalMessage: message.clientId === this.getClientId(),\n\t\t\t\t...extractSafePropertiesFromMessage(message),\n\t\t\t});\n\t\t}\n\n\t\t// The queue is\n\t\t// 1. paused only when the next message to be processed is the beginning of a batch. Done in two places:\n\t\t// - in afterOpProcessing() - processing ops until reaching start of incomplete batch\n\t\t// - here, when queue was empty and start of batch showed up (batchMetadata === true below).\n\t\t// 2. resumed when batch end comes in (batchMetadata === false below)\n\n\t\tif (batchMetadata) {\n\t\t\tassert(\n\t\t\t\tthis.currentBatchClientId === undefined,\n\t\t\t\t0x29e /* \"there can't be active batch\" */,\n\t\t\t);\n\t\t\tassert(\n\t\t\t\t!this.localPaused,\n\t\t\t\t0x29f /* \"we should be processing ops when there is no active batch\" */,\n\t\t\t);\n\t\t\tthis.pauseSequenceNumber = message.sequenceNumber;\n\t\t\t// TODO: Verify whether this should be able to handle server-generated ops (with null clientId)\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion\n\t\t\tthis.currentBatchClientId = message.clientId as string;\n\t\t\t// Start of the batch\n\t\t\t// Only pause processing if queue has no other ops!\n\t\t\t// If there are any other ops in the queue, processing will be stopped when they are processed!\n\t\t\tif (this.deltaManager.inbound.length === 1) {\n\t\t\t\tthis.pauseQueue();\n\t\t\t}\n\t\t} else if (batchMetadata === false) {\n\t\t\tassert(\n\t\t\t\tthis.pauseSequenceNumber !== undefined,\n\t\t\t\t0x2a0 /* \"batch presence was validated above\" */,\n\t\t\t);\n\t\t\t// Batch is complete, we can process it!\n\t\t\tthis.resumeQueue(this.pauseSequenceNumber, message);\n\t\t\tthis.pauseSequenceNumber = undefined;\n\t\t\tthis.currentBatchClientId = undefined;\n\t\t} else {\n\t\t\t// Continuation of current batch. Do nothing\n\t\t\tassert(this.currentBatchClientId !== undefined, 0x2a1 /* \"logic error\" */);\n\t\t}\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"scheduleManager.js","sourceRoot":"","sources":["../src/scheduleManager.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport type { EventEmitter } from \"@fluid-internal/client-utils\";\nimport { performance } from \"@fluid-internal/client-utils\";\nimport { IDeltaManager } from \"@fluidframework/container-definitions/internal\";\nimport { assert } from \"@fluidframework/core-utils/internal\";\nimport {\n\tIDocumentMessage,\n\tISequencedDocumentMessage,\n} from \"@fluidframework/driver-definitions/internal\";\nimport { isRuntimeMessage } from \"@fluidframework/driver-utils/internal\";\nimport {\n\tITelemetryLoggerExt,\n\tDataCorruptionError,\n\tDataProcessingError,\n\tcreateChildLogger,\n\textractSafePropertiesFromMessage,\n} from \"@fluidframework/telemetry-utils/internal\";\n\nimport { DeltaScheduler } from \"./deltaScheduler.js\";\nimport { IBatchMetadata } from \"./metadata.js\";\nimport { pkgVersion } from \"./packageVersion.js\";\n\ntype IRuntimeMessageMetadata =\n\t| undefined\n\t| {\n\t\t\tbatch?: boolean;\n\t };\n\n/**\n * This class has the following responsibilities:\n *\n * 1. It tracks batches as we process ops and raises \"batchBegin\" and \"batchEnd\" events.\n * As part of it, it validates batch correctness (i.e. no system ops in the middle of batch)\n *\n * 2. It creates instance of ScheduleManagerCore that ensures we never start processing ops from batch\n * unless all ops of the batch are in.\n */\nexport class ScheduleManager {\n\tprivate readonly deltaScheduler: DeltaScheduler;\n\n\tconstructor(\n\t\tprivate readonly deltaManager: IDeltaManager<ISequencedDocumentMessage, IDocumentMessage>,\n\t\tprivate readonly emitter: EventEmitter,\n\t\treadonly getClientId: () => string | undefined,\n\t\tprivate readonly logger: ITelemetryLoggerExt,\n\t) {\n\t\tthis.deltaScheduler = new DeltaScheduler(\n\t\t\tthis.deltaManager,\n\t\t\tcreateChildLogger({ logger: this.logger, namespace: \"DeltaScheduler\" }),\n\t\t);\n\t\tvoid new ScheduleManagerCore(deltaManager, getClientId, logger);\n\t}\n\n\tpublic batchBegin(message: ISequencedDocumentMessage) {\n\t\tthis.emitter.emit(\"batchBegin\", message);\n\t\tthis.deltaScheduler.batchBegin(message);\n\t}\n\n\tpublic batchEnd(error: any | undefined, message: ISequencedDocumentMessage) {\n\t\tthis.emitter.emit(\"batchEnd\", error, message);\n\t\tthis.deltaScheduler.batchEnd(message);\n\t}\n}\n\n/**\n * This class controls pausing and resuming of inbound queue to ensure that we never\n * start processing ops in a batch IF we do not have all ops in the batch.\n */\nclass ScheduleManagerCore {\n\tprivate pauseSequenceNumber: number | undefined;\n\tprivate currentBatchClientId: string | undefined;\n\tprivate localPaused = false;\n\tprivate timePaused = 0;\n\tprivate batchCount = 0;\n\n\tconstructor(\n\t\tprivate readonly deltaManager: IDeltaManager<ISequencedDocumentMessage, IDocumentMessage>,\n\t\tprivate readonly getClientId: () => string | undefined,\n\t\tprivate readonly logger: ITelemetryLoggerExt,\n\t) {\n\t\t// Listen for delta manager sends and add batch metadata to messages\n\t\tthis.deltaManager.on(\"prepareSend\", (messages: IDocumentMessage[]) => {\n\t\t\tif (messages.length === 0) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// First message will have the batch flag set to true if doing a batched send\n\t\t\tconst firstMessageMetadata = messages[0].metadata as IRuntimeMessageMetadata;\n\t\t\tif (!firstMessageMetadata?.batch) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// If the batch contains only a single op, clear the batch flag.\n\t\t\tif (messages.length === 1) {\n\t\t\t\tdelete firstMessageMetadata.batch;\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Set the batch flag to false on the last message to indicate the end of the send batch\n\t\t\tconst lastMessage = messages[messages.length - 1];\n\t\t\t// TODO: It's not clear if this shallow clone is required, as opposed to just setting \"batch\" to false.\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion\n\t\t\tlastMessage.metadata = { ...(lastMessage.metadata as any), batch: false };\n\t\t});\n\n\t\t// Listen for updates and peek at the inbound\n\t\tthis.deltaManager.inbound.on(\"push\", (message: ISequencedDocumentMessage) => {\n\t\t\tthis.trackPending(message);\n\t\t});\n\n\t\t// Start with baseline - empty inbound queue.\n\t\tassert(!this.localPaused, 0x293 /* \"initial state\" */);\n\n\t\tconst allPending = this.deltaManager.inbound.toArray();\n\t\tfor (const pending of allPending) {\n\t\t\tthis.trackPending(pending);\n\t\t}\n\n\t\t// We are intentionally directly listening to the \"op\" to inspect system ops as well.\n\t\t// If we do not observe system ops, we are likely to hit 0x296 assert when system ops\n\t\t// precedes start of incomplete batch.\n\t\tthis.deltaManager.on(\"op\", (message) => this.afterOpProcessing(message));\n\t}\n\n\t/**\n\t * The only public function in this class - called when we processed an op,\n\t * to make decision if op processing should be paused or not after that.\n\t */\n\tpublic afterOpProcessing(message: ISequencedDocumentMessage) {\n\t\tassert(\n\t\t\t!this.localPaused,\n\t\t\t0x294 /* \"can't have op processing paused if we are processing an op\" */,\n\t\t);\n\n\t\t// If the inbound queue is ever empty, nothing to do!\n\t\tif (this.deltaManager.inbound.length === 0) {\n\t\t\tassert(\n\t\t\t\tthis.pauseSequenceNumber === undefined,\n\t\t\t\t0x295 /* \"there should be no pending batch if we have no ops\" */,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\t// The queue is\n\t\t// 1. paused only when the next message to be processed is the beginning of a batch. Done in two places:\n\t\t// - here (processing ops until reaching start of incomplete batch)\n\t\t// - in trackPending(), when queue was empty and start of batch showed up.\n\t\t// 2. resumed when batch end comes in (in trackPending())\n\n\t\t// do we have incomplete batch to worry about?\n\t\tif (this.pauseSequenceNumber !== undefined) {\n\t\t\tif (message.sequenceNumber >= this.pauseSequenceNumber) {\n\t\t\t\tthrow DataProcessingError.create(\n\t\t\t\t\t// Former assert 0x296\n\t\t\t\t\t\"Incomplete batch\",\n\t\t\t\t\t\"ScheduleManager\",\n\t\t\t\t\tmessage,\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: message.type,\n\t\t\t\t\t\tcontentType: typeof message.contents,\n\t\t\t\t\t\tbatch: (message.metadata as IBatchMetadata | undefined)?.batch,\n\t\t\t\t\t\tcompression: message.compression,\n\t\t\t\t\t\tpauseSeqNum: this.pauseSequenceNumber,\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// If the next op is the start of incomplete batch, then we can't process it until it's fully in - pause!\n\t\t\tif (message.sequenceNumber + 1 === this.pauseSequenceNumber) {\n\t\t\t\tthis.pauseQueue();\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate pauseQueue() {\n\t\tassert(!this.localPaused, 0x297 /* \"always called from resumed state\" */);\n\t\tthis.localPaused = true;\n\t\tthis.timePaused = performance.now();\n\t\t// eslint-disable-next-line @typescript-eslint/no-floating-promises\n\t\tthis.deltaManager.inbound.pause();\n\t}\n\n\tprivate resumeQueue(startBatch: number, messageEndBatch: ISequencedDocumentMessage) {\n\t\tconst endBatch = messageEndBatch.sequenceNumber;\n\t\tconst duration = this.localPaused ? performance.now() - this.timePaused : undefined;\n\n\t\tthis.batchCount++;\n\t\tif (this.batchCount % 1000 === 1) {\n\t\t\tthis.logger.sendTelemetryEvent({\n\t\t\t\teventName: \"BatchStats\",\n\t\t\t\tsequenceNumber: endBatch,\n\t\t\t\tlength: endBatch - startBatch + 1,\n\t\t\t\tmsnDistance: endBatch - messageEndBatch.minimumSequenceNumber,\n\t\t\t\tduration,\n\t\t\t\tbatchCount: this.batchCount,\n\t\t\t\tinterrupted: this.localPaused,\n\t\t\t});\n\t\t}\n\n\t\t// Return early if no change in value\n\t\tif (!this.localPaused) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis.localPaused = false;\n\n\t\tthis.deltaManager.inbound.resume();\n\t}\n\n\t/**\n\t * Called for each incoming op (i.e. inbound \"push\" notification)\n\t */\n\tprivate trackPending(message: ISequencedDocumentMessage) {\n\t\tassert(\n\t\t\tthis.deltaManager.inbound.length !== 0,\n\t\t\t0x298 /* \"we have something in the queue that generates this event\" */,\n\t\t);\n\n\t\tassert(\n\t\t\t(this.currentBatchClientId === undefined) === (this.pauseSequenceNumber === undefined),\n\t\t\t0x299 /* \"non-synchronized state\" */,\n\t\t);\n\n\t\tconst metadata = message.metadata as IRuntimeMessageMetadata;\n\t\t// batchMetadata will be true for the message that starts a batch, false for the one that ends it, and\n\t\t// undefined for all other messages.\n\t\tconst batchMetadata = metadata?.batch;\n\n\t\t// Protocol messages are never part of a runtime batch of messages\n\t\tif (!isRuntimeMessage(message)) {\n\t\t\t// Protocol messages should never show up in the middle of the batch!\n\t\t\tif (this.currentBatchClientId !== undefined) {\n\t\t\t\tthrow DataProcessingError.create(\n\t\t\t\t\t\"Received a system message during batch processing\", // Formerly known as assert 0x29a\n\t\t\t\t\t\"trackPending\",\n\t\t\t\t\tmessage,\n\t\t\t\t\t{\n\t\t\t\t\t\truntimeVersion: pkgVersion,\n\t\t\t\t\t\tbatchClientId:\n\t\t\t\t\t\t\t// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing\n\t\t\t\t\t\t\tthis.currentBatchClientId === null ? \"null\" : this.currentBatchClientId,\n\t\t\t\t\t\tpauseSequenceNumber: this.pauseSequenceNumber,\n\t\t\t\t\t\tlocalBatch: this.currentBatchClientId === this.getClientId(),\n\t\t\t\t\t\tmessageType: message.type,\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tassert(batchMetadata === undefined, 0x29b /* \"system op in a batch?\" */);\n\t\t\tassert(\n\t\t\t\t!this.localPaused,\n\t\t\t\t0x29c /* \"we should be processing ops when there is no active batch\" */,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\tif (this.currentBatchClientId === undefined && batchMetadata === undefined) {\n\t\t\tassert(\n\t\t\t\t!this.localPaused,\n\t\t\t\t0x29d /* \"we should be processing ops when there is no active batch\" */,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\t// If we got here, the message is part of a batch. Either starting, in progress, or ending.\n\n\t\t// If this is not the start of the batch, error out if the message was sent by a client other than the one that\n\t\t// started the current batch (it should not be possible for ops from other clients to get interleaved with a batch).\n\t\tif (\n\t\t\tthis.currentBatchClientId !== undefined &&\n\t\t\tthis.currentBatchClientId !== message.clientId\n\t\t) {\n\t\t\tthrow new DataCorruptionError(\"OpBatchIncomplete\", {\n\t\t\t\truntimeVersion: pkgVersion,\n\t\t\t\tbatchClientId:\n\t\t\t\t\t// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing\n\t\t\t\t\tthis.currentBatchClientId === null ? \"null\" : this.currentBatchClientId,\n\t\t\t\tpauseSequenceNumber: this.pauseSequenceNumber,\n\t\t\t\tlocalBatch: this.currentBatchClientId === this.getClientId(),\n\t\t\t\tlocalMessage: message.clientId === this.getClientId(),\n\t\t\t\t...extractSafePropertiesFromMessage(message),\n\t\t\t});\n\t\t}\n\n\t\t// The queue is\n\t\t// 1. paused only when the next message to be processed is the beginning of a batch. Done in two places:\n\t\t// - in afterOpProcessing() - processing ops until reaching start of incomplete batch\n\t\t// - here, when queue was empty and start of batch showed up (batchMetadata === true below).\n\t\t// 2. resumed when batch end comes in (batchMetadata === false below)\n\n\t\tif (batchMetadata) {\n\t\t\tassert(\n\t\t\t\tthis.currentBatchClientId === undefined,\n\t\t\t\t0x29e /* \"there can't be active batch\" */,\n\t\t\t);\n\t\t\tassert(\n\t\t\t\t!this.localPaused,\n\t\t\t\t0x29f /* \"we should be processing ops when there is no active batch\" */,\n\t\t\t);\n\t\t\tthis.pauseSequenceNumber = message.sequenceNumber;\n\t\t\t// TODO: Verify whether this should be able to handle server-generated ops (with null clientId)\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion\n\t\t\tthis.currentBatchClientId = message.clientId as string;\n\t\t\t// Start of the batch\n\t\t\t// Only pause processing if queue has no other ops!\n\t\t\t// If there are any other ops in the queue, processing will be stopped when they are processed!\n\t\t\tif (this.deltaManager.inbound.length === 1) {\n\t\t\t\tthis.pauseQueue();\n\t\t\t}\n\t\t} else if (batchMetadata === false) {\n\t\t\tassert(\n\t\t\t\tthis.pauseSequenceNumber !== undefined,\n\t\t\t\t0x2a0 /* \"batch presence was validated above\" */,\n\t\t\t);\n\t\t\t// Batch is complete, we can process it!\n\t\t\tthis.resumeQueue(this.pauseSequenceNumber, message);\n\t\t\tthis.pauseSequenceNumber = undefined;\n\t\t\tthis.currentBatchClientId = undefined;\n\t\t} else {\n\t\t\t// Continuation of current batch. Do nothing\n\t\t\tassert(this.currentBatchClientId !== undefined, 0x2a1 /* \"logic error\" */);\n\t\t}\n\t}\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"summarizerNodeUtils.d.ts","sourceRoot":"","sources":["../../../src/summary/summarizerNode/summarizerNodeUtils.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,oCAAoC,CAAC;AACnE,OAAO,EAAE,aAAa,EAAE,MAAM,6CAA6C,CAAC;AAE5E,OAAO,EACN,mBAAmB,EACnB,gBAAgB,EAChB,MAAM,0CAA0C,CAAC;AAElD,MAAM,WAAW,qBAAqB;IACrC,4DAA4D;IAC5D,gBAAgB,EAAE,OAAO,CAAC;IAC1B,sFAAsF;IACtF,cAAc,EAAE,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,mBAAmB;IACnC,iEAAiE;IACjE,KAAK,EAAE,MAAM,CAAC;IACd,wDAAwD;IACxD,YAAY,EAAE,MAAM,CAAC;IACrB,6HAA6H;IAC7H,eAAe,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CAC7B;AAED;;;;GAIG;AACH,MAAM,MAAM,qBAAqB,GAC9B;IACA,OAAO,EAAE,IAAI,CAAC;CACb,GACD;IACA,OAAO,EAAE,KAAK,CAAC;IACf,yBAAyB;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,mDAAmD;IACnD,EAAE,EAAE;QACH,GAAG,EAAE,gBAAgB,CAAC,YAAY,CAAC;QACnC,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;KAC1B,CAAC;IACF,gEAAgE;IAChE,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEL,MAAM,WAAW,2BAA2B;IAC3C,YAAY,CACX,uBAAuB,EAAE,MAAM,EAC/B,aAAa,EAAE,mBAAmB,EAClC,sBAAsB,EAAE,MAAM,GAC5B,mBAAmB,CAAC;IACvB,eAAe,IAAI,qBAAqB,CAAC;IACzC,eAAe,CAAC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9C,YAAY,IAAI,IAAI,CAAC;IACrB,oBAAoB,CACnB,cAAc,EAAE,MAAM,EACtB,aAAa,EAAE,MAAM,GACnB,OAAO,CAAC,qBAAqB,CAAC,CAAC;CAClC;AAED,+DAA+D;AAC/D,qBAAa,WAAW;aACa,IAAI,EAAE,MAAM;IAAhD,OAAO;WACO,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW;WAGjC,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,WAAW;
|
|
1
|
+
{"version":3,"file":"summarizerNodeUtils.d.ts","sourceRoot":"","sources":["../../../src/summary/summarizerNode/summarizerNodeUtils.ts"],"names":[],"mappings":"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"}
|
|
@@ -14,8 +14,6 @@ export class EscapedPath {
|
|
|
14
14
|
static createAndConcat(pathParts) {
|
|
15
15
|
let ret = EscapedPath.create(pathParts[0] ?? "");
|
|
16
16
|
for (let i = 1; i < pathParts.length; i++) {
|
|
17
|
-
// Non null asserting here since we are iterating over pathParts
|
|
18
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
19
17
|
ret = ret.concat(EscapedPath.create(pathParts[i]));
|
|
20
18
|
}
|
|
21
19
|
return ret;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"summarizerNodeUtils.js","sourceRoot":"","sources":["../../../src/summary/summarizerNode/summarizerNodeUtils.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,EAAE,gBAAgB,EAAE,MAAM,8CAA8C,CAAC;AA2DhF,+DAA+D;AAC/D,MAAM,OAAO,WAAW;IACvB,YAAoC,IAAY;QAAZ,SAAI,GAAJ,IAAI,CAAQ;IAAG,CAAC;IAC7C,MAAM,CAAC,MAAM,CAAC,IAAY;QAChC,OAAO,IAAI,WAAW,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAC,CAAC;IAClD,CAAC;IACM,MAAM,CAAC,eAAe,CAAC,SAAmB;QAChD,IAAI,GAAG,GAAG,WAAW,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QACjD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,SAAS,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YAC3C,
|
|
1
|
+
{"version":3,"file":"summarizerNodeUtils.js","sourceRoot":"","sources":["../../../src/summary/summarizerNode/summarizerNodeUtils.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 { SummaryObject } from \"@fluidframework/driver-definitions\";\nimport { ISnapshotTree } from \"@fluidframework/driver-definitions/internal\";\nimport { channelsTreeName } from \"@fluidframework/runtime-definitions/internal\";\nimport {\n\tITelemetryLoggerExt,\n\tTelemetryDataTag,\n} from \"@fluidframework/telemetry-utils/internal\";\n\nexport interface IRefreshSummaryResult {\n\t/** Tells whether this summary is tracked by this client. */\n\tisSummaryTracked: boolean;\n\t/** Tells whether this summary is newer than the latest one tracked by this client. */\n\tisSummaryNewer: boolean;\n}\n\nexport interface IStartSummaryResult {\n\t/** The number of summarizerNodes at the start of the summary. */\n\tnodes: number;\n\t/** The number of summarizerNodes in the wrong state. */\n\tinvalidNodes: number;\n\t/** The invalid sequence numbers and their values. It should be in the format of validateSequenceNumber-nodeSequenceNumber */\n\tmismatchNumbers: Set<string>;\n}\n\n/**\n * Return type of validateSummary function. In case of success, the object returned should have success: true.\n * In case of failure, the object returned should have success: false and additional properties to indicate what\n * the failure was, where it was, can it be retried, etc.\n */\nexport type ValidateSummaryResult =\n\t| {\n\t\t\tsuccess: true;\n\t }\n\t| {\n\t\t\tsuccess: false;\n\t\t\t/** The failure reason */\n\t\t\treason: string;\n\t\t\t/** id of the node that failed during validation */\n\t\t\tid: {\n\t\t\t\ttag: TelemetryDataTag.CodeArtifact;\n\t\t\t\tvalue: string | undefined;\n\t\t\t};\n\t\t\t/** If the error can be retried, time to wait before retrying */\n\t\t\tretryAfterSeconds?: number;\n\t };\n\nexport interface ISummarizerNodeRootContract {\n\tstartSummary(\n\t\treferenceSequenceNumber: number,\n\t\tsummaryLogger: ITelemetryLoggerExt,\n\t\tlatestSummaryRefSeqNum: number,\n\t): IStartSummaryResult;\n\tvalidateSummary(): ValidateSummaryResult;\n\tcompleteSummary(proposalHandle: string): void;\n\tclearSummary(): void;\n\trefreshLatestSummary(\n\t\tproposalHandle: string,\n\t\tsummaryRefSeq: number,\n\t): Promise<IRefreshSummaryResult>;\n}\n\n/** Path for nodes in a tree with escaped special characters */\nexport class EscapedPath {\n\tprivate constructor(public readonly path: string) {}\n\tpublic static create(path: string): EscapedPath {\n\t\treturn new EscapedPath(encodeURIComponent(path));\n\t}\n\tpublic static createAndConcat(pathParts: string[]): EscapedPath {\n\t\tlet ret = EscapedPath.create(pathParts[0] ?? \"\");\n\t\tfor (let i = 1; i < pathParts.length; i++) {\n\t\t\tret = ret.concat(EscapedPath.create(pathParts[i]));\n\t\t}\n\t\treturn ret;\n\t}\n\tpublic toString(): string {\n\t\treturn this.path;\n\t}\n\tpublic concat(path: EscapedPath): EscapedPath {\n\t\treturn new EscapedPath(`${this.path}/${path.path}`);\n\t}\n}\n\n/** Information about a summary relevant to a specific node in the tree */\nexport class SummaryNode {\n\t/** Creates an instance that is valid for the root with specific basePath and localPath */\n\tpublic static createForRoot(referenceSequenceNumber: number): SummaryNode {\n\t\treturn new SummaryNode({\n\t\t\treferenceSequenceNumber,\n\t\t\tbasePath: undefined,\n\t\t\tlocalPath: EscapedPath.create(\"\"), // root hard-coded to \"\"\n\t\t});\n\t}\n\n\t/** Summary reference sequence number, i.e. last sequence number seen when it was created */\n\tpublic get referenceSequenceNumber(): number {\n\t\treturn this.summary.referenceSequenceNumber;\n\t}\n\t/** Full path to parent node, or undefined if this is the root */\n\tpublic get basePath(): EscapedPath | undefined {\n\t\treturn this.summary.basePath;\n\t}\n\t/** Relative path to this node from its parent node */\n\tpublic get localPath(): EscapedPath {\n\t\treturn this.summary.localPath;\n\t}\n\t/** Relative path from this node to its node innermost base summary */\n\tpublic get additionalPath(): EscapedPath | undefined {\n\t\treturn this.summary.additionalPath;\n\t}\n\tpublic set additionalPath(additionalPath: EscapedPath | undefined) {\n\t\tthis.summary.additionalPath = additionalPath;\n\t}\n\tconstructor(\n\t\tprivate readonly summary: {\n\t\t\treadonly referenceSequenceNumber: number;\n\t\t\treadonly basePath: EscapedPath | undefined;\n\t\t\treadonly localPath: EscapedPath;\n\t\t\tadditionalPath?: EscapedPath;\n\t\t},\n\t) {}\n\n\t/** Gets the full path to this node, to be used when sending a handle */\n\tpublic get fullPath(): EscapedPath {\n\t\treturn this.basePath?.concat(this.localPath) ?? this.localPath;\n\t}\n\n\t/**\n\t * Gets the full path to this node's innermost base summary.\n\t * The children nodes can use this as their basePath to determine their path.\n\t */\n\tpublic get fullPathForChildren(): EscapedPath {\n\t\treturn this.additionalPath !== undefined\n\t\t\t? this.fullPath.concat(this.additionalPath)\n\t\t\t: this.fullPath;\n\t}\n\n\t/**\n\t * Creates a new node within the same summary for a child of this node.\n\t * @param id - id of the child node\n\t */\n\tpublic createForChild(id: string): SummaryNode {\n\t\treturn new SummaryNode({\n\t\t\treferenceSequenceNumber: this.referenceSequenceNumber,\n\t\t\tbasePath: this.fullPathForChildren,\n\t\t\tlocalPath: EscapedPath.create(id),\n\t\t});\n\t}\n}\n\n/**\n * Represents the details needed to create a child summarizer node.\n */\nexport interface ICreateChildDetails {\n\t/** Latest summary from server node data */\n\tlatestSummary: SummaryNode | undefined;\n\t/** Sequence number of latest known change to the node */\n\tchangeSequenceNumber: number;\n\t/** A unique id of this child to be logged when sending telemetry. */\n\ttelemetryNodeId: string;\n}\n\nexport interface ISubtreeInfo<T extends ISnapshotTree | SummaryObject> {\n\t/** Tree to use to find children subtrees */\n\tchildrenTree: T;\n\t/** Additional path part where children are isolated */\n\tchildrenPathPart: string | undefined;\n}\n\n/**\n * Checks if the summary contains .channels subtree where the children subtrees\n * would be located if exists.\n * @param baseSummary - summary to check\n */\nexport function parseSummaryForSubtrees(\n\tbaseSummary: ISnapshotTree,\n): ISubtreeInfo<ISnapshotTree> {\n\t// New versions of snapshots have child nodes isolated in .channels subtree\n\tconst channelsSubtree = baseSummary.trees[channelsTreeName];\n\tif (channelsSubtree !== undefined) {\n\t\treturn {\n\t\t\tchildrenTree: channelsSubtree,\n\t\t\tchildrenPathPart: channelsTreeName,\n\t\t};\n\t}\n\treturn {\n\t\tchildrenTree: baseSummary,\n\t\tchildrenPathPart: undefined,\n\t};\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"summaryCollection.d.ts","sourceRoot":"","sources":["../../src/summary/summaryCollection.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"summaryCollection.d.ts","sourceRoot":"","sources":["../../src/summary/summaryCollection.ts"],"names":[],"mappings":"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"}
|
|
@@ -200,12 +200,11 @@ export class SummaryCollection extends TypedEventEmitter {
|
|
|
200
200
|
return this.lastAck;
|
|
201
201
|
}
|
|
202
202
|
parseContent(op) {
|
|
203
|
-
//
|
|
204
|
-
//
|
|
205
|
-
//
|
|
206
|
-
//
|
|
207
|
-
//
|
|
208
|
-
// parsing in loader layer!
|
|
203
|
+
// This should become unconditional once (Loader LTS) DeltaManager.processInboundMessage() stops parsing content (ADO #12052)
|
|
204
|
+
// Note: Until that change is made in the loader, this case will never be hit.
|
|
205
|
+
// Then there will be a long time of needing both cases, until LTS catches up to the change.
|
|
206
|
+
// That said, we may instead move to listen for "op" events from ContainerRuntime,
|
|
207
|
+
// and parsing may not be required at all if ContainerRuntime.process() would parse it for all types of ops.
|
|
209
208
|
if (typeof op.contents === "string") {
|
|
210
209
|
op.contents = JSON.parse(op.contents);
|
|
211
210
|
}
|
|
@@ -223,7 +222,6 @@ export class SummaryCollection extends TypedEventEmitter {
|
|
|
223
222
|
case MessageType.SummaryAck:
|
|
224
223
|
case MessageType.SummaryNack:
|
|
225
224
|
// Old files (prior to PR #10077) may not contain this info
|
|
226
|
-
// back-compat: ADO #1385: remove cast when ISequencedDocumentMessage changes are propagated
|
|
227
225
|
if (op.data !== undefined) {
|
|
228
226
|
op.contents = JSON.parse(op.data);
|
|
229
227
|
}
|