@fluidframework/container-runtime 2.0.0-dev.1.4.6.106135 → 2.0.0-dev.2.3.0.115467
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 +1 -1
- package/dist/blobManager.d.ts +20 -5
- package/dist/blobManager.d.ts.map +1 -1
- package/dist/blobManager.js +57 -15
- package/dist/blobManager.js.map +1 -1
- package/dist/containerRuntime.d.ts +88 -51
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +205 -300
- package/dist/containerRuntime.js.map +1 -1
- package/dist/dataStore.d.ts.map +1 -1
- package/dist/dataStore.js +6 -0
- package/dist/dataStore.js.map +1 -1
- package/dist/dataStoreContext.d.ts +14 -21
- package/dist/dataStoreContext.d.ts.map +1 -1
- package/dist/dataStoreContext.js +71 -57
- package/dist/dataStoreContext.js.map +1 -1
- package/dist/dataStoreContexts.js +1 -1
- package/dist/dataStoreContexts.js.map +1 -1
- package/dist/dataStores.d.ts +11 -10
- package/dist/dataStores.d.ts.map +1 -1
- package/dist/dataStores.js +51 -20
- package/dist/dataStores.js.map +1 -1
- package/dist/garbageCollection.d.ts +40 -32
- package/dist/garbageCollection.d.ts.map +1 -1
- package/dist/garbageCollection.js +227 -161
- package/dist/garbageCollection.js.map +1 -1
- package/dist/garbageCollectionConstants.d.ts +19 -0
- package/dist/garbageCollectionConstants.d.ts.map +1 -0
- package/dist/garbageCollectionConstants.js +34 -0
- package/dist/garbageCollectionConstants.js.map +1 -0
- package/dist/gcSweepReadyUsageDetection.d.ts.map +1 -1
- package/dist/gcSweepReadyUsageDetection.js +5 -14
- package/dist/gcSweepReadyUsageDetection.js.map +1 -1
- package/dist/index.d.ts +6 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -9
- package/dist/index.js.map +1 -1
- package/dist/opLifecycle/batchManager.d.ts +30 -0
- package/dist/opLifecycle/batchManager.d.ts.map +1 -0
- package/dist/{batchManager.js → opLifecycle/batchManager.js} +25 -15
- package/dist/opLifecycle/batchManager.js.map +1 -0
- package/dist/opLifecycle/definitions.d.ts +40 -0
- package/dist/opLifecycle/definitions.d.ts.map +1 -0
- package/dist/opLifecycle/definitions.js +7 -0
- package/dist/opLifecycle/definitions.js.map +1 -0
- package/dist/opLifecycle/index.d.ts +12 -0
- package/dist/opLifecycle/index.d.ts.map +1 -0
- package/dist/opLifecycle/index.js +21 -0
- package/dist/opLifecycle/index.js.map +1 -0
- package/dist/opLifecycle/opCompressor.d.ts +18 -0
- package/dist/opLifecycle/opCompressor.d.ts.map +1 -0
- package/dist/opLifecycle/opCompressor.js +53 -0
- package/dist/opLifecycle/opCompressor.js.map +1 -0
- package/dist/opLifecycle/opDecompressor.d.ts +20 -0
- package/dist/opLifecycle/opDecompressor.d.ts.map +1 -0
- package/dist/opLifecycle/opDecompressor.js +72 -0
- package/dist/opLifecycle/opDecompressor.js.map +1 -0
- package/dist/opLifecycle/opSplitter.d.ts +17 -0
- package/dist/opLifecycle/opSplitter.d.ts.map +1 -0
- package/dist/opLifecycle/opSplitter.js +61 -0
- package/dist/opLifecycle/opSplitter.js.map +1 -0
- package/dist/opLifecycle/outbox.d.ts +47 -0
- package/dist/opLifecycle/outbox.d.ts.map +1 -0
- package/dist/opLifecycle/outbox.js +153 -0
- package/dist/opLifecycle/outbox.js.map +1 -0
- package/dist/opLifecycle/remoteMessageProcessor.d.ts +26 -0
- package/dist/opLifecycle/remoteMessageProcessor.d.ts.map +1 -0
- package/dist/opLifecycle/remoteMessageProcessor.js +81 -0
- package/dist/opLifecycle/remoteMessageProcessor.js.map +1 -0
- 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 +6 -26
- package/dist/pendingStateManager.d.ts.map +1 -1
- package/dist/pendingStateManager.js +42 -62
- package/dist/pendingStateManager.js.map +1 -1
- package/dist/runningSummarizer.d.ts +3 -2
- package/dist/runningSummarizer.d.ts.map +1 -1
- package/dist/runningSummarizer.js +10 -3
- package/dist/runningSummarizer.js.map +1 -1
- package/dist/scheduleManager.js.map +1 -1
- package/dist/summarizer.js +7 -2
- package/dist/summarizer.js.map +1 -1
- package/dist/summarizerClientElection.js +1 -1
- package/dist/summarizerClientElection.js.map +1 -1
- package/dist/summarizerHeuristics.d.ts.map +1 -1
- package/dist/summarizerHeuristics.js +0 -3
- package/dist/summarizerHeuristics.js.map +1 -1
- package/dist/summarizerTypes.d.ts +19 -2
- package/dist/summarizerTypes.d.ts.map +1 -1
- package/dist/summarizerTypes.js.map +1 -1
- package/dist/summaryFormat.d.ts +4 -2
- package/dist/summaryFormat.d.ts.map +1 -1
- package/dist/summaryFormat.js +2 -2
- package/dist/summaryFormat.js.map +1 -1
- package/dist/summaryGenerator.d.ts.map +1 -1
- package/dist/summaryGenerator.js +3 -2
- package/dist/summaryGenerator.js.map +1 -1
- package/dist/summaryManager.d.ts.map +1 -1
- package/dist/summaryManager.js +10 -6
- package/dist/summaryManager.js.map +1 -1
- package/garbageCollection.md +27 -22
- package/lib/blobManager.d.ts +20 -5
- package/lib/blobManager.d.ts.map +1 -1
- package/lib/blobManager.js +59 -17
- package/lib/blobManager.js.map +1 -1
- package/lib/containerRuntime.d.ts +88 -51
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +203 -297
- package/lib/containerRuntime.js.map +1 -1
- package/lib/dataStore.d.ts.map +1 -1
- package/lib/dataStore.js +6 -0
- package/lib/dataStore.js.map +1 -1
- package/lib/dataStoreContext.d.ts +14 -21
- package/lib/dataStoreContext.d.ts.map +1 -1
- package/lib/dataStoreContext.js +75 -61
- package/lib/dataStoreContext.js.map +1 -1
- package/lib/dataStoreContexts.js +1 -1
- package/lib/dataStoreContexts.js.map +1 -1
- package/lib/dataStores.d.ts +11 -10
- package/lib/dataStores.d.ts.map +1 -1
- package/lib/dataStores.js +53 -22
- package/lib/dataStores.js.map +1 -1
- package/lib/garbageCollection.d.ts +40 -32
- package/lib/garbageCollection.d.ts.map +1 -1
- package/lib/garbageCollection.js +220 -154
- package/lib/garbageCollection.js.map +1 -1
- package/lib/garbageCollectionConstants.d.ts +19 -0
- package/lib/garbageCollectionConstants.d.ts.map +1 -0
- package/lib/garbageCollectionConstants.js +31 -0
- package/lib/garbageCollectionConstants.js.map +1 -0
- package/lib/gcSweepReadyUsageDetection.d.ts.map +1 -1
- package/lib/gcSweepReadyUsageDetection.js +4 -13
- package/lib/gcSweepReadyUsageDetection.js.map +1 -1
- package/lib/index.d.ts +6 -6
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +3 -4
- package/lib/index.js.map +1 -1
- package/lib/opLifecycle/batchManager.d.ts +30 -0
- package/lib/opLifecycle/batchManager.d.ts.map +1 -0
- package/lib/{batchManager.js → opLifecycle/batchManager.js} +25 -15
- package/lib/opLifecycle/batchManager.js.map +1 -0
- package/lib/opLifecycle/definitions.d.ts +40 -0
- package/lib/opLifecycle/definitions.d.ts.map +1 -0
- package/lib/opLifecycle/definitions.js +6 -0
- package/lib/opLifecycle/definitions.js.map +1 -0
- package/lib/opLifecycle/index.d.ts +12 -0
- package/lib/opLifecycle/index.d.ts.map +1 -0
- package/lib/opLifecycle/index.js +11 -0
- package/lib/opLifecycle/index.js.map +1 -0
- package/lib/opLifecycle/opCompressor.d.ts +18 -0
- package/lib/opLifecycle/opCompressor.d.ts.map +1 -0
- package/lib/opLifecycle/opCompressor.js +49 -0
- package/lib/opLifecycle/opCompressor.js.map +1 -0
- package/lib/opLifecycle/opDecompressor.d.ts +20 -0
- package/lib/opLifecycle/opDecompressor.d.ts.map +1 -0
- package/lib/opLifecycle/opDecompressor.js +68 -0
- package/lib/opLifecycle/opDecompressor.js.map +1 -0
- package/lib/opLifecycle/opSplitter.d.ts +17 -0
- package/lib/opLifecycle/opSplitter.d.ts.map +1 -0
- package/lib/opLifecycle/opSplitter.js +57 -0
- package/lib/opLifecycle/opSplitter.js.map +1 -0
- package/lib/opLifecycle/outbox.d.ts +47 -0
- package/lib/opLifecycle/outbox.d.ts.map +1 -0
- package/lib/opLifecycle/outbox.js +149 -0
- package/lib/opLifecycle/outbox.js.map +1 -0
- package/lib/opLifecycle/remoteMessageProcessor.d.ts +26 -0
- package/lib/opLifecycle/remoteMessageProcessor.d.ts.map +1 -0
- package/lib/opLifecycle/remoteMessageProcessor.js +76 -0
- package/lib/opLifecycle/remoteMessageProcessor.js.map +1 -0
- 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 +6 -26
- package/lib/pendingStateManager.d.ts.map +1 -1
- package/lib/pendingStateManager.js +42 -62
- package/lib/pendingStateManager.js.map +1 -1
- package/lib/runningSummarizer.d.ts +3 -2
- package/lib/runningSummarizer.d.ts.map +1 -1
- package/lib/runningSummarizer.js +10 -3
- package/lib/runningSummarizer.js.map +1 -1
- package/lib/scheduleManager.js.map +1 -1
- package/lib/summarizer.js +7 -2
- package/lib/summarizer.js.map +1 -1
- package/lib/summarizerClientElection.js +1 -1
- package/lib/summarizerClientElection.js.map +1 -1
- package/lib/summarizerHeuristics.d.ts.map +1 -1
- package/lib/summarizerHeuristics.js +0 -3
- package/lib/summarizerHeuristics.js.map +1 -1
- package/lib/summarizerTypes.d.ts +19 -2
- package/lib/summarizerTypes.d.ts.map +1 -1
- package/lib/summarizerTypes.js.map +1 -1
- package/lib/summaryFormat.d.ts +4 -2
- package/lib/summaryFormat.d.ts.map +1 -1
- package/lib/summaryFormat.js +1 -1
- package/lib/summaryFormat.js.map +1 -1
- package/lib/summaryGenerator.d.ts.map +1 -1
- package/lib/summaryGenerator.js +3 -2
- package/lib/summaryGenerator.js.map +1 -1
- package/lib/summaryManager.d.ts.map +1 -1
- package/lib/summaryManager.js +10 -6
- package/lib/summaryManager.js.map +1 -1
- package/package.json +32 -71
- package/prettier.config.cjs +8 -0
- package/src/blobManager.ts +74 -19
- package/src/containerRuntime.ts +286 -369
- package/src/dataStore.ts +13 -1
- package/src/dataStoreContext.ts +100 -76
- package/src/dataStoreContexts.ts +1 -1
- package/src/dataStores.ts +61 -22
- package/src/garbageCollection.ts +282 -163
- package/src/garbageCollectionConstants.ts +35 -0
- package/src/gcSweepReadyUsageDetection.ts +3 -11
- package/src/index.ts +9 -8
- package/src/{batchManager.ts → opLifecycle/batchManager.ts} +42 -28
- package/src/opLifecycle/definitions.ts +44 -0
- package/src/opLifecycle/index.ts +17 -0
- package/src/opLifecycle/opCompressor.ts +64 -0
- package/src/opLifecycle/opDecompressor.ts +84 -0
- package/src/opLifecycle/opSplitter.ts +78 -0
- package/src/opLifecycle/outbox.ts +204 -0
- package/src/opLifecycle/remoteMessageProcessor.ts +90 -0
- package/src/packageVersion.ts +1 -1
- package/src/pendingStateManager.ts +57 -96
- package/src/runningSummarizer.ts +11 -3
- package/src/scheduleManager.ts +1 -0
- package/src/summarizer.ts +6 -6
- package/src/summarizerClientElection.ts +1 -1
- package/src/summarizerHeuristics.ts +0 -3
- package/src/summarizerTypes.ts +20 -7
- package/src/summaryFormat.ts +5 -3
- package/src/summaryGenerator.ts +3 -2
- package/src/summaryManager.ts +18 -7
- package/dist/batchManager.d.ts +0 -37
- package/dist/batchManager.d.ts.map +0 -1
- package/dist/batchManager.js.map +0 -1
- package/lib/batchManager.d.ts +0 -37
- package/lib/batchManager.d.ts.map +0 -1
- package/lib/batchManager.js.map +0 -1
package/lib/containerRuntime.js
CHANGED
|
@@ -15,7 +15,6 @@ import { Summarizer } from "./summarizer";
|
|
|
15
15
|
import { SummaryManager } from "./summaryManager";
|
|
16
16
|
import { ReportOpPerfTelemetry, } from "./connectionTelemetry";
|
|
17
17
|
import { PendingStateManager, } from "./pendingStateManager";
|
|
18
|
-
import { BatchManager } from "./batchManager";
|
|
19
18
|
import { pkgVersion } from "./packageVersion";
|
|
20
19
|
import { BlobManager } from "./blobManager";
|
|
21
20
|
import { DataStores, getSummaryForDatastores } from "./dataStores";
|
|
@@ -25,11 +24,13 @@ import { OrderedClientCollection, OrderedClientElection } from "./orderedClientE
|
|
|
25
24
|
import { SummarizerClientElection, summarizerClientType } from "./summarizerClientElection";
|
|
26
25
|
import { formExponentialFn, Throttler } from "./throttler";
|
|
27
26
|
import { RunWhileConnectedCoordinator } from "./runWhileConnectedCoordinator";
|
|
28
|
-
import { GarbageCollector, GCNodeType,
|
|
27
|
+
import { GarbageCollector, GCNodeType, } from "./garbageCollection";
|
|
28
|
+
import { gcTreeKey, } from "./garbageCollectionConstants";
|
|
29
29
|
import { channelToDataStore, isDataStoreAliasMessage, } from "./dataStore";
|
|
30
30
|
import { BindBatchTracker } from "./batchTracker";
|
|
31
31
|
import { SerializedSnapshotStorage } from "./serializedSnapshotStorage";
|
|
32
32
|
import { ScheduleManager } from "./scheduleManager";
|
|
33
|
+
import { OpCompressor, OpDecompressor, Outbox, OpSplitter, RemoteMessageProcessor, } from "./opLifecycle";
|
|
33
34
|
export var ContainerMessageType;
|
|
34
35
|
(function (ContainerMessageType) {
|
|
35
36
|
// An op to be delivered to store
|
|
@@ -58,6 +59,7 @@ export const DefaultSummaryConfiguration = {
|
|
|
58
59
|
summarizerClientElection: false,
|
|
59
60
|
nonRuntimeOpWeight: 0.1,
|
|
60
61
|
runtimeOpWeight: 1.0,
|
|
62
|
+
nonRuntimeHeuristicThreshold: 20,
|
|
61
63
|
};
|
|
62
64
|
/**
|
|
63
65
|
* Accepted header keys for requests coming to the runtime.
|
|
@@ -74,8 +76,20 @@ export var RuntimeHeaders;
|
|
|
74
76
|
/** True if the request is coming from an IFluidHandle. */
|
|
75
77
|
RuntimeHeaders["viaHandle"] = "viaHandle";
|
|
76
78
|
})(RuntimeHeaders || (RuntimeHeaders = {}));
|
|
79
|
+
/**
|
|
80
|
+
* Available compression algorithms for op compression.
|
|
81
|
+
*/
|
|
82
|
+
export var CompressionAlgorithms;
|
|
83
|
+
(function (CompressionAlgorithms) {
|
|
84
|
+
CompressionAlgorithms["lz4"] = "lz4";
|
|
85
|
+
})(CompressionAlgorithms || (CompressionAlgorithms = {}));
|
|
77
86
|
const maxConsecutiveReconnectsKey = "Fluid.ContainerRuntime.MaxConsecutiveReconnects";
|
|
78
87
|
const defaultFlushMode = FlushMode.TurnBased;
|
|
88
|
+
// The actual limit is 1Mb (socket.io and Kafka limits)
|
|
89
|
+
// We can't estimate it fully, as we
|
|
90
|
+
// - do not know what properties relay service will add
|
|
91
|
+
// - we do not stringify final op, thus we do not know how much escaping will be added.
|
|
92
|
+
const defaultMaxBatchSizeInBytes = 950 * 1024;
|
|
79
93
|
/**
|
|
80
94
|
* @deprecated - use ContainerRuntimeMessage instead
|
|
81
95
|
*/
|
|
@@ -93,42 +107,7 @@ export var RuntimeMessage;
|
|
|
93
107
|
* @deprecated - please use version in driver-utils
|
|
94
108
|
*/
|
|
95
109
|
export function isRuntimeMessage(message) {
|
|
96
|
-
|
|
97
|
-
return true;
|
|
98
|
-
}
|
|
99
|
-
return false;
|
|
100
|
-
}
|
|
101
|
-
/**
|
|
102
|
-
* Unpacks runtime messages
|
|
103
|
-
*
|
|
104
|
-
* @remarks This API makes no promises regarding backward-compatability. This is internal API.
|
|
105
|
-
* @param message - message (as it observed in storage / service)
|
|
106
|
-
* @returns unpacked runtime message
|
|
107
|
-
*
|
|
108
|
-
* @internal
|
|
109
|
-
*/
|
|
110
|
-
export function unpackRuntimeMessage(message) {
|
|
111
|
-
if (message.type === MessageType.Operation) {
|
|
112
|
-
// legacy op format?
|
|
113
|
-
if (message.contents.address !== undefined && message.contents.type === undefined) {
|
|
114
|
-
message.type = ContainerMessageType.FluidDataStoreOp;
|
|
115
|
-
}
|
|
116
|
-
else {
|
|
117
|
-
// new format
|
|
118
|
-
const innerContents = message.contents;
|
|
119
|
-
assert(innerContents.type !== undefined, 0x121 /* "Undefined inner contents type!" */);
|
|
120
|
-
message.type = innerContents.type;
|
|
121
|
-
message.contents = innerContents.contents;
|
|
122
|
-
}
|
|
123
|
-
return true;
|
|
124
|
-
}
|
|
125
|
-
else {
|
|
126
|
-
// Legacy format, but it's already "unpacked",
|
|
127
|
-
// i.e. message.type is actually ContainerMessageType.
|
|
128
|
-
// Or it's non-runtime message.
|
|
129
|
-
// Nothing to do in such case.
|
|
130
|
-
return false;
|
|
131
|
-
}
|
|
110
|
+
return Object.values(RuntimeMessage).includes(message.type);
|
|
132
111
|
}
|
|
133
112
|
/**
|
|
134
113
|
* Legacy ID for the built-in AgentScheduler. To minimize disruption while removing it, retaining this as a
|
|
@@ -155,8 +134,11 @@ export function getDeviceSpec() {
|
|
|
155
134
|
* It will define the store level mappings.
|
|
156
135
|
*/
|
|
157
136
|
export class ContainerRuntime extends TypedEventEmitter {
|
|
137
|
+
/**
|
|
138
|
+
* @internal
|
|
139
|
+
*/
|
|
158
140
|
constructor(context, registry, metadata, electedSummarizerData, chunks, dataStoreAliasMap, runtimeOptions, containerScope, logger, existing, blobManagerSnapshot, _storage, requestHandler, summaryConfiguration) {
|
|
159
|
-
var _a, _b, _c, _d;
|
|
141
|
+
var _a, _b, _c, _d, _e, _f;
|
|
160
142
|
if (summaryConfiguration === void 0) { summaryConfiguration = Object.assign(Object.assign({}, DefaultSummaryConfiguration), (_a = runtimeOptions.summaryOptions) === null || _a === void 0 ? void 0 : _a.summaryConfigOverrides); }
|
|
161
143
|
super();
|
|
162
144
|
this.context = context;
|
|
@@ -169,7 +151,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
169
151
|
this.summaryConfiguration = summaryConfiguration;
|
|
170
152
|
this.defaultMaxConsecutiveReconnects = 7;
|
|
171
153
|
this._orderSequentiallyCalls = 0;
|
|
172
|
-
this.
|
|
154
|
+
this.flushMicroTaskExists = false;
|
|
173
155
|
this.savedOps = [];
|
|
174
156
|
this.consecutiveReconnects = 0;
|
|
175
157
|
this._disposed = false;
|
|
@@ -181,12 +163,6 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
181
163
|
signalTimestamp: 0,
|
|
182
164
|
trackingSignalSequenceNumber: undefined,
|
|
183
165
|
};
|
|
184
|
-
// Provide lower soft limit - we want to have some number of ops to get efficiency in compression & bandwidth usage,
|
|
185
|
-
// but at the same time we want to send these ops sooner, to reduce overall latency of processing a batch.
|
|
186
|
-
// So there is some ballance here, that depends on compression algorithm and its efficiency working with smaller
|
|
187
|
-
// payloads. That number represents final (compressed) bits (once compression is implemented).
|
|
188
|
-
this.pendingAttachBatch = new BatchManager(64 * 1024);
|
|
189
|
-
this.pendingBatch = new BatchManager();
|
|
190
166
|
this.summarizeOnDemand = (...args) => {
|
|
191
167
|
if (this.clientDetails.type === summarizerClientType) {
|
|
192
168
|
return this.summarizer.summarizeOnDemand(...args);
|
|
@@ -215,9 +191,29 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
215
191
|
throw new UsageError(`Can't summarize, disableSummaries: ${this.summariesDisabled}`);
|
|
216
192
|
}
|
|
217
193
|
};
|
|
194
|
+
let loadSummaryNumber;
|
|
195
|
+
// Get the container creation metadata. For new container, we initialize these. For existing containers,
|
|
196
|
+
// get the values from the metadata blob.
|
|
197
|
+
if (existing) {
|
|
198
|
+
this.createContainerMetadata = {
|
|
199
|
+
createContainerRuntimeVersion: metadata === null || metadata === void 0 ? void 0 : metadata.createContainerRuntimeVersion,
|
|
200
|
+
createContainerTimestamp: metadata === null || metadata === void 0 ? void 0 : metadata.createContainerTimestamp,
|
|
201
|
+
};
|
|
202
|
+
// summaryNumber was renamed from summaryCount. For older docs that haven't been opened for a long time,
|
|
203
|
+
// the count is reset to 0.
|
|
204
|
+
loadSummaryNumber = (_b = metadata === null || metadata === void 0 ? void 0 : metadata.summaryNumber) !== null && _b !== void 0 ? _b : 0;
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
this.createContainerMetadata = {
|
|
208
|
+
createContainerRuntimeVersion: pkgVersion,
|
|
209
|
+
createContainerTimestamp: Date.now(),
|
|
210
|
+
};
|
|
211
|
+
loadSummaryNumber = 0;
|
|
212
|
+
}
|
|
213
|
+
this.nextSummaryNumber = loadSummaryNumber + 1;
|
|
218
214
|
this.messageAtLastSummary = metadata === null || metadata === void 0 ? void 0 : metadata.message;
|
|
219
215
|
this._connected = this.context.connected;
|
|
220
|
-
this.
|
|
216
|
+
this.remoteMessageProcessor = new RemoteMessageProcessor(new OpSplitter(chunks), new OpDecompressor());
|
|
221
217
|
this.handleContext = new ContainerFluidHandleContext("", this);
|
|
222
218
|
this.mc = loggerToMonitoringContext(ChildLogger.create(this.logger, "ContainerRuntime"));
|
|
223
219
|
if (this.summaryConfiguration.state === "enabled") {
|
|
@@ -229,10 +225,17 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
229
225
|
this.maxOpsSinceLastSummary = this.getMaxOpsSinceLastSummary();
|
|
230
226
|
this.initialSummarizerDelayMs = this.getInitialSummarizerDelayMs();
|
|
231
227
|
this.maxConsecutiveReconnects =
|
|
232
|
-
(
|
|
228
|
+
(_c = this.mc.config.getNumber(maxConsecutiveReconnectsKey)) !== null && _c !== void 0 ? _c : this.defaultMaxConsecutiveReconnects;
|
|
233
229
|
this._flushMode = runtimeOptions.flushMode;
|
|
234
230
|
const pendingRuntimeState = context.pendingLocalState;
|
|
235
|
-
const baseSnapshot = (
|
|
231
|
+
const baseSnapshot = (_d = pendingRuntimeState === null || pendingRuntimeState === void 0 ? void 0 : pendingRuntimeState.baseSnapshot) !== null && _d !== void 0 ? _d : context.baseSnapshot;
|
|
232
|
+
const maxSnapshotCacheDurationMs = (_f = (_e = this._storage) === null || _e === void 0 ? void 0 : _e.policies) === null || _f === void 0 ? void 0 : _f.maximumCacheDurationMs;
|
|
233
|
+
if (maxSnapshotCacheDurationMs !== undefined && maxSnapshotCacheDurationMs > 5 * 24 * 60 * 60 * 1000) {
|
|
234
|
+
// This is a runtime enforcement of what's already explicit in the policy's type itself,
|
|
235
|
+
// which dictates the value is either undefined or exactly 5 days in ms.
|
|
236
|
+
// As long as the actual value is less than 5 days, the assumptions GC makes here are valid.
|
|
237
|
+
throw new UsageError("Driver's maximumCacheDurationMs policy cannot exceed 5 days");
|
|
238
|
+
}
|
|
236
239
|
this.garbageCollector = GarbageCollector.create({
|
|
237
240
|
runtime: this,
|
|
238
241
|
gcOptions: this.runtimeOptions.gcOptions,
|
|
@@ -240,6 +243,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
240
243
|
baseLogger: this.mc.logger,
|
|
241
244
|
existing,
|
|
242
245
|
metadata,
|
|
246
|
+
createContainerMetadata: this.createContainerMetadata,
|
|
243
247
|
isSummarizerClient: this.context.clientDetails.type === summarizerClientType,
|
|
244
248
|
getNodePackagePath: async (nodePath) => this.getGCNodePackagePath(nodePath),
|
|
245
249
|
getLastSummaryTimestampMs: () => { var _a; return (_a = this.messageAtLastSummary) === null || _a === void 0 ? void 0 : _a.timestamp; },
|
|
@@ -265,28 +269,37 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
265
269
|
gcDisabled: !this.garbageCollector.shouldRunGC,
|
|
266
270
|
});
|
|
267
271
|
if (baseSnapshot) {
|
|
268
|
-
this.summarizerNode.
|
|
272
|
+
this.summarizerNode.updateBaseSummaryState(baseSnapshot);
|
|
269
273
|
}
|
|
270
|
-
this.dataStores = new DataStores(getSummaryForDatastores(baseSnapshot, metadata), this, (attachMsg) => this.submit(ContainerMessageType.Attach, attachMsg), (id, createParam) => (summarizeInternal, getGCDataFn, getBaseGCDetailsFn) => this.summarizerNode.createChild(summarizeInternal, id, createParam, undefined, getGCDataFn, getBaseGCDetailsFn), (id) => this.summarizerNode.deleteChild(id), this.mc.logger, async () => this.garbageCollector.getBaseGCDetails(), (path, timestampMs, packagePath) => this.garbageCollector.nodeUpdated(path, "Changed", timestampMs, packagePath), new Map(dataStoreAliasMap)
|
|
274
|
+
this.dataStores = new DataStores(getSummaryForDatastores(baseSnapshot, metadata), this, (attachMsg) => this.submit(ContainerMessageType.Attach, attachMsg), (id, createParam) => (summarizeInternal, getGCDataFn, getBaseGCDetailsFn) => this.summarizerNode.createChild(summarizeInternal, id, createParam, undefined, getGCDataFn, getBaseGCDetailsFn), (id) => this.summarizerNode.deleteChild(id), this.mc.logger, async () => this.garbageCollector.getBaseGCDetails(), (path, timestampMs, packagePath) => this.garbageCollector.nodeUpdated(path, "Changed", timestampMs, packagePath), new Map(dataStoreAliasMap));
|
|
271
275
|
this.blobManager = new BlobManager(this.handleContext, blobManagerSnapshot, () => this.storage, (blobId, localId) => {
|
|
272
276
|
if (!this.disposed) {
|
|
273
277
|
this.submit(ContainerMessageType.BlobAttach, undefined, undefined, { blobId, localId });
|
|
274
278
|
}
|
|
275
279
|
}, (blobPath) => this.garbageCollector.nodeUpdated(blobPath, "Loaded"), this, pendingRuntimeState === null || pendingRuntimeState === void 0 ? void 0 : pendingRuntimeState.pendingAttachmentBlobs);
|
|
276
280
|
this.scheduleManager = new ScheduleManager(context.deltaManager, this, () => this.clientId, ChildLogger.create(this.logger, "ScheduleManager"));
|
|
277
|
-
this.deltaSender = this.deltaManager;
|
|
278
281
|
this.pendingStateManager = new PendingStateManager({
|
|
279
282
|
applyStashedOp: this.applyStashedOp.bind(this),
|
|
280
283
|
clientId: () => this.clientId,
|
|
281
284
|
close: this.closeFn,
|
|
282
285
|
connected: () => this.connected,
|
|
283
286
|
flush: this.flush.bind(this),
|
|
284
|
-
flushMode: () => this.flushMode,
|
|
285
287
|
reSubmit: this.reSubmit.bind(this),
|
|
286
|
-
|
|
287
|
-
|
|
288
|
+
rollback: this.rollback.bind(this),
|
|
289
|
+
orderSequentially: this.orderSequentially.bind(this),
|
|
290
|
+
}, pendingRuntimeState === null || pendingRuntimeState === void 0 ? void 0 : pendingRuntimeState.pending);
|
|
291
|
+
this.outbox = new Outbox({
|
|
292
|
+
shouldSend: () => this.canSendOps(),
|
|
293
|
+
pendingStateManager: this.pendingStateManager,
|
|
294
|
+
containerContext: this.context,
|
|
295
|
+
compressor: new OpCompressor(this.mc.logger),
|
|
296
|
+
config: {
|
|
297
|
+
compressionOptions: runtimeOptions.compressionOptions,
|
|
298
|
+
maxBatchSizeInBytes: runtimeOptions.maxBatchSizeInBytes,
|
|
299
|
+
},
|
|
300
|
+
});
|
|
288
301
|
this.context.quorum.on("removeMember", (clientId) => {
|
|
289
|
-
this.
|
|
302
|
+
this.remoteMessageProcessor.clearPartialMessagesFor(clientId);
|
|
290
303
|
});
|
|
291
304
|
this.summaryCollection = new SummaryCollection(this.deltaManager, this.logger);
|
|
292
305
|
this.dirtyContainer = this.context.attachState !== AttachState.Attached
|
|
@@ -308,7 +321,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
308
321
|
// if summaries are enabled and we are not the summarizer client.
|
|
309
322
|
const defaultAction = () => {
|
|
310
323
|
if (this.summaryCollection.opsSinceLastAck > this.maxOpsSinceLastSummary) {
|
|
311
|
-
this.logger.
|
|
324
|
+
this.logger.sendTelemetryEvent({ eventName: "SummaryStatus:Behind" });
|
|
312
325
|
// unregister default to no log on every op after falling behind
|
|
313
326
|
// and register summary ack handler to re-register this handler
|
|
314
327
|
// after successful summary
|
|
@@ -354,26 +367,6 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
354
367
|
});
|
|
355
368
|
// logging hardware telemetry
|
|
356
369
|
logger.sendTelemetryEvent(Object.assign({ eventName: "DeviceSpec" }, getDeviceSpec()));
|
|
357
|
-
let loadSummaryNumber;
|
|
358
|
-
// Get the container creation metadata. For new container, we initialize these. For existing containers,
|
|
359
|
-
// get the values from the metadata blob.
|
|
360
|
-
if (existing) {
|
|
361
|
-
this.createContainerMetadata = {
|
|
362
|
-
createContainerRuntimeVersion: metadata === null || metadata === void 0 ? void 0 : metadata.createContainerRuntimeVersion,
|
|
363
|
-
createContainerTimestamp: metadata === null || metadata === void 0 ? void 0 : metadata.createContainerTimestamp,
|
|
364
|
-
};
|
|
365
|
-
// summaryNumber was renamed from summaryCount. For older docs that haven't been opened for a long time,
|
|
366
|
-
// the count is reset to 0.
|
|
367
|
-
loadSummaryNumber = (_d = metadata === null || metadata === void 0 ? void 0 : metadata.summaryNumber) !== null && _d !== void 0 ? _d : 0;
|
|
368
|
-
}
|
|
369
|
-
else {
|
|
370
|
-
this.createContainerMetadata = {
|
|
371
|
-
createContainerRuntimeVersion: pkgVersion,
|
|
372
|
-
createContainerTimestamp: Date.now(),
|
|
373
|
-
};
|
|
374
|
-
loadSummaryNumber = 0;
|
|
375
|
-
}
|
|
376
|
-
this.nextSummaryNumber = loadSummaryNumber + 1;
|
|
377
370
|
this.logger.sendTelemetryEvent(Object.assign(Object.assign(Object.assign({ eventName: "ContainerLoadStats" }, this.createContainerMetadata), this.dataStores.containerLoadStats), { summaryNumber: loadSummaryNumber, summaryFormatVersion: metadata === null || metadata === void 0 ? void 0 : metadata.summaryFormatVersion, disableIsolatedChannels: metadata === null || metadata === void 0 ? void 0 : metadata.disableIsolatedChannels, gcVersion: metadata === null || metadata === void 0 ? void 0 : metadata.gcFeature }));
|
|
378
371
|
ReportOpPerfTelemetry(this.context.clientId, this.deltaManager, this.logger);
|
|
379
372
|
BindBatchTracker(this, this.logger);
|
|
@@ -387,8 +380,10 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
387
380
|
* @param requestHandler - Request handlers for the container runtime
|
|
388
381
|
* @param runtimeOptions - Additional options to be passed to the runtime
|
|
389
382
|
* @param existing - (optional) When loading from an existing snapshot. Precedes context.existing if provided
|
|
383
|
+
* @param containerRuntimeCtor - (optional) Constructor to use to create the ContainerRuntime instance. This
|
|
384
|
+
* allows mixin classes to leverage this method to define their own async initializer.
|
|
390
385
|
*/
|
|
391
|
-
static async load(context, registryEntries, requestHandler, runtimeOptions = {}, containerScope = context.scope, existing) {
|
|
386
|
+
static async load(context, registryEntries, requestHandler, runtimeOptions = {}, containerScope = context.scope, existing, containerRuntimeCtor = ContainerRuntime) {
|
|
392
387
|
var _a, _b, _c;
|
|
393
388
|
// If taggedLogger exists, use it. Otherwise, wrap the vanilla logger:
|
|
394
389
|
// back-compat: Remove the TaggedLoggerAdapter fallback once all the host are using loader > 0.45
|
|
@@ -399,7 +394,10 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
399
394
|
runtimeVersion: pkgVersion,
|
|
400
395
|
},
|
|
401
396
|
});
|
|
402
|
-
const { summaryOptions = {}, gcOptions = {}, loadSequenceNumberVerification = "close", flushMode = defaultFlushMode, enableOfflineLoad = false,
|
|
397
|
+
const { summaryOptions = {}, gcOptions = {}, loadSequenceNumberVerification = "close", flushMode = defaultFlushMode, enableOfflineLoad = false, compressionOptions = {
|
|
398
|
+
minimumBatchSizeInBytes: Number.POSITIVE_INFINITY,
|
|
399
|
+
compressionAlgorithm: CompressionAlgorithms.lz4
|
|
400
|
+
}, maxBatchSizeInBytes = defaultMaxBatchSizeInBytes, } = runtimeOptions;
|
|
403
401
|
const pendingRuntimeState = context.pendingLocalState;
|
|
404
402
|
const baseSnapshot = (_b = pendingRuntimeState === null || pendingRuntimeState === void 0 ? void 0 : pendingRuntimeState.baseSnapshot) !== null && _b !== void 0 ? _b : context.baseSnapshot;
|
|
405
403
|
const storage = !pendingRuntimeState ?
|
|
@@ -448,19 +446,22 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
448
446
|
}
|
|
449
447
|
}
|
|
450
448
|
}
|
|
451
|
-
const runtime = new
|
|
449
|
+
const runtime = new containerRuntimeCtor(context, registry, metadata, electedSummarizerData, chunks !== null && chunks !== void 0 ? chunks : [], aliases !== null && aliases !== void 0 ? aliases : [], {
|
|
452
450
|
summaryOptions,
|
|
453
451
|
gcOptions,
|
|
454
452
|
loadSequenceNumberVerification,
|
|
455
453
|
flushMode,
|
|
456
454
|
enableOfflineLoad,
|
|
455
|
+
compressionOptions,
|
|
456
|
+
maxBatchSizeInBytes,
|
|
457
457
|
}, containerScope, logger, loadExisting, blobManagerSnapshot, storage, requestHandler);
|
|
458
458
|
if (pendingRuntimeState) {
|
|
459
459
|
await runtime.processSavedOps(pendingRuntimeState);
|
|
460
460
|
// delete these once runtime has seen them to save space
|
|
461
461
|
pendingRuntimeState.savedOps = [];
|
|
462
462
|
}
|
|
463
|
-
|
|
463
|
+
// Initialize the base state of the runtime before it's returned.
|
|
464
|
+
await runtime.initializeBaseState();
|
|
464
465
|
return runtime;
|
|
465
466
|
}
|
|
466
467
|
get options() {
|
|
@@ -509,9 +510,6 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
509
510
|
return (_a = this.summarizerClientElection) === null || _a === void 0 ? void 0 : _a.electedClientId;
|
|
510
511
|
}
|
|
511
512
|
get disposed() { return this._disposed; }
|
|
512
|
-
get emptyBatch() {
|
|
513
|
-
return this.pendingBatch.empty && this.pendingAttachBatch.empty;
|
|
514
|
-
}
|
|
515
513
|
get summarizer() {
|
|
516
514
|
assert(this._summarizer !== undefined, 0x257 /* "This is not summarizing container" */);
|
|
517
515
|
return this._summarizer;
|
|
@@ -567,6 +565,13 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
567
565
|
? this.summaryConfiguration.initialSummarizerDelayMs
|
|
568
566
|
: 0;
|
|
569
567
|
}
|
|
568
|
+
/**
|
|
569
|
+
* Initializes the state from the base snapshot this container runtime loaded from.
|
|
570
|
+
*/
|
|
571
|
+
async initializeBaseState() {
|
|
572
|
+
await this.initializeBaseSnapshotBlobs();
|
|
573
|
+
await this.garbageCollector.initializeBaseState();
|
|
574
|
+
}
|
|
570
575
|
dispose(error) {
|
|
571
576
|
var _a;
|
|
572
577
|
if (this._disposed) {
|
|
@@ -665,13 +670,16 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
665
670
|
return (_a = this.dataStores.aliases.get(maybeAlias)) !== null && _a !== void 0 ? _a : maybeAlias;
|
|
666
671
|
}
|
|
667
672
|
async getDataStoreFromRequest(id, request) {
|
|
668
|
-
var _a, _b, _c;
|
|
673
|
+
var _a, _b, _c, _d, _e;
|
|
669
674
|
const wait = typeof ((_a = request.headers) === null || _a === void 0 ? void 0 : _a[RuntimeHeaders.wait]) === "boolean"
|
|
670
675
|
? (_b = request.headers) === null || _b === void 0 ? void 0 : _b[RuntimeHeaders.wait]
|
|
671
676
|
: true;
|
|
677
|
+
const viaHandle = typeof ((_c = request.headers) === null || _c === void 0 ? void 0 : _c[RuntimeHeaders.viaHandle]) === "boolean"
|
|
678
|
+
? (_d = request.headers) === null || _d === void 0 ? void 0 : _d[RuntimeHeaders.viaHandle]
|
|
679
|
+
: false;
|
|
672
680
|
await this.dataStores.waitIfPendingAlias(id);
|
|
673
681
|
const internalId = this.internalId(id);
|
|
674
|
-
const dataStoreContext = await this.dataStores.getDataStore(internalId, wait);
|
|
682
|
+
const dataStoreContext = await this.dataStores.getDataStore(internalId, wait, viaHandle);
|
|
675
683
|
/**
|
|
676
684
|
* If GC should run and this an external app request with "externalRequest" header, we need to return
|
|
677
685
|
* an error if the data store being requested is marked as unreferenced as per the data store's base
|
|
@@ -680,7 +688,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
680
688
|
* This is a workaround to handle scenarios where a data store shared with an external app is deleted
|
|
681
689
|
* and marked as unreferenced by GC. Returning an error will fail to load the data store for the app.
|
|
682
690
|
*/
|
|
683
|
-
if (((
|
|
691
|
+
if (((_e = request.headers) === null || _e === void 0 ? void 0 : _e[RuntimeHeaders.externalRequest]) && this.garbageCollector.shouldRunGC) {
|
|
684
692
|
// The data store is referenced if used routes in the base summary has a route to self.
|
|
685
693
|
// Older documents may not have used routes in the summary. They are considered referenced.
|
|
686
694
|
const usedRoutes = (await dataStoreContext.getBaseGCDetails()).usedRoutes;
|
|
@@ -709,8 +717,8 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
709
717
|
addContainerStateToSummary(summaryTree, fullTree, trackState, telemetryContext) {
|
|
710
718
|
var _a;
|
|
711
719
|
this.addMetadataToSummary(summaryTree);
|
|
712
|
-
if (this.
|
|
713
|
-
const content = JSON.stringify([...this.
|
|
720
|
+
if (this.remoteMessageProcessor.partialMessages.size > 0) {
|
|
721
|
+
const content = JSON.stringify([...this.remoteMessageProcessor.partialMessages]);
|
|
714
722
|
addBlobToSummary(summaryTree, chunksBlobName, content);
|
|
715
723
|
}
|
|
716
724
|
const dataStoreAliases = this.dataStores.aliases;
|
|
@@ -727,11 +735,9 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
727
735
|
if (Object.keys(blobManagerSummary.summary.tree).length > 0) {
|
|
728
736
|
addTreeToSummary(summaryTree, blobsTreeName, blobManagerSummary);
|
|
729
737
|
}
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
addSummarizeResultToSummary(summaryTree, gcTreeKey, gcSummary);
|
|
734
|
-
}
|
|
738
|
+
const gcSummary = this.garbageCollector.summarize(fullTree, trackState, telemetryContext);
|
|
739
|
+
if (gcSummary !== undefined) {
|
|
740
|
+
addSummarizeResultToSummary(summaryTree, gcTreeKey, gcSummary);
|
|
735
741
|
}
|
|
736
742
|
}
|
|
737
743
|
// Track how many times the container tries to reconnect with pending messages.
|
|
@@ -876,31 +882,21 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
876
882
|
process(messageArg, local) {
|
|
877
883
|
var _a;
|
|
878
884
|
this.verifyNotClosed();
|
|
879
|
-
// Do shallow copy of message, as methods below will modify it.
|
|
880
|
-
// There might be multiple container instances receiving same message
|
|
881
|
-
// We do not need to make deep copy, as each layer will just replace message.content itself,
|
|
882
|
-
// but would not modify contents details
|
|
883
|
-
let message = Object.assign({}, messageArg);
|
|
884
|
-
// back-compat: ADO #1385: eventually should become unconditional, but only for runtime messages!
|
|
885
|
-
// System message may have no contents, or in some cases (mostly for back-compat) they may have actual objects.
|
|
886
|
-
// Old ops may contain empty string (I assume noops).
|
|
887
|
-
if (typeof message.contents === "string" && message.contents !== "") {
|
|
888
|
-
message.contents = JSON.parse(message.contents);
|
|
889
|
-
}
|
|
890
|
-
// Caveat: This will return false for runtime message in very old format, that are used in snapshot tests
|
|
891
|
-
// This format was not shipped to production workflows.
|
|
892
|
-
const runtimeMessage = unpackRuntimeMessage(message);
|
|
893
885
|
if ((_a = this.mc.config.getBoolean("enableOfflineLoad")) !== null && _a !== void 0 ? _a : this.runtimeOptions.enableOfflineLoad) {
|
|
894
886
|
this.savedOps.push(messageArg);
|
|
895
887
|
}
|
|
888
|
+
// Whether or not the message is actually a runtime message.
|
|
889
|
+
// It may be a legacy runtime message (ie already unpacked and ContainerMessageType)
|
|
890
|
+
// or something different, like a system message.
|
|
891
|
+
const runtimeMessage = messageArg.type === MessageType.Operation;
|
|
892
|
+
// Do shallow copy of message, as the processing flow will modify it.
|
|
893
|
+
const messageCopy = Object.assign({}, messageArg);
|
|
894
|
+
const message = this.remoteMessageProcessor.process(messageCopy);
|
|
896
895
|
// Surround the actual processing of the operation with messages to the schedule manager indicating
|
|
897
896
|
// the beginning and end. This allows it to emit appropriate events and/or pause the processing of new
|
|
898
897
|
// messages once a batch has been fully processed.
|
|
899
898
|
this.scheduleManager.beforeOpProcessing(message);
|
|
900
899
|
try {
|
|
901
|
-
// Chunk processing must come first given that we will transform the message to the unchunked version
|
|
902
|
-
// once all pieces are available
|
|
903
|
-
message = this.processRemoteChunkedMessage(message);
|
|
904
900
|
let localOpMetadata;
|
|
905
901
|
if (local && runtimeMessage) {
|
|
906
902
|
localOpMetadata = this.pendingStateManager.processPendingLocalMessage(message);
|
|
@@ -1004,112 +1000,31 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1004
1000
|
async getRootDataStoreChannel(id, wait = true) {
|
|
1005
1001
|
await this.dataStores.waitIfPendingAlias(id);
|
|
1006
1002
|
const internalId = this.internalId(id);
|
|
1007
|
-
const context = await this.dataStores.getDataStore(internalId, wait);
|
|
1003
|
+
const context = await this.dataStores.getDataStore(internalId, wait, false /* viaHandle */);
|
|
1008
1004
|
assert(await context.isRoot(), 0x12b /* "did not get root data store" */);
|
|
1009
1005
|
return context.realize();
|
|
1010
1006
|
}
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
this.mc.logger.sendTelemetryEvent({
|
|
1016
|
-
eventName: "FlushMode Updated",
|
|
1017
|
-
old: this._flushMode,
|
|
1018
|
-
new: mode,
|
|
1019
|
-
});
|
|
1020
|
-
// Flush any pending batches if switching to immediate
|
|
1021
|
-
if (mode === FlushMode.Immediate) {
|
|
1022
|
-
this.flush();
|
|
1023
|
-
}
|
|
1024
|
-
this._flushMode = mode;
|
|
1025
|
-
// Let the PendingStateManager know that FlushMode has been updated.
|
|
1026
|
-
this.pendingStateManager.onFlushModeUpdated(mode);
|
|
1027
|
-
}
|
|
1007
|
+
/**
|
|
1008
|
+
* Flush the pending ops manually.
|
|
1009
|
+
* This method is expected to be called at the end of a batch.
|
|
1010
|
+
*/
|
|
1028
1011
|
flush() {
|
|
1029
1012
|
assert(this._orderSequentiallyCalls === 0, 0x24c /* "Cannot call `flush()` from `orderSequentially`'s callback" */);
|
|
1030
|
-
this.
|
|
1031
|
-
|
|
1032
|
-
assert(this.emptyBatch, 0x3cf /* reentrancy */);
|
|
1033
|
-
}
|
|
1034
|
-
flushBatch(batch) {
|
|
1035
|
-
const length = batch.length;
|
|
1036
|
-
if (length > 1) {
|
|
1037
|
-
batch[0].metadata = Object.assign(Object.assign({}, batch[0].metadata), { batch: true });
|
|
1038
|
-
batch[length - 1].metadata = Object.assign(Object.assign({}, batch[length - 1].metadata), { batch: false });
|
|
1039
|
-
// This assert fires for the following reason (there might be more cases like that):
|
|
1040
|
-
// AgentScheduler will send ops in response to ConsensusRegisterCollection's "atomicChanged" event handler,
|
|
1041
|
-
// i.e. in the middle of op processing!
|
|
1042
|
-
// Sending ops while processing ops is not good idea - it's not defined when
|
|
1043
|
-
// referenceSequenceNumber changes in op processing sequence (at the beginning or end of op processing),
|
|
1044
|
-
// If we send ops in response to processing multiple ops, then we for sure hit this assert!
|
|
1045
|
-
// Tracked via ADO #1834
|
|
1046
|
-
// assert(batch[0].referenceSequenceNumber === batch[length - 1].referenceSequenceNumber,
|
|
1047
|
-
// "Batch should be generated synchronously, without processing ops in the middle!");
|
|
1048
|
-
}
|
|
1049
|
-
let clientSequenceNumber = -1;
|
|
1050
|
-
// Did we disconnect in the middle of turn-based batch?
|
|
1051
|
-
// If so, do nothing, as pending state manager will resubmit it correctly on reconnect.
|
|
1052
|
-
if (this.canSendOps()) {
|
|
1053
|
-
if (this.context.submitBatchFn !== undefined) {
|
|
1054
|
-
const batchToSend = [];
|
|
1055
|
-
for (const message of batch) {
|
|
1056
|
-
batchToSend.push({ contents: message.contents, metadata: message.metadata });
|
|
1057
|
-
}
|
|
1058
|
-
// returns clientSequenceNumber of last message in a batch
|
|
1059
|
-
clientSequenceNumber = this.context.submitBatchFn(batchToSend);
|
|
1060
|
-
}
|
|
1061
|
-
else {
|
|
1062
|
-
// Legacy path - supporting old loader versions. Can be removed only when LTS moves above
|
|
1063
|
-
// version that has support for batches (submitBatchFn)
|
|
1064
|
-
for (const message of batch) {
|
|
1065
|
-
clientSequenceNumber = this.context.submitFn(MessageType.Operation, message.deserializedContent, true, // batch
|
|
1066
|
-
message.metadata);
|
|
1067
|
-
}
|
|
1068
|
-
this.deltaSender.flush();
|
|
1069
|
-
}
|
|
1070
|
-
// Convert from clientSequenceNumber of last message in the batch to clientSequenceNumber of first message.
|
|
1071
|
-
clientSequenceNumber -= batch.length - 1;
|
|
1072
|
-
assert(clientSequenceNumber >= 0, 0x3d0 /* clientSequenceNumber can't be negative */);
|
|
1073
|
-
}
|
|
1074
|
-
// Let the PendingStateManager know that a message was submitted.
|
|
1075
|
-
// In future, need to shift toward keeping batch as a whole!
|
|
1076
|
-
for (const message of batch) {
|
|
1077
|
-
this.pendingStateManager.onSubmitMessage(message.deserializedContent.type, clientSequenceNumber, message.referenceSequenceNumber, message.deserializedContent.contents, message.localOpMetadata, message.metadata);
|
|
1078
|
-
clientSequenceNumber++;
|
|
1079
|
-
}
|
|
1080
|
-
this.pendingStateManager.onFlush();
|
|
1013
|
+
this.outbox.flush();
|
|
1014
|
+
assert(this.outbox.isEmpty, 0x3cf /* reentrancy */);
|
|
1081
1015
|
}
|
|
1082
1016
|
orderSequentially(callback) {
|
|
1083
|
-
// If flush mode is already TurnBased we are either
|
|
1084
|
-
// nested in another orderSequentially, or
|
|
1085
|
-
// the app is flushing manually, in which
|
|
1086
|
-
// case this invocation doesn't own
|
|
1087
|
-
// flushing.
|
|
1088
|
-
if (this.flushMode === FlushMode.TurnBased) {
|
|
1089
|
-
this.trackOrderSequentiallyCalls(callback);
|
|
1090
|
-
return;
|
|
1091
|
-
}
|
|
1092
|
-
const savedFlushMode = this.flushMode;
|
|
1093
|
-
this.setFlushMode(FlushMode.TurnBased);
|
|
1094
|
-
try {
|
|
1095
|
-
this.trackOrderSequentiallyCalls(callback);
|
|
1096
|
-
this.flush();
|
|
1097
|
-
}
|
|
1098
|
-
finally {
|
|
1099
|
-
this.setFlushMode(savedFlushMode);
|
|
1100
|
-
}
|
|
1101
|
-
}
|
|
1102
|
-
trackOrderSequentiallyCalls(callback) {
|
|
1103
1017
|
let checkpoint;
|
|
1018
|
+
let result;
|
|
1104
1019
|
if (this.mc.config.getBoolean("Fluid.ContainerRuntime.EnableRollback")) {
|
|
1105
1020
|
// Note: we are not touching this.pendingAttachBatch here, for two reasons:
|
|
1106
1021
|
// 1. It would not help, as we flush attach ops as they become available.
|
|
1107
1022
|
// 2. There is no way to undo process of data store creation.
|
|
1108
|
-
checkpoint = this.
|
|
1023
|
+
checkpoint = this.outbox.checkpoint().mainBatch;
|
|
1109
1024
|
}
|
|
1110
1025
|
try {
|
|
1111
1026
|
this._orderSequentiallyCalls++;
|
|
1112
|
-
callback();
|
|
1027
|
+
result = callback();
|
|
1113
1028
|
}
|
|
1114
1029
|
catch (error) {
|
|
1115
1030
|
if (checkpoint) {
|
|
@@ -1134,6 +1049,10 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1134
1049
|
finally {
|
|
1135
1050
|
this._orderSequentiallyCalls--;
|
|
1136
1051
|
}
|
|
1052
|
+
if (this.flushMode === FlushMode.Immediate && this._orderSequentiallyCalls === 0) {
|
|
1053
|
+
this.flush();
|
|
1054
|
+
}
|
|
1055
|
+
return result;
|
|
1137
1056
|
}
|
|
1138
1057
|
async createDataStore(pkg) {
|
|
1139
1058
|
const internalId = uuid();
|
|
@@ -1160,6 +1079,12 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1160
1079
|
canSendOps() {
|
|
1161
1080
|
return this.connected && !this.deltaManager.readOnlyInfo.readonly;
|
|
1162
1081
|
}
|
|
1082
|
+
/**
|
|
1083
|
+
* Are we in the middle of batching ops together?
|
|
1084
|
+
*/
|
|
1085
|
+
currentlyBatching() {
|
|
1086
|
+
return this.flushMode === FlushMode.TurnBased || this._orderSequentiallyCalls !== 0;
|
|
1087
|
+
}
|
|
1163
1088
|
getQuorum() {
|
|
1164
1089
|
return this.context.quorum;
|
|
1165
1090
|
}
|
|
@@ -1310,28 +1235,33 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1310
1235
|
* Implementation of IGarbageCollectionRuntime::updateUsedRoutes.
|
|
1311
1236
|
* After GC has run, called to notify this container's nodes of routes that are used in it.
|
|
1312
1237
|
* @param usedRoutes - The routes that are used in all nodes in this Container.
|
|
1313
|
-
* @param gcTimestamp - The time when GC was run that generated these used routes. If any node node becomes
|
|
1314
|
-
* unreferenced as part of this GC run, this should be used to update the time when it happens.
|
|
1315
1238
|
*/
|
|
1316
|
-
updateUsedRoutes(usedRoutes
|
|
1239
|
+
updateUsedRoutes(usedRoutes) {
|
|
1317
1240
|
// Update our summarizer node's used routes. Updating used routes in summarizer node before
|
|
1318
1241
|
// summarizing is required and asserted by the the summarizer node. We are the root and are
|
|
1319
1242
|
// always referenced, so the used routes is only self-route (empty string).
|
|
1320
1243
|
this.summarizerNode.updateUsedRoutes([""]);
|
|
1244
|
+
const blobManagerUsedRoutes = [];
|
|
1321
1245
|
const dataStoreUsedRoutes = [];
|
|
1322
1246
|
for (const route of usedRoutes) {
|
|
1323
|
-
if (
|
|
1247
|
+
if (this.isBlobPath(route)) {
|
|
1248
|
+
blobManagerUsedRoutes.push(route);
|
|
1249
|
+
}
|
|
1250
|
+
else {
|
|
1324
1251
|
dataStoreUsedRoutes.push(route);
|
|
1325
1252
|
}
|
|
1326
1253
|
}
|
|
1327
|
-
|
|
1254
|
+
this.blobManager.updateUsedRoutes(blobManagerUsedRoutes);
|
|
1255
|
+
this.dataStores.updateUsedRoutes(dataStoreUsedRoutes);
|
|
1328
1256
|
}
|
|
1329
1257
|
/**
|
|
1330
|
-
*
|
|
1331
|
-
*
|
|
1332
|
-
* @param unusedRoutes - The routes that are unused in all data stores in this Container.
|
|
1258
|
+
* This is called to update objects whose routes are unused. The unused objects are either deleted or marked as
|
|
1259
|
+
* tombstones.
|
|
1260
|
+
* @param unusedRoutes - The routes that are unused in all data stores and attachment blobs in this Container.
|
|
1261
|
+
* @param tombstone - if true, the objects corresponding to unused routes are marked tombstones. Otherwise, they
|
|
1262
|
+
* are deleted.
|
|
1333
1263
|
*/
|
|
1334
|
-
|
|
1264
|
+
updateUnusedRoutes(unusedRoutes, tombstone) {
|
|
1335
1265
|
const blobManagerUnusedRoutes = [];
|
|
1336
1266
|
const dataStoreUnusedRoutes = [];
|
|
1337
1267
|
for (const route of unusedRoutes) {
|
|
@@ -1342,8 +1272,8 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1342
1272
|
dataStoreUnusedRoutes.push(route);
|
|
1343
1273
|
}
|
|
1344
1274
|
}
|
|
1345
|
-
this.blobManager.
|
|
1346
|
-
this.dataStores.
|
|
1275
|
+
this.blobManager.updateUnusedRoutes(blobManagerUnusedRoutes, tombstone);
|
|
1276
|
+
this.dataStores.updateUnusedRoutes(dataStoreUnusedRoutes, tombstone);
|
|
1347
1277
|
}
|
|
1348
1278
|
/**
|
|
1349
1279
|
* Returns a server generated referenced timestamp to be used to track unreferenced nodes by GC.
|
|
@@ -1423,21 +1353,14 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1423
1353
|
const summaryNumberLogger = ChildLogger.create(summaryLogger, undefined, {
|
|
1424
1354
|
all: { summaryNumber },
|
|
1425
1355
|
});
|
|
1426
|
-
assert(this.
|
|
1356
|
+
assert(this.outbox.isEmpty, 0x3d1 /* Can't trigger summary in the middle of a batch */);
|
|
1427
1357
|
let latestSnapshotVersionId;
|
|
1428
1358
|
if (refreshLatestAck) {
|
|
1429
1359
|
const latestSnapshotInfo = await this.refreshLatestSummaryAckFromServer(ChildLogger.create(summaryNumberLogger, undefined, { all: { safeSummary: true } }));
|
|
1430
1360
|
const latestSnapshotRefSeq = latestSnapshotInfo.latestSnapshotRefSeq;
|
|
1431
1361
|
latestSnapshotVersionId = latestSnapshotInfo.latestSnapshotVersionId;
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
await PerformanceEvent.timedExecAsync(summaryNumberLogger, {
|
|
1435
|
-
eventName: "WaitingForSeq",
|
|
1436
|
-
lastSequenceNumber: this.deltaManager.lastSequenceNumber,
|
|
1437
|
-
targetSequenceNumber: latestSnapshotRefSeq,
|
|
1438
|
-
lastKnownSeqNumber: this.deltaManager.lastKnownSeqNumber,
|
|
1439
|
-
}, async () => waitForSeq(this.deltaManager, latestSnapshotRefSeq), { start: true, end: true, cancel: "error" });
|
|
1440
|
-
}
|
|
1362
|
+
// We might need to catch up to the latest summary's reference sequence number before pausing.
|
|
1363
|
+
await this.waitForDeltaManagerToCatchup(latestSnapshotRefSeq, summaryNumberLogger);
|
|
1441
1364
|
}
|
|
1442
1365
|
try {
|
|
1443
1366
|
await this.deltaManager.inbound.pause();
|
|
@@ -1603,40 +1526,8 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1603
1526
|
this.deltaManager.inbound.resume();
|
|
1604
1527
|
}
|
|
1605
1528
|
}
|
|
1606
|
-
processRemoteChunkedMessage(message) {
|
|
1607
|
-
if (message.type !== ContainerMessageType.ChunkedOp) {
|
|
1608
|
-
return message;
|
|
1609
|
-
}
|
|
1610
|
-
const clientId = message.clientId;
|
|
1611
|
-
const chunkedContent = message.contents;
|
|
1612
|
-
this.addChunk(clientId, chunkedContent);
|
|
1613
|
-
if (chunkedContent.chunkId === chunkedContent.totalChunks) {
|
|
1614
|
-
const newMessage = Object.assign({}, message);
|
|
1615
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
1616
|
-
const serializedContent = this.chunkMap.get(clientId).join("");
|
|
1617
|
-
newMessage.contents = JSON.parse(serializedContent);
|
|
1618
|
-
newMessage.type = chunkedContent.originalType;
|
|
1619
|
-
this.clearPartialChunks(clientId);
|
|
1620
|
-
return newMessage;
|
|
1621
|
-
}
|
|
1622
|
-
return message;
|
|
1623
|
-
}
|
|
1624
|
-
addChunk(clientId, chunkedContent) {
|
|
1625
|
-
let map = this.chunkMap.get(clientId);
|
|
1626
|
-
if (map === undefined) {
|
|
1627
|
-
map = [];
|
|
1628
|
-
this.chunkMap.set(clientId, map);
|
|
1629
|
-
}
|
|
1630
|
-
assert(chunkedContent.chunkId === map.length + 1, 0x131 /* "Mismatch between new chunkId and expected chunkMap" */); // 1-based indexing
|
|
1631
|
-
map.push(chunkedContent.contents);
|
|
1632
|
-
}
|
|
1633
|
-
clearPartialChunks(clientId) {
|
|
1634
|
-
if (this.chunkMap.has(clientId)) {
|
|
1635
|
-
this.chunkMap.delete(clientId);
|
|
1636
|
-
}
|
|
1637
|
-
}
|
|
1638
1529
|
hasPendingMessages() {
|
|
1639
|
-
return this.pendingStateManager.hasPendingMessages() || !this.
|
|
1530
|
+
return this.pendingStateManager.hasPendingMessages() || !this.outbox.isEmpty;
|
|
1640
1531
|
}
|
|
1641
1532
|
updateDocumentDirtyState(dirty) {
|
|
1642
1533
|
if (this.attachState !== AttachState.Attached) {
|
|
@@ -1680,7 +1571,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1680
1571
|
const deserializedContent = { type, contents };
|
|
1681
1572
|
const serializedContent = JSON.stringify(deserializedContent);
|
|
1682
1573
|
if (this.deltaManager.readOnlyInfo.readonly) {
|
|
1683
|
-
this.logger.
|
|
1574
|
+
this.logger.sendTelemetryEvent({ eventName: "SubmitOpInReadonly", connected: this.connected });
|
|
1684
1575
|
}
|
|
1685
1576
|
const message = {
|
|
1686
1577
|
contents: serializedContent,
|
|
@@ -1710,44 +1601,24 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1710
1601
|
// issue than sending.
|
|
1711
1602
|
// Please note that this does not change file format, so it can be disabled in the future if this
|
|
1712
1603
|
// optimization no longer makes sense (for example, batch compression may make it less appealing).
|
|
1713
|
-
if (type === ContainerMessageType.Attach &&
|
|
1604
|
+
if (this.currentlyBatching() && type === ContainerMessageType.Attach &&
|
|
1714
1605
|
this.mc.config.getBoolean("Fluid.ContainerRuntime.disableAttachOpReorder") !== true) {
|
|
1715
|
-
|
|
1716
|
-
// BatchManager has two limits - soft limit & hard limit. Soft limit is only engaged
|
|
1717
|
-
// when queue is not empty.
|
|
1718
|
-
// Flush queue & retry. Failure on retry would mean - single message is bigger than hard limit
|
|
1719
|
-
this.flushBatch(this.pendingAttachBatch.popBatch());
|
|
1720
|
-
if (!this.pendingAttachBatch.push(message)) {
|
|
1721
|
-
throw new GenericError("BatchTooLarge",
|
|
1722
|
-
/* error */ undefined, {
|
|
1723
|
-
opSize: message.contents.length,
|
|
1724
|
-
count: this.pendingAttachBatch.length,
|
|
1725
|
-
limit: this.pendingAttachBatch.limit,
|
|
1726
|
-
});
|
|
1727
|
-
}
|
|
1728
|
-
}
|
|
1606
|
+
this.outbox.submitAttach(message);
|
|
1729
1607
|
}
|
|
1730
1608
|
else {
|
|
1731
|
-
|
|
1732
|
-
throw new GenericError("BatchTooLarge",
|
|
1733
|
-
/* error */ undefined, {
|
|
1734
|
-
opSize: message.contents.length,
|
|
1735
|
-
count: this.pendingBatch.length,
|
|
1736
|
-
limit: this.pendingBatch.limit,
|
|
1737
|
-
});
|
|
1738
|
-
}
|
|
1609
|
+
this.outbox.submit(message);
|
|
1739
1610
|
}
|
|
1740
|
-
if (this.
|
|
1611
|
+
if (!this.currentlyBatching()) {
|
|
1741
1612
|
this.flush();
|
|
1742
1613
|
}
|
|
1743
|
-
else if (!this.
|
|
1744
|
-
this.
|
|
1614
|
+
else if (!this.flushMicroTaskExists) {
|
|
1615
|
+
this.flushMicroTaskExists = true;
|
|
1745
1616
|
// Queue a microtask to detect the end of the turn and force a flush.
|
|
1746
1617
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
1747
1618
|
Promise.resolve().then(() => {
|
|
1748
|
-
this.
|
|
1619
|
+
this.flushMicroTaskExists = false;
|
|
1749
1620
|
this.flush();
|
|
1750
|
-
});
|
|
1621
|
+
}).catch((error) => { this.closeFn(error); });
|
|
1751
1622
|
}
|
|
1752
1623
|
}
|
|
1753
1624
|
catch (error) {
|
|
@@ -1762,7 +1633,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1762
1633
|
this.verifyNotClosed();
|
|
1763
1634
|
assert(this.connected, 0x133 /* "Container disconnected when trying to submit system message" */);
|
|
1764
1635
|
// System message should not be sent in the middle of the batch.
|
|
1765
|
-
assert(this.
|
|
1636
|
+
assert(this.outbox.isEmpty, 0x3d4 /* System op in the middle of a batch */);
|
|
1766
1637
|
// back-compat: ADO #1385: Make this call unconditional in the future
|
|
1767
1638
|
return this.context.submitSummaryFn !== undefined
|
|
1768
1639
|
? this.context.submitSummaryFn(contents)
|
|
@@ -1817,18 +1688,41 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1817
1688
|
throw new Error(`Can't rollback ${type}`);
|
|
1818
1689
|
}
|
|
1819
1690
|
}
|
|
1691
|
+
async waitForDeltaManagerToCatchup(latestSnapshotRefSeq, summaryLogger) {
|
|
1692
|
+
if (latestSnapshotRefSeq > this.deltaManager.lastSequenceNumber) {
|
|
1693
|
+
// We need to catch up to the latest summary's reference sequence number before proceeding.
|
|
1694
|
+
await PerformanceEvent.timedExecAsync(summaryLogger, {
|
|
1695
|
+
eventName: "WaitingForSeq",
|
|
1696
|
+
lastSequenceNumber: this.deltaManager.lastSequenceNumber,
|
|
1697
|
+
targetSequenceNumber: latestSnapshotRefSeq,
|
|
1698
|
+
lastKnownSeqNumber: this.deltaManager.lastKnownSeqNumber,
|
|
1699
|
+
}, async () => waitForSeq(this.deltaManager, latestSnapshotRefSeq), { start: true, end: true, cancel: "error" });
|
|
1700
|
+
}
|
|
1701
|
+
}
|
|
1820
1702
|
/** Implementation of ISummarizerInternalsProvider.refreshLatestSummaryAck */
|
|
1821
|
-
async refreshLatestSummaryAck(
|
|
1703
|
+
async refreshLatestSummaryAck(options) {
|
|
1704
|
+
const { proposalHandle, ackHandle, summaryRefSeq, summaryLogger } = options;
|
|
1822
1705
|
const readAndParseBlob = async (id) => readAndParse(this.storage, id);
|
|
1823
1706
|
// The call to fetch the snapshot is very expensive and not always needed.
|
|
1824
1707
|
// It should only be done by the summarizerNode, if required.
|
|
1708
|
+
// When fetching from storage we will always get the latest version and do not use the ackHandle.
|
|
1825
1709
|
const snapshotTreeFetcher = async () => {
|
|
1826
|
-
const fetchResult = await this.fetchSnapshotFromStorage(
|
|
1710
|
+
const fetchResult = await this.fetchSnapshotFromStorage(null, summaryLogger, {
|
|
1827
1711
|
eventName: "RefreshLatestSummaryGetSnapshot",
|
|
1828
1712
|
ackHandle,
|
|
1829
1713
|
summaryRefSeq,
|
|
1830
|
-
fetchLatest:
|
|
1714
|
+
fetchLatest: true,
|
|
1715
|
+
});
|
|
1716
|
+
const latestSnapshotRefSeq = await seqFromTree(fetchResult.snapshotTree, readAndParseBlob);
|
|
1717
|
+
summaryLogger.sendTelemetryEvent({
|
|
1718
|
+
eventName: "LatestSummaryRetrieved",
|
|
1719
|
+
ackHandle,
|
|
1720
|
+
lastSequenceNumber: latestSnapshotRefSeq,
|
|
1721
|
+
targetSequenceNumber: summaryRefSeq,
|
|
1831
1722
|
});
|
|
1723
|
+
// In case we had to retrieve the latest snapshot and it is different than summaryRefSeq,
|
|
1724
|
+
// wait for the delta manager to catch up before refreshing the latest Summary.
|
|
1725
|
+
await this.waitForDeltaManagerToCatchup(latestSnapshotRefSeq, summaryLogger);
|
|
1832
1726
|
return fetchResult.snapshotTree;
|
|
1833
1727
|
};
|
|
1834
1728
|
const result = await this.summarizerNode.refreshLatestSummary(proposalHandle, summaryRefSeq, snapshotTreeFetcher, readAndParseBlob, summaryLogger);
|
|
@@ -1873,7 +1767,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1873
1767
|
this.baseSnapshotBlobs = SerializedSnapshotStorage.serializeTreeWithBlobContents(snapshot);
|
|
1874
1768
|
}
|
|
1875
1769
|
}
|
|
1876
|
-
async
|
|
1770
|
+
async initializeBaseSnapshotBlobs() {
|
|
1877
1771
|
var _a;
|
|
1878
1772
|
if (!((_a = this.mc.config.getBoolean("enableOfflineLoad")) !== null && _a !== void 0 ? _a : this.runtimeOptions.enableOfflineLoad) ||
|
|
1879
1773
|
this.attachState !== AttachState.Attached || this.context.pendingLocalState) {
|
|
@@ -1891,6 +1785,9 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1891
1785
|
// getPendingLocalState() is only exposed through Container.closeAndGetPendingLocalState(), so it's safe
|
|
1892
1786
|
// to close current batch.
|
|
1893
1787
|
this.flush();
|
|
1788
|
+
if (this._orderSequentiallyCalls !== 0) {
|
|
1789
|
+
throw new UsageError("can't get state during orderSequentially");
|
|
1790
|
+
}
|
|
1894
1791
|
const previousPendingState = this.context.pendingLocalState;
|
|
1895
1792
|
if (previousPendingState) {
|
|
1896
1793
|
return {
|
|
@@ -1958,6 +1855,9 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1958
1855
|
throw new UsageError(`Summary heuristic configuration property "${prop}" cannot be less than 0`);
|
|
1959
1856
|
}
|
|
1960
1857
|
}
|
|
1858
|
+
if (configuration.minIdleTime > configuration.maxIdleTime) {
|
|
1859
|
+
throw new UsageError(`"minIdleTime" [${configuration.minIdleTime}] cannot be greater than "maxIdleTime" [${configuration.maxIdleTime}]`);
|
|
1860
|
+
}
|
|
1961
1861
|
}
|
|
1962
1862
|
}
|
|
1963
1863
|
/**
|
|
@@ -1967,12 +1867,18 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1967
1867
|
const waitForSeq = async (deltaManager, targetSeq) => new Promise((resolve, reject) => {
|
|
1968
1868
|
// TODO: remove cast to any when actual event is determined
|
|
1969
1869
|
deltaManager.on("closed", reject);
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1870
|
+
// If we already reached target sequence number, simply resolve the promise.
|
|
1871
|
+
if (deltaManager.lastSequenceNumber >= targetSeq) {
|
|
1872
|
+
resolve();
|
|
1873
|
+
}
|
|
1874
|
+
else {
|
|
1875
|
+
const handleOp = (message) => {
|
|
1876
|
+
if (message.sequenceNumber >= targetSeq) {
|
|
1877
|
+
resolve();
|
|
1878
|
+
deltaManager.off("op", handleOp);
|
|
1879
|
+
}
|
|
1880
|
+
};
|
|
1881
|
+
deltaManager.on("op", handleOp);
|
|
1882
|
+
}
|
|
1977
1883
|
});
|
|
1978
1884
|
//# sourceMappingURL=containerRuntime.js.map
|