@fluidframework/container-runtime 2.118.1 → 3.0.1
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 +68 -4
- package/README.md +53 -50
- package/{api-extractor.json → api-extractor/api-extractor-model.json} +1 -1
- package/api-report/container-runtime.legacy.alpha.api.md +3 -1
- package/api-report/container-runtime.legacy.beta.api.md +3 -1
- package/container-runtime.test-files.tar +0 -0
- package/dist/batchTracker.d.ts.map +1 -1
- package/dist/batchTracker.js +5 -1
- package/dist/batchTracker.js.map +1 -1
- package/dist/blobManager/blobManager.d.ts +1 -1
- package/dist/blobManager/blobManager.d.ts.map +1 -1
- package/dist/blobManager/blobManager.js +264 -231
- package/dist/blobManager/blobManager.js.map +1 -1
- package/dist/blobManager/blobManagerSnapSum.d.ts.map +1 -1
- package/dist/channelCollection.d.ts.map +1 -1
- package/dist/channelCollection.js +70 -58
- package/dist/channelCollection.js.map +1 -1
- package/dist/connectionTelemetry.js +38 -20
- package/dist/connectionTelemetry.js.map +1 -1
- package/dist/containerCompatibility.d.ts +3 -3
- package/dist/containerCompatibility.d.ts.map +1 -1
- package/dist/containerCompatibility.js +36 -45
- package/dist/containerCompatibility.js.map +1 -1
- package/dist/containerHandleContext.d.ts.map +1 -1
- package/dist/containerHandleContext.js +4 -0
- package/dist/containerHandleContext.js.map +1 -1
- package/dist/containerRuntime.d.ts +4 -4
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +346 -223
- package/dist/containerRuntime.js.map +1 -1
- package/dist/dataStore.d.ts.map +1 -1
- package/dist/dataStore.js +9 -1
- package/dist/dataStore.js.map +1 -1
- package/dist/dataStoreContext.d.ts.map +1 -1
- package/dist/dataStoreContext.js +184 -127
- package/dist/dataStoreContext.js.map +1 -1
- package/dist/dataStoreContexts.js +43 -42
- package/dist/dataStoreContexts.js.map +1 -1
- package/dist/dataStoreRegistry.js +1 -0
- package/dist/dataStoreRegistry.js.map +1 -1
- package/dist/deltaManagerProxies.js +5 -0
- package/dist/deltaManagerProxies.js.map +1 -1
- package/dist/deltaScheduler.js +104 -99
- package/dist/deltaScheduler.js.map +1 -1
- package/dist/error.js +2 -1
- package/dist/error.js.map +1 -1
- package/dist/gc/garbageCollection.js +85 -55
- package/dist/gc/garbageCollection.js.map +1 -1
- package/dist/gc/gcConfigs.js +2 -3
- package/dist/gc/gcConfigs.js.map +1 -1
- package/dist/gc/gcHelpers.js +12 -13
- package/dist/gc/gcHelpers.js.map +1 -1
- package/dist/gc/gcReferenceGraphAlgorithm.js +1 -2
- package/dist/gc/gcReferenceGraphAlgorithm.js.map +1 -1
- package/dist/gc/gcSummaryStateTracker.js +8 -3
- package/dist/gc/gcSummaryStateTracker.js.map +1 -1
- package/dist/gc/gcTelemetry.js +12 -5
- package/dist/gc/gcTelemetry.js.map +1 -1
- package/dist/gc/gcUnreferencedStateTracker.js +18 -1
- package/dist/gc/gcUnreferencedStateTracker.js.map +1 -1
- package/dist/inboundBatchAggregator.js +122 -117
- package/dist/inboundBatchAggregator.js.map +1 -1
- package/dist/metadata.d.ts.map +1 -1
- package/dist/metadata.js +3 -3
- package/dist/metadata.js.map +1 -1
- package/dist/opLifecycle/batchManager.d.ts.map +1 -1
- package/dist/opLifecycle/batchManager.js +11 -5
- package/dist/opLifecycle/batchManager.js.map +1 -1
- package/dist/opLifecycle/duplicateBatchDetector.js +18 -18
- package/dist/opLifecycle/duplicateBatchDetector.js.map +1 -1
- package/dist/opLifecycle/index.d.ts +2 -2
- package/dist/opLifecycle/index.d.ts.map +1 -1
- package/dist/opLifecycle/index.js +2 -1
- package/dist/opLifecycle/index.js.map +1 -1
- package/dist/opLifecycle/opCompressor.js +2 -1
- package/dist/opLifecycle/opCompressor.js.map +1 -1
- package/dist/opLifecycle/opDecompressor.js +10 -6
- package/dist/opLifecycle/opDecompressor.js.map +1 -1
- package/dist/opLifecycle/opGroupingManager.js +5 -3
- package/dist/opLifecycle/opGroupingManager.js.map +1 -1
- package/dist/opLifecycle/opSerialization.d.ts +11 -1
- package/dist/opLifecycle/opSerialization.d.ts.map +1 -1
- package/dist/opLifecycle/opSerialization.js +22 -6
- package/dist/opLifecycle/opSerialization.js.map +1 -1
- package/dist/opLifecycle/opSplitter.d.ts +17 -1
- package/dist/opLifecycle/opSplitter.d.ts.map +1 -1
- package/dist/opLifecycle/opSplitter.js +46 -9
- package/dist/opLifecycle/opSplitter.js.map +1 -1
- package/dist/opLifecycle/outbox.d.ts.map +1 -1
- package/dist/opLifecycle/outbox.js +17 -13
- package/dist/opLifecycle/outbox.js.map +1 -1
- package/dist/opLifecycle/remoteMessageProcessor.js +6 -3
- package/dist/opLifecycle/remoteMessageProcessor.js.map +1 -1
- package/dist/opProperties.d.ts.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.d.ts.map +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/pendingStateManager.js +54 -47
- package/dist/pendingStateManager.js.map +1 -1
- package/dist/runCounter.js +10 -30
- package/dist/runCounter.js.map +1 -1
- package/dist/runtimeLayerCompatState.d.ts +1 -1
- package/dist/runtimeLayerCompatState.d.ts.map +1 -1
- package/dist/runtimeLayerCompatState.js +31 -11
- package/dist/runtimeLayerCompatState.js.map +1 -1
- package/dist/signalTelemetryProcessing.js +15 -17
- package/dist/signalTelemetryProcessing.js.map +1 -1
- package/dist/storageServiceWithAttachBlobs.js +2 -0
- package/dist/storageServiceWithAttachBlobs.js.map +1 -1
- package/dist/summary/documentSchema.d.ts.map +1 -1
- package/dist/summary/documentSchema.js +51 -13
- package/dist/summary/documentSchema.js.map +1 -1
- package/dist/summary/orderedClientElection.js +25 -17
- package/dist/summary/orderedClientElection.js.map +1 -1
- package/dist/summary/summarizerClientElection.d.ts.map +1 -1
- package/dist/summary/summarizerClientElection.js +18 -7
- package/dist/summary/summarizerClientElection.js.map +1 -1
- package/dist/summary/summarizerNode/summarizerNode.d.ts.map +1 -1
- package/dist/summary/summarizerNode/summarizerNode.js +22 -13
- package/dist/summary/summarizerNode/summarizerNode.js.map +1 -1
- package/dist/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
- package/dist/summary/summarizerNode/summarizerNodeWithGc.js +20 -6
- package/dist/summary/summarizerNode/summarizerNodeWithGc.js.map +1 -1
- package/dist/summary/summarizerUtils.d.ts.map +1 -1
- package/dist/summary/summarizerUtils.js +4 -3
- package/dist/summary/summarizerUtils.js.map +1 -1
- package/dist/summary/summaryCollection.js +23 -11
- package/dist/summary/summaryCollection.js.map +1 -1
- package/dist/summary/summaryDelayLoadedModule/runWhileConnectedCoordinator.js +4 -2
- package/dist/summary/summaryDelayLoadedModule/runWhileConnectedCoordinator.js.map +1 -1
- package/dist/summary/summaryDelayLoadedModule/runningSummarizer.d.ts.map +1 -1
- package/dist/summary/summaryDelayLoadedModule/runningSummarizer.js +65 -46
- package/dist/summary/summaryDelayLoadedModule/runningSummarizer.js.map +1 -1
- package/dist/summary/summaryDelayLoadedModule/summarizer.d.ts.map +1 -1
- package/dist/summary/summaryDelayLoadedModule/summarizer.js +15 -6
- package/dist/summary/summaryDelayLoadedModule/summarizer.js.map +1 -1
- package/dist/summary/summaryDelayLoadedModule/summarizerHeuristics.js +30 -25
- package/dist/summary/summaryDelayLoadedModule/summarizerHeuristics.js.map +1 -1
- package/dist/summary/summaryDelayLoadedModule/summaryGenerator.js +9 -0
- package/dist/summary/summaryDelayLoadedModule/summaryGenerator.js.map +1 -1
- package/dist/summary/summaryDelayLoadedModule/summaryResultBuilder.js +3 -5
- package/dist/summary/summaryDelayLoadedModule/summaryResultBuilder.js.map +1 -1
- package/dist/summary/summaryFormat.d.ts.map +1 -1
- package/dist/summary/summaryFormat.js +7 -7
- package/dist/summary/summaryFormat.js.map +1 -1
- package/dist/summary/summaryHelpers.js +5 -5
- package/dist/summary/summaryHelpers.js.map +1 -1
- package/dist/summary/summaryManager.js +66 -54
- package/dist/summary/summaryManager.js.map +1 -1
- package/dist/throttler.d.ts.map +1 -1
- package/dist/throttler.js +4 -1
- package/dist/throttler.js.map +1 -1
- package/dist/versionMarks/inboundBatch.d.ts +8 -6
- package/dist/versionMarks/inboundBatch.d.ts.map +1 -1
- package/dist/versionMarks/inboundBatch.js +11 -7
- package/dist/versionMarks/inboundBatch.js.map +1 -1
- package/dist/versionMarks/versionMarkResolver.d.ts +30 -23
- package/dist/versionMarks/versionMarkResolver.d.ts.map +1 -1
- package/dist/versionMarks/versionMarkResolver.js +112 -63
- package/dist/versionMarks/versionMarkResolver.js.map +1 -1
- package/lib/batchTracker.d.ts.map +1 -1
- package/lib/batchTracker.js +5 -1
- package/lib/batchTracker.js.map +1 -1
- package/lib/blobManager/blobManager.d.ts +1 -1
- package/lib/blobManager/blobManager.d.ts.map +1 -1
- package/lib/blobManager/blobManager.js +264 -231
- package/lib/blobManager/blobManager.js.map +1 -1
- package/lib/blobManager/blobManagerSnapSum.d.ts.map +1 -1
- package/lib/channelCollection.d.ts.map +1 -1
- package/lib/channelCollection.js +65 -53
- package/lib/channelCollection.js.map +1 -1
- package/lib/connectionTelemetry.js +36 -18
- package/lib/connectionTelemetry.js.map +1 -1
- package/lib/containerCompatibility.d.ts +3 -3
- package/lib/containerCompatibility.d.ts.map +1 -1
- package/lib/containerCompatibility.js +33 -42
- package/lib/containerCompatibility.js.map +1 -1
- package/lib/containerHandleContext.d.ts.map +1 -1
- package/lib/containerHandleContext.js +4 -0
- package/lib/containerHandleContext.js.map +1 -1
- package/lib/containerRuntime.d.ts +4 -4
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +341 -218
- package/lib/containerRuntime.js.map +1 -1
- package/lib/dataStore.d.ts.map +1 -1
- package/lib/dataStore.js +9 -1
- package/lib/dataStore.js.map +1 -1
- package/lib/dataStoreContext.d.ts.map +1 -1
- package/lib/dataStoreContext.js +182 -125
- package/lib/dataStoreContext.js.map +1 -1
- package/lib/dataStoreContexts.js +43 -42
- package/lib/dataStoreContexts.js.map +1 -1
- package/lib/dataStoreRegistry.js +1 -0
- package/lib/dataStoreRegistry.js.map +1 -1
- package/lib/deltaManagerProxies.js +5 -0
- package/lib/deltaManagerProxies.js.map +1 -1
- package/lib/deltaScheduler.js +104 -99
- package/lib/deltaScheduler.js.map +1 -1
- package/lib/error.js +2 -1
- package/lib/error.js.map +1 -1
- package/lib/gc/garbageCollection.js +85 -55
- package/lib/gc/garbageCollection.js.map +1 -1
- package/lib/gc/gcSummaryStateTracker.js +8 -3
- package/lib/gc/gcSummaryStateTracker.js.map +1 -1
- package/lib/gc/gcTelemetry.js +12 -5
- package/lib/gc/gcTelemetry.js.map +1 -1
- package/lib/gc/gcUnreferencedStateTracker.js +18 -1
- package/lib/gc/gcUnreferencedStateTracker.js.map +1 -1
- package/lib/inboundBatchAggregator.js +122 -117
- package/lib/inboundBatchAggregator.js.map +1 -1
- package/lib/legacy.d.ts +1 -1
- package/lib/legacyAlpha.d.ts +1 -1
- package/lib/metadata.d.ts.map +1 -1
- package/lib/opLifecycle/batchManager.d.ts.map +1 -1
- package/lib/opLifecycle/batchManager.js +8 -2
- package/lib/opLifecycle/batchManager.js.map +1 -1
- package/lib/opLifecycle/duplicateBatchDetector.js +18 -18
- package/lib/opLifecycle/duplicateBatchDetector.js.map +1 -1
- package/lib/opLifecycle/index.d.ts +2 -2
- package/lib/opLifecycle/index.d.ts.map +1 -1
- package/lib/opLifecycle/index.js +2 -2
- package/lib/opLifecycle/index.js.map +1 -1
- package/lib/opLifecycle/opCompressor.js +3 -2
- package/lib/opLifecycle/opCompressor.js.map +1 -1
- package/lib/opLifecycle/opDecompressor.js +10 -6
- package/lib/opLifecycle/opDecompressor.js.map +1 -1
- package/lib/opLifecycle/opGroupingManager.js +3 -1
- package/lib/opLifecycle/opGroupingManager.js.map +1 -1
- package/lib/opLifecycle/opSerialization.d.ts +11 -1
- package/lib/opLifecycle/opSerialization.d.ts.map +1 -1
- package/lib/opLifecycle/opSerialization.js +16 -0
- package/lib/opLifecycle/opSerialization.js.map +1 -1
- package/lib/opLifecycle/opSplitter.d.ts +17 -1
- package/lib/opLifecycle/opSplitter.d.ts.map +1 -1
- package/lib/opLifecycle/opSplitter.js +44 -7
- package/lib/opLifecycle/opSplitter.js.map +1 -1
- package/lib/opLifecycle/outbox.d.ts.map +1 -1
- package/lib/opLifecycle/outbox.js +14 -10
- package/lib/opLifecycle/outbox.js.map +1 -1
- package/lib/opLifecycle/remoteMessageProcessor.js +4 -1
- package/lib/opLifecycle/remoteMessageProcessor.js.map +1 -1
- package/lib/opProperties.d.ts.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.d.ts.map +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/pendingStateManager.js +52 -45
- package/lib/pendingStateManager.js.map +1 -1
- package/lib/public.d.ts +1 -1
- package/lib/runCounter.js +10 -30
- package/lib/runCounter.js.map +1 -1
- package/lib/runtimeLayerCompatState.d.ts +1 -1
- package/lib/runtimeLayerCompatState.d.ts.map +1 -1
- package/lib/runtimeLayerCompatState.js +28 -8
- package/lib/runtimeLayerCompatState.js.map +1 -1
- package/lib/signalTelemetryProcessing.js +15 -17
- package/lib/signalTelemetryProcessing.js.map +1 -1
- package/lib/storageServiceWithAttachBlobs.js +2 -0
- package/lib/storageServiceWithAttachBlobs.js.map +1 -1
- package/lib/summary/documentSchema.d.ts.map +1 -1
- package/lib/summary/documentSchema.js +51 -13
- package/lib/summary/documentSchema.js.map +1 -1
- package/lib/summary/orderedClientElection.js +25 -17
- package/lib/summary/orderedClientElection.js.map +1 -1
- package/lib/summary/summarizerClientElection.d.ts.map +1 -1
- package/lib/summary/summarizerClientElection.js +18 -7
- package/lib/summary/summarizerClientElection.js.map +1 -1
- package/lib/summary/summarizerNode/summarizerNode.d.ts.map +1 -1
- package/lib/summary/summarizerNode/summarizerNode.js +22 -13
- package/lib/summary/summarizerNode/summarizerNode.js.map +1 -1
- package/lib/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
- package/lib/summary/summarizerNode/summarizerNodeWithGc.js +20 -6
- package/lib/summary/summarizerNode/summarizerNodeWithGc.js.map +1 -1
- package/lib/summary/summarizerUtils.d.ts.map +1 -1
- package/lib/summary/summarizerUtils.js +1 -0
- package/lib/summary/summarizerUtils.js.map +1 -1
- package/lib/summary/summaryCollection.js +23 -11
- package/lib/summary/summaryCollection.js.map +1 -1
- package/lib/summary/summaryDelayLoadedModule/runWhileConnectedCoordinator.js +4 -2
- package/lib/summary/summaryDelayLoadedModule/runWhileConnectedCoordinator.js.map +1 -1
- package/lib/summary/summaryDelayLoadedModule/runningSummarizer.d.ts.map +1 -1
- package/lib/summary/summaryDelayLoadedModule/runningSummarizer.js +65 -46
- package/lib/summary/summaryDelayLoadedModule/runningSummarizer.js.map +1 -1
- package/lib/summary/summaryDelayLoadedModule/summarizer.d.ts.map +1 -1
- package/lib/summary/summaryDelayLoadedModule/summarizer.js +15 -6
- package/lib/summary/summaryDelayLoadedModule/summarizer.js.map +1 -1
- package/lib/summary/summaryDelayLoadedModule/summarizerHeuristics.js +30 -25
- package/lib/summary/summaryDelayLoadedModule/summarizerHeuristics.js.map +1 -1
- package/lib/summary/summaryDelayLoadedModule/summaryGenerator.js +9 -0
- package/lib/summary/summaryDelayLoadedModule/summaryGenerator.js.map +1 -1
- package/lib/summary/summaryDelayLoadedModule/summaryResultBuilder.js +3 -5
- package/lib/summary/summaryDelayLoadedModule/summaryResultBuilder.js.map +1 -1
- package/lib/summary/summaryFormat.d.ts.map +1 -1
- package/lib/summary/summaryManager.js +66 -54
- package/lib/summary/summaryManager.js.map +1 -1
- package/lib/throttler.d.ts.map +1 -1
- package/lib/throttler.js +4 -1
- package/lib/throttler.js.map +1 -1
- package/lib/versionMarks/inboundBatch.d.ts +8 -6
- package/lib/versionMarks/inboundBatch.d.ts.map +1 -1
- package/lib/versionMarks/inboundBatch.js +10 -5
- package/lib/versionMarks/inboundBatch.js.map +1 -1
- package/lib/versionMarks/versionMarkResolver.d.ts +30 -23
- package/lib/versionMarks/versionMarkResolver.d.ts.map +1 -1
- package/lib/versionMarks/versionMarkResolver.js +112 -63
- package/lib/versionMarks/versionMarkResolver.js.map +1 -1
- package/package.json +60 -102
- package/src/containerCompatibility.ts +33 -42
- package/src/containerRuntime.ts +15 -12
- package/src/opLifecycle/index.ts +7 -1
- package/src/opLifecycle/opCompressor.ts +2 -2
- package/src/opLifecycle/opSerialization.ts +22 -1
- package/src/opLifecycle/opSplitter.ts +63 -6
- package/src/packageVersion.ts +1 -1
- package/src/runtimeLayerCompatState.ts +28 -21
- package/src/summary/documentSchema.ts +31 -6
- package/src/versionMarks/DEV.md +595 -157
- package/src/versionMarks/inboundBatch.ts +18 -8
- package/src/versionMarks/versionMarkResolver.ts +144 -75
- package/tsconfig.json +1 -1
- package/internal.d.ts +0 -11
- package/legacy/alpha.d.ts +0 -11
- package/legacy.d.ts +0 -11
- /package/api-extractor/{api-extractor.current.json → api-extractor-report.current.json} +0 -0
- /package/api-extractor/{api-extractor.legacy.json → api-extractor-report.legacy.json} +0 -0
|
@@ -5,11 +5,11 @@
|
|
|
5
5
|
export { addBatchMetadata, type BatchId, BatchManager, type BatchSequenceNumbers, getEffectiveBatchId, generateBatchId, type IBatchManagerOptions, } from "./batchManager.js";
|
|
6
6
|
export type { LocalBatch, LocalBatchMessage, LocalEmptyBatchPlaceholder, OutboundBatch, OutboundBatchMessage, OutboundSingletonBatch, IBatchCheckpoint, IChunkedOp, } from "./definitions.js";
|
|
7
7
|
export { DuplicateBatchDetector } from "./duplicateBatchDetector.js";
|
|
8
|
-
export { serializeOp, ensureContentsDeserialized, } from "./opSerialization.js";
|
|
8
|
+
export { serializeOp, ensureContentsDeserialized, tryGetDeserializedRuntimeOpCopy, } from "./opSerialization.js";
|
|
9
9
|
export { type BatchResubmitInfo, estimateSocketSize, localBatchToOutboundBatch, Outbox, getLongStack, } from "./outbox.js";
|
|
10
10
|
export { OpCompressor } from "./opCompressor.js";
|
|
11
11
|
export { OpDecompressor } from "./opDecompressor.js";
|
|
12
|
-
export { OpSplitter, splitOp, isChunkedMessage } from "./opSplitter.js";
|
|
12
|
+
export { type OpSplitterOptions, OpSplitter, splitOp, isChunkedMessage, } from "./opSplitter.js";
|
|
13
13
|
export { type InboundMessageResult, type BatchStartInfo, RemoteMessageProcessor, unpackRuntimeMessage, } from "./remoteMessageProcessor.js";
|
|
14
14
|
export { type EmptyGroupedBatch, largeBatchThreshold, OpGroupingManager, type OpGroupingManagerConfig, isGroupedBatch, } from "./opGroupingManager.js";
|
|
15
15
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/opLifecycle/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACN,gBAAgB,EAChB,KAAK,OAAO,EACZ,YAAY,EACZ,KAAK,oBAAoB,EACzB,mBAAmB,EACnB,eAAe,EACf,KAAK,oBAAoB,GACzB,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EACX,UAAU,EACV,iBAAiB,EACjB,0BAA0B,EAC1B,aAAa,EACb,oBAAoB,EACpB,sBAAsB,EACtB,gBAAgB,EAChB,UAAU,GACV,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AACrE,OAAO,EACN,WAAW,EACX,0BAA0B,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/opLifecycle/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACN,gBAAgB,EAChB,KAAK,OAAO,EACZ,YAAY,EACZ,KAAK,oBAAoB,EACzB,mBAAmB,EACnB,eAAe,EACf,KAAK,oBAAoB,GACzB,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EACX,UAAU,EACV,iBAAiB,EACjB,0BAA0B,EAC1B,aAAa,EACb,oBAAoB,EACpB,sBAAsB,EACtB,gBAAgB,EAChB,UAAU,GACV,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AACrE,OAAO,EACN,WAAW,EACX,0BAA0B,EAC1B,+BAA+B,GAC/B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACN,KAAK,iBAAiB,EACtB,kBAAkB,EAClB,yBAAyB,EACzB,MAAM,EACN,YAAY,GACZ,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EACN,KAAK,iBAAiB,EACtB,UAAU,EACV,OAAO,EACP,gBAAgB,GAChB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACN,KAAK,oBAAoB,EACzB,KAAK,cAAc,EACnB,sBAAsB,EACtB,oBAAoB,GACpB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EACN,KAAK,iBAAiB,EACtB,mBAAmB,EACnB,iBAAiB,EACjB,KAAK,uBAAuB,EAC5B,cAAc,GACd,MAAM,wBAAwB,CAAC"}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Licensed under the MIT License.
|
|
5
5
|
*/
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
exports.isGroupedBatch = exports.OpGroupingManager = exports.largeBatchThreshold = exports.unpackRuntimeMessage = exports.RemoteMessageProcessor = exports.isChunkedMessage = exports.splitOp = exports.OpSplitter = exports.OpDecompressor = exports.OpCompressor = exports.getLongStack = exports.Outbox = exports.localBatchToOutboundBatch = exports.estimateSocketSize = exports.ensureContentsDeserialized = exports.serializeOp = exports.DuplicateBatchDetector = exports.generateBatchId = exports.getEffectiveBatchId = exports.BatchManager = exports.addBatchMetadata = void 0;
|
|
7
|
+
exports.isGroupedBatch = exports.OpGroupingManager = exports.largeBatchThreshold = exports.unpackRuntimeMessage = exports.RemoteMessageProcessor = exports.isChunkedMessage = exports.splitOp = exports.OpSplitter = exports.OpDecompressor = exports.OpCompressor = exports.getLongStack = exports.Outbox = exports.localBatchToOutboundBatch = exports.estimateSocketSize = exports.tryGetDeserializedRuntimeOpCopy = exports.ensureContentsDeserialized = exports.serializeOp = exports.DuplicateBatchDetector = exports.generateBatchId = exports.getEffectiveBatchId = exports.BatchManager = exports.addBatchMetadata = void 0;
|
|
8
8
|
var batchManager_js_1 = require("./batchManager.js");
|
|
9
9
|
Object.defineProperty(exports, "addBatchMetadata", { enumerable: true, get: function () { return batchManager_js_1.addBatchMetadata; } });
|
|
10
10
|
Object.defineProperty(exports, "BatchManager", { enumerable: true, get: function () { return batchManager_js_1.BatchManager; } });
|
|
@@ -15,6 +15,7 @@ Object.defineProperty(exports, "DuplicateBatchDetector", { enumerable: true, get
|
|
|
15
15
|
var opSerialization_js_1 = require("./opSerialization.js");
|
|
16
16
|
Object.defineProperty(exports, "serializeOp", { enumerable: true, get: function () { return opSerialization_js_1.serializeOp; } });
|
|
17
17
|
Object.defineProperty(exports, "ensureContentsDeserialized", { enumerable: true, get: function () { return opSerialization_js_1.ensureContentsDeserialized; } });
|
|
18
|
+
Object.defineProperty(exports, "tryGetDeserializedRuntimeOpCopy", { enumerable: true, get: function () { return opSerialization_js_1.tryGetDeserializedRuntimeOpCopy; } });
|
|
18
19
|
var outbox_js_1 = require("./outbox.js");
|
|
19
20
|
Object.defineProperty(exports, "estimateSocketSize", { enumerable: true, get: function () { return outbox_js_1.estimateSocketSize; } });
|
|
20
21
|
Object.defineProperty(exports, "localBatchToOutboundBatch", { enumerable: true, get: function () { return outbox_js_1.localBatchToOutboundBatch; } });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/opLifecycle/index.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAEH,qDAQ2B;AAP1B,mHAAA,gBAAgB,OAAA;AAEhB,+GAAA,YAAY,OAAA;AAEZ,sHAAA,mBAAmB,OAAA;AACnB,kHAAA,eAAe,OAAA;AAahB,yEAAqE;AAA5D,mIAAA,sBAAsB,OAAA;AAC/B,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/opLifecycle/index.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAEH,qDAQ2B;AAP1B,mHAAA,gBAAgB,OAAA;AAEhB,+GAAA,YAAY,OAAA;AAEZ,sHAAA,mBAAmB,OAAA;AACnB,kHAAA,eAAe,OAAA;AAahB,yEAAqE;AAA5D,mIAAA,sBAAsB,OAAA;AAC/B,2DAI8B;AAH7B,iHAAA,WAAW,OAAA;AACX,gIAAA,0BAA0B,OAAA;AAC1B,qIAAA,+BAA+B,OAAA;AAEhC,yCAMqB;AAJpB,+GAAA,kBAAkB,OAAA;AAClB,sHAAA,yBAAyB,OAAA;AACzB,mGAAA,MAAM,OAAA;AACN,yGAAA,YAAY,OAAA;AAEb,qDAAiD;AAAxC,+GAAA,YAAY,OAAA;AACrB,yDAAqD;AAA5C,mHAAA,cAAc,OAAA;AACvB,iDAKyB;AAHxB,2GAAA,UAAU,OAAA;AACV,wGAAA,OAAO,OAAA;AACP,iHAAA,gBAAgB,OAAA;AAEjB,yEAKqC;AAFpC,mIAAA,sBAAsB,OAAA;AACtB,iIAAA,oBAAoB,OAAA;AAErB,+DAMgC;AAJ/B,2HAAA,mBAAmB,OAAA;AACnB,yHAAA,iBAAiB,OAAA;AAEjB,sHAAA,cAAc,OAAA","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nexport {\n\taddBatchMetadata,\n\ttype BatchId,\n\tBatchManager,\n\ttype BatchSequenceNumbers,\n\tgetEffectiveBatchId,\n\tgenerateBatchId,\n\ttype IBatchManagerOptions,\n} from \"./batchManager.js\";\nexport type {\n\tLocalBatch,\n\tLocalBatchMessage,\n\tLocalEmptyBatchPlaceholder,\n\tOutboundBatch,\n\tOutboundBatchMessage,\n\tOutboundSingletonBatch,\n\tIBatchCheckpoint,\n\tIChunkedOp,\n} from \"./definitions.js\";\nexport { DuplicateBatchDetector } from \"./duplicateBatchDetector.js\";\nexport {\n\tserializeOp,\n\tensureContentsDeserialized,\n\ttryGetDeserializedRuntimeOpCopy,\n} from \"./opSerialization.js\";\nexport {\n\ttype BatchResubmitInfo,\n\testimateSocketSize,\n\tlocalBatchToOutboundBatch,\n\tOutbox,\n\tgetLongStack,\n} from \"./outbox.js\";\nexport { OpCompressor } from \"./opCompressor.js\";\nexport { OpDecompressor } from \"./opDecompressor.js\";\nexport {\n\ttype OpSplitterOptions,\n\tOpSplitter,\n\tsplitOp,\n\tisChunkedMessage,\n} from \"./opSplitter.js\";\nexport {\n\ttype InboundMessageResult,\n\ttype BatchStartInfo,\n\tRemoteMessageProcessor,\n\tunpackRuntimeMessage,\n} from \"./remoteMessageProcessor.js\";\nexport {\n\ttype EmptyGroupedBatch,\n\tlargeBatchThreshold,\n\tOpGroupingManager,\n\ttype OpGroupingManagerConfig,\n\tisGroupedBatch,\n} from \"./opGroupingManager.js\";\n"]}
|
|
@@ -18,6 +18,7 @@ const outbox_js_1 = require("./outbox.js");
|
|
|
18
18
|
* Use opGroupingManager to group a batch into a singleton batch suitable for compression.
|
|
19
19
|
*/
|
|
20
20
|
class OpCompressor {
|
|
21
|
+
logger;
|
|
21
22
|
constructor(logger) {
|
|
22
23
|
this.logger = (0, internal_2.createChildLogger)({ logger, namespace: "OpCompressor" });
|
|
23
24
|
}
|
|
@@ -32,7 +33,7 @@ class OpCompressor {
|
|
|
32
33
|
const compressionStart = Date.now();
|
|
33
34
|
const contentsAsBuffer = new TextEncoder().encode(this.serializeBatchContents(batch));
|
|
34
35
|
const compressedContents = (0, lz4js_1.compress)(contentsAsBuffer);
|
|
35
|
-
const compressedContent = client_utils_1.
|
|
36
|
+
const compressedContent = (0, client_utils_1.Uint8ArrayToString)(compressedContents, "base64");
|
|
36
37
|
const duration = Date.now() - compressionStart;
|
|
37
38
|
const messages = [
|
|
38
39
|
{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"opCompressor.js","sourceRoot":"","sources":["../../src/opLifecycle/opCompressor.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAEH,+
|
|
1
|
+
{"version":3,"file":"opCompressor.js","sourceRoot":"","sources":["../../src/opLifecycle/opCompressor.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 { Uint8ArrayToString } from \"@fluid-internal/client-utils\";\nimport type { ITelemetryBaseLogger } from \"@fluidframework/core-interfaces\";\nimport { assert } from \"@fluidframework/core-utils/internal\";\nimport {\n\tcreateChildLogger,\n\tDataProcessingError,\n\ttype TelemetryLoggerExt,\n} from \"@fluidframework/telemetry-utils/internal\";\nimport { compress } from \"lz4js\";\n\nimport { CompressionAlgorithms } from \"../compressionDefinitions.js\";\n\nimport type { OutboundBatchMessage, OutboundSingletonBatch } from \"./definitions.js\";\nimport { estimateSocketSize } from \"./outbox.js\";\n\n/**\n * Compresses batches of ops.\n *\n * @remarks Only single-message batches are supported\n * Use opGroupingManager to group a batch into a singleton batch suitable for compression.\n */\nexport class OpCompressor {\n\tprivate readonly logger: TelemetryLoggerExt;\n\n\tconstructor(logger: ITelemetryBaseLogger) {\n\t\tthis.logger = createChildLogger({ logger, namespace: \"OpCompressor\" });\n\t}\n\n\t/**\n\t * Combines the contents of the singleton batch into a single JSON string and compresses it, putting\n\t * the resulting string as the message contents in place of the original uncompressed payload.\n\t * @param batch - The batch to compress. Must have only 1 message\n\t * @returns A singleton batch containing a single compressed message\n\t */\n\tpublic compressBatch(batch: OutboundSingletonBatch): OutboundSingletonBatch {\n\t\tassert(\n\t\t\tbatch.contentSizeInBytes > 0 && batch.messages.length === 1,\n\t\t\t0x5a4 /* Batch should not be empty and should contain a single message */,\n\t\t);\n\n\t\tconst compressionStart = Date.now();\n\t\tconst contentsAsBuffer = new TextEncoder().encode(this.serializeBatchContents(batch));\n\t\tconst compressedContents = compress(contentsAsBuffer);\n\t\tconst compressedContent = Uint8ArrayToString(compressedContents, \"base64\");\n\t\tconst duration = Date.now() - compressionStart;\n\n\t\tconst messages: [OutboundBatchMessage] = [\n\t\t\t{\n\t\t\t\t...batch.messages[0],\n\t\t\t\tcontents: JSON.stringify({ packedContents: compressedContent }),\n\t\t\t\tmetadata: batch.messages[0].metadata,\n\t\t\t\tcompression: CompressionAlgorithms.lz4,\n\t\t\t},\n\t\t];\n\n\t\tconst compressedBatch: OutboundSingletonBatch = {\n\t\t\tcontentSizeInBytes: compressedContent.length,\n\t\t\tmessages,\n\t\t\treferenceSequenceNumber: batch.referenceSequenceNumber,\n\t\t};\n\n\t\tif (batch.contentSizeInBytes > 200000) {\n\t\t\tthis.logger.sendPerformanceEvent({\n\t\t\t\teventName: \"CompressedBatch\",\n\t\t\t\tduration,\n\t\t\t\tsizeBeforeCompression: batch.contentSizeInBytes,\n\t\t\t\tsizeAfterCompression: compressedBatch.contentSizeInBytes,\n\t\t\t\topCount: compressedBatch.messages.length,\n\t\t\t\tsocketSize: estimateSocketSize(compressedBatch),\n\t\t\t});\n\t\t}\n\n\t\treturn compressedBatch;\n\t}\n\n\t/**\n\t * Combine the batch's content strings into a single JSON string (a serialized array)\n\t */\n\tprivate serializeBatchContents(batch: OutboundSingletonBatch): string {\n\t\tconst [message, ...none] = batch.messages;\n\t\tassert(none.length === 0, 0xb78 /* Batch should only contain a single message */);\n\t\ttry {\n\t\t\t// This is expressed as a JSON array, for legacy reasons\n\t\t\treturn `[${message.contents}]`;\n\t\t} catch (error: unknown) {\n\t\t\tif ((error as Partial<Error>).message === \"Invalid string length\") {\n\t\t\t\t// This is how string interpolation signals that\n\t\t\t\t// the content size exceeds its capacity\n\t\t\t\tconst dpe = DataProcessingError.create(\"Payload too large\", \"OpCompressor\");\n\t\t\t\tthis.logger.sendErrorEvent(\n\t\t\t\t\t{\n\t\t\t\t\t\teventName: \"BatchTooLarge\",\n\t\t\t\t\t\tsize: batch.contentSizeInBytes,\n\t\t\t\t\t\tlength: batch.messages.length,\n\t\t\t\t\t},\n\t\t\t\t\tdpe,\n\t\t\t\t);\n\t\t\t\tthrow dpe;\n\t\t\t}\n\n\t\t\tthrow error;\n\t\t}\n\t}\n}\n"]}
|
|
@@ -22,13 +22,13 @@ const compressionDefinitions_js_1 = require("../compressionDefinitions.js");
|
|
|
22
22
|
* because we need that functionality for back compat.
|
|
23
23
|
*/
|
|
24
24
|
class OpDecompressor {
|
|
25
|
+
activeBatch = false;
|
|
26
|
+
// TODO: better typing
|
|
27
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
28
|
+
rootMessageContents;
|
|
29
|
+
processedCount = 0;
|
|
30
|
+
logger;
|
|
25
31
|
constructor(logger) {
|
|
26
|
-
this.activeBatch = false;
|
|
27
|
-
this.processedCount = 0;
|
|
28
|
-
/**
|
|
29
|
-
* Is the decompressed and stored batch only comprised of a single message
|
|
30
|
-
*/
|
|
31
|
-
this.isSingleMessageBatch = false;
|
|
32
32
|
this.logger = (0, internal_2.createChildLogger)({ logger, namespace: "OpDecompressor" });
|
|
33
33
|
}
|
|
34
34
|
isCompressedMessage(message) {
|
|
@@ -70,6 +70,10 @@ class OpDecompressor {
|
|
|
70
70
|
get currentlyUnrolling() {
|
|
71
71
|
return this.activeBatch;
|
|
72
72
|
}
|
|
73
|
+
/**
|
|
74
|
+
* Is the decompressed and stored batch only comprised of a single message
|
|
75
|
+
*/
|
|
76
|
+
isSingleMessageBatch = false;
|
|
73
77
|
/**
|
|
74
78
|
* Decompress the given compressed message and store it to be subsequently unrolled.
|
|
75
79
|
* The stored message will be of type `any[]` where each element represents a message's `contents`
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"opDecompressor.js","sourceRoot":"","sources":["../../src/opLifecycle/opDecompressor.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 { IsoBuffer, Uint8ArrayToString } from \"@fluid-internal/client-utils\";\nimport type { ITelemetryBaseLogger } from \"@fluidframework/core-interfaces\";\nimport { assert } from \"@fluidframework/core-utils/internal\";\nimport type { ISequencedDocumentMessage } from \"@fluidframework/driver-definitions/internal\";\nimport {\n\tcreateChildLogger,\n\ttype TelemetryLoggerExt,\n} from \"@fluidframework/telemetry-utils/internal\";\nimport { decompress } from \"lz4js\";\n\nimport { CompressionAlgorithms } from \"../compressionDefinitions.js\";\nimport type { IBatchMetadata } from \"../metadata.js\";\n\n/**\n * Compression makes assumptions about the shape of message contents. This interface codifies those assumptions, but does not validate them.\n */\ninterface IPackedContentsContents {\n\tpackedContents: string;\n}\n\n/**\n * State machine that \"unrolls\" contents of compressed batches of ops after decompressing them.\n * This class relies on some implicit contracts defined below:\n * 1. A compressed batch will have its first message with batch metadata set to true and compressed set to true\n * 2. Messages in the middle of a compressed batch will have neither batch metadata nor the compression property set\n * 3. The final message of a batch will have batch metadata set to false\n * 4. An individually compressed op will have undefined batch metadata and compression set to true\n *\n * Compressed batches from current code are always a single message but this class needs to handle a legacy compressed batch with multiple messages\n * because we need that functionality for back compat.\n */\nexport class OpDecompressor {\n\tprivate activeBatch = false;\n\t// TODO: better typing\n\t// eslint-disable-next-line @typescript-eslint/no-explicit-any\n\tprivate rootMessageContents: any | undefined;\n\tprivate processedCount = 0;\n\tprivate readonly logger: TelemetryLoggerExt;\n\n\tconstructor(logger: ITelemetryBaseLogger) {\n\t\tthis.logger = createChildLogger({ logger, namespace: \"OpDecompressor\" });\n\t}\n\n\tpublic isCompressedMessage(message: ISequencedDocumentMessage): boolean {\n\t\tif (message.compression === CompressionAlgorithms.lz4) {\n\t\t\treturn true;\n\t\t}\n\n\t\t/**\n\t\t * Back-compat self healing mechanism for ADO:3538, as loaders from\n\t\t * version client_v2.0.0-internal.1.2.0 to client_v2.0.0-internal.2.2.0 do not\n\t\t * support adding the proper compression metadata to compressed messages submitted\n\t\t * by the runtime. Should be removed after the loader reaches sufficient saturation\n\t\t * for a version greater or equal than client_v2.0.0-internal.2.2.0.\n\t\t *\n\t\t * The condition holds true for compressed messages, regardless of metadata. We are ultimately\n\t\t * looking for a message with a single property `packedContents` inside `contents`, of type 'string'\n\t\t * with a base64 encoded value.\n\t\t */\n\t\ttry {\n\t\t\tif (\n\t\t\t\tmessage.contents !== null &&\n\t\t\t\ttypeof message.contents === \"object\" &&\n\t\t\t\tObject.keys(message.contents).length === 1 &&\n\t\t\t\ttypeof (message.contents as { packedContents?: unknown }).packedContents ===\n\t\t\t\t\t\"string\" &&\n\t\t\t\t(message.contents as IPackedContentsContents).packedContents.length > 0 &&\n\t\t\t\tIsoBuffer.from(\n\t\t\t\t\t(message.contents as IPackedContentsContents).packedContents,\n\t\t\t\t\t\"base64\",\n\t\t\t\t).toString(\"base64\") === (message.contents as IPackedContentsContents).packedContents\n\t\t\t) {\n\t\t\t\tthis.logger.sendTelemetryEvent({\n\t\t\t\t\teventName: \"LegacyCompression\",\n\t\t\t\t\ttype: message.type,\n\t\t\t\t\tbatch: (message.metadata as IBatchMetadata | undefined)?.batch,\n\t\t\t\t});\n\t\t\t\treturn true;\n\t\t\t}\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\tpublic get currentlyUnrolling(): boolean {\n\t\treturn this.activeBatch;\n\t}\n\n\t/**\n\t * Is the decompressed and stored batch only comprised of a single message\n\t */\n\tprivate isSingleMessageBatch = false;\n\n\t/**\n\t * Decompress the given compressed message and store it to be subsequently unrolled.\n\t * The stored message will be of type `any[]` where each element represents a message's `contents`\n\t */\n\tpublic decompressAndStore(message: ISequencedDocumentMessage): void {\n\t\tassert(\n\t\t\tmessage.compression === undefined || message.compression === CompressionAlgorithms.lz4,\n\t\t\t0x511 /* Only lz4 compression is supported */,\n\t\t);\n\t\tassert(\n\t\t\tthis.isCompressedMessage(message),\n\t\t\t0x940 /* provided message should be compressed */,\n\t\t);\n\n\t\tassert(this.activeBatch === false, 0x4b8 /* shouldn't have multiple active batches */);\n\t\tthis.activeBatch = true;\n\n\t\tconst batchMetadata = (message.metadata as IBatchMetadata | undefined)?.batch;\n\t\tif (batchMetadata === undefined) {\n\t\t\tthis.isSingleMessageBatch = true;\n\t\t} else {\n\t\t\tassert(batchMetadata === true, 0x941 /* invalid batch metadata */);\n\t\t}\n\n\t\tconst contents = IsoBuffer.from(\n\t\t\t(message.contents as IPackedContentsContents).packedContents,\n\t\t\t\"base64\",\n\t\t);\n\t\tconst decompressedMessage = decompress(contents);\n\t\tconst intoString = Uint8ArrayToString(decompressedMessage);\n\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n\t\tthis.rootMessageContents = JSON.parse(intoString);\n\t}\n\n\t/**\n\t * Unroll the next message from the decompressed content provided to {@link decompressAndStore}\n\t * @returns the unrolled `ISequencedDocumentMessage`\n\t */\n\tpublic unroll(message: ISequencedDocumentMessage): ISequencedDocumentMessage {\n\t\tassert(this.currentlyUnrolling, 0x942 /* not currently unrolling */);\n\t\tassert(this.rootMessageContents !== undefined, 0x943 /* missing rootMessageContents */);\n\t\tassert(\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n\t\t\tthis.rootMessageContents.length > this.processedCount,\n\t\t\t0x944 /* no more content to unroll */,\n\t\t);\n\n\t\tconst batchMetadata = (message.metadata as IBatchMetadata | undefined)?.batch;\n\n\t\tif (batchMetadata === false || this.isSingleMessageBatch) {\n\t\t\t// End of compressed batch\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n\t\t\tconst returnMessage = newMessage(message, this.rootMessageContents[this.processedCount]);\n\n\t\t\tthis.activeBatch = false;\n\t\t\tthis.isSingleMessageBatch = false;\n\t\t\tthis.rootMessageContents = undefined;\n\t\t\tthis.processedCount = 0;\n\n\t\t\treturn returnMessage;\n\t\t} else if (batchMetadata === true) {\n\t\t\t// Start of compressed batch\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n\t\t\treturn newMessage(message, this.rootMessageContents[this.processedCount++]);\n\t\t}\n\n\t\tassert(batchMetadata === undefined, 0x945 /* invalid batch metadata */);\n\t\tassert(message.contents === undefined, 0x512 /* Expecting empty message */);\n\n\t\t// Continuation of compressed batch\n\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n\t\treturn newMessage(message, this.rootMessageContents[this.processedCount++]);\n\t}\n}\n\n// We should not be mutating the input message nor its metadata\nconst newMessage = (\n\toriginalMessage: ISequencedDocumentMessage,\n\tcontents: unknown,\n): ISequencedDocumentMessage => ({\n\t...originalMessage,\n\tcontents,\n\tcompression: undefined,\n\t// TODO: It should already be the case that we're not modifying any metadata, not clear if/why this shallow clone should be required.\n\n\tmetadata:\n\t\toriginalMessage.metadata === undefined ? undefined : { ...originalMessage.metadata },\n});\n"]}
|
|
1
|
+
{"version":3,"file":"opDecompressor.js","sourceRoot":"","sources":["../../src/opLifecycle/opDecompressor.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAEH,+DAA6E;AAE7E,kEAA6D;AAE7D,uEAGkD;AAClD,iCAAmC;AAEnC,4EAAqE;AAUrE;;;;;;;;;;GAUG;AACH,MAAa,cAAc;IAClB,WAAW,GAAG,KAAK,CAAC;IAC5B,sBAAsB;IACtB,8DAA8D;IACtD,mBAAmB,CAAkB;IACrC,cAAc,GAAG,CAAC,CAAC;IACV,MAAM,CAAqB;IAE5C,YAAY,MAA4B;QACvC,IAAI,CAAC,MAAM,GAAG,IAAA,4BAAiB,EAAC,EAAE,MAAM,EAAE,SAAS,EAAE,gBAAgB,EAAE,CAAC,CAAC;IAC1E,CAAC;IAEM,mBAAmB,CAAC,OAAkC;QAC5D,IAAI,OAAO,CAAC,WAAW,KAAK,iDAAqB,CAAC,GAAG,EAAE,CAAC;YACvD,OAAO,IAAI,CAAC;QACb,CAAC;QAED;;;;;;;;;;WAUG;QACH,IAAI,CAAC;YACJ,IACC,OAAO,CAAC,QAAQ,KAAK,IAAI;gBACzB,OAAO,OAAO,CAAC,QAAQ,KAAK,QAAQ;gBACpC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,MAAM,KAAK,CAAC;gBAC1C,OAAQ,OAAO,CAAC,QAAyC,CAAC,cAAc;oBACvE,QAAQ;gBACR,OAAO,CAAC,QAAoC,CAAC,cAAc,CAAC,MAAM,GAAG,CAAC;gBACvE,wBAAS,CAAC,IAAI,CACZ,OAAO,CAAC,QAAoC,CAAC,cAAc,EAC5D,QAAQ,CACR,CAAC,QAAQ,CAAC,QAAQ,CAAC,KAAM,OAAO,CAAC,QAAoC,CAAC,cAAc,EACpF,CAAC;gBACF,IAAI,CAAC,MAAM,CAAC,kBAAkB,CAAC;oBAC9B,SAAS,EAAE,mBAAmB;oBAC9B,IAAI,EAAE,OAAO,CAAC,IAAI;oBAClB,KAAK,EAAG,OAAO,CAAC,QAAuC,EAAE,KAAK;iBAC9D,CAAC,CAAC;gBACH,OAAO,IAAI,CAAC;YACb,CAAC;QACF,CAAC;QAAC,MAAM,CAAC;YACR,OAAO,KAAK,CAAC;QACd,CAAC;QAED,OAAO,KAAK,CAAC;IACd,CAAC;IAED,IAAW,kBAAkB;QAC5B,OAAO,IAAI,CAAC,WAAW,CAAC;IACzB,CAAC;IAED;;OAEG;IACK,oBAAoB,GAAG,KAAK,CAAC;IAErC;;;OAGG;IACI,kBAAkB,CAAC,OAAkC;QAC3D,IAAA,iBAAM,EACL,OAAO,CAAC,WAAW,KAAK,SAAS,IAAI,OAAO,CAAC,WAAW,KAAK,iDAAqB,CAAC,GAAG,EACtF,KAAK,CAAC,uCAAuC,CAC7C,CAAC;QACF,IAAA,iBAAM,EACL,IAAI,CAAC,mBAAmB,CAAC,OAAO,CAAC,EACjC,KAAK,CAAC,2CAA2C,CACjD,CAAC;QAEF,IAAA,iBAAM,EAAC,IAAI,CAAC,WAAW,KAAK,KAAK,EAAE,KAAK,CAAC,4CAA4C,CAAC,CAAC;QACvF,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;QAExB,MAAM,aAAa,GAAI,OAAO,CAAC,QAAuC,EAAE,KAAK,CAAC;QAC9E,IAAI,aAAa,KAAK,SAAS,EAAE,CAAC;YACjC,IAAI,CAAC,oBAAoB,GAAG,IAAI,CAAC;QAClC,CAAC;aAAM,CAAC;YACP,IAAA,iBAAM,EAAC,aAAa,KAAK,IAAI,EAAE,KAAK,CAAC,4BAA4B,CAAC,CAAC;QACpE,CAAC;QAED,MAAM,QAAQ,GAAG,wBAAS,CAAC,IAAI,CAC7B,OAAO,CAAC,QAAoC,CAAC,cAAc,EAC5D,QAAQ,CACR,CAAC;QACF,MAAM,mBAAmB,GAAG,IAAA,kBAAU,EAAC,QAAQ,CAAC,CAAC;QACjD,MAAM,UAAU,GAAG,IAAA,iCAAkB,EAAC,mBAAmB,CAAC,CAAC;QAC3D,mEAAmE;QACnE,IAAI,CAAC,mBAAmB,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IACnD,CAAC;IAED;;;OAGG;IACI,MAAM,CAAC,OAAkC;QAC/C,IAAA,iBAAM,EAAC,IAAI,CAAC,kBAAkB,EAAE,KAAK,CAAC,6BAA6B,CAAC,CAAC;QACrE,IAAA,iBAAM,EAAC,IAAI,CAAC,mBAAmB,KAAK,SAAS,EAAE,KAAK,CAAC,iCAAiC,CAAC,CAAC;QACxF,IAAA,iBAAM;QACL,sEAAsE;QACtE,IAAI,CAAC,mBAAmB,CAAC,MAAM,GAAG,IAAI,CAAC,cAAc,EACrD,KAAK,CAAC,+BAA+B,CACrC,CAAC;QAEF,MAAM,aAAa,GAAI,OAAO,CAAC,QAAuC,EAAE,KAAK,CAAC;QAE9E,IAAI,aAAa,KAAK,KAAK,IAAI,IAAI,CAAC,oBAAoB,EAAE,CAAC;YAC1D,0BAA0B;YAC1B,sEAAsE;YACtE,MAAM,aAAa,GAAG,UAAU,CAAC,OAAO,EAAE,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC;YAEzF,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC;YACzB,IAAI,CAAC,oBAAoB,GAAG,KAAK,CAAC;YAClC,IAAI,CAAC,mBAAmB,GAAG,SAAS,CAAC;YACrC,IAAI,CAAC,cAAc,GAAG,CAAC,CAAC;YAExB,OAAO,aAAa,CAAC;QACtB,CAAC;aAAM,IAAI,aAAa,KAAK,IAAI,EAAE,CAAC;YACnC,4BAA4B;YAC5B,sEAAsE;YACtE,OAAO,UAAU,CAAC,OAAO,EAAE,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC;QAC7E,CAAC;QAED,IAAA,iBAAM,EAAC,aAAa,KAAK,SAAS,EAAE,KAAK,CAAC,4BAA4B,CAAC,CAAC;QACxE,IAAA,iBAAM,EAAC,OAAO,CAAC,QAAQ,KAAK,SAAS,EAAE,KAAK,CAAC,6BAA6B,CAAC,CAAC;QAE5E,mCAAmC;QACnC,sEAAsE;QACtE,OAAO,UAAU,CAAC,OAAO,EAAE,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC;IAC7E,CAAC;CACD;AAzID,wCAyIC;AAED,+DAA+D;AAC/D,MAAM,UAAU,GAAG,CAClB,eAA0C,EAC1C,QAAiB,EACW,EAAE,CAAC,CAAC;IAChC,GAAG,eAAe;IAClB,QAAQ;IACR,WAAW,EAAE,SAAS;IACtB,qIAAqI;IAErI,QAAQ,EACP,eAAe,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,GAAG,eAAe,CAAC,QAAQ,EAAE;CACrF,CAAC,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { IsoBuffer, Uint8ArrayToString } from \"@fluid-internal/client-utils\";\nimport type { ITelemetryBaseLogger } from \"@fluidframework/core-interfaces\";\nimport { assert } from \"@fluidframework/core-utils/internal\";\nimport type { ISequencedDocumentMessage } from \"@fluidframework/driver-definitions/internal\";\nimport {\n\tcreateChildLogger,\n\ttype TelemetryLoggerExt,\n} from \"@fluidframework/telemetry-utils/internal\";\nimport { decompress } from \"lz4js\";\n\nimport { CompressionAlgorithms } from \"../compressionDefinitions.js\";\nimport type { IBatchMetadata } from \"../metadata.js\";\n\n/**\n * Compression makes assumptions about the shape of message contents. This interface codifies those assumptions, but does not validate them.\n */\ninterface IPackedContentsContents {\n\tpackedContents: string;\n}\n\n/**\n * State machine that \"unrolls\" contents of compressed batches of ops after decompressing them.\n * This class relies on some implicit contracts defined below:\n * 1. A compressed batch will have its first message with batch metadata set to true and compressed set to true\n * 2. Messages in the middle of a compressed batch will have neither batch metadata nor the compression property set\n * 3. The final message of a batch will have batch metadata set to false\n * 4. An individually compressed op will have undefined batch metadata and compression set to true\n *\n * Compressed batches from current code are always a single message but this class needs to handle a legacy compressed batch with multiple messages\n * because we need that functionality for back compat.\n */\nexport class OpDecompressor {\n\tprivate activeBatch = false;\n\t// TODO: better typing\n\t// eslint-disable-next-line @typescript-eslint/no-explicit-any\n\tprivate rootMessageContents: any | undefined;\n\tprivate processedCount = 0;\n\tprivate readonly logger: TelemetryLoggerExt;\n\n\tconstructor(logger: ITelemetryBaseLogger) {\n\t\tthis.logger = createChildLogger({ logger, namespace: \"OpDecompressor\" });\n\t}\n\n\tpublic isCompressedMessage(message: ISequencedDocumentMessage): boolean {\n\t\tif (message.compression === CompressionAlgorithms.lz4) {\n\t\t\treturn true;\n\t\t}\n\n\t\t/**\n\t\t * Back-compat self healing mechanism for ADO:3538, as loaders from\n\t\t * version client_v2.0.0-internal.1.2.0 to client_v2.0.0-internal.2.2.0 do not\n\t\t * support adding the proper compression metadata to compressed messages submitted\n\t\t * by the runtime. Should be removed after the loader reaches sufficient saturation\n\t\t * for a version greater or equal than client_v2.0.0-internal.2.2.0.\n\t\t *\n\t\t * The condition holds true for compressed messages, regardless of metadata. We are ultimately\n\t\t * looking for a message with a single property `packedContents` inside `contents`, of type 'string'\n\t\t * with a base64 encoded value.\n\t\t */\n\t\ttry {\n\t\t\tif (\n\t\t\t\tmessage.contents !== null &&\n\t\t\t\ttypeof message.contents === \"object\" &&\n\t\t\t\tObject.keys(message.contents).length === 1 &&\n\t\t\t\ttypeof (message.contents as { packedContents?: unknown }).packedContents ===\n\t\t\t\t\t\"string\" &&\n\t\t\t\t(message.contents as IPackedContentsContents).packedContents.length > 0 &&\n\t\t\t\tIsoBuffer.from(\n\t\t\t\t\t(message.contents as IPackedContentsContents).packedContents,\n\t\t\t\t\t\"base64\",\n\t\t\t\t).toString(\"base64\") === (message.contents as IPackedContentsContents).packedContents\n\t\t\t) {\n\t\t\t\tthis.logger.sendTelemetryEvent({\n\t\t\t\t\teventName: \"LegacyCompression\",\n\t\t\t\t\ttype: message.type,\n\t\t\t\t\tbatch: (message.metadata as IBatchMetadata | undefined)?.batch,\n\t\t\t\t});\n\t\t\t\treturn true;\n\t\t\t}\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\tpublic get currentlyUnrolling(): boolean {\n\t\treturn this.activeBatch;\n\t}\n\n\t/**\n\t * Is the decompressed and stored batch only comprised of a single message\n\t */\n\tprivate isSingleMessageBatch = false;\n\n\t/**\n\t * Decompress the given compressed message and store it to be subsequently unrolled.\n\t * The stored message will be of type `any[]` where each element represents a message's `contents`\n\t */\n\tpublic decompressAndStore(message: ISequencedDocumentMessage): void {\n\t\tassert(\n\t\t\tmessage.compression === undefined || message.compression === CompressionAlgorithms.lz4,\n\t\t\t0x511 /* Only lz4 compression is supported */,\n\t\t);\n\t\tassert(\n\t\t\tthis.isCompressedMessage(message),\n\t\t\t0x940 /* provided message should be compressed */,\n\t\t);\n\n\t\tassert(this.activeBatch === false, 0x4b8 /* shouldn't have multiple active batches */);\n\t\tthis.activeBatch = true;\n\n\t\tconst batchMetadata = (message.metadata as IBatchMetadata | undefined)?.batch;\n\t\tif (batchMetadata === undefined) {\n\t\t\tthis.isSingleMessageBatch = true;\n\t\t} else {\n\t\t\tassert(batchMetadata === true, 0x941 /* invalid batch metadata */);\n\t\t}\n\n\t\tconst contents = IsoBuffer.from(\n\t\t\t(message.contents as IPackedContentsContents).packedContents,\n\t\t\t\"base64\",\n\t\t);\n\t\tconst decompressedMessage = decompress(contents);\n\t\tconst intoString = Uint8ArrayToString(decompressedMessage);\n\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n\t\tthis.rootMessageContents = JSON.parse(intoString);\n\t}\n\n\t/**\n\t * Unroll the next message from the decompressed content provided to {@link decompressAndStore}\n\t * @returns the unrolled `ISequencedDocumentMessage`\n\t */\n\tpublic unroll(message: ISequencedDocumentMessage): ISequencedDocumentMessage {\n\t\tassert(this.currentlyUnrolling, 0x942 /* not currently unrolling */);\n\t\tassert(this.rootMessageContents !== undefined, 0x943 /* missing rootMessageContents */);\n\t\tassert(\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n\t\t\tthis.rootMessageContents.length > this.processedCount,\n\t\t\t0x944 /* no more content to unroll */,\n\t\t);\n\n\t\tconst batchMetadata = (message.metadata as IBatchMetadata | undefined)?.batch;\n\n\t\tif (batchMetadata === false || this.isSingleMessageBatch) {\n\t\t\t// End of compressed batch\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n\t\t\tconst returnMessage = newMessage(message, this.rootMessageContents[this.processedCount]);\n\n\t\t\tthis.activeBatch = false;\n\t\t\tthis.isSingleMessageBatch = false;\n\t\t\tthis.rootMessageContents = undefined;\n\t\t\tthis.processedCount = 0;\n\n\t\t\treturn returnMessage;\n\t\t} else if (batchMetadata === true) {\n\t\t\t// Start of compressed batch\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n\t\t\treturn newMessage(message, this.rootMessageContents[this.processedCount++]);\n\t\t}\n\n\t\tassert(batchMetadata === undefined, 0x945 /* invalid batch metadata */);\n\t\tassert(message.contents === undefined, 0x512 /* Expecting empty message */);\n\n\t\t// Continuation of compressed batch\n\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n\t\treturn newMessage(message, this.rootMessageContents[this.processedCount++]);\n\t}\n}\n\n// We should not be mutating the input message nor its metadata\nconst newMessage = (\n\toriginalMessage: ISequencedDocumentMessage,\n\tcontents: unknown,\n): ISequencedDocumentMessage => ({\n\t...originalMessage,\n\tcontents,\n\tcompression: undefined,\n\t// TODO: It should already be the case that we're not modifying any metadata, not clear if/why this shallow clone should be required.\n\n\tmetadata:\n\t\toriginalMessage.metadata === undefined ? undefined : { ...originalMessage.metadata },\n});\n"]}
|
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
* Licensed under the MIT License.
|
|
5
5
|
*/
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
exports.OpGroupingManager = exports.
|
|
7
|
+
exports.OpGroupingManager = exports.largeBatchThreshold = void 0;
|
|
8
|
+
exports.isGroupedBatch = isGroupedBatch;
|
|
8
9
|
const internal_1 = require("@fluidframework/core-utils/internal");
|
|
9
10
|
const internal_2 = require("@fluidframework/telemetry-utils/internal");
|
|
10
11
|
/**
|
|
@@ -20,8 +21,10 @@ function isGroupContents(opContents) {
|
|
|
20
21
|
function isGroupedBatch(op) {
|
|
21
22
|
return isGroupContents(op.contents);
|
|
22
23
|
}
|
|
23
|
-
exports.isGroupedBatch = isGroupedBatch;
|
|
24
24
|
class OpGroupingManager {
|
|
25
|
+
config;
|
|
26
|
+
static groupedBatchOp = "groupedBatch";
|
|
27
|
+
logger;
|
|
25
28
|
constructor(config, logger) {
|
|
26
29
|
this.config = config;
|
|
27
30
|
this.logger = (0, internal_2.createChildLogger)({ logger, namespace: "OpGroupingManager" });
|
|
@@ -129,5 +132,4 @@ class OpGroupingManager {
|
|
|
129
132
|
}
|
|
130
133
|
}
|
|
131
134
|
exports.OpGroupingManager = OpGroupingManager;
|
|
132
|
-
OpGroupingManager.groupedBatchOp = "groupedBatch";
|
|
133
135
|
//# sourceMappingURL=opGroupingManager.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"opGroupingManager.js","sourceRoot":"","sources":["../../src/opLifecycle/opGroupingManager.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport type { ITelemetryBaseLogger } from \"@fluidframework/core-interfaces\";\nimport { assert } from \"@fluidframework/core-utils/internal\";\nimport type { ISequencedDocumentMessage } from \"@fluidframework/driver-definitions/internal\";\nimport {\n\tcreateChildLogger,\n\ttype TelemetryLoggerExt,\n} from \"@fluidframework/telemetry-utils/internal\";\n\nimport type {\n\tLocalEmptyBatchPlaceholder,\n\tOutboundBatch,\n\tOutboundSingletonBatch,\n} from \"./definitions.js\";\n\n/**\n * The number of ops in a batch above which the batch is considered \"large\"\n * for telemetry purposes. Used by both {@link OpGroupingManager} (GroupLargeBatch event)\n * and as the default staging-mode auto-flush threshold.\n */\nexport const largeBatchThreshold = 1000;\n\n/**\n * Grouping makes assumptions about the shape of message contents. This interface codifies those assumptions, but does not validate them.\n */\ninterface IGroupedBatchMessageContents {\n\ttype: typeof OpGroupingManager.groupedBatchOp;\n\tcontents: IGroupedMessage[];\n}\n\ninterface IGroupedMessage {\n\tcontents?: unknown;\n\tmetadata?: Record<string, unknown>;\n\tcompression?: string;\n}\n\nfunction isGroupContents(opContents: unknown): opContents is IGroupedBatchMessageContents {\n\treturn (\n\t\t(opContents as Partial<IGroupedBatchMessageContents>)?.type ===\n\t\tOpGroupingManager.groupedBatchOp\n\t);\n}\n\nexport function isGroupedBatch(op: ISequencedDocumentMessage): boolean {\n\treturn isGroupContents(op.contents);\n}\n\nexport interface OpGroupingManagerConfig {\n\treadonly groupedBatchingEnabled: boolean;\n}\n\n/**\n * This is the type of an empty grouped batch we send over the wire\n * We also put this in the placeholder for an empty batch in the PendingStateManager.\n * But most places throughout the ContainerRuntime, this will not be used (just as Grouped Batches in general don't appear outside opLifecycle dir)\n */\nexport interface EmptyGroupedBatch {\n\ttype: typeof OpGroupingManager.groupedBatchOp;\n\tcontents: readonly [];\n}\n\nexport class OpGroupingManager {\n\tstatic readonly groupedBatchOp = \"groupedBatch\";\n\tprivate readonly logger: TelemetryLoggerExt;\n\n\tconstructor(\n\t\tprivate readonly config: OpGroupingManagerConfig,\n\t\tlogger: ITelemetryBaseLogger,\n\t) {\n\t\tthis.logger = createChildLogger({ logger, namespace: \"OpGroupingManager\" });\n\t}\n\n\t/**\n\t * Creates a new batch with a single message of type \"groupedBatch\" and empty contents.\n\t * This is needed as a placeholder if a batch becomes empty on resubmit, but we are tracking batch IDs.\n\t * @param resubmittingBatchId - batch ID of the resubmitting batch\n\t * @param referenceSequenceNumber - reference sequence number\n\t * @returns The outbound batch as well as the interior placeholder message\n\t */\n\tpublic createEmptyGroupedBatch(\n\t\tresubmittingBatchId: string,\n\t\treferenceSequenceNumber: number,\n\t): {\n\t\toutboundBatch: OutboundSingletonBatch;\n\t\tplaceholderMessage: LocalEmptyBatchPlaceholder;\n\t} {\n\t\tassert(\n\t\t\tthis.config.groupedBatchingEnabled,\n\t\t\t0xa00 /* cannot create empty grouped batch when grouped batching is disabled */,\n\t\t);\n\n\t\tconst emptyGroupedBatch: EmptyGroupedBatch = {\n\t\t\ttype: \"groupedBatch\",\n\t\t\tcontents: [],\n\t\t};\n\t\tconst serializedOp = JSON.stringify(emptyGroupedBatch);\n\n\t\tconst placeholderMessage: LocalEmptyBatchPlaceholder = {\n\t\t\tmetadata: { batchId: resubmittingBatchId, groupedOpCount: 0 },\n\t\t\tlocalOpMetadata: { emptyBatch: true },\n\t\t\treferenceSequenceNumber,\n\t\t\truntimeOp: emptyGroupedBatch,\n\t\t};\n\t\tconst outboundBatch: OutboundSingletonBatch = {\n\t\t\tcontentSizeInBytes: 0,\n\t\t\tmessages: [{ ...placeholderMessage, runtimeOp: undefined, contents: serializedOp }],\n\t\t\treferenceSequenceNumber,\n\t\t};\n\t\treturn { outboundBatch, placeholderMessage };\n\t}\n\n\t/**\n\t * Converts the given batch into a \"grouped batch\" - a batch with a single message of type \"groupedBatch\",\n\t * with contents being an array of the original batch's messages.\n\t *\n\t * If the batch already has only 1 message, it is returned as-is.\n\t *\n\t * @remarks Remember that a BatchMessage has its content JSON serialized, so the incoming batch message contents\n\t * must be parsed first, and then the type and contents mentioned above are hidden in that JSON serialization.\n\t */\n\tpublic groupBatch(batch: OutboundBatch): OutboundSingletonBatch {\n\t\tassert(this.groupedBatchingEnabled(), 0xb79 /* grouping disabled! */);\n\t\tassert(batch.messages.length > 0, 0xb7a /* Unexpected attempt to group an empty batch */);\n\n\t\tif (batch.messages.length === 1) {\n\t\t\treturn batch as OutboundSingletonBatch;\n\t\t}\n\n\t\t// Use > (not >=) so that batches flushed exactly at the staging-mode\n\t\t// auto-flush threshold (which defaults to largeBatchThreshold) don't\n\t\t// trigger this event. Only genuinely oversized batches are logged.\n\t\tif (batch.messages.length > largeBatchThreshold) {\n\t\t\tthis.logger.sendTelemetryEvent({\n\t\t\t\teventName: \"GroupLargeBatch\",\n\t\t\t\tlength: batch.messages.length,\n\t\t\t\treentrant: batch.hasReentrantOps,\n\t\t\t\treferenceSequenceNumber: batch.messages[0].referenceSequenceNumber,\n\t\t\t});\n\t\t}\n\t\t// We expect this will be on the first message, if present at all.\n\t\tlet groupedBatchId: unknown;\n\t\tfor (const message of batch.messages) {\n\t\t\tif (message.metadata !== undefined) {\n\t\t\t\tconst { batch: _batch, batchId, ...rest } = message.metadata;\n\t\t\t\tif (batchId !== undefined) {\n\t\t\t\t\tgroupedBatchId = batchId;\n\t\t\t\t}\n\t\t\t\tassert(Object.keys(rest).length === 0, 0x5dd /* cannot group ops with metadata */);\n\t\t\t}\n\t\t}\n\n\t\tconst serializedContent = JSON.stringify({\n\t\t\ttype: OpGroupingManager.groupedBatchOp,\n\t\t\tcontents: batch.messages.map<IGroupedMessage>((message) => ({\n\t\t\t\tcontents:\n\t\t\t\t\tmessage.contents === undefined\n\t\t\t\t\t\t? undefined\n\t\t\t\t\t\t: (JSON.parse(message.contents) as unknown),\n\t\t\t\tmetadata: message.metadata,\n\t\t\t\tcompression: message.compression,\n\t\t\t})),\n\t\t});\n\n\t\tconst groupedBatch: OutboundSingletonBatch = {\n\t\t\t...batch,\n\t\t\tmessages: [\n\t\t\t\t{\n\t\t\t\t\tmetadata: { batchId: groupedBatchId, groupedOpCount: batch.messages.length },\n\t\t\t\t\treferenceSequenceNumber: batch.messages[0].referenceSequenceNumber,\n\t\t\t\t\tcontents: serializedContent,\n\t\t\t\t},\n\t\t\t],\n\t\t};\n\t\treturn groupedBatch;\n\t}\n\n\tpublic ungroupOp(op: ISequencedDocumentMessage): ISequencedDocumentMessage[] {\n\t\tassert(isGroupContents(op.contents), 0x947 /* can only ungroup a grouped batch */);\n\t\tconst contents: IGroupedBatchMessageContents = op.contents;\n\n\t\tlet fakeCsn = 1;\n\t\treturn contents.contents.map((subMessage) => ({\n\t\t\t...op,\n\t\t\tclientSequenceNumber: fakeCsn++,\n\t\t\tcontents: subMessage.contents,\n\t\t\tmetadata: subMessage.metadata,\n\t\t\tcompression: subMessage.compression,\n\t\t}));\n\t}\n\n\tpublic groupedBatchingEnabled(): boolean {\n\t\treturn this.config.groupedBatchingEnabled;\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"opGroupingManager.js","sourceRoot":"","sources":["../../src/opLifecycle/opGroupingManager.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport type { ITelemetryBaseLogger } from \"@fluidframework/core-interfaces\";\nimport { assert } from \"@fluidframework/core-utils/internal\";\nimport type { ISequencedDocumentMessage } from \"@fluidframework/driver-definitions/internal\";\nimport {\n\tcreateChildLogger,\n\ttype TelemetryLoggerExt,\n} from \"@fluidframework/telemetry-utils/internal\";\n\nimport type {\n\tLocalEmptyBatchPlaceholder,\n\tOutboundBatch,\n\tOutboundSingletonBatch,\n} from \"./definitions.js\";\n\n/**\n * The number of ops in a batch above which the batch is considered \"large\"\n * for telemetry purposes. Used by both {@link OpGroupingManager} (GroupLargeBatch event)\n * and as the default staging-mode auto-flush threshold.\n */\nexport const largeBatchThreshold = 1000;\n\n/**\n * Grouping makes assumptions about the shape of message contents. This interface codifies those assumptions, but does not validate them.\n */\ninterface IGroupedBatchMessageContents {\n\ttype: typeof OpGroupingManager.groupedBatchOp;\n\tcontents: IGroupedMessage[];\n}\n\ninterface IGroupedMessage {\n\tcontents?: unknown;\n\tmetadata?: Record<string, unknown>;\n\tcompression?: string;\n}\n\nfunction isGroupContents(opContents: unknown): opContents is IGroupedBatchMessageContents {\n\treturn (\n\t\t(opContents as Partial<IGroupedBatchMessageContents>)?.type ===\n\t\tOpGroupingManager.groupedBatchOp\n\t);\n}\n\nexport function isGroupedBatch(op: ISequencedDocumentMessage): boolean {\n\treturn isGroupContents(op.contents);\n}\n\nexport interface OpGroupingManagerConfig {\n\treadonly groupedBatchingEnabled: boolean;\n}\n\n/**\n * This is the type of an empty grouped batch we send over the wire\n * We also put this in the placeholder for an empty batch in the PendingStateManager.\n * But most places throughout the ContainerRuntime, this will not be used (just as Grouped Batches in general don't appear outside opLifecycle dir)\n */\nexport interface EmptyGroupedBatch {\n\ttype: typeof OpGroupingManager.groupedBatchOp;\n\tcontents: readonly [];\n}\n\nexport class OpGroupingManager {\n\tstatic readonly groupedBatchOp = \"groupedBatch\";\n\tprivate readonly logger: TelemetryLoggerExt;\n\n\tconstructor(\n\t\tprivate readonly config: OpGroupingManagerConfig,\n\t\tlogger: ITelemetryBaseLogger,\n\t) {\n\t\tthis.logger = createChildLogger({ logger, namespace: \"OpGroupingManager\" });\n\t}\n\n\t/**\n\t * Creates a new batch with a single message of type \"groupedBatch\" and empty contents.\n\t * This is needed as a placeholder if a batch becomes empty on resubmit, but we are tracking batch IDs.\n\t * @param resubmittingBatchId - batch ID of the resubmitting batch\n\t * @param referenceSequenceNumber - reference sequence number\n\t * @returns The outbound batch as well as the interior placeholder message\n\t */\n\tpublic createEmptyGroupedBatch(\n\t\tresubmittingBatchId: string,\n\t\treferenceSequenceNumber: number,\n\t): {\n\t\toutboundBatch: OutboundSingletonBatch;\n\t\tplaceholderMessage: LocalEmptyBatchPlaceholder;\n\t} {\n\t\tassert(\n\t\t\tthis.config.groupedBatchingEnabled,\n\t\t\t0xa00 /* cannot create empty grouped batch when grouped batching is disabled */,\n\t\t);\n\n\t\tconst emptyGroupedBatch: EmptyGroupedBatch = {\n\t\t\ttype: \"groupedBatch\",\n\t\t\tcontents: [],\n\t\t};\n\t\tconst serializedOp = JSON.stringify(emptyGroupedBatch);\n\n\t\tconst placeholderMessage: LocalEmptyBatchPlaceholder = {\n\t\t\tmetadata: { batchId: resubmittingBatchId, groupedOpCount: 0 },\n\t\t\tlocalOpMetadata: { emptyBatch: true },\n\t\t\treferenceSequenceNumber,\n\t\t\truntimeOp: emptyGroupedBatch,\n\t\t};\n\t\tconst outboundBatch: OutboundSingletonBatch = {\n\t\t\tcontentSizeInBytes: 0,\n\t\t\tmessages: [{ ...placeholderMessage, runtimeOp: undefined, contents: serializedOp }],\n\t\t\treferenceSequenceNumber,\n\t\t};\n\t\treturn { outboundBatch, placeholderMessage };\n\t}\n\n\t/**\n\t * Converts the given batch into a \"grouped batch\" - a batch with a single message of type \"groupedBatch\",\n\t * with contents being an array of the original batch's messages.\n\t *\n\t * If the batch already has only 1 message, it is returned as-is.\n\t *\n\t * @remarks Remember that a BatchMessage has its content JSON serialized, so the incoming batch message contents\n\t * must be parsed first, and then the type and contents mentioned above are hidden in that JSON serialization.\n\t */\n\tpublic groupBatch(batch: OutboundBatch): OutboundSingletonBatch {\n\t\tassert(this.groupedBatchingEnabled(), 0xb79 /* grouping disabled! */);\n\t\tassert(batch.messages.length > 0, 0xb7a /* Unexpected attempt to group an empty batch */);\n\n\t\tif (batch.messages.length === 1) {\n\t\t\treturn batch as OutboundSingletonBatch;\n\t\t}\n\n\t\t// Use > (not >=) so that batches flushed exactly at the staging-mode\n\t\t// auto-flush threshold (which defaults to largeBatchThreshold) don't\n\t\t// trigger this event. Only genuinely oversized batches are logged.\n\t\tif (batch.messages.length > largeBatchThreshold) {\n\t\t\tthis.logger.sendTelemetryEvent({\n\t\t\t\teventName: \"GroupLargeBatch\",\n\t\t\t\tlength: batch.messages.length,\n\t\t\t\treentrant: batch.hasReentrantOps,\n\t\t\t\treferenceSequenceNumber: batch.messages[0].referenceSequenceNumber,\n\t\t\t});\n\t\t}\n\t\t// We expect this will be on the first message, if present at all.\n\t\tlet groupedBatchId: unknown;\n\t\tfor (const message of batch.messages) {\n\t\t\tif (message.metadata !== undefined) {\n\t\t\t\tconst { batch: _batch, batchId, ...rest } = message.metadata;\n\t\t\t\tif (batchId !== undefined) {\n\t\t\t\t\tgroupedBatchId = batchId;\n\t\t\t\t}\n\t\t\t\tassert(Object.keys(rest).length === 0, 0x5dd /* cannot group ops with metadata */);\n\t\t\t}\n\t\t}\n\n\t\tconst serializedContent = JSON.stringify({\n\t\t\ttype: OpGroupingManager.groupedBatchOp,\n\t\t\tcontents: batch.messages.map<IGroupedMessage>((message) => ({\n\t\t\t\tcontents:\n\t\t\t\t\tmessage.contents === undefined\n\t\t\t\t\t\t? undefined\n\t\t\t\t\t\t: (JSON.parse(message.contents) as unknown),\n\t\t\t\tmetadata: message.metadata,\n\t\t\t\tcompression: message.compression,\n\t\t\t})),\n\t\t});\n\n\t\tconst groupedBatch: OutboundSingletonBatch = {\n\t\t\t...batch,\n\t\t\tmessages: [\n\t\t\t\t{\n\t\t\t\t\tmetadata: { batchId: groupedBatchId, groupedOpCount: batch.messages.length },\n\t\t\t\t\treferenceSequenceNumber: batch.messages[0].referenceSequenceNumber,\n\t\t\t\t\tcontents: serializedContent,\n\t\t\t\t},\n\t\t\t],\n\t\t};\n\t\treturn groupedBatch;\n\t}\n\n\tpublic ungroupOp(op: ISequencedDocumentMessage): ISequencedDocumentMessage[] {\n\t\tassert(isGroupContents(op.contents), 0x947 /* can only ungroup a grouped batch */);\n\t\tconst contents: IGroupedBatchMessageContents = op.contents;\n\n\t\tlet fakeCsn = 1;\n\t\treturn contents.contents.map((subMessage) => ({\n\t\t\t...op,\n\t\t\tclientSequenceNumber: fakeCsn++,\n\t\t\tcontents: subMessage.contents,\n\t\t\tmetadata: subMessage.metadata,\n\t\t\tcompression: subMessage.compression,\n\t\t}));\n\t}\n\n\tpublic groupedBatchingEnabled(): boolean {\n\t\treturn this.config.groupedBatchingEnabled;\n\t}\n}\n"]}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
|
-
import type
|
|
5
|
+
import { type ISequencedDocumentMessage } from "@fluidframework/driver-definitions/internal";
|
|
6
6
|
import type { LocalContainerRuntimeMessage } from "../messageTypes.js";
|
|
7
7
|
import type { EmptyGroupedBatch } from "./opGroupingManager.js";
|
|
8
8
|
/**
|
|
@@ -13,6 +13,16 @@ import type { EmptyGroupedBatch } from "./opGroupingManager.js";
|
|
|
13
13
|
* @param mutableMessage - op message received
|
|
14
14
|
*/
|
|
15
15
|
export declare function ensureContentsDeserialized(mutableMessage: ISequencedDocumentMessage): void;
|
|
16
|
+
/**
|
|
17
|
+
* If `message` is a modern runtime-envelope op (type "op" with a client id), returns a shallow copy
|
|
18
|
+
* with its contents deserialized ({@link ensureContentsDeserialized}); otherwise `undefined`.
|
|
19
|
+
*
|
|
20
|
+
* @remarks Only runtime ops may be deserialized (system ops carry non-JSON payloads), and copying keeps
|
|
21
|
+
* the caller's op untouched for the unpack pipeline. Shared so callers don't re-derive the invariant.
|
|
22
|
+
*/
|
|
23
|
+
export declare function tryGetDeserializedRuntimeOpCopy(message: ISequencedDocumentMessage): (ISequencedDocumentMessage & {
|
|
24
|
+
clientId: string;
|
|
25
|
+
}) | undefined;
|
|
16
26
|
/**
|
|
17
27
|
* Before submitting an op to the Outbox, its contents must be serialized using this function.
|
|
18
28
|
* @remarks The deserialization on process happens via the function {@link ensureContentsDeserialized}.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"opSerialization.d.ts","sourceRoot":"","sources":["../../src/opLifecycle/opSerialization.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"opSerialization.d.ts","sourceRoot":"","sources":["../../src/opLifecycle/opSerialization.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAEN,KAAK,yBAAyB,EAC9B,MAAM,6CAA6C,CAAC;AAOrD,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,oBAAoB,CAAC;AAEvE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAEhE;;;;;;GAMG;AACH,wBAAgB,0BAA0B,CAAC,cAAc,EAAE,yBAAyB,GAAG,IAAI,CAM1F;AAED;;;;;;GAMG;AACH,wBAAgB,+BAA+B,CAC9C,OAAO,EAAE,yBAAyB,GAChC,CAAC,yBAAyB,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,GAAG,SAAS,CAOhE;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAC1B,WAAW,EACR,iBAAiB,GACjB,4BAA4B,GAC5B,4BAA4B,EAAE,GAC/B;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,CAcrB"}
|
|
@@ -4,8 +4,11 @@
|
|
|
4
4
|
* Licensed under the MIT License.
|
|
5
5
|
*/
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
exports.
|
|
8
|
-
|
|
7
|
+
exports.ensureContentsDeserialized = ensureContentsDeserialized;
|
|
8
|
+
exports.tryGetDeserializedRuntimeOpCopy = tryGetDeserializedRuntimeOpCopy;
|
|
9
|
+
exports.serializeOp = serializeOp;
|
|
10
|
+
const internal_1 = require("@fluidframework/driver-definitions/internal");
|
|
11
|
+
const internal_2 = require("@fluidframework/runtime-utils/internal");
|
|
9
12
|
/**
|
|
10
13
|
* Takes an incoming runtime message (outer type "op"), JSON.parses the message's contents in place,
|
|
11
14
|
* if needed (old Loader does this for us).
|
|
@@ -20,7 +23,21 @@ function ensureContentsDeserialized(mutableMessage) {
|
|
|
20
23
|
mutableMessage.contents = JSON.parse(mutableMessage.contents);
|
|
21
24
|
}
|
|
22
25
|
}
|
|
23
|
-
|
|
26
|
+
/**
|
|
27
|
+
* If `message` is a modern runtime-envelope op (type "op" with a client id), returns a shallow copy
|
|
28
|
+
* with its contents deserialized ({@link ensureContentsDeserialized}); otherwise `undefined`.
|
|
29
|
+
*
|
|
30
|
+
* @remarks Only runtime ops may be deserialized (system ops carry non-JSON payloads), and copying keeps
|
|
31
|
+
* the caller's op untouched for the unpack pipeline. Shared so callers don't re-derive the invariant.
|
|
32
|
+
*/
|
|
33
|
+
function tryGetDeserializedRuntimeOpCopy(message) {
|
|
34
|
+
if (message.type !== internal_1.MessageType.Operation || typeof message.clientId !== "string") {
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
const messageCopy = { ...message };
|
|
38
|
+
ensureContentsDeserialized(messageCopy);
|
|
39
|
+
return messageCopy;
|
|
40
|
+
}
|
|
24
41
|
/**
|
|
25
42
|
* Before submitting an op to the Outbox, its contents must be serialized using this function.
|
|
26
43
|
* @remarks The deserialization on process happens via the function {@link ensureContentsDeserialized}.
|
|
@@ -33,12 +50,11 @@ function serializeOp(toSerialize) {
|
|
|
33
50
|
// replacer:
|
|
34
51
|
(key, value) => {
|
|
35
52
|
// If 'value' is an IFluidHandle return its encoded form.
|
|
36
|
-
if ((0,
|
|
37
|
-
return (0,
|
|
53
|
+
if ((0, internal_2.isFluidHandle)(value)) {
|
|
54
|
+
return (0, internal_2.encodeHandleForSerialization)((0, internal_2.toFluidHandleInternal)(value));
|
|
38
55
|
}
|
|
39
56
|
return value;
|
|
40
57
|
}),
|
|
41
58
|
};
|
|
42
59
|
}
|
|
43
|
-
exports.serializeOp = serializeOp;
|
|
44
60
|
//# sourceMappingURL=opSerialization.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"opSerialization.js","sourceRoot":"","sources":["../../src/opLifecycle/opSerialization.ts"],"names":[],"mappings":";AAAA;;;GAGG
|
|
1
|
+
{"version":3,"file":"opSerialization.js","sourceRoot":"","sources":["../../src/opLifecycle/opSerialization.ts"],"names":[],"mappings":";AAAA;;;GAGG;;AAuBH,gEAMC;AASD,0EASC;AAQD,kCAmBC;AAxED,0EAGqD;AACrD,qEAIgD;AAMhD;;;;;;GAMG;AACH,SAAgB,0BAA0B,CAAC,cAAyC;IACnF,yEAAyE;IACzE,qFAAqF;IACrF,IAAI,OAAO,cAAc,CAAC,QAAQ,KAAK,QAAQ,IAAI,cAAc,CAAC,QAAQ,KAAK,EAAE,EAAE,CAAC;QACnF,cAAc,CAAC,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC;IAC/D,CAAC;AACF,CAAC;AAED;;;;;;GAMG;AACH,SAAgB,+BAA+B,CAC9C,OAAkC;IAElC,IAAI,OAAO,CAAC,IAAI,KAAK,sBAAW,CAAC,SAAS,IAAI,OAAO,OAAO,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;QACpF,OAAO,SAAS,CAAC;IAClB,CAAC;IACD,MAAM,WAAW,GAAG,EAAE,GAAG,OAAO,EAAE,CAAC;IACnC,0BAA0B,CAAC,WAAW,CAAC,CAAC;IACxC,OAAO,WAA+D,CAAC;AACxE,CAAC;AAED;;;;;GAKG;AACH,SAAgB,WAAW,CAC1B,WAGiC;IAEjC,OAAO;QACN,OAAO,EAAE,IAAI,CAAC,SAAS,CACtB,WAAW;QACX,YAAY;QACZ,CAAC,GAAG,EAAE,KAAc,EAAE,EAAE;YACvB,yDAAyD;YACzD,IAAI,IAAA,wBAAa,EAAC,KAAK,CAAC,EAAE,CAAC;gBAC1B,OAAO,IAAA,uCAA4B,EAAC,IAAA,gCAAqB,EAAC,KAAK,CAAC,CAAC,CAAC;YACnE,CAAC;YACD,OAAO,KAAK,CAAC;QACd,CAAC,CACD;KACD,CAAC;AACH,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport {\n\tMessageType,\n\ttype ISequencedDocumentMessage,\n} from \"@fluidframework/driver-definitions/internal\";\nimport {\n\tencodeHandleForSerialization,\n\tisFluidHandle,\n\ttoFluidHandleInternal,\n} from \"@fluidframework/runtime-utils/internal\";\n\nimport type { LocalContainerRuntimeMessage } from \"../messageTypes.js\";\n\nimport type { EmptyGroupedBatch } from \"./opGroupingManager.js\";\n\n/**\n * Takes an incoming runtime message (outer type \"op\"), JSON.parses the message's contents in place,\n * if needed (old Loader does this for us).\n * Only to be used for runtime messages. The contents here would be the virtualized payload for a batch of ops.\n * @remarks Serialization during submit happens via {@link serializeOp}\n * @param mutableMessage - op message received\n */\nexport function ensureContentsDeserialized(mutableMessage: ISequencedDocumentMessage): void {\n\t// This should become unconditional once Loader LTS reaches 2.4 or later.\n\t// There will be a long time of needing both cases, until LTS advances to that point.\n\tif (typeof mutableMessage.contents === \"string\" && mutableMessage.contents !== \"\") {\n\t\tmutableMessage.contents = JSON.parse(mutableMessage.contents);\n\t}\n}\n\n/**\n * If `message` is a modern runtime-envelope op (type \"op\" with a client id), returns a shallow copy\n * with its contents deserialized ({@link ensureContentsDeserialized}); otherwise `undefined`.\n *\n * @remarks Only runtime ops may be deserialized (system ops carry non-JSON payloads), and copying keeps\n * the caller's op untouched for the unpack pipeline. Shared so callers don't re-derive the invariant.\n */\nexport function tryGetDeserializedRuntimeOpCopy(\n\tmessage: ISequencedDocumentMessage,\n): (ISequencedDocumentMessage & { clientId: string }) | undefined {\n\tif (message.type !== MessageType.Operation || typeof message.clientId !== \"string\") {\n\t\treturn undefined;\n\t}\n\tconst messageCopy = { ...message };\n\tensureContentsDeserialized(messageCopy);\n\treturn messageCopy as ISequencedDocumentMessage & { clientId: string };\n}\n\n/**\n * Before submitting an op to the Outbox, its contents must be serialized using this function.\n * @remarks The deserialization on process happens via the function {@link ensureContentsDeserialized}.\n *\n * @param toSerialize - op message to serialize. Also supports an array of ops.\n */\nexport function serializeOp(\n\ttoSerialize:\n\t\t| EmptyGroupedBatch\n\t\t| LocalContainerRuntimeMessage\n\t\t| LocalContainerRuntimeMessage[],\n): { content: string } {\n\treturn {\n\t\tcontent: JSON.stringify(\n\t\t\ttoSerialize,\n\t\t\t// replacer:\n\t\t\t(key, value: unknown) => {\n\t\t\t\t// If 'value' is an IFluidHandle return its encoded form.\n\t\t\t\tif (isFluidHandle(value)) {\n\t\t\t\t\treturn encodeHandleForSerialization(toFluidHandleInternal(value));\n\t\t\t\t}\n\t\t\t\treturn value;\n\t\t\t},\n\t\t),\n\t};\n}\n"]}
|
|
@@ -7,6 +7,19 @@ import type { ITelemetryBaseLogger } from "@fluidframework/core-interfaces";
|
|
|
7
7
|
import type { ISequencedDocumentMessage } from "@fluidframework/driver-definitions/internal";
|
|
8
8
|
import type { IChunkedOp, OutboundBatchMessage, OutboundSingletonBatch } from "./definitions.js";
|
|
9
9
|
export declare function isChunkedMessage(message: ISequencedDocumentMessage): boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Configures inbound chunk processing.
|
|
12
|
+
*
|
|
13
|
+
* @internal
|
|
14
|
+
*/
|
|
15
|
+
export interface OpSplitterOptions {
|
|
16
|
+
/**
|
|
17
|
+
* Allows the first observed chunk stream for each client to begin after chunk 1. The unreconstructable
|
|
18
|
+
* remainder of that stream is discarded, after which chunk ordering is strict. Historical readers use
|
|
19
|
+
* this because their requested range can begin within a stream; live processing leaves it disabled.
|
|
20
|
+
*/
|
|
21
|
+
readonly allowInitialPartialChunkStream?: boolean;
|
|
22
|
+
}
|
|
10
23
|
/**
|
|
11
24
|
* Responsible for creating and reconstructing chunked messages.
|
|
12
25
|
*/
|
|
@@ -14,12 +27,15 @@ export declare class OpSplitter {
|
|
|
14
27
|
private readonly submitBatchFn;
|
|
15
28
|
readonly chunkSizeInBytes: number;
|
|
16
29
|
private readonly maxBatchSizeInBytes;
|
|
30
|
+
private readonly options;
|
|
17
31
|
private readonly chunkMap;
|
|
32
|
+
private readonly nextInitialChunkIdByClient;
|
|
18
33
|
private readonly logger;
|
|
19
|
-
constructor(chunks: [string, string[]][], submitBatchFn: ((batch: IBatchMessage[], referenceSequenceNumber?: number) => number) | undefined, chunkSizeInBytes: number, maxBatchSizeInBytes: number, logger: ITelemetryBaseLogger);
|
|
34
|
+
constructor(chunks: [string, string[]][], submitBatchFn: ((batch: IBatchMessage[], referenceSequenceNumber?: number) => number) | undefined, chunkSizeInBytes: number, maxBatchSizeInBytes: number, logger: ITelemetryBaseLogger, options?: OpSplitterOptions);
|
|
20
35
|
get isBatchChunkingEnabled(): boolean;
|
|
21
36
|
get chunks(): ReadonlyMap<string, string[]>;
|
|
22
37
|
clearPartialChunks(clientId: string): void;
|
|
38
|
+
private throwChunkIdMismatch;
|
|
23
39
|
private addChunk;
|
|
24
40
|
/**
|
|
25
41
|
* Takes a singleton batch, and splits the interior message into chunks, sending the chunks separately and
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"opSplitter.d.ts","sourceRoot":"","sources":["../../src/opLifecycle/opSplitter.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gDAAgD,CAAC;AACpF,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,iCAAiC,CAAC;AAE5E,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,6CAA6C,CAAC;AAa7F,OAAO,KAAK,EACX,UAAU,EACV,oBAAoB,EACpB,sBAAsB,EACtB,MAAM,kBAAkB,CAAC;AAG1B,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,yBAAyB,GAAG,OAAO,CAE5E;AAWD;;GAEG;AACH,qBAAa,UAAU;
|
|
1
|
+
{"version":3,"file":"opSplitter.d.ts","sourceRoot":"","sources":["../../src/opLifecycle/opSplitter.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gDAAgD,CAAC;AACpF,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,iCAAiC,CAAC;AAE5E,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,6CAA6C,CAAC;AAa7F,OAAO,KAAK,EACX,UAAU,EACV,oBAAoB,EACpB,sBAAsB,EACtB,MAAM,kBAAkB,CAAC;AAG1B,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,yBAAyB,GAAG,OAAO,CAE5E;AAED;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IACjC;;;;OAIG;IACH,QAAQ,CAAC,8BAA8B,CAAC,EAAE,OAAO,CAAC;CAClD;AAWD;;GAEG;AACH,qBAAa,UAAU;IASrB,OAAO,CAAC,QAAQ,CAAC,aAAa;aAGd,gBAAgB,EAAE,MAAM;IACxC,OAAO,CAAC,QAAQ,CAAC,mBAAmB;IAEpC,OAAO,CAAC,QAAQ,CAAC,OAAO;IAbzB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAwB;IAEjD,OAAO,CAAC,QAAQ,CAAC,0BAA0B,CAA6B;IACxE,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqB;gBAG3C,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,EAAE,EACX,aAAa,EAC3B,CAAC,CAAC,KAAK,EAAE,aAAa,EAAE,EAAE,uBAAuB,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC,GACtE,SAAS,EACI,gBAAgB,EAAE,MAAM,EACvB,mBAAmB,EAAE,MAAM,EAC5C,MAAM,EAAE,oBAAoB,EACX,OAAO,GAAE,iBAAsB;IAWjD,IAAW,sBAAsB,IAAI,OAAO,CAI3C;IAED,IAAW,MAAM,IAAI,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAEjD;IAEM,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAMjD,OAAO,CAAC,oBAAoB;IAa5B,OAAO,CAAC,QAAQ;IAqBhB;;;;;;;;;;;;;;;;;;;;;;;;;OAyBG;IACI,0BAA0B,CAAC,KAAK,EAAE,sBAAsB,GAAG,sBAAsB;IA8EjF,YAAY,CAAC,OAAO,EAAE,yBAAyB,GAAG,kBAAkB;CAkE3E;AAED,KAAK,kBAAkB,GACpB;IACA,QAAQ,CAAC,YAAY,EAAE,KAAK,CAAC;CAC5B,GACD;IACA,QAAQ,CAAC,YAAY,EAAE,IAAI,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE,yBAAyB,CAAC;CAC3C,CAAC;AAkBL;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,OAAO,GACnB,IAAI,oBAAoB,EACxB,kBAAkB,MAAM,EACxB,UAAS,OAAe,KACtB,UAAU,EAkDZ,CAAC"}
|
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
* Licensed under the MIT License.
|
|
5
5
|
*/
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
exports.splitOp = exports.OpSplitter =
|
|
7
|
+
exports.splitOp = exports.OpSplitter = void 0;
|
|
8
|
+
exports.isChunkedMessage = isChunkedMessage;
|
|
8
9
|
const internal_1 = require("@fluidframework/core-utils/internal");
|
|
9
10
|
const internal_2 = require("@fluidframework/telemetry-utils/internal");
|
|
10
11
|
const messageTypes_js_1 = require("../messageTypes.js");
|
|
@@ -12,7 +13,6 @@ const outbox_js_1 = require("./outbox.js");
|
|
|
12
13
|
function isChunkedMessage(message) {
|
|
13
14
|
return isChunkedContents(message.contents);
|
|
14
15
|
}
|
|
15
|
-
exports.isChunkedMessage = isChunkedMessage;
|
|
16
16
|
function isChunkedContents(contents) {
|
|
17
17
|
return contents?.type === messageTypes_js_1.ContainerMessageType.ChunkedOp;
|
|
18
18
|
}
|
|
@@ -20,11 +20,26 @@ function isChunkedContents(contents) {
|
|
|
20
20
|
* Responsible for creating and reconstructing chunked messages.
|
|
21
21
|
*/
|
|
22
22
|
class OpSplitter {
|
|
23
|
-
|
|
23
|
+
submitBatchFn;
|
|
24
|
+
chunkSizeInBytes;
|
|
25
|
+
maxBatchSizeInBytes;
|
|
26
|
+
options;
|
|
27
|
+
// Local copy of incomplete received chunks.
|
|
28
|
+
chunkMap;
|
|
29
|
+
// No entry = unseen; 1 = aligned; >1 = the next initial partial chunk to discard.
|
|
30
|
+
nextInitialChunkIdByClient = new Map();
|
|
31
|
+
logger;
|
|
32
|
+
constructor(chunks, submitBatchFn, chunkSizeInBytes, maxBatchSizeInBytes, logger, options = {}) {
|
|
24
33
|
this.submitBatchFn = submitBatchFn;
|
|
25
34
|
this.chunkSizeInBytes = chunkSizeInBytes;
|
|
26
35
|
this.maxBatchSizeInBytes = maxBatchSizeInBytes;
|
|
36
|
+
this.options = options;
|
|
27
37
|
this.chunkMap = new Map(chunks);
|
|
38
|
+
if (this.options.allowInitialPartialChunkStream === true) {
|
|
39
|
+
for (const clientId of this.chunkMap.keys()) {
|
|
40
|
+
this.nextInitialChunkIdByClient.set(clientId, 1);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
28
43
|
this.logger = (0, internal_2.createChildLogger)({ logger, namespace: "OpSplitter" });
|
|
29
44
|
}
|
|
30
45
|
get isBatchChunkingEnabled() {
|
|
@@ -38,6 +53,14 @@ class OpSplitter {
|
|
|
38
53
|
this.chunkMap.delete(clientId);
|
|
39
54
|
}
|
|
40
55
|
}
|
|
56
|
+
throwChunkIdMismatch(originalMessage, chunkedContent, expectedChunkId) {
|
|
57
|
+
throw new internal_2.DataCorruptionError("Chunk Id mismatch", {
|
|
58
|
+
...(0, internal_2.extractSafePropertiesFromMessage)(originalMessage),
|
|
59
|
+
chunkMapLength: expectedChunkId - 1,
|
|
60
|
+
chunkId: chunkedContent.chunkId,
|
|
61
|
+
totalChunks: chunkedContent.totalChunks,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
41
64
|
addChunk(clientId, chunkedContent, originalMessage) {
|
|
42
65
|
let map = this.chunkMap.get(clientId);
|
|
43
66
|
if (map === undefined) {
|
|
@@ -48,12 +71,7 @@ class OpSplitter {
|
|
|
48
71
|
// We are expecting the chunks to be processed sequentially, in the same order as they are sent.
|
|
49
72
|
// Therefore, the chunkId of the incoming op needs to match the length of the array (1-based indexing)
|
|
50
73
|
// holding the existing chunks for that particular clientId.
|
|
51
|
-
|
|
52
|
-
...(0, internal_2.extractSafePropertiesFromMessage)(originalMessage),
|
|
53
|
-
chunkMapLength: map.length,
|
|
54
|
-
chunkId: chunkedContent.chunkId,
|
|
55
|
-
totalChunks: chunkedContent.totalChunks,
|
|
56
|
-
});
|
|
74
|
+
this.throwChunkIdMismatch(originalMessage, chunkedContent, map.length + 1);
|
|
57
75
|
}
|
|
58
76
|
map.push(chunkedContent.contents);
|
|
59
77
|
}
|
|
@@ -138,6 +156,25 @@ class OpSplitter {
|
|
|
138
156
|
// TODO: Verify whether this should be able to handle server-generated ops (with null clientId)
|
|
139
157
|
const clientId = message.clientId;
|
|
140
158
|
const chunkedContent = contents.contents;
|
|
159
|
+
if (this.options.allowInitialPartialChunkStream === true) {
|
|
160
|
+
const nextInitialChunkId = this.nextInitialChunkIdByClient.get(clientId);
|
|
161
|
+
if (nextInitialChunkId !== 1) {
|
|
162
|
+
if (nextInitialChunkId !== undefined &&
|
|
163
|
+
chunkedContent.chunkId !== nextInitialChunkId) {
|
|
164
|
+
this.throwChunkIdMismatch(message, chunkedContent, nextInitialChunkId);
|
|
165
|
+
}
|
|
166
|
+
if (nextInitialChunkId === undefined &&
|
|
167
|
+
(chunkedContent.chunkId <= 1 || chunkedContent.chunkId > chunkedContent.totalChunks)) {
|
|
168
|
+
this.nextInitialChunkIdByClient.set(clientId, 1);
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
this.nextInitialChunkIdByClient.set(clientId, chunkedContent.chunkId < chunkedContent.totalChunks
|
|
172
|
+
? chunkedContent.chunkId + 1
|
|
173
|
+
: 1);
|
|
174
|
+
return { isFinalChunk: false };
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
141
178
|
this.addChunk(clientId, chunkedContent, message);
|
|
142
179
|
if (chunkedContent.chunkId < chunkedContent.totalChunks) {
|
|
143
180
|
// We are processing the op in chunks but haven't reached
|