@fluidframework/container-runtime 2.0.0-internal.3.0.5 → 2.0.0-internal.3.1.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/.eslintrc.js +19 -19
- package/.mocharc.js +2 -2
- package/api-extractor.json +2 -2
- package/dist/batchTracker.d.ts.map +1 -1
- package/dist/batchTracker.js +2 -1
- package/dist/batchTracker.js.map +1 -1
- package/dist/blobManager.d.ts +9 -2
- package/dist/blobManager.d.ts.map +1 -1
- package/dist/blobManager.js +80 -33
- package/dist/blobManager.js.map +1 -1
- package/dist/connectionTelemetry.d.ts.map +1 -1
- package/dist/connectionTelemetry.js +11 -9
- package/dist/connectionTelemetry.js.map +1 -1
- package/dist/containerHandleContext.d.ts.map +1 -1
- package/dist/containerHandleContext.js +3 -1
- package/dist/containerHandleContext.js.map +1 -1
- package/dist/containerRuntime.d.ts +11 -1
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +116 -72
- package/dist/containerRuntime.js.map +1 -1
- package/dist/dataStore.d.ts.map +1 -1
- package/dist/dataStore.js +11 -9
- package/dist/dataStore.js.map +1 -1
- package/dist/dataStoreContext.d.ts +18 -13
- package/dist/dataStoreContext.d.ts.map +1 -1
- package/dist/dataStoreContext.js +68 -55
- package/dist/dataStoreContext.js.map +1 -1
- package/dist/dataStoreContexts.d.ts.map +1 -1
- package/dist/dataStoreContexts.js +7 -3
- package/dist/dataStoreContexts.js.map +1 -1
- package/dist/dataStoreRegistry.d.ts.map +1 -1
- package/dist/dataStoreRegistry.js +3 -1
- package/dist/dataStoreRegistry.js.map +1 -1
- package/dist/dataStores.d.ts +26 -1
- package/dist/dataStores.d.ts.map +1 -1
- package/dist/dataStores.js +103 -18
- package/dist/dataStores.js.map +1 -1
- package/dist/deltaScheduler.d.ts.map +1 -1
- package/dist/deltaScheduler.js +8 -3
- package/dist/deltaScheduler.js.map +1 -1
- package/dist/garbageCollection.d.ts +33 -14
- package/dist/garbageCollection.d.ts.map +1 -1
- package/dist/garbageCollection.js +178 -92
- package/dist/garbageCollection.js.map +1 -1
- package/dist/garbageCollectionConstants.d.ts +1 -0
- package/dist/garbageCollectionConstants.d.ts.map +1 -1
- package/dist/garbageCollectionConstants.js +4 -1
- package/dist/garbageCollectionConstants.js.map +1 -1
- package/dist/garbageCollectionHelpers.d.ts +26 -0
- package/dist/garbageCollectionHelpers.d.ts.map +1 -0
- package/dist/garbageCollectionHelpers.js +45 -0
- package/dist/garbageCollectionHelpers.js.map +1 -0
- package/dist/gcSweepReadyUsageDetection.d.ts +5 -5
- package/dist/gcSweepReadyUsageDetection.d.ts.map +1 -1
- package/dist/gcSweepReadyUsageDetection.js +14 -10
- package/dist/gcSweepReadyUsageDetection.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/opLifecycle/batchManager.d.ts +5 -5
- package/dist/opLifecycle/batchManager.d.ts.map +1 -1
- package/dist/opLifecycle/batchManager.js +19 -12
- package/dist/opLifecycle/batchManager.js.map +1 -1
- package/dist/opLifecycle/definitions.d.ts.map +1 -1
- package/dist/opLifecycle/definitions.js.map +1 -1
- package/dist/opLifecycle/index.d.ts.map +1 -1
- package/dist/opLifecycle/index.js.map +1 -1
- package/dist/opLifecycle/opCompressor.d.ts.map +1 -1
- package/dist/opLifecycle/opCompressor.js.map +1 -1
- package/dist/opLifecycle/opDecompressor.d.ts +0 -4
- package/dist/opLifecycle/opDecompressor.d.ts.map +1 -1
- package/dist/opLifecycle/opDecompressor.js +7 -43
- package/dist/opLifecycle/opDecompressor.js.map +1 -1
- package/dist/opLifecycle/opSplitter.d.ts.map +1 -1
- package/dist/opLifecycle/opSplitter.js +4 -1
- package/dist/opLifecycle/opSplitter.js.map +1 -1
- package/dist/opLifecycle/outbox.d.ts.map +1 -1
- package/dist/opLifecycle/outbox.js +20 -19
- package/dist/opLifecycle/outbox.js.map +1 -1
- package/dist/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
- package/dist/opLifecycle/remoteMessageProcessor.js.map +1 -1
- package/dist/opProperties.d.ts.map +1 -1
- package/dist/opProperties.js +1 -3
- package/dist/opProperties.js.map +1 -1
- package/dist/orderedClientElection.d.ts.map +1 -1
- package/dist/orderedClientElection.js +10 -4
- package/dist/orderedClientElection.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 +7 -0
- package/dist/pendingStateManager.d.ts.map +1 -1
- package/dist/pendingStateManager.js +7 -4
- package/dist/pendingStateManager.js.map +1 -1
- package/dist/runWhileConnectedCoordinator.d.ts.map +1 -1
- package/dist/runWhileConnectedCoordinator.js.map +1 -1
- package/dist/runningSummarizer.d.ts.map +1 -1
- package/dist/runningSummarizer.js +34 -21
- package/dist/runningSummarizer.js.map +1 -1
- package/dist/scheduleManager.d.ts.map +1 -1
- package/dist/scheduleManager.js +3 -2
- package/dist/scheduleManager.js.map +1 -1
- package/dist/serializedSnapshotStorage.d.ts +2 -2
- package/dist/serializedSnapshotStorage.d.ts.map +1 -1
- package/dist/serializedSnapshotStorage.js +5 -3
- package/dist/serializedSnapshotStorage.js.map +1 -1
- package/dist/summarizer.d.ts +2 -2
- package/dist/summarizer.d.ts.map +1 -1
- package/dist/summarizer.js +37 -17
- package/dist/summarizer.js.map +1 -1
- package/dist/summarizerClientElection.d.ts.map +1 -1
- package/dist/summarizerClientElection.js.map +1 -1
- package/dist/summarizerHandle.d.ts.map +1 -1
- package/dist/summarizerHandle.js.map +1 -1
- package/dist/summarizerHeuristics.d.ts.map +1 -1
- package/dist/summarizerHeuristics.js +6 -9
- package/dist/summarizerHeuristics.js.map +1 -1
- package/dist/summarizerTypes.d.ts +21 -21
- package/dist/summarizerTypes.d.ts.map +1 -1
- package/dist/summarizerTypes.js.map +1 -1
- package/dist/summaryCollection.d.ts.map +1 -1
- package/dist/summaryCollection.js +18 -8
- package/dist/summaryCollection.js.map +1 -1
- package/dist/summaryFormat.d.ts +5 -2
- package/dist/summaryFormat.d.ts.map +1 -1
- package/dist/summaryFormat.js +18 -10
- package/dist/summaryFormat.js.map +1 -1
- package/dist/summaryGenerator.d.ts.map +1 -1
- package/dist/summaryGenerator.js +35 -16
- package/dist/summaryGenerator.js.map +1 -1
- package/dist/summaryManager.d.ts.map +1 -1
- package/dist/summaryManager.js +21 -9
- package/dist/summaryManager.js.map +1 -1
- package/dist/throttler.d.ts +2 -2
- package/dist/throttler.d.ts.map +1 -1
- package/dist/throttler.js +4 -4
- package/dist/throttler.js.map +1 -1
- package/garbageCollection.md +15 -2
- package/lib/batchTracker.d.ts.map +1 -1
- package/lib/batchTracker.js +2 -1
- package/lib/batchTracker.js.map +1 -1
- package/lib/blobManager.d.ts +9 -2
- package/lib/blobManager.d.ts.map +1 -1
- package/lib/blobManager.js +82 -35
- package/lib/blobManager.js.map +1 -1
- package/lib/connectionTelemetry.d.ts.map +1 -1
- package/lib/connectionTelemetry.js +11 -9
- package/lib/connectionTelemetry.js.map +1 -1
- package/lib/containerHandleContext.d.ts.map +1 -1
- package/lib/containerHandleContext.js +3 -1
- package/lib/containerHandleContext.js.map +1 -1
- package/lib/containerRuntime.d.ts +11 -1
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +122 -78
- package/lib/containerRuntime.js.map +1 -1
- package/lib/dataStore.d.ts.map +1 -1
- package/lib/dataStore.js +11 -9
- package/lib/dataStore.js.map +1 -1
- package/lib/dataStoreContext.d.ts +18 -13
- package/lib/dataStoreContext.d.ts.map +1 -1
- package/lib/dataStoreContext.js +71 -58
- package/lib/dataStoreContext.js.map +1 -1
- package/lib/dataStoreContexts.d.ts.map +1 -1
- package/lib/dataStoreContexts.js +7 -3
- package/lib/dataStoreContexts.js.map +1 -1
- package/lib/dataStoreRegistry.d.ts.map +1 -1
- package/lib/dataStoreRegistry.js +3 -1
- package/lib/dataStoreRegistry.js.map +1 -1
- package/lib/dataStores.d.ts +26 -1
- package/lib/dataStores.d.ts.map +1 -1
- package/lib/dataStores.js +109 -24
- package/lib/dataStores.js.map +1 -1
- package/lib/deltaScheduler.d.ts.map +1 -1
- package/lib/deltaScheduler.js +9 -4
- package/lib/deltaScheduler.js.map +1 -1
- package/lib/garbageCollection.d.ts +33 -14
- package/lib/garbageCollection.d.ts.map +1 -1
- package/lib/garbageCollection.js +180 -94
- package/lib/garbageCollection.js.map +1 -1
- package/lib/garbageCollectionConstants.d.ts +1 -0
- package/lib/garbageCollectionConstants.d.ts.map +1 -1
- package/lib/garbageCollectionConstants.js +3 -0
- package/lib/garbageCollectionConstants.js.map +1 -1
- package/lib/garbageCollectionHelpers.d.ts +26 -0
- package/lib/garbageCollectionHelpers.d.ts.map +1 -0
- package/lib/garbageCollectionHelpers.js +40 -0
- package/lib/garbageCollectionHelpers.js.map +1 -0
- package/lib/gcSweepReadyUsageDetection.d.ts +5 -5
- package/lib/gcSweepReadyUsageDetection.d.ts.map +1 -1
- package/lib/gcSweepReadyUsageDetection.js +14 -10
- package/lib/gcSweepReadyUsageDetection.js.map +1 -1
- package/lib/index.d.ts +2 -2
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -1
- package/lib/index.js.map +1 -1
- package/lib/opLifecycle/batchManager.d.ts +5 -5
- package/lib/opLifecycle/batchManager.d.ts.map +1 -1
- package/lib/opLifecycle/batchManager.js +19 -12
- package/lib/opLifecycle/batchManager.js.map +1 -1
- package/lib/opLifecycle/definitions.d.ts.map +1 -1
- package/lib/opLifecycle/definitions.js.map +1 -1
- package/lib/opLifecycle/index.d.ts.map +1 -1
- package/lib/opLifecycle/index.js.map +1 -1
- package/lib/opLifecycle/opCompressor.d.ts.map +1 -1
- package/lib/opLifecycle/opCompressor.js.map +1 -1
- package/lib/opLifecycle/opDecompressor.d.ts +0 -4
- package/lib/opLifecycle/opDecompressor.d.ts.map +1 -1
- package/lib/opLifecycle/opDecompressor.js +7 -43
- package/lib/opLifecycle/opDecompressor.js.map +1 -1
- package/lib/opLifecycle/opSplitter.d.ts.map +1 -1
- package/lib/opLifecycle/opSplitter.js +5 -2
- package/lib/opLifecycle/opSplitter.js.map +1 -1
- package/lib/opLifecycle/outbox.d.ts.map +1 -1
- package/lib/opLifecycle/outbox.js +20 -19
- package/lib/opLifecycle/outbox.js.map +1 -1
- package/lib/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
- package/lib/opLifecycle/remoteMessageProcessor.js.map +1 -1
- package/lib/opProperties.d.ts.map +1 -1
- package/lib/opProperties.js +1 -3
- package/lib/opProperties.js.map +1 -1
- package/lib/orderedClientElection.d.ts.map +1 -1
- package/lib/orderedClientElection.js +10 -4
- package/lib/orderedClientElection.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 +7 -0
- package/lib/pendingStateManager.d.ts.map +1 -1
- package/lib/pendingStateManager.js +7 -4
- package/lib/pendingStateManager.js.map +1 -1
- package/lib/runWhileConnectedCoordinator.d.ts.map +1 -1
- package/lib/runWhileConnectedCoordinator.js.map +1 -1
- package/lib/runningSummarizer.d.ts.map +1 -1
- package/lib/runningSummarizer.js +35 -22
- package/lib/runningSummarizer.js.map +1 -1
- package/lib/scheduleManager.d.ts.map +1 -1
- package/lib/scheduleManager.js +3 -2
- package/lib/scheduleManager.js.map +1 -1
- package/lib/serializedSnapshotStorage.d.ts +2 -2
- package/lib/serializedSnapshotStorage.d.ts.map +1 -1
- package/lib/serializedSnapshotStorage.js +5 -3
- package/lib/serializedSnapshotStorage.js.map +1 -1
- package/lib/summarizer.d.ts +2 -2
- package/lib/summarizer.d.ts.map +1 -1
- package/lib/summarizer.js +37 -17
- package/lib/summarizer.js.map +1 -1
- package/lib/summarizerClientElection.d.ts.map +1 -1
- package/lib/summarizerClientElection.js.map +1 -1
- package/lib/summarizerHandle.d.ts.map +1 -1
- package/lib/summarizerHandle.js.map +1 -1
- package/lib/summarizerHeuristics.d.ts.map +1 -1
- package/lib/summarizerHeuristics.js +6 -9
- package/lib/summarizerHeuristics.js.map +1 -1
- package/lib/summarizerTypes.d.ts +21 -21
- package/lib/summarizerTypes.d.ts.map +1 -1
- package/lib/summarizerTypes.js.map +1 -1
- package/lib/summaryCollection.d.ts.map +1 -1
- package/lib/summaryCollection.js +18 -8
- package/lib/summaryCollection.js.map +1 -1
- package/lib/summaryFormat.d.ts +5 -2
- package/lib/summaryFormat.d.ts.map +1 -1
- package/lib/summaryFormat.js +20 -12
- package/lib/summaryFormat.js.map +1 -1
- package/lib/summaryGenerator.d.ts.map +1 -1
- package/lib/summaryGenerator.js +35 -16
- package/lib/summaryGenerator.js.map +1 -1
- package/lib/summaryManager.d.ts.map +1 -1
- package/lib/summaryManager.js +21 -9
- package/lib/summaryManager.js.map +1 -1
- package/lib/throttler.d.ts +2 -2
- package/lib/throttler.d.ts.map +1 -1
- package/lib/throttler.js +4 -4
- package/lib/throttler.js.map +1 -1
- package/package.json +121 -115
- package/prettier.config.cjs +1 -1
- package/src/batchTracker.ts +54 -49
- package/src/blobManager.ts +793 -672
- package/src/connectionTelemetry.ts +280 -249
- package/src/containerHandleContext.ts +27 -29
- package/src/containerRuntime.ts +3168 -2988
- package/src/dataStore.ts +172 -159
- package/src/dataStoreContext.ts +1098 -1055
- package/src/dataStoreContexts.ts +178 -161
- package/src/dataStoreRegistry.ts +25 -20
- package/src/dataStores.ts +884 -728
- package/src/deltaScheduler.ts +158 -150
- package/src/garbageCollection.ts +1860 -1688
- package/src/garbageCollectionConstants.ts +3 -0
- package/src/garbageCollectionHelpers.ts +61 -0
- package/src/gcSweepReadyUsageDetection.ts +89 -83
- package/src/index.ts +67 -66
- package/src/opLifecycle/README.md +152 -0
- package/src/opLifecycle/batchManager.ts +145 -141
- package/src/opLifecycle/definitions.ts +29 -29
- package/src/opLifecycle/index.ts +5 -5
- package/src/opLifecycle/opCompressor.ts +54 -53
- package/src/opLifecycle/opDecompressor.ts +100 -128
- package/src/opLifecycle/opSplitter.ts +214 -188
- package/src/opLifecycle/outbox.ts +204 -195
- package/src/opLifecycle/remoteMessageProcessor.ts +62 -62
- package/src/opProperties.ts +11 -9
- package/src/orderedClientElection.ts +489 -457
- package/src/packageVersion.ts +1 -1
- package/src/pendingStateManager.ts +384 -338
- package/src/runWhileConnectedCoordinator.ts +78 -71
- package/src/runningSummarizer.ts +619 -581
- package/src/scheduleManager.ts +299 -269
- package/src/serializedSnapshotStorage.ts +126 -112
- package/src/summarizer.ts +417 -381
- package/src/summarizerClientElection.ts +107 -100
- package/src/summarizerHandle.ts +11 -9
- package/src/summarizerHeuristics.ts +183 -186
- package/src/summarizerTypes.ts +344 -330
- package/src/summaryCollection.ts +378 -349
- package/src/summaryFormat.ts +165 -143
- package/src/summaryGenerator.ts +465 -410
- package/src/summaryManager.ts +377 -348
- package/src/throttler.ts +131 -122
- package/tsconfig.esnext.json +6 -6
- package/tsconfig.json +9 -13
- package/dist/garbageCollectionTombstoneUtils.d.ts +0 -14
- package/dist/garbageCollectionTombstoneUtils.d.ts.map +0 -1
- package/dist/garbageCollectionTombstoneUtils.js +0 -23
- package/dist/garbageCollectionTombstoneUtils.js.map +0 -1
- package/lib/garbageCollectionTombstoneUtils.d.ts +0 -14
- package/lib/garbageCollectionTombstoneUtils.d.ts.map +0 -1
- package/lib/garbageCollectionTombstoneUtils.js +0 -19
- package/lib/garbageCollectionTombstoneUtils.js.map +0 -1
- package/src/garbageCollectionTombstoneUtils.ts +0 -28
|
@@ -33,6 +33,9 @@ export const throwOnTombstoneLoadKey = "Fluid.GarbageCollection.ThrowOnTombstone
|
|
|
33
33
|
export const throwOnTombstoneUsageKey = "Fluid.GarbageCollection.ThrowOnTombstoneUsage";
|
|
34
34
|
// Feature gate to enable GC version upgrade.
|
|
35
35
|
export const gcVersionUpgradeToV2Key = "Fluid.GarbageCollection.GCVersionUpgradeToV2";
|
|
36
|
+
// Feature gate to enable GC sweep for datastores.
|
|
37
|
+
// TODO: Remove Test from the flag when we are confident to turn on sweep
|
|
38
|
+
export const sweepDatastoresKey = "Fluid.GarbageCollection.Test.SweepDataStores";
|
|
36
39
|
|
|
37
40
|
// One day in milliseconds.
|
|
38
41
|
export const oneDayMs = 1 * 24 * 60 * 60 * 1000;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
3
|
+
* Licensed under the MIT License.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { ITelemetryGenericEvent } from "@fluidframework/common-definitions";
|
|
7
|
+
import { packagePathToTelemetryProperty } from "@fluidframework/runtime-utils";
|
|
8
|
+
import { MonitoringContext } from "@fluidframework/telemetry-utils";
|
|
9
|
+
import {
|
|
10
|
+
disableTombstoneKey,
|
|
11
|
+
runSweepKey,
|
|
12
|
+
throwOnTombstoneLoadKey,
|
|
13
|
+
throwOnTombstoneUsageKey,
|
|
14
|
+
} from "./garbageCollectionConstants";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Consolidates info / logic for logging when we encounter unexpected usage of GC'd objects. For example, when a
|
|
18
|
+
* tombstoned or deleted object is loaded.
|
|
19
|
+
*/
|
|
20
|
+
export function sendGCUnexpectedUsageEvent(
|
|
21
|
+
mc: MonitoringContext,
|
|
22
|
+
event: ITelemetryGenericEvent & {
|
|
23
|
+
category: "error" | "generic";
|
|
24
|
+
gcTombstoneEnforcementAllowed: boolean | undefined;
|
|
25
|
+
},
|
|
26
|
+
packagePath: readonly string[] | undefined,
|
|
27
|
+
error?: unknown,
|
|
28
|
+
) {
|
|
29
|
+
event.pkg = packagePathToTelemetryProperty(packagePath);
|
|
30
|
+
event.tombstoneFlags = JSON.stringify({
|
|
31
|
+
DisableTombstone: mc.config.getBoolean(disableTombstoneKey),
|
|
32
|
+
ThrowOnTombstoneUsage: mc.config.getBoolean(throwOnTombstoneUsageKey),
|
|
33
|
+
ThrowOnTombstoneLoad: mc.config.getBoolean(throwOnTombstoneLoadKey),
|
|
34
|
+
});
|
|
35
|
+
event.sweepFlags = JSON.stringify({
|
|
36
|
+
EnableSweepFlag: mc.config.getBoolean(runSweepKey),
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
mc.logger.sendTelemetryEvent(event, error);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* In order to protect old documents that were created at a time when known bugs exist that violate GC's invariants
|
|
44
|
+
* such that enforcing GC (Fail on Tombstone load/usage, GC Sweep) would cause legitimate data loss,
|
|
45
|
+
* the container author may increment the generation value for Tombstone such that containers created
|
|
46
|
+
* with a different value will not be subjected to GC enforcement.
|
|
47
|
+
* If no generation is provided at runtime, this defaults to return true to maintain expected default behavior
|
|
48
|
+
* @param persistedGeneration - The persisted feature support value
|
|
49
|
+
* @param currentGeneration - The current app-provided feature support value
|
|
50
|
+
* @returns true if GC Enforcement (Fail on Tombstone load/usage) should be allowed
|
|
51
|
+
*/
|
|
52
|
+
export function shouldAllowGcTombstoneEnforcement(
|
|
53
|
+
persistedGeneration: number | undefined,
|
|
54
|
+
currentGeneration: number | undefined,
|
|
55
|
+
): boolean {
|
|
56
|
+
// If no Generation value is provided for this session, then we should default to letting Tombstone feature behave as intended.
|
|
57
|
+
if (currentGeneration === undefined) {
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
return persistedGeneration === currentGeneration;
|
|
61
|
+
}
|
|
@@ -6,10 +6,10 @@
|
|
|
6
6
|
import { ITelemetryProperties } from "@fluidframework/common-definitions";
|
|
7
7
|
import { ICriticalContainerError } from "@fluidframework/container-definitions";
|
|
8
8
|
import {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
IConfigProvider,
|
|
10
|
+
IFluidErrorBase,
|
|
11
|
+
LoggingError,
|
|
12
|
+
MonitoringContext,
|
|
13
13
|
} from "@fluidframework/telemetry-utils";
|
|
14
14
|
import { oneDayMs } from "./garbageCollectionConstants";
|
|
15
15
|
|
|
@@ -17,30 +17,33 @@ import { oneDayMs } from "./garbageCollectionConstants";
|
|
|
17
17
|
* Feature Gate Key -
|
|
18
18
|
* How many days between closing the container from this error (avoids locking user out of their file altogether)
|
|
19
19
|
*/
|
|
20
|
-
export const skipClosureForXDaysKey =
|
|
20
|
+
export const skipClosureForXDaysKey =
|
|
21
|
+
"Fluid.GarbageCollection.Dogfood.SweepReadyUsageDetection.SkipClosureForXDays";
|
|
21
22
|
|
|
22
23
|
/**
|
|
23
24
|
* LocalStorage key (NOT via feature gate / monitoring context)
|
|
24
25
|
* A map from docId to info about the last time we closed due to this error
|
|
25
26
|
*/
|
|
26
|
-
export const closuresMapLocalStorageKey =
|
|
27
|
+
export const closuresMapLocalStorageKey =
|
|
28
|
+
"Fluid.GarbageCollection.Dogfood.SweepReadyUsageDetection.Closures";
|
|
27
29
|
|
|
28
30
|
/**
|
|
29
31
|
* Feature gate key to enable closing the container if SweepReady objects are used.
|
|
30
32
|
* Value should contain keywords "interactiveClient" and/or "summarizer" to enable detection in each container type
|
|
31
33
|
*/
|
|
32
34
|
const sweepReadyUsageDetectionSetting = {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
35
|
+
read(config: IConfigProvider) {
|
|
36
|
+
const sweepReadyUsageDetectionKey =
|
|
37
|
+
"Fluid.GarbageCollection.Dogfood.SweepReadyUsageDetection";
|
|
38
|
+
const value = config.getString(sweepReadyUsageDetectionKey);
|
|
39
|
+
if (value === undefined) {
|
|
40
|
+
return { interactiveClient: false, summarizer: false };
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
interactiveClient: value.includes("interactiveClient"),
|
|
44
|
+
summarizer: value.includes("summarizer"),
|
|
45
|
+
};
|
|
46
|
+
},
|
|
44
47
|
};
|
|
45
48
|
|
|
46
49
|
/**
|
|
@@ -52,8 +55,8 @@ const sweepReadyUsageDetectionSetting = {
|
|
|
52
55
|
* since only the Summarizer has the latest truth about unreferenced node tracking
|
|
53
56
|
*/
|
|
54
57
|
export class SweepReadyUsageError extends LoggingError implements IFluidErrorBase {
|
|
55
|
-
|
|
56
|
-
|
|
58
|
+
/** This errorType will be in temporary use (until Sweep is fully implemented) so don't add to any errorType type */
|
|
59
|
+
public errorType: string = "unreferencedObjectUsedAfterGarbageCollected";
|
|
57
60
|
}
|
|
58
61
|
|
|
59
62
|
/**
|
|
@@ -65,75 +68,78 @@ export class SweepReadyUsageError extends LoggingError implements IFluidErrorBas
|
|
|
65
68
|
* (via skipClosureForXDaysKey above. Uses localStorage and closuresMapLocalStorageKey to implement this behavior)
|
|
66
69
|
*/
|
|
67
70
|
export class SweepReadyUsageDetectionHandler {
|
|
68
|
-
|
|
71
|
+
private readonly localStorage: Pick<Storage, "getItem" | "setItem">;
|
|
69
72
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
73
|
+
constructor(
|
|
74
|
+
private readonly uniqueContainerKey: string,
|
|
75
|
+
private readonly mc: MonitoringContext,
|
|
76
|
+
private readonly closeFn: (error?: ICriticalContainerError) => void,
|
|
77
|
+
localStorageOverride?: Pick<Storage, "getItem" | "setItem">,
|
|
78
|
+
) {
|
|
79
|
+
const noopStorage = { getItem: () => null, setItem: () => {} };
|
|
80
|
+
// localStorage is not defined in Node environment, so fall back to noopStorage if needed.
|
|
81
|
+
this.localStorage = localStorageOverride ?? globalThis.localStorage ?? noopStorage;
|
|
79
82
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
83
|
+
if (this.localStorage === noopStorage) {
|
|
84
|
+
// This means the Skip Closure Period logic will not work.
|
|
85
|
+
this.mc.logger.sendTelemetryEvent({
|
|
86
|
+
eventName: "SweepReadyUsageDetectionHandlerNoopStorage",
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
}
|
|
85
90
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
91
|
+
/**
|
|
92
|
+
* If SweepReady Usage Detection is enabled, close the interactive container.
|
|
93
|
+
* If the SkipClosureForXDays setting is set, don't close the container more than once in that period.
|
|
94
|
+
*
|
|
95
|
+
* Once Sweep is fully implemented, this will be removed since the objects will be gone
|
|
96
|
+
* and errors will arise elsewhere in the runtime
|
|
97
|
+
*/
|
|
98
|
+
public usageDetectedInInteractiveClient(errorProps: ITelemetryProperties) {
|
|
99
|
+
if (!sweepReadyUsageDetectionSetting.read(this.mc.config).interactiveClient) {
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
97
102
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
lastCloseTime = pastClosuresMap[this.uniqueContainerKey]?.lastCloseTime;
|
|
103
|
+
// Default stance is we close every time - this reflects the severity of SweepReady Object Usage.
|
|
104
|
+
// However, we may choose to "throttle" the closures by setting the SkipClosureForXDays setting,
|
|
105
|
+
// which will only allow the container to close once during that period, to avoid locking users out.
|
|
106
|
+
let shouldClose: boolean = true;
|
|
107
|
+
let pastClosuresMap: Record<string, { lastCloseTime: number } | undefined> = {};
|
|
108
|
+
let lastCloseTime: number | undefined;
|
|
109
|
+
const skipClosureForXDays = this.mc.config.getNumber(skipClosureForXDaysKey);
|
|
110
|
+
if (skipClosureForXDays !== undefined) {
|
|
111
|
+
// Read pastClosuresMap from localStorage then extract the lastCloseTime from the map
|
|
112
|
+
try {
|
|
113
|
+
const rawValue = this.localStorage.getItem(closuresMapLocalStorageKey);
|
|
114
|
+
const parsedValue = rawValue === null ? {} : JSON.parse(rawValue);
|
|
115
|
+
if (typeof parsedValue === "object") {
|
|
116
|
+
pastClosuresMap = parsedValue;
|
|
117
|
+
}
|
|
118
|
+
} catch (e) {}
|
|
119
|
+
lastCloseTime = pastClosuresMap[this.uniqueContainerKey]?.lastCloseTime;
|
|
116
120
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
121
|
+
// Don't close if we did already within the Skip Closure Period
|
|
122
|
+
if (
|
|
123
|
+
lastCloseTime !== undefined &&
|
|
124
|
+
Date.now() < lastCloseTime + skipClosureForXDays * oneDayMs
|
|
125
|
+
) {
|
|
126
|
+
shouldClose = false;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
122
129
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
this.localStorage.setItem(closuresMapLocalStorageKey, JSON.stringify(pastClosuresMap));
|
|
130
|
+
const error = new SweepReadyUsageError("SweepReady object used in Non-Summarizer Client", {
|
|
131
|
+
errorDetails: JSON.stringify({ ...errorProps, lastCloseTime, skipClosureForXDays }),
|
|
132
|
+
});
|
|
133
|
+
if (shouldClose) {
|
|
134
|
+
// Update closures map in localStorage before closing
|
|
135
|
+
// Note there is a race condition between different tabs updating localStorage and overwriting
|
|
136
|
+
// each others' updates. If so, some tab will crash again. Just reload one at a time to get unstuck
|
|
137
|
+
pastClosuresMap[this.uniqueContainerKey] = { lastCloseTime: Date.now() };
|
|
138
|
+
this.localStorage.setItem(closuresMapLocalStorageKey, JSON.stringify(pastClosuresMap));
|
|
133
139
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
140
|
+
this.closeFn(error);
|
|
141
|
+
} else {
|
|
142
|
+
this.mc.logger.sendErrorEvent({ eventName: "SweepReadyObject_UsageAllowed" }, error);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
139
145
|
}
|
package/src/index.ts
CHANGED
|
@@ -4,79 +4,80 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
export {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
7
|
+
ContainerMessageType,
|
|
8
|
+
ContainerRuntimeMessage,
|
|
9
|
+
IGCRuntimeOptions,
|
|
10
|
+
ISummaryRuntimeOptions,
|
|
11
|
+
ISummaryBaseConfiguration,
|
|
12
|
+
ISummaryConfigurationHeuristics,
|
|
13
|
+
ISummaryConfigurationDisableSummarizer,
|
|
14
|
+
ISummaryConfigurationDisableHeuristics,
|
|
15
|
+
IContainerRuntimeOptions,
|
|
16
|
+
IRootSummaryTreeWithStats,
|
|
17
|
+
isRuntimeMessage,
|
|
18
|
+
RuntimeMessage,
|
|
19
|
+
agentSchedulerId,
|
|
20
|
+
ContainerRuntime,
|
|
21
|
+
RuntimeHeaders,
|
|
22
|
+
AllowTombstoneRequestHeaderKey,
|
|
23
|
+
TombstoneResponseHeaderKey,
|
|
24
|
+
ISummaryConfiguration,
|
|
25
|
+
DefaultSummaryConfiguration,
|
|
26
|
+
ICompressionRuntimeOptions,
|
|
27
|
+
CompressionAlgorithms,
|
|
28
28
|
} from "./containerRuntime";
|
|
29
29
|
export { FluidDataStoreRegistry } from "./dataStoreRegistry";
|
|
30
|
+
export { IGCStats } from "./garbageCollection";
|
|
30
31
|
export {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
IPendingLocalState,
|
|
36
|
-
IPendingMessage,
|
|
37
|
-
IPendingState,
|
|
32
|
+
IPendingFlush,
|
|
33
|
+
IPendingLocalState,
|
|
34
|
+
IPendingMessage,
|
|
35
|
+
IPendingState,
|
|
38
36
|
} from "./pendingStateManager";
|
|
39
37
|
export { Summarizer } from "./summarizer";
|
|
40
38
|
export {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
39
|
+
EnqueueSummarizeResult,
|
|
40
|
+
IAckSummaryResult,
|
|
41
|
+
IBaseSummarizeResult,
|
|
42
|
+
IBroadcastSummaryResult,
|
|
43
|
+
ICancellationToken,
|
|
44
|
+
IConnectableRuntime,
|
|
45
|
+
IEnqueueSummarizeOptions,
|
|
46
|
+
IGenerateSummaryTreeResult,
|
|
47
|
+
IGeneratedSummaryStats,
|
|
48
|
+
INackSummaryResult,
|
|
49
|
+
IOnDemandSummarizeOptions,
|
|
50
|
+
IProvideSummarizer,
|
|
51
|
+
IRefreshSummaryAckOptions,
|
|
52
|
+
ISubmitSummaryOpResult,
|
|
53
|
+
ISubmitSummaryOptions,
|
|
54
|
+
ISummarizeOptions,
|
|
55
|
+
ISummarizeResults,
|
|
56
|
+
ISummarizer,
|
|
57
|
+
ISummarizerEvents,
|
|
58
|
+
ISummarizerInternalsProvider,
|
|
59
|
+
ISummarizerRuntime,
|
|
60
|
+
ISummarizingWarning,
|
|
61
|
+
ISummaryCancellationToken,
|
|
62
|
+
IUploadSummaryResult,
|
|
63
|
+
SubmitSummaryResult,
|
|
64
|
+
SummarizeResultPart,
|
|
65
|
+
SummarizerStopReason,
|
|
68
66
|
} from "./summarizerTypes";
|
|
69
67
|
export {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
68
|
+
IAckedSummary,
|
|
69
|
+
IClientSummaryWatcher,
|
|
70
|
+
ISummary,
|
|
71
|
+
ISummaryCollectionOpEvents,
|
|
72
|
+
ISummaryAckMessage,
|
|
73
|
+
ISummaryNackMessage,
|
|
74
|
+
ISummaryOpMessage,
|
|
75
|
+
OpActionEventListener,
|
|
76
|
+
OpActionEventName,
|
|
77
|
+
SummaryCollection,
|
|
80
78
|
} from "./summaryCollection";
|
|
81
|
-
export {
|
|
79
|
+
export {
|
|
80
|
+
ICancellableSummarizerController,
|
|
81
|
+
neverCancelledSummaryToken,
|
|
82
|
+
} from "./runWhileConnectedCoordinator";
|
|
82
83
|
export { IChunkedOp, unpackRuntimeMessage } from "./opLifecycle";
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# Configs and feature gates for solving the 1MB limit.
|
|
2
|
+
|
|
3
|
+
## Introduction
|
|
4
|
+
|
|
5
|
+
There is a current limitation regarding the size of the payload a Fluid client can send and receive. [The limit is 1MB per payload](https://github.com/microsoft/FluidFramework/issues/9023) and it is currently enforced explicitly with the `BatchTooLarge` error which closes the container.
|
|
6
|
+
|
|
7
|
+
There are two features which can be used to work around this size limit, batch compression and compressed batch chunking. This document describes how to enable/disable them, along with a brief description of how they work.
|
|
8
|
+
|
|
9
|
+
**The features are still considered experimental and for safety and back-compat reasons they are disabled by default.**
|
|
10
|
+
|
|
11
|
+
## Table of contents
|
|
12
|
+
|
|
13
|
+
- [Introduction](#introduction)
|
|
14
|
+
- [Enabling compression](#enabling-compression)
|
|
15
|
+
- [Enabling chunking for compression](#enabling-chunking-for-compression)
|
|
16
|
+
- [Disabling in case of emergency](#disabling-in-case-of-emergency)
|
|
17
|
+
- [Example configs](#example-configs)
|
|
18
|
+
- [How it works](#how-it-works)
|
|
19
|
+
|
|
20
|
+
## Enabling compression
|
|
21
|
+
|
|
22
|
+
**Compression targets payloads which exceed the max batch size (1MB) and it is disabled by default.** To enable compression, use the `IContainerRuntimeOptions.compressionOptions` property, of type `ICompressionRuntimeOptions`.
|
|
23
|
+
|
|
24
|
+
`ICompressionRuntimeOptions` has two properties:
|
|
25
|
+
|
|
26
|
+
- `minimumBatchSizeInBytes` – the minimum size of the batch for which compression should kick in. If the payload is too small, compression may not yield too many benefits. To target the original 1MB issue, a good value here would be to match the default maxBatchSizeInBytes (972800), however, experimentally, a good lower value could be at around 614400 bytes.
|
|
27
|
+
- `compressionAlgorithm` – currently, only `lz4` is supported.
|
|
28
|
+
|
|
29
|
+
## Enabling chunking for compression
|
|
30
|
+
|
|
31
|
+
**Op chunking for compression targets payloads which exceed the max batch size (1MB) after compression.** So, only payloads which are already compressed. By default, the feature is disabled.
|
|
32
|
+
|
|
33
|
+
To enable, use the `IContainerRuntimeOptions.chunkSizeInBytes` property, which represents the size of the chunked ops, when chunking is necessary. When chunking is performed, the large op is split into smaller ops (chunks). This config represents the size of the chunks. The value enables a trade-off between large chunks / few ops and small chunks / many ops. A good value for this would be at around 614400.
|
|
34
|
+
|
|
35
|
+
This config would govern chunking compressed batches only. We will not be enabling chunking across all types of ops/batches but **only when compression is enabled and when the batch is compressed**, and its payload size is more than `maxBatchSizeInBytes`. Therefore, for this feature to be working, it is required that compression is enabled using `IContainerRuntimeOptions.compressionOptions`.
|
|
36
|
+
|
|
37
|
+
It is recommended to also change the `maxBatchSizeInBytes` property in `IContainerRuntimeOptions`. By default, if unspecified it is 972800. We recommend a lower value such as `716800`, to account for any overhead on the server side. The reason for this is that chunking will only kick in after this configuration limit is exceeded. If the limit is too high, we might allow batches which are under 1MB but can increase in size due to overhead after they reach the server.
|
|
38
|
+
|
|
39
|
+
## Disabling in case of emergency
|
|
40
|
+
|
|
41
|
+
If the features are enabled using the configs, they can be disabled at runtime via feature gates as following:
|
|
42
|
+
|
|
43
|
+
- `Fluid.ContainerRuntime.DisableCompression` - if set to true, will disable compression (this has a side effect of also disabling chunking, as chunking is invoked only for compressed payloads).
|
|
44
|
+
- `Fluid.ContainerRuntime.DisableCompressionChunking` - if set to true, will disable chunking for compression.
|
|
45
|
+
|
|
46
|
+
## Example configs
|
|
47
|
+
|
|
48
|
+
Enable only compression:
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
const runtimeOptions: IContainerRuntimeOptions = {
|
|
52
|
+
compressionOptions: {
|
|
53
|
+
minimumBatchSizeInBytes: 614400,
|
|
54
|
+
compressionAlgorithm: CompressionAlgorithms.lz4,
|
|
55
|
+
},
|
|
56
|
+
maxBatchSizeInBytes: 716800,
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Enable compression and chunking:
|
|
61
|
+
|
|
62
|
+
```
|
|
63
|
+
const runtimeOptions: IContainerRuntimeOptions = {
|
|
64
|
+
compressionOptions: {
|
|
65
|
+
minimumBatchSizeInBytes: 614400,
|
|
66
|
+
compressionAlgorithm: CompressionAlgorithms.lz4,
|
|
67
|
+
},
|
|
68
|
+
chunkSizeInBytes: 614400,
|
|
69
|
+
maxBatchSizeInBytes: 716800,
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## How it works
|
|
74
|
+
|
|
75
|
+
Compression currently works as a runtime layer over the regular op sending/receiving pipeline.
|
|
76
|
+
|
|
77
|
+
If we have a batch with a size larger than the configured minimum required for compression (in the example let’s say it’s 850 bytes), as following:
|
|
78
|
+
|
|
79
|
+
```
|
|
80
|
+
+-----------+-----------+-----------+-----------+
|
|
81
|
+
| Op 1 | Op 2 | Op 3 | Op 4 |
|
|
82
|
+
| SeqNum: 1 | SeqNum: 2 | SeqNum: 3 | SeqNum: 4 |
|
|
83
|
+
| Size: 100 | Size: 150 | Size: 200 | Size: 400 |
|
|
84
|
+
+-----------+-----------+-----------+-----------+
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
The total size of the batch is 850 bytes. The client which needs to send the batch would compress the batch to a smaller size (200 bytes) and will send a new batch like the following:
|
|
88
|
+
|
|
89
|
+
```
|
|
90
|
+
+--------------------+-----------+-----------+-----------+
|
|
91
|
+
| Op 1 | Op 2 | Op 3 | Op 4 |
|
|
92
|
+
| SeqNum: 1 | SeqNum: 2 | SeqNum: 3 | SeqNum: 4 |
|
|
93
|
+
| Size: 200 | Size: 0 | Size: 0 | Size: 0 |
|
|
94
|
+
| Compression: 'lz4' | | | |
|
|
95
|
+
+--------------------+-----------+-----------+-----------+
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
The first op in the batch is the only one with content (which is opaque due to it being compressed), the rest of the ops serve only to reserve the sequence numbers so that the state machine which rebuilds the original batch on the receiving client can reconstruct the original batch.
|
|
99
|
+
|
|
100
|
+
When the batch is received by a client, it will detect the first op as being compressed, it will decompress it and store it in memory. For each empty op subsequently received, it will fetch the uncompressed content from memory and rebuild the original ops. The original ops are then processed by the runtime and applied accordingly.
|
|
101
|
+
So, compression virtualizes the batch.
|
|
102
|
+
|
|
103
|
+
After compression, the first op in the batch can exceed 1MB, therefore it would still be rejected. In this case, another layer of virtualization is added after compression (and before decompression, symmetrically on the receiving end).
|
|
104
|
+
|
|
105
|
+
The first op in the compressed batch can be chunked into smaller ops which can be sent outside the original batch. However, to conveniently maintain the batch semantics, the last chunk (the chunk which triggers rebuilding the original op) is the first op in the new batch.
|
|
106
|
+
|
|
107
|
+
To illustrate, let’s take the large batch below:
|
|
108
|
+
|
|
109
|
+
```
|
|
110
|
+
+--------------------+-----------+-----------+-----------+
|
|
111
|
+
| Op 1 | Op 2 | Op 3 | Op 4 |
|
|
112
|
+
| SeqNum: 1 | SeqNum: 2 | SeqNum: 3 | SeqNum: 4 |
|
|
113
|
+
| Size: 900 | Size: 0 | Size: 0 | Size: 0 |
|
|
114
|
+
| Compression: 'lz4' | | | |
|
|
115
|
+
+--------------------+-----------+-----------+-----------+
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
This will produce the following batches:
|
|
119
|
+
|
|
120
|
+
```
|
|
121
|
+
+-----------+
|
|
122
|
+
| Chunk 1/3 |
|
|
123
|
+
| SeqNum: 1 |
|
|
124
|
+
| Size: 300 |
|
|
125
|
+
+-----------+
|
|
126
|
+
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
```
|
|
130
|
+
+-----------+
|
|
131
|
+
| Chunk 2/3 |
|
|
132
|
+
| SeqNum: 2 |
|
|
133
|
+
| Size: 300 |
|
|
134
|
+
+-----------+
|
|
135
|
+
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
```
|
|
139
|
+
+-----------+-----------+-----------+-----------+
|
|
140
|
+
| Chunk 3/3 | Op 2 | Op 3 | Op 4 |
|
|
141
|
+
| SeqNum: 3 | SeqNum: 4 | SeqNum: 5 | SeqNum: 6 |
|
|
142
|
+
| Size: 300 | Size: 0 | Size: 0 | Size: 0 |
|
|
143
|
+
+-----------+-----------+-----------+-----------+
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
The first 2 chunks are sent in their own batches, while the last chunk is the first op in the last batch which contains the ops reserving the required sequence numbers.
|
|
147
|
+
|
|
148
|
+
Notice that the sequence numbers don’t matter here, as all ops will be based off the same reference sequence number, so the sequence number will be recalculated for all, without additional work.
|
|
149
|
+
|
|
150
|
+
Additionally, as compression preserves the original uncompressed batch layout in terms of the number of ops by using empty ops to reserve the sequence numbers, this ensures that the clients will always receive the exact count of ops to rebuild the uncompressed batch sequentially.
|
|
151
|
+
|
|
152
|
+
On the receiving end, the client will accumulate chunks 1 and 2 and keep them in memory. When chunk 3 is received, the original large, decompressed op will be rebuilt, and the runtime will then process the batch as if it is a compressed batch.
|