@fluidframework/container-runtime 2.0.0-internal.3.2.2 → 2.0.0-internal.3.3.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/dist/containerRuntime.d.ts +32 -53
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +40 -18
- package/dist/containerRuntime.js.map +1 -1
- package/dist/dataStores.d.ts.map +1 -1
- package/dist/dataStores.js +8 -3
- package/dist/dataStores.js.map +1 -1
- package/dist/deltaManagerSummarizerProxy.d.ts +19 -0
- package/dist/deltaManagerSummarizerProxy.d.ts.map +1 -0
- package/dist/deltaManagerSummarizerProxy.js +40 -0
- package/dist/deltaManagerSummarizerProxy.js.map +1 -0
- package/dist/gc/garbageCollection.d.ts +2 -33
- package/dist/gc/garbageCollection.d.ts.map +1 -1
- package/dist/gc/garbageCollection.js +36 -181
- package/dist/gc/garbageCollection.js.map +1 -1
- package/dist/gc/gcConfigs.d.ts +22 -0
- package/dist/gc/gcConfigs.d.ts.map +1 -0
- package/dist/gc/gcConfigs.js +138 -0
- package/dist/gc/gcConfigs.js.map +1 -0
- package/dist/gc/gcDefinitions.d.ts +101 -3
- package/dist/gc/gcDefinitions.d.ts.map +1 -1
- package/dist/gc/gcDefinitions.js +8 -3
- package/dist/gc/gcDefinitions.js.map +1 -1
- package/dist/gc/gcHelpers.d.ts +12 -1
- package/dist/gc/gcHelpers.d.ts.map +1 -1
- package/dist/gc/gcHelpers.js +55 -1
- package/dist/gc/gcHelpers.js.map +1 -1
- package/dist/gc/gcSummaryStateTracker.d.ts +1 -2
- package/dist/gc/gcSummaryStateTracker.d.ts.map +1 -1
- package/dist/gc/gcSummaryStateTracker.js +28 -37
- package/dist/gc/gcSummaryStateTracker.js.map +1 -1
- package/dist/gc/index.d.ts +3 -2
- package/dist/gc/index.d.ts.map +1 -1
- package/dist/gc/index.js +2 -1
- package/dist/gc/index.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 +9 -0
- package/dist/opLifecycle/batchManager.d.ts.map +1 -1
- package/dist/opLifecycle/batchManager.js +19 -2
- package/dist/opLifecycle/batchManager.js.map +1 -1
- package/dist/opLifecycle/index.d.ts +1 -1
- 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.d.ts.map +1 -1
- package/dist/opLifecycle/opCompressor.js +24 -10
- package/dist/opLifecycle/opCompressor.js.map +1 -1
- package/dist/opLifecycle/opSplitter.d.ts +14 -2
- package/dist/opLifecycle/opSplitter.d.ts.map +1 -1
- package/dist/opLifecycle/opSplitter.js +35 -18
- package/dist/opLifecycle/opSplitter.js.map +1 -1
- package/dist/opLifecycle/outbox.d.ts.map +1 -1
- package/dist/opLifecycle/outbox.js +25 -19
- package/dist/opLifecycle/outbox.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/storageServiceWithAttachBlobs.d.ts +17 -0
- package/dist/storageServiceWithAttachBlobs.d.ts.map +1 -0
- package/dist/storageServiceWithAttachBlobs.js +32 -0
- package/dist/storageServiceWithAttachBlobs.js.map +1 -0
- package/dist/summary/runWhileConnectedCoordinator.d.ts +3 -2
- package/dist/summary/runWhileConnectedCoordinator.d.ts.map +1 -1
- package/dist/summary/runWhileConnectedCoordinator.js +5 -4
- package/dist/summary/runWhileConnectedCoordinator.js.map +1 -1
- package/dist/summary/summarizerTypes.d.ts +2 -0
- package/dist/summary/summarizerTypes.d.ts.map +1 -1
- package/dist/summary/summarizerTypes.js.map +1 -1
- package/lib/containerRuntime.d.ts +32 -53
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +41 -19
- package/lib/containerRuntime.js.map +1 -1
- package/lib/dataStores.d.ts.map +1 -1
- package/lib/dataStores.js +9 -4
- package/lib/dataStores.js.map +1 -1
- package/lib/deltaManagerSummarizerProxy.d.ts +19 -0
- package/lib/deltaManagerSummarizerProxy.d.ts.map +1 -0
- package/lib/deltaManagerSummarizerProxy.js +36 -0
- package/lib/deltaManagerSummarizerProxy.js.map +1 -0
- package/lib/gc/garbageCollection.d.ts +2 -33
- package/lib/gc/garbageCollection.d.ts.map +1 -1
- package/lib/gc/garbageCollection.js +39 -184
- package/lib/gc/garbageCollection.js.map +1 -1
- package/lib/gc/gcConfigs.d.ts +22 -0
- package/lib/gc/gcConfigs.d.ts.map +1 -0
- package/lib/gc/gcConfigs.js +134 -0
- package/lib/gc/gcConfigs.js.map +1 -0
- package/lib/gc/gcDefinitions.d.ts +101 -3
- package/lib/gc/gcDefinitions.d.ts.map +1 -1
- package/lib/gc/gcDefinitions.js +7 -2
- package/lib/gc/gcDefinitions.js.map +1 -1
- package/lib/gc/gcHelpers.d.ts +12 -1
- package/lib/gc/gcHelpers.d.ts.map +1 -1
- package/lib/gc/gcHelpers.js +53 -0
- package/lib/gc/gcHelpers.js.map +1 -1
- package/lib/gc/gcSummaryStateTracker.d.ts +1 -2
- package/lib/gc/gcSummaryStateTracker.d.ts.map +1 -1
- package/lib/gc/gcSummaryStateTracker.js +28 -37
- package/lib/gc/gcSummaryStateTracker.js.map +1 -1
- package/lib/gc/index.d.ts +3 -2
- package/lib/gc/index.d.ts.map +1 -1
- package/lib/gc/index.js +1 -1
- package/lib/gc/index.js.map +1 -1
- package/lib/index.d.ts +2 -2
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js.map +1 -1
- package/lib/opLifecycle/batchManager.d.ts +9 -0
- package/lib/opLifecycle/batchManager.d.ts.map +1 -1
- package/lib/opLifecycle/batchManager.js +17 -1
- package/lib/opLifecycle/batchManager.js.map +1 -1
- package/lib/opLifecycle/index.d.ts +1 -1
- package/lib/opLifecycle/index.d.ts.map +1 -1
- package/lib/opLifecycle/index.js +1 -1
- package/lib/opLifecycle/index.js.map +1 -1
- package/lib/opLifecycle/opCompressor.d.ts.map +1 -1
- package/lib/opLifecycle/opCompressor.js +25 -11
- package/lib/opLifecycle/opCompressor.js.map +1 -1
- package/lib/opLifecycle/opSplitter.d.ts +14 -2
- package/lib/opLifecycle/opSplitter.d.ts.map +1 -1
- package/lib/opLifecycle/opSplitter.js +35 -18
- package/lib/opLifecycle/opSplitter.js.map +1 -1
- package/lib/opLifecycle/outbox.d.ts.map +1 -1
- package/lib/opLifecycle/outbox.js +26 -20
- package/lib/opLifecycle/outbox.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/storageServiceWithAttachBlobs.d.ts +17 -0
- package/lib/storageServiceWithAttachBlobs.d.ts.map +1 -0
- package/lib/storageServiceWithAttachBlobs.js +28 -0
- package/lib/storageServiceWithAttachBlobs.js.map +1 -0
- package/lib/summary/runWhileConnectedCoordinator.d.ts +3 -2
- package/lib/summary/runWhileConnectedCoordinator.d.ts.map +1 -1
- package/lib/summary/runWhileConnectedCoordinator.js +5 -4
- package/lib/summary/runWhileConnectedCoordinator.js.map +1 -1
- package/lib/summary/summarizerTypes.d.ts +2 -0
- package/lib/summary/summarizerTypes.d.ts.map +1 -1
- package/lib/summary/summarizerTypes.js.map +1 -1
- package/package.json +20 -31
- package/src/containerRuntime.ts +71 -74
- package/src/dataStores.ts +9 -4
- package/src/deltaManagerSummarizerProxy.ts +46 -0
- package/src/gc/garbageCollection.ts +50 -290
- package/src/gc/gcConfigs.ts +177 -0
- package/src/gc/gcDefinitions.ts +110 -4
- package/src/gc/gcHelpers.ts +78 -1
- package/src/gc/gcSummaryStateTracker.ts +35 -42
- package/src/gc/index.ts +8 -2
- package/src/index.ts +1 -2
- package/src/opLifecycle/README.md +2 -2
- package/src/opLifecycle/batchManager.ts +19 -1
- package/src/opLifecycle/index.ts +1 -1
- package/src/opLifecycle/opCompressor.ts +31 -12
- package/src/opLifecycle/opSplitter.ts +44 -20
- package/src/opLifecycle/outbox.ts +32 -20
- package/src/packageVersion.ts +1 -1
- package/src/storageServiceWithAttachBlobs.ts +38 -0
- package/src/summary/runWhileConnectedCoordinator.ts +7 -7
- package/src/summary/summarizerTypes.ts +2 -0
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
3
|
+
* Licensed under the MIT License.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { UsageError } from "@fluidframework/driver-utils";
|
|
7
|
+
import { MonitoringContext } from "@fluidframework/telemetry-utils";
|
|
8
|
+
import { IContainerRuntimeMetadata } from "../summary";
|
|
9
|
+
import {
|
|
10
|
+
defaultInactiveTimeoutMs,
|
|
11
|
+
defaultSessionExpiryDurationMs,
|
|
12
|
+
disableTombstoneKey,
|
|
13
|
+
GCFeatureMatrix,
|
|
14
|
+
gcTestModeKey,
|
|
15
|
+
gcTombstoneGenerationOptionName,
|
|
16
|
+
GCVersion,
|
|
17
|
+
IGarbageCollectorConfigs,
|
|
18
|
+
IGCRuntimeOptions,
|
|
19
|
+
maxSnapshotCacheExpiryMs,
|
|
20
|
+
oneDayMs,
|
|
21
|
+
runGCKey,
|
|
22
|
+
runSessionExpiryKey,
|
|
23
|
+
runSweepKey,
|
|
24
|
+
} from "./gcDefinitions";
|
|
25
|
+
import { getGCVersion } from "./gcHelpers";
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Generates configurations for the Garbage Collector that it uses to determine what to run and how.
|
|
29
|
+
* @param mc - The monitoring context for reading configs from the config provider.
|
|
30
|
+
* @param createParams - The creation params:
|
|
31
|
+
* gcOptions - The garbage collector runtime options.
|
|
32
|
+
* metadata - The container runtime's createParams.metadata.
|
|
33
|
+
* existing - Whether the container is new or an existing one.
|
|
34
|
+
* @returns The garbage collector configurations.
|
|
35
|
+
*/
|
|
36
|
+
export function generateGCConfigs(
|
|
37
|
+
mc: MonitoringContext,
|
|
38
|
+
createParams: {
|
|
39
|
+
gcOptions: IGCRuntimeOptions;
|
|
40
|
+
metadata: IContainerRuntimeMetadata | undefined;
|
|
41
|
+
existing: boolean;
|
|
42
|
+
},
|
|
43
|
+
): IGarbageCollectorConfigs {
|
|
44
|
+
let gcEnabled: boolean;
|
|
45
|
+
let sweepEnabled: boolean;
|
|
46
|
+
let sessionExpiryTimeoutMs: number | undefined;
|
|
47
|
+
let sweepTimeoutMs: number | undefined;
|
|
48
|
+
let persistedGcFeatureMatrix: GCFeatureMatrix | undefined;
|
|
49
|
+
let gcVersionInBaseSnapshot: GCVersion | undefined;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* The following GC state is enabled during container creation and cannot be changed throughout its lifetime:
|
|
53
|
+
* 1. Whether running GC mark phase is allowed or not.
|
|
54
|
+
* 2. Whether running GC sweep phase is allowed or not.
|
|
55
|
+
* 3. Whether GC session expiry is enabled or not.
|
|
56
|
+
* For existing containers, we get this information from the createParams.metadata blob of its summary.
|
|
57
|
+
*/
|
|
58
|
+
if (createParams.existing) {
|
|
59
|
+
gcVersionInBaseSnapshot = getGCVersion(createParams.metadata);
|
|
60
|
+
// Existing documents which did not have createParams.metadata blob or had GC disabled have version as 0. For all
|
|
61
|
+
// other existing documents, GC is enabled.
|
|
62
|
+
gcEnabled = gcVersionInBaseSnapshot > 0;
|
|
63
|
+
sweepEnabled = createParams.metadata?.sweepEnabled ?? false;
|
|
64
|
+
sessionExpiryTimeoutMs = createParams.metadata?.sessionExpiryTimeoutMs;
|
|
65
|
+
sweepTimeoutMs =
|
|
66
|
+
createParams.metadata?.sweepTimeoutMs ?? computeSweepTimeout(sessionExpiryTimeoutMs); // Backfill old documents that didn't persist this
|
|
67
|
+
persistedGcFeatureMatrix = createParams.metadata?.gcFeatureMatrix;
|
|
68
|
+
} else {
|
|
69
|
+
// Sweep should not be enabled without enabling GC mark phase. We could silently disable sweep in this
|
|
70
|
+
// scenario but explicitly failing makes it clearer and promotes correct usage.
|
|
71
|
+
if (createParams.gcOptions.sweepAllowed && createParams.gcOptions.gcAllowed === false) {
|
|
72
|
+
throw new UsageError("GC sweep phase cannot be enabled without enabling GC mark phase");
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// This Test Override only applies for new containers
|
|
76
|
+
const testOverrideSweepTimeoutMs = mc.config.getNumber(
|
|
77
|
+
"Fluid.GarbageCollection.TestOverride.SweepTimeoutMs",
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
// For new documents, GC is enabled by default. It can be explicitly disabled by setting the gcAllowed
|
|
81
|
+
// flag in GC options to false.
|
|
82
|
+
gcEnabled = createParams.gcOptions.gcAllowed !== false;
|
|
83
|
+
// The sweep phase has to be explicitly enabled by setting the sweepAllowed flag in GC options to true.
|
|
84
|
+
sweepEnabled = createParams.gcOptions.sweepAllowed === true;
|
|
85
|
+
|
|
86
|
+
// Set the Session Expiry if GC is enabled and session expiry flag isn't explicitly set to false.
|
|
87
|
+
if (gcEnabled && mc.config.getBoolean(runSessionExpiryKey) !== false) {
|
|
88
|
+
sessionExpiryTimeoutMs =
|
|
89
|
+
createParams.gcOptions.sessionExpiryTimeoutMs ?? defaultSessionExpiryDurationMs;
|
|
90
|
+
}
|
|
91
|
+
sweepTimeoutMs = testOverrideSweepTimeoutMs ?? computeSweepTimeout(sessionExpiryTimeoutMs);
|
|
92
|
+
|
|
93
|
+
if (createParams.gcOptions[gcTombstoneGenerationOptionName] !== undefined) {
|
|
94
|
+
persistedGcFeatureMatrix = {
|
|
95
|
+
tombstoneGeneration: createParams.gcOptions[gcTombstoneGenerationOptionName],
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Whether GC should run or not. The following conditions have to be met to run sweep:
|
|
102
|
+
*
|
|
103
|
+
* 1. GC should be enabled for this container.
|
|
104
|
+
*
|
|
105
|
+
* 2. GC should not be disabled via disableGC GC option.
|
|
106
|
+
*
|
|
107
|
+
* These conditions can be overridden via runGCKey feature flag.
|
|
108
|
+
*/
|
|
109
|
+
const shouldRunGC =
|
|
110
|
+
mc.config.getBoolean(runGCKey) ??
|
|
111
|
+
// GC must be enabled for the document.
|
|
112
|
+
(gcEnabled &&
|
|
113
|
+
// GC must not be disabled via GC options.
|
|
114
|
+
!createParams.gcOptions.disableGC);
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Whether sweep should run or not. The following conditions have to be met to run sweep:
|
|
118
|
+
*
|
|
119
|
+
* 1. Overall GC or mark phase must be enabled (this.configs.shouldRunGC).
|
|
120
|
+
* 2. Sweep timeout should be available. Without this, we wouldn't know when an object should be deleted.
|
|
121
|
+
* 3. The driver must implement the policy limiting the age of snapshots used for loading. Otherwise
|
|
122
|
+
* the Sweep Timeout calculation is not valid. We use the persisted value to ensure consistency over time.
|
|
123
|
+
* 4. Sweep should be enabled for this container (this.sweepEnabled). This can be overridden via runSweep
|
|
124
|
+
* feature flag.
|
|
125
|
+
*/
|
|
126
|
+
const shouldRunSweep =
|
|
127
|
+
shouldRunGC &&
|
|
128
|
+
sweepTimeoutMs !== undefined &&
|
|
129
|
+
(mc.config.getBoolean(runSweepKey) ?? sweepEnabled);
|
|
130
|
+
|
|
131
|
+
// Override inactive timeout if test config or gc options to override it is set.
|
|
132
|
+
const inactiveTimeoutMs =
|
|
133
|
+
mc.config.getNumber("Fluid.GarbageCollection.TestOverride.InactiveTimeoutMs") ??
|
|
134
|
+
createParams.gcOptions.inactiveTimeoutMs ??
|
|
135
|
+
defaultInactiveTimeoutMs;
|
|
136
|
+
|
|
137
|
+
// Inactive timeout must be greater than sweep timeout since a node goes from active -> inactive -> sweep ready.
|
|
138
|
+
if (sweepTimeoutMs !== undefined && inactiveTimeoutMs > sweepTimeoutMs) {
|
|
139
|
+
throw new UsageError("inactive timeout should not be greater than the sweep timeout");
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Whether we are running in test mode. In this mode, unreferenced nodes are immediately deleted.
|
|
143
|
+
const testMode =
|
|
144
|
+
mc.config.getBoolean(gcTestModeKey) ?? createParams.gcOptions.runGCInTestMode === true;
|
|
145
|
+
// Whether we are running in tombstone mode. This is enabled by default if sweep won't run. It can be disabled
|
|
146
|
+
// via feature flags.
|
|
147
|
+
const tombstoneMode = !shouldRunSweep && mc.config.getBoolean(disableTombstoneKey) !== true;
|
|
148
|
+
const runFullGC = createParams.gcOptions.runFullGC;
|
|
149
|
+
|
|
150
|
+
return {
|
|
151
|
+
gcEnabled,
|
|
152
|
+
sweepEnabled,
|
|
153
|
+
shouldRunGC,
|
|
154
|
+
shouldRunSweep,
|
|
155
|
+
runFullGC,
|
|
156
|
+
testMode,
|
|
157
|
+
tombstoneMode,
|
|
158
|
+
sessionExpiryTimeoutMs,
|
|
159
|
+
sweepTimeoutMs,
|
|
160
|
+
inactiveTimeoutMs,
|
|
161
|
+
persistedGcFeatureMatrix,
|
|
162
|
+
gcVersionInBaseSnapshot,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Sweep timeout is the time after which unreferenced content can be swept.
|
|
168
|
+
* Sweep timeout = session expiry timeout + snapshot cache expiry timeout + one day buffer.
|
|
169
|
+
*
|
|
170
|
+
* The snapshot cache expiry timeout cannot be known precisely but the upper bound is 5 days.
|
|
171
|
+
* The buffer is added to account for any clock skew or other edge cases.
|
|
172
|
+
* We use server timestamps throughout so the skew should be minimal but make it 1 day to be safe.
|
|
173
|
+
*/
|
|
174
|
+
function computeSweepTimeout(sessionExpiryTimeoutMs: number | undefined): number | undefined {
|
|
175
|
+
const bufferMs = oneDayMs;
|
|
176
|
+
return sessionExpiryTimeoutMs && sessionExpiryTimeoutMs + maxSnapshotCacheExpiryMs + bufferMs;
|
|
177
|
+
}
|
package/src/gc/gcDefinitions.ts
CHANGED
|
@@ -14,7 +14,6 @@ import {
|
|
|
14
14
|
} from "@fluidframework/runtime-definitions";
|
|
15
15
|
import { ReadAndParseBlob, RefreshSummaryResult } from "@fluidframework/runtime-utils";
|
|
16
16
|
import { ITelemetryLogger } from "@fluidframework/common-definitions";
|
|
17
|
-
import { IGCRuntimeOptions } from "../containerRuntime";
|
|
18
17
|
import { IContainerRuntimeMetadata, ICreateContainerMetadata } from "../summary";
|
|
19
18
|
|
|
20
19
|
export type GCVersion = number;
|
|
@@ -35,8 +34,6 @@ export const runSweepKey = "Fluid.GarbageCollection.RunSweep";
|
|
|
35
34
|
export const gcTestModeKey = "Fluid.GarbageCollection.GCTestMode";
|
|
36
35
|
// Feature gate key to expire a session after a set period of time.
|
|
37
36
|
export const runSessionExpiryKey = "Fluid.GarbageCollection.RunSessionExpiry";
|
|
38
|
-
// Feature gate key to write the gc blob as a handle if the data is the same.
|
|
39
|
-
export const trackGCStateKey = "Fluid.GarbageCollection.TrackGCState";
|
|
40
37
|
// Feature gate key to turn GC sweep log off.
|
|
41
38
|
export const disableSweepLogKey = "Fluid.GarbageCollection.DisableSweepLog";
|
|
42
39
|
// Feature gate key to disable the tombstone feature, i.e., tombstone information is not read / written into summary.
|
|
@@ -56,6 +53,14 @@ export const sweepAttachmentBlobsKey = "Fluid.GarbageCollection.Test.SweepAttach
|
|
|
56
53
|
// One day in milliseconds.
|
|
57
54
|
export const oneDayMs = 1 * 24 * 60 * 60 * 1000;
|
|
58
55
|
|
|
56
|
+
/**
|
|
57
|
+
* The maximum snapshot cache expiry in the driver. This is used to calculate the sweep timeout.
|
|
58
|
+
* Sweep timeout = session expiry timeout + snapshot cache expiry timeout + a buffer.
|
|
59
|
+
* The snapshot cache expiry timeout cannot be known precisely but the upper bound is 5 days, i.e., any snapshot
|
|
60
|
+
* in cache will be invalidated before 5 days.
|
|
61
|
+
*/
|
|
62
|
+
export const maxSnapshotCacheExpiryMs = 5 * oneDayMs;
|
|
63
|
+
|
|
59
64
|
export const defaultInactiveTimeoutMs = 7 * oneDayMs; // 7 days
|
|
60
65
|
export const defaultSessionExpiryDurationMs = 30 * oneDayMs; // 30 days
|
|
61
66
|
|
|
@@ -138,7 +143,11 @@ export const GCNodeType = {
|
|
|
138
143
|
};
|
|
139
144
|
export type GCNodeType = typeof GCNodeType[keyof typeof GCNodeType];
|
|
140
145
|
|
|
141
|
-
|
|
146
|
+
// NOTE: Once this is removed from the package exports in the next major, the deprecation tag can be removed as well
|
|
147
|
+
/**
|
|
148
|
+
* @deprecated - Was only to be used internally anyway, no replacement provided.
|
|
149
|
+
* Defines the APIs for the runtime object to be passed to the garbage collector.
|
|
150
|
+
*/
|
|
142
151
|
export interface IGarbageCollectionRuntime {
|
|
143
152
|
/** Before GC runs, called to notify the runtime to update any pending GC state. */
|
|
144
153
|
updateStateBeforeGC(): Promise<void>;
|
|
@@ -232,6 +241,103 @@ export interface IGarbageCollectorCreateParams {
|
|
|
232
241
|
readonly getContainerDiagnosticId: () => string;
|
|
233
242
|
}
|
|
234
243
|
|
|
244
|
+
export interface IGCRuntimeOptions {
|
|
245
|
+
/**
|
|
246
|
+
* Flag that if true, will enable running garbage collection (GC) for a new container.
|
|
247
|
+
*
|
|
248
|
+
* GC has mark phase and sweep phase. In mark phase, unreferenced objects are identified
|
|
249
|
+
* and marked as such in the summary. This option enables the mark phase.
|
|
250
|
+
* In sweep phase, unreferenced objects are eventually deleted from the container if they meet certain conditions.
|
|
251
|
+
* Sweep phase can be enabled via the "sweepAllowed" option.
|
|
252
|
+
*
|
|
253
|
+
* Note: This setting is persisted in the container's summary and cannot be changed.
|
|
254
|
+
*/
|
|
255
|
+
gcAllowed?: boolean;
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Flag that if true, enables GC's sweep phase for a new container.
|
|
259
|
+
*
|
|
260
|
+
* This will allow GC to eventually delete unreferenced objects from the container.
|
|
261
|
+
* This flag should only be set to true if "gcAllowed" is true.
|
|
262
|
+
*
|
|
263
|
+
* Note: This setting is persisted in the container's summary and cannot be changed.
|
|
264
|
+
*/
|
|
265
|
+
sweepAllowed?: boolean;
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Flag that if true, will disable garbage collection for the session.
|
|
269
|
+
* Can be used to disable running GC on containers where it is allowed via the gcAllowed option.
|
|
270
|
+
*/
|
|
271
|
+
disableGC?: boolean;
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Flag that will bypass optimizations and generate GC data for all nodes irrespective of whether a node
|
|
275
|
+
* changed or not.
|
|
276
|
+
*/
|
|
277
|
+
runFullGC?: boolean;
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Maximum session duration for a new container. If not present, a default value will be used.
|
|
281
|
+
*
|
|
282
|
+
* Note: This setting is persisted in the container's summary and cannot be changed.
|
|
283
|
+
*/
|
|
284
|
+
sessionExpiryTimeoutMs?: number;
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Allows additional GC options to be passed.
|
|
288
|
+
*/
|
|
289
|
+
[key: string]: any;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* The configurations for Garbage Collector that determines what runs and how.
|
|
294
|
+
*/
|
|
295
|
+
export interface IGarbageCollectorConfigs {
|
|
296
|
+
/**
|
|
297
|
+
* Tracks if GC is enabled for this document. This is specified during document creation and doesn't change
|
|
298
|
+
* throughout its lifetime.
|
|
299
|
+
*/
|
|
300
|
+
readonly gcEnabled: boolean;
|
|
301
|
+
/**
|
|
302
|
+
* Tracks if sweep phase is enabled for this document. This is specified during document creation and doesn't change
|
|
303
|
+
* throughout its lifetime.
|
|
304
|
+
*/
|
|
305
|
+
readonly sweepEnabled: boolean;
|
|
306
|
+
/**
|
|
307
|
+
* Tracks if GC should run or not. Even if GC is enabled for a document (see gcEnabled), it can be explicitly
|
|
308
|
+
* disabled via runtime options or feature flags.
|
|
309
|
+
*/
|
|
310
|
+
readonly shouldRunGC: boolean;
|
|
311
|
+
/**
|
|
312
|
+
* Tracks if sweep phase should run or not. Even if the sweep phase is enabled for a document (see sweepEnabled), it
|
|
313
|
+
* can be explicitly disabled via feature flags. It also won't run if session expiry is not enabled.
|
|
314
|
+
*/
|
|
315
|
+
readonly shouldRunSweep: boolean;
|
|
316
|
+
/**
|
|
317
|
+
* If true, bypass optimizations and generate GC data for all nodes irrespective of whether a node changed or not.
|
|
318
|
+
*/
|
|
319
|
+
readonly runFullGC: boolean | undefined;
|
|
320
|
+
/** The time in ms to expire a session for a client for gc. */
|
|
321
|
+
readonly sessionExpiryTimeoutMs: number | undefined;
|
|
322
|
+
/** The time after which an unreferenced node is ready to be swept. */
|
|
323
|
+
readonly sweepTimeoutMs: number | undefined;
|
|
324
|
+
/** The time after which an unreferenced node is inactive. */
|
|
325
|
+
readonly inactiveTimeoutMs: number;
|
|
326
|
+
/** Tracks whether GC should run in test mode. In this mode, unreferenced objects are deleted immediately. */
|
|
327
|
+
readonly testMode: boolean;
|
|
328
|
+
/**
|
|
329
|
+
* Tracks whether GC should run in tombstone mode. In this mode, sweep ready objects are marked as tombstones.
|
|
330
|
+
* In interactive (non-summarizer) clients, tombstone objects behave as if they are deleted, i.e., access to them
|
|
331
|
+
* is not allowed. However, these objects can be accessed after referencing them first. It is used as a staging
|
|
332
|
+
* step for sweep where accidental sweep ready objects can be recovered.
|
|
333
|
+
*/
|
|
334
|
+
readonly tombstoneMode: boolean;
|
|
335
|
+
/** @see GCFeatureMatrix. */
|
|
336
|
+
readonly persistedGcFeatureMatrix: GCFeatureMatrix | undefined;
|
|
337
|
+
/** The version of GC in the base snapshot. */
|
|
338
|
+
readonly gcVersionInBaseSnapshot: GCVersion | undefined;
|
|
339
|
+
}
|
|
340
|
+
|
|
235
341
|
/** The state of node that is unreferenced. */
|
|
236
342
|
export const UnreferencedState = {
|
|
237
343
|
/** The node is active, i.e., it can become referenced again. */
|
package/src/gc/gcHelpers.ts
CHANGED
|
@@ -4,12 +4,23 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { ITelemetryGenericEvent } from "@fluidframework/common-definitions";
|
|
7
|
+
import { assert } from "@fluidframework/common-utils";
|
|
8
|
+
import { ISnapshotTree } from "@fluidframework/protocol-definitions";
|
|
7
9
|
import {
|
|
10
|
+
gcTreeKey,
|
|
8
11
|
IGarbageCollectionNodeData,
|
|
12
|
+
IGarbageCollectionSnapshotData,
|
|
9
13
|
IGarbageCollectionState,
|
|
14
|
+
IGarbageCollectionSummaryDetailsLegacy,
|
|
10
15
|
} from "@fluidframework/runtime-definitions";
|
|
11
|
-
import { packagePathToTelemetryProperty } from "@fluidframework/runtime-utils";
|
|
16
|
+
import { packagePathToTelemetryProperty, ReadAndParseBlob } from "@fluidframework/runtime-utils";
|
|
12
17
|
import { MonitoringContext } from "@fluidframework/telemetry-utils";
|
|
18
|
+
import { getSummaryForDatastores } from "../dataStores";
|
|
19
|
+
import {
|
|
20
|
+
dataStoreAttributesBlobName,
|
|
21
|
+
IContainerRuntimeMetadata,
|
|
22
|
+
ReadFluidDataStoreAttributes,
|
|
23
|
+
} from "../summary";
|
|
13
24
|
import {
|
|
14
25
|
disableTombstoneKey,
|
|
15
26
|
GCVersion,
|
|
@@ -74,6 +85,9 @@ export function shouldAllowGcTombstoneEnforcement(
|
|
|
74
85
|
return persistedGeneration === currentGeneration;
|
|
75
86
|
}
|
|
76
87
|
|
|
88
|
+
/**
|
|
89
|
+
* Sorts the given GC state as per the id of the GC nodes. It also sorts the outbound routes array of each node.
|
|
90
|
+
*/
|
|
77
91
|
export function generateSortedGCState(gcState: IGarbageCollectionState): IGarbageCollectionState {
|
|
78
92
|
const sortableArray: [string, IGarbageCollectionNodeData][] = Object.entries(gcState.gcNodes);
|
|
79
93
|
sortableArray.sort(([a], [b]) => a.localeCompare(b));
|
|
@@ -84,3 +98,66 @@ export function generateSortedGCState(gcState: IGarbageCollectionState): IGarbag
|
|
|
84
98
|
}
|
|
85
99
|
return sortedGCState;
|
|
86
100
|
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* This is for back-compat only - Before GC data was written at the root of the summary tree, individual GC blobs were
|
|
104
|
+
* written at data store's snapshot tree. This function consolidates them into the new IGarbageCollectionState format.
|
|
105
|
+
*/
|
|
106
|
+
export async function getSnapshotDataFromOldSnapshotFormat(
|
|
107
|
+
oldSnapshot: ISnapshotTree,
|
|
108
|
+
metadata: IContainerRuntimeMetadata | undefined,
|
|
109
|
+
readAndParseBlob: ReadAndParseBlob,
|
|
110
|
+
): Promise<IGarbageCollectionSnapshotData | undefined> {
|
|
111
|
+
// Add a node for the root node that is not present in older snapshot format.
|
|
112
|
+
const gcState: IGarbageCollectionState = {
|
|
113
|
+
gcNodes: { "/": { outboundRoutes: [] } },
|
|
114
|
+
};
|
|
115
|
+
const dataStoreSnapshotTree = getSummaryForDatastores(oldSnapshot, metadata);
|
|
116
|
+
assert(
|
|
117
|
+
dataStoreSnapshotTree !== undefined,
|
|
118
|
+
0x2a8 /* "Expected data store snapshot tree in base snapshot" */,
|
|
119
|
+
);
|
|
120
|
+
for (const [dsId, dsSnapshotTree] of Object.entries(dataStoreSnapshotTree.trees)) {
|
|
121
|
+
const blobId = dsSnapshotTree.blobs[gcTreeKey];
|
|
122
|
+
if (blobId === undefined) {
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const gcSummaryDetails = await readAndParseBlob<IGarbageCollectionSummaryDetailsLegacy>(
|
|
127
|
+
blobId,
|
|
128
|
+
);
|
|
129
|
+
// If there are no nodes for this data store, skip it.
|
|
130
|
+
if (gcSummaryDetails.gcData?.gcNodes === undefined) {
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const dsRootId = `/${dsId}`;
|
|
135
|
+
// Since we used to write GC data at data store level, we won't have an entry for the root ("/").
|
|
136
|
+
// Construct that entry by adding root data store ids to its outbound routes.
|
|
137
|
+
const initialSnapshotDetails = await readAndParseBlob<ReadFluidDataStoreAttributes>(
|
|
138
|
+
dsSnapshotTree.blobs[dataStoreAttributesBlobName],
|
|
139
|
+
);
|
|
140
|
+
if (initialSnapshotDetails.isRootDataStore) {
|
|
141
|
+
gcState.gcNodes["/"].outboundRoutes.push(dsRootId);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
for (const [id, outboundRoutes] of Object.entries(gcSummaryDetails.gcData.gcNodes)) {
|
|
145
|
+
// Prefix the data store id to the GC node ids to make them relative to the root from being
|
|
146
|
+
// relative to the data store. Similar to how its done in DataStore::getGCData.
|
|
147
|
+
const rootId = id === "/" ? dsRootId : `${dsRootId}${id}`;
|
|
148
|
+
gcState.gcNodes[rootId] = {
|
|
149
|
+
outboundRoutes: Array.from(outboundRoutes),
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
assert(
|
|
153
|
+
gcState.gcNodes[dsRootId] !== undefined,
|
|
154
|
+
0x2a9 /* GC nodes for data store not in GC blob */,
|
|
155
|
+
);
|
|
156
|
+
gcState.gcNodes[dsRootId].unreferencedTimestampMs = gcSummaryDetails.unrefTimestamp;
|
|
157
|
+
}
|
|
158
|
+
// If there is only one node (root node just added above), either GC is disabled or we are loading from
|
|
159
|
+
// the first summary generated by detached container. In both cases, GC was not run - return undefined.
|
|
160
|
+
return Object.keys(gcState.gcNodes).length === 1
|
|
161
|
+
? undefined
|
|
162
|
+
: { gcState, tombstones: undefined, deletedNodes: undefined };
|
|
163
|
+
}
|
|
@@ -63,8 +63,6 @@ export class GCSummaryStateTracker {
|
|
|
63
63
|
constructor(
|
|
64
64
|
// Tells whether GC should run or not.
|
|
65
65
|
private readonly shouldRunGC: boolean,
|
|
66
|
-
// Tells whether GC state should be tracked across summaries or not.
|
|
67
|
-
private readonly trackGCState: boolean,
|
|
68
66
|
// Tells whether tombstone mode is enabled or not.
|
|
69
67
|
private readonly tombstoneMode: boolean,
|
|
70
68
|
private readonly mc: MonitoringContext,
|
|
@@ -159,38 +157,37 @@ export class GCSummaryStateTracker {
|
|
|
159
157
|
* Otherwise, write the GC summary tree. In the tree, for each of these that changed, write a summary blob and
|
|
160
158
|
* for each of these that did not change, write a summary handle.
|
|
161
159
|
*/
|
|
162
|
-
|
|
163
|
-
|
|
160
|
+
this.pendingSummaryData = {
|
|
161
|
+
serializedGCState,
|
|
162
|
+
serializedTombstones,
|
|
163
|
+
serializedDeletedNodes,
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
if (trackState && !fullTree && this.latestSummaryData !== undefined) {
|
|
167
|
+
// If nothing changed since last summary, send a summary handle for the entire GC data.
|
|
168
|
+
if (
|
|
169
|
+
this.latestSummaryData.serializedGCState === serializedGCState &&
|
|
170
|
+
this.latestSummaryData.serializedTombstones === serializedTombstones
|
|
171
|
+
) {
|
|
172
|
+
const stats = mergeStats();
|
|
173
|
+
stats.handleNodeCount++;
|
|
174
|
+
return {
|
|
175
|
+
summary: {
|
|
176
|
+
type: SummaryType.Handle,
|
|
177
|
+
handle: `/${gcTreeKey}`,
|
|
178
|
+
handleType: SummaryType.Tree,
|
|
179
|
+
},
|
|
180
|
+
stats,
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// If some state changed, build a GC summary tree.
|
|
185
|
+
return this.buildGCSummaryTree(
|
|
164
186
|
serializedGCState,
|
|
165
187
|
serializedTombstones,
|
|
166
188
|
serializedDeletedNodes,
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
// If nothing changed since last summary, send a summary handle for the entire GC data.
|
|
170
|
-
if (
|
|
171
|
-
this.latestSummaryData.serializedGCState === serializedGCState &&
|
|
172
|
-
this.latestSummaryData.serializedTombstones === serializedTombstones
|
|
173
|
-
) {
|
|
174
|
-
const stats = mergeStats();
|
|
175
|
-
stats.handleNodeCount++;
|
|
176
|
-
return {
|
|
177
|
-
summary: {
|
|
178
|
-
type: SummaryType.Handle,
|
|
179
|
-
handle: `/${gcTreeKey}`,
|
|
180
|
-
handleType: SummaryType.Tree,
|
|
181
|
-
},
|
|
182
|
-
stats,
|
|
183
|
-
};
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
// If some state changed, build a GC summary tree.
|
|
187
|
-
return this.buildGCSummaryTree(
|
|
188
|
-
serializedGCState,
|
|
189
|
-
serializedTombstones,
|
|
190
|
-
serializedDeletedNodes,
|
|
191
|
-
true /* trackState */,
|
|
192
|
-
);
|
|
193
|
-
}
|
|
189
|
+
true /* trackState */,
|
|
190
|
+
);
|
|
194
191
|
}
|
|
195
192
|
// If not tracking GC state, build a GC summary tree without any summary handles.
|
|
196
193
|
return this.buildGCSummaryTree(
|
|
@@ -290,10 +287,8 @@ export class GCSummaryStateTracker {
|
|
|
290
287
|
// Update latest state from pending.
|
|
291
288
|
if (result.wasSummaryTracked) {
|
|
292
289
|
this.latestSummaryGCVersion = this.currentGCVersion;
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
this.pendingSummaryData = undefined;
|
|
296
|
-
}
|
|
290
|
+
this.latestSummaryData = this.pendingSummaryData;
|
|
291
|
+
this.pendingSummaryData = undefined;
|
|
297
292
|
return undefined;
|
|
298
293
|
}
|
|
299
294
|
|
|
@@ -328,12 +323,10 @@ export class GCSummaryStateTracker {
|
|
|
328
323
|
}
|
|
329
324
|
|
|
330
325
|
// If tracking state across summaries, update latest summary data from the snapshot's GC data.
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
};
|
|
337
|
-
}
|
|
326
|
+
this.latestSummaryData = {
|
|
327
|
+
serializedGCState: JSON.stringify(generateSortedGCState(gcSnapshotData.gcState)),
|
|
328
|
+
serializedTombstones: JSON.stringify(gcSnapshotData.tombstones),
|
|
329
|
+
serializedDeletedNodes: JSON.stringify(gcSnapshotData.deletedNodes),
|
|
330
|
+
};
|
|
338
331
|
}
|
|
339
332
|
}
|
package/src/gc/index.ts
CHANGED
|
@@ -14,10 +14,12 @@ export {
|
|
|
14
14
|
gcTombstoneGenerationOptionName,
|
|
15
15
|
GCVersion,
|
|
16
16
|
gcVersionUpgradeToV2Key,
|
|
17
|
-
IGarbageCollectionRuntime,
|
|
17
|
+
IGarbageCollectionRuntime, // Deprecated
|
|
18
18
|
IGarbageCollector,
|
|
19
|
+
IGarbageCollectorConfigs,
|
|
19
20
|
IGarbageCollectorCreateParams,
|
|
20
21
|
IGCMetadata,
|
|
22
|
+
IGCRuntimeOptions,
|
|
21
23
|
IGCStats,
|
|
22
24
|
oneDayMs,
|
|
23
25
|
runGCKey,
|
|
@@ -30,7 +32,11 @@ export {
|
|
|
30
32
|
throwOnTombstoneUsageKey,
|
|
31
33
|
UnreferencedState,
|
|
32
34
|
} from "./gcDefinitions";
|
|
33
|
-
export {
|
|
35
|
+
export {
|
|
36
|
+
getSnapshotDataFromOldSnapshotFormat,
|
|
37
|
+
sendGCUnexpectedUsageEvent,
|
|
38
|
+
shouldAllowGcTombstoneEnforcement,
|
|
39
|
+
} from "./gcHelpers";
|
|
34
40
|
export { GCSummaryStateTracker } from "./gcSummaryStateTracker";
|
|
35
41
|
export {
|
|
36
42
|
skipClosureForXDaysKey,
|
package/src/index.ts
CHANGED
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
export {
|
|
7
7
|
ContainerMessageType,
|
|
8
8
|
ContainerRuntimeMessage,
|
|
9
|
-
IGCRuntimeOptions,
|
|
10
9
|
ISummaryRuntimeOptions,
|
|
11
10
|
ISummaryBaseConfiguration,
|
|
12
11
|
ISummaryConfigurationHeuristics,
|
|
@@ -27,7 +26,7 @@ export {
|
|
|
27
26
|
CompressionAlgorithms,
|
|
28
27
|
} from "./containerRuntime";
|
|
29
28
|
export { FluidDataStoreRegistry } from "./dataStoreRegistry";
|
|
30
|
-
export { IGCStats } from "./gc";
|
|
29
|
+
export { IGCRuntimeOptions, IGCStats } from "./gc";
|
|
31
30
|
export {
|
|
32
31
|
IPendingFlush,
|
|
33
32
|
IPendingLocalState,
|
|
@@ -30,9 +30,9 @@ There are two features which can be used to work around this size limit, batch c
|
|
|
30
30
|
|
|
31
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
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
|
|
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 both the size of the chunks and the threshold for the feature to activate. 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 204800.
|
|
34
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 `
|
|
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 `IContainerRuntimeOptions.chunkSizeInBytes`. Therefore, for this feature to be working, it is required that compression is enabled using `IContainerRuntimeOptions.compressionOptions`.
|
|
36
36
|
|
|
37
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
38
|
|
|
@@ -12,6 +12,12 @@ export interface IBatchManagerOptions {
|
|
|
12
12
|
readonly compressionOptions?: ICompressionRuntimeOptions;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
/**
|
|
16
|
+
* Estimated size of the stringification overhead for an op accumulated
|
|
17
|
+
* from runtime to loader to the service.
|
|
18
|
+
*/
|
|
19
|
+
const opOverhead = 200;
|
|
20
|
+
|
|
15
21
|
/**
|
|
16
22
|
* Helper class that manages partial batch & rollback.
|
|
17
23
|
*/
|
|
@@ -43,7 +49,7 @@ export class BatchManager {
|
|
|
43
49
|
// Also content will be strigified, and that adds a lot of overhead due to a lot of escape characters.
|
|
44
50
|
// Not taking it into account, as compression work should help there - compressed payload will be
|
|
45
51
|
// initially stored as base64, and that requires only 2 extra escape characters.
|
|
46
|
-
const socketMessageSize = contentSize +
|
|
52
|
+
const socketMessageSize = contentSize + opOverhead * opCount;
|
|
47
53
|
|
|
48
54
|
// If we were provided soft limit, check for exceeding it.
|
|
49
55
|
// But only if we have any ops, as the intention here is to flush existing ops (on exceeding this limit)
|
|
@@ -118,3 +124,15 @@ const addBatchMetadata = (batch: IBatch): IBatch => {
|
|
|
118
124
|
|
|
119
125
|
return batch;
|
|
120
126
|
};
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Estimates the real size in bytes on the socket for a given batch. It assumes that
|
|
130
|
+
* the envelope size (and the size of an empty op) is 200 bytes, taking into account
|
|
131
|
+
* extra overhead from stringification.
|
|
132
|
+
*
|
|
133
|
+
* @param batch - the batch to inspect
|
|
134
|
+
* @returns An estimate of the payload size in bytes which will be produced when the batch is sent over the wire
|
|
135
|
+
*/
|
|
136
|
+
export const estimateSocketSize = (batch: IBatch): number => {
|
|
137
|
+
return batch.contentSizeInBytes + opOverhead * batch.content.length;
|
|
138
|
+
};
|