@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/src/containerRuntime.ts
CHANGED
|
@@ -16,14 +16,12 @@ import {
|
|
|
16
16
|
IFluidTokenProvider,
|
|
17
17
|
IContainerContext,
|
|
18
18
|
IDeltaManager,
|
|
19
|
-
IDeltaSender,
|
|
20
19
|
IRuntime,
|
|
21
20
|
ICriticalContainerError,
|
|
22
21
|
AttachState,
|
|
23
22
|
ILoaderOptions,
|
|
24
23
|
LoaderHeader,
|
|
25
24
|
ISnapshotTreeWithBlobContents,
|
|
26
|
-
IBatchMessage,
|
|
27
25
|
} from "@fluidframework/container-definitions";
|
|
28
26
|
import {
|
|
29
27
|
IContainerRuntime,
|
|
@@ -119,7 +117,6 @@ import {
|
|
|
119
117
|
IPendingLocalState,
|
|
120
118
|
PendingStateManager,
|
|
121
119
|
} from "./pendingStateManager";
|
|
122
|
-
import { BatchManager, BatchMessage } from "./batchManager";
|
|
123
120
|
import { pkgVersion } from "./packageVersion";
|
|
124
121
|
import { BlobManager, IBlobManagerLoadInfo, IPendingBlobs } from "./blobManager";
|
|
125
122
|
import { DataStores, getSummaryForDatastores } from "./dataStores";
|
|
@@ -147,17 +144,20 @@ import {
|
|
|
147
144
|
ISummarizerInternalsProvider,
|
|
148
145
|
ISummarizerOptions,
|
|
149
146
|
ISummarizerRuntime,
|
|
147
|
+
IRefreshSummaryAckOptions,
|
|
150
148
|
} from "./summarizerTypes";
|
|
151
149
|
import { formExponentialFn, Throttler } from "./throttler";
|
|
152
150
|
import { RunWhileConnectedCoordinator } from "./runWhileConnectedCoordinator";
|
|
153
151
|
import {
|
|
154
152
|
GarbageCollector,
|
|
155
153
|
GCNodeType,
|
|
156
|
-
gcTreeKey,
|
|
157
154
|
IGarbageCollectionRuntime,
|
|
158
155
|
IGarbageCollector,
|
|
159
156
|
IGCStats,
|
|
160
157
|
} from "./garbageCollection";
|
|
158
|
+
import {
|
|
159
|
+
gcTreeKey,
|
|
160
|
+
} from "./garbageCollectionConstants";
|
|
161
161
|
import {
|
|
162
162
|
channelToDataStore,
|
|
163
163
|
IDataStoreAliasMessage,
|
|
@@ -166,6 +166,15 @@ import {
|
|
|
166
166
|
import { BindBatchTracker } from "./batchTracker";
|
|
167
167
|
import { ISerializedBaseSnapshotBlobs, SerializedSnapshotStorage } from "./serializedSnapshotStorage";
|
|
168
168
|
import { ScheduleManager } from "./scheduleManager";
|
|
169
|
+
import {
|
|
170
|
+
BatchMessage,
|
|
171
|
+
IBatchCheckpoint,
|
|
172
|
+
OpCompressor,
|
|
173
|
+
OpDecompressor,
|
|
174
|
+
Outbox,
|
|
175
|
+
OpSplitter,
|
|
176
|
+
RemoteMessageProcessor,
|
|
177
|
+
} from "./opLifecycle";
|
|
169
178
|
|
|
170
179
|
export enum ContainerMessageType {
|
|
171
180
|
// An op to be delivered to store
|
|
@@ -187,16 +196,6 @@ export enum ContainerMessageType {
|
|
|
187
196
|
Alias = "alias",
|
|
188
197
|
}
|
|
189
198
|
|
|
190
|
-
export interface IChunkedOp {
|
|
191
|
-
chunkId: number;
|
|
192
|
-
|
|
193
|
-
totalChunks: number;
|
|
194
|
-
|
|
195
|
-
contents: string;
|
|
196
|
-
|
|
197
|
-
originalType: MessageType | ContainerMessageType;
|
|
198
|
-
}
|
|
199
|
-
|
|
200
199
|
export interface ContainerRuntimeMessage {
|
|
201
200
|
contents: any;
|
|
202
201
|
type: ContainerMessageType;
|
|
@@ -209,6 +208,7 @@ export interface ISummaryBaseConfiguration {
|
|
|
209
208
|
initialSummarizerDelayMs: number;
|
|
210
209
|
|
|
211
210
|
/**
|
|
211
|
+
* @deprecated
|
|
212
212
|
* Flag that will enable changing elected summarizer client after maxOpsSinceLastSummary.
|
|
213
213
|
* This defaults to false (disabled) and must be explicitly set to true to enable.
|
|
214
214
|
*/
|
|
@@ -229,11 +229,6 @@ export interface ISummaryBaseConfiguration {
|
|
|
229
229
|
|
|
230
230
|
export interface ISummaryConfigurationHeuristics extends ISummaryBaseConfiguration {
|
|
231
231
|
state: "enabled";
|
|
232
|
-
/**
|
|
233
|
-
* @deprecated Please move all implementations to {@link ISummaryConfigurationHeuristics.minIdleTime} and
|
|
234
|
-
* {@link ISummaryConfigurationHeuristics.maxIdleTime} instead.
|
|
235
|
-
*/
|
|
236
|
-
idleTime?: number;
|
|
237
232
|
/**
|
|
238
233
|
* Defines the maximum allowed time, since the last received Ack, before running the summary
|
|
239
234
|
* with reason maxTime.
|
|
@@ -279,6 +274,17 @@ export interface ISummaryConfigurationHeuristics extends ISummaryBaseConfigurati
|
|
|
279
274
|
* For example: (multiplier) * (number of non-runtime ops) = weighted number of non-runtime ops
|
|
280
275
|
*/
|
|
281
276
|
nonRuntimeOpWeight: number;
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Number of ops since last summary needed before a non-runtime op can trigger running summary heuristics.
|
|
280
|
+
*
|
|
281
|
+
* Note: Any runtime ops sent before the threshold is reached will trigger heuristics normally.
|
|
282
|
+
* This threshold ONLY applies to non-runtime ops triggering summaries.
|
|
283
|
+
*
|
|
284
|
+
* For example: Say the threshold is 20. Sending 19 non-runtime ops will not trigger any heuristic checks.
|
|
285
|
+
* Sending the 20th non-runtime op will trigger the heuristic checks for summarizing.
|
|
286
|
+
*/
|
|
287
|
+
nonRuntimeHeuristicThreshold?: number;
|
|
282
288
|
}
|
|
283
289
|
|
|
284
290
|
export interface ISummaryConfigurationDisableSummarizer {
|
|
@@ -318,6 +324,8 @@ export const DefaultSummaryConfiguration: ISummaryConfiguration = {
|
|
|
318
324
|
nonRuntimeOpWeight: 0.1,
|
|
319
325
|
|
|
320
326
|
runtimeOpWeight: 1.0,
|
|
327
|
+
|
|
328
|
+
nonRuntimeHeuristicThreshold: 20,
|
|
321
329
|
};
|
|
322
330
|
|
|
323
331
|
export interface IGCRuntimeOptions {
|
|
@@ -416,6 +424,22 @@ export interface ISummaryRuntimeOptions {
|
|
|
416
424
|
summarizerOptions?: Readonly<Partial<ISummarizerOptions>>;
|
|
417
425
|
}
|
|
418
426
|
|
|
427
|
+
/**
|
|
428
|
+
* Options for op compression.
|
|
429
|
+
* @experimental - Not ready for use
|
|
430
|
+
*/
|
|
431
|
+
export interface ICompressionRuntimeOptions {
|
|
432
|
+
/**
|
|
433
|
+
* The minimum size the batch's payload must exceed before the batch's contents will be compressed.
|
|
434
|
+
*/
|
|
435
|
+
readonly minimumBatchSizeInBytes: number;
|
|
436
|
+
|
|
437
|
+
/**
|
|
438
|
+
* The compression algorithm that will be used to compress the op.
|
|
439
|
+
*/
|
|
440
|
+
readonly compressionAlgorithm: CompressionAlgorithms;
|
|
441
|
+
}
|
|
442
|
+
|
|
419
443
|
/**
|
|
420
444
|
* Options for container runtime.
|
|
421
445
|
*/
|
|
@@ -442,6 +466,22 @@ export interface IContainerRuntimeOptions {
|
|
|
442
466
|
* Save enough runtime state to be able to serialize upon request and load to the same state in a new container.
|
|
443
467
|
*/
|
|
444
468
|
readonly enableOfflineLoad?: boolean;
|
|
469
|
+
/**
|
|
470
|
+
* Enables the runtime to compress ops. Compression is disabled when undefined.
|
|
471
|
+
* @experimental Not ready for use.
|
|
472
|
+
*/
|
|
473
|
+
readonly compressionOptions?: ICompressionRuntimeOptions;
|
|
474
|
+
/**
|
|
475
|
+
* If specified, when in FlushMode.TurnBased, if the size of the ops between JS turns exceeds this value,
|
|
476
|
+
* an error will be thrown and the container will close.
|
|
477
|
+
*
|
|
478
|
+
* If unspecified, the limit is 950 * 1024.
|
|
479
|
+
*
|
|
480
|
+
* 'Infinity' will disable any limit.
|
|
481
|
+
*
|
|
482
|
+
* @experimental This config should be driven by the connection with the service and will be moved in the future.
|
|
483
|
+
*/
|
|
484
|
+
readonly maxBatchSizeInBytes?: number;
|
|
445
485
|
}
|
|
446
486
|
|
|
447
487
|
/**
|
|
@@ -467,6 +507,13 @@ export enum RuntimeHeaders {
|
|
|
467
507
|
viaHandle = "viaHandle",
|
|
468
508
|
}
|
|
469
509
|
|
|
510
|
+
/**
|
|
511
|
+
* Available compression algorithms for op compression.
|
|
512
|
+
*/
|
|
513
|
+
export enum CompressionAlgorithms {
|
|
514
|
+
lz4 = "lz4",
|
|
515
|
+
}
|
|
516
|
+
|
|
470
517
|
/**
|
|
471
518
|
* @deprecated
|
|
472
519
|
* Untagged logger is unsupported going forward. There are old loaders with old ContainerContexts that only
|
|
@@ -513,6 +560,12 @@ const maxConsecutiveReconnectsKey = "Fluid.ContainerRuntime.MaxConsecutiveReconn
|
|
|
513
560
|
|
|
514
561
|
const defaultFlushMode = FlushMode.TurnBased;
|
|
515
562
|
|
|
563
|
+
// The actual limit is 1Mb (socket.io and Kafka limits)
|
|
564
|
+
// We can't estimate it fully, as we
|
|
565
|
+
// - do not know what properties relay service will add
|
|
566
|
+
// - we do not stringify final op, thus we do not know how much escaping will be added.
|
|
567
|
+
const defaultMaxBatchSizeInBytes = 950 * 1024;
|
|
568
|
+
|
|
516
569
|
/**
|
|
517
570
|
* @deprecated - use ContainerRuntimeMessage instead
|
|
518
571
|
*/
|
|
@@ -530,41 +583,7 @@ export enum RuntimeMessage {
|
|
|
530
583
|
* @deprecated - please use version in driver-utils
|
|
531
584
|
*/
|
|
532
585
|
export function isRuntimeMessage(message: ISequencedDocumentMessage): boolean {
|
|
533
|
-
|
|
534
|
-
return true;
|
|
535
|
-
}
|
|
536
|
-
return false;
|
|
537
|
-
}
|
|
538
|
-
|
|
539
|
-
/**
|
|
540
|
-
* Unpacks runtime messages
|
|
541
|
-
*
|
|
542
|
-
* @remarks This API makes no promises regarding backward-compatability. This is internal API.
|
|
543
|
-
* @param message - message (as it observed in storage / service)
|
|
544
|
-
* @returns unpacked runtime message
|
|
545
|
-
*
|
|
546
|
-
* @internal
|
|
547
|
-
*/
|
|
548
|
-
export function unpackRuntimeMessage(message: ISequencedDocumentMessage) {
|
|
549
|
-
if (message.type === MessageType.Operation) {
|
|
550
|
-
// legacy op format?
|
|
551
|
-
if (message.contents.address !== undefined && message.contents.type === undefined) {
|
|
552
|
-
message.type = ContainerMessageType.FluidDataStoreOp;
|
|
553
|
-
} else {
|
|
554
|
-
// new format
|
|
555
|
-
const innerContents = message.contents as ContainerRuntimeMessage;
|
|
556
|
-
assert(innerContents.type !== undefined, 0x121 /* "Undefined inner contents type!" */);
|
|
557
|
-
message.type = innerContents.type;
|
|
558
|
-
message.contents = innerContents.contents;
|
|
559
|
-
}
|
|
560
|
-
return true;
|
|
561
|
-
} else {
|
|
562
|
-
// Legacy format, but it's already "unpacked",
|
|
563
|
-
// i.e. message.type is actually ContainerMessageType.
|
|
564
|
-
// Or it's non-runtime message.
|
|
565
|
-
// Nothing to do in such case.
|
|
566
|
-
return false;
|
|
567
|
-
}
|
|
586
|
+
return (Object.values(RuntimeMessage) as string[]).includes(message.type);
|
|
568
587
|
}
|
|
569
588
|
|
|
570
589
|
/**
|
|
@@ -609,6 +628,8 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
609
628
|
* @param requestHandler - Request handlers for the container runtime
|
|
610
629
|
* @param runtimeOptions - Additional options to be passed to the runtime
|
|
611
630
|
* @param existing - (optional) When loading from an existing snapshot. Precedes context.existing if provided
|
|
631
|
+
* @param containerRuntimeCtor - (optional) Constructor to use to create the ContainerRuntime instance. This
|
|
632
|
+
* allows mixin classes to leverage this method to define their own async initializer.
|
|
612
633
|
*/
|
|
613
634
|
public static async load(
|
|
614
635
|
context: IContainerContext,
|
|
@@ -617,6 +638,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
617
638
|
runtimeOptions: IContainerRuntimeOptions = {},
|
|
618
639
|
containerScope: FluidObject = context.scope,
|
|
619
640
|
existing?: boolean,
|
|
641
|
+
containerRuntimeCtor: typeof ContainerRuntime = ContainerRuntime
|
|
620
642
|
): Promise<ContainerRuntime> {
|
|
621
643
|
// If taggedLogger exists, use it. Otherwise, wrap the vanilla logger:
|
|
622
644
|
// back-compat: Remove the TaggedLoggerAdapter fallback once all the host are using loader > 0.45
|
|
@@ -635,6 +657,11 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
635
657
|
loadSequenceNumberVerification = "close",
|
|
636
658
|
flushMode = defaultFlushMode,
|
|
637
659
|
enableOfflineLoad = false,
|
|
660
|
+
compressionOptions = {
|
|
661
|
+
minimumBatchSizeInBytes: Number.POSITIVE_INFINITY,
|
|
662
|
+
compressionAlgorithm: CompressionAlgorithms.lz4
|
|
663
|
+
},
|
|
664
|
+
maxBatchSizeInBytes = defaultMaxBatchSizeInBytes,
|
|
638
665
|
} = runtimeOptions;
|
|
639
666
|
|
|
640
667
|
const pendingRuntimeState = context.pendingLocalState as IPendingRuntimeState | undefined;
|
|
@@ -697,7 +724,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
697
724
|
}
|
|
698
725
|
}
|
|
699
726
|
|
|
700
|
-
const runtime = new
|
|
727
|
+
const runtime = new containerRuntimeCtor(
|
|
701
728
|
context,
|
|
702
729
|
registry,
|
|
703
730
|
metadata,
|
|
@@ -710,6 +737,8 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
710
737
|
loadSequenceNumberVerification,
|
|
711
738
|
flushMode,
|
|
712
739
|
enableOfflineLoad,
|
|
740
|
+
compressionOptions,
|
|
741
|
+
maxBatchSizeInBytes,
|
|
713
742
|
},
|
|
714
743
|
containerScope,
|
|
715
744
|
logger,
|
|
@@ -725,7 +754,8 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
725
754
|
pendingRuntimeState.savedOps = [];
|
|
726
755
|
}
|
|
727
756
|
|
|
728
|
-
|
|
757
|
+
// Initialize the base state of the runtime before it's returned.
|
|
758
|
+
await runtime.initializeBaseState();
|
|
729
759
|
|
|
730
760
|
return runtime;
|
|
731
761
|
}
|
|
@@ -787,6 +817,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
787
817
|
|
|
788
818
|
// internal logger for ContainerRuntime. Use this.logger for stores, summaries, etc.
|
|
789
819
|
private readonly mc: MonitoringContext;
|
|
820
|
+
|
|
790
821
|
private readonly summarizerClientElection?: SummarizerClientElection;
|
|
791
822
|
/**
|
|
792
823
|
* summaryManager will only be created if this client is permitted to spawn a summarizing client
|
|
@@ -802,8 +833,8 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
802
833
|
private readonly defaultMaxConsecutiveReconnects = 7;
|
|
803
834
|
|
|
804
835
|
private _orderSequentiallyCalls: number = 0;
|
|
805
|
-
private _flushMode: FlushMode;
|
|
806
|
-
private
|
|
836
|
+
private readonly _flushMode: FlushMode;
|
|
837
|
+
private flushMicroTaskExists = false;
|
|
807
838
|
|
|
808
839
|
private _connected: boolean;
|
|
809
840
|
|
|
@@ -847,32 +878,19 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
847
878
|
* It is created only by summarizing container (i.e. one with clientType === "summarizer")
|
|
848
879
|
*/
|
|
849
880
|
private readonly _summarizer?: Summarizer;
|
|
850
|
-
private readonly deltaSender: IDeltaSender;
|
|
851
881
|
private readonly scheduleManager: ScheduleManager;
|
|
852
882
|
private readonly blobManager: BlobManager;
|
|
853
883
|
private readonly pendingStateManager: PendingStateManager;
|
|
854
|
-
|
|
855
|
-
// Provide lower soft limit - we want to have some number of ops to get efficiency in compression & bandwidth usage,
|
|
856
|
-
// but at the same time we want to send these ops sooner, to reduce overall latency of processing a batch.
|
|
857
|
-
// So there is some ballance here, that depends on compression algorithm and its efficiency working with smaller
|
|
858
|
-
// payloads. That number represents final (compressed) bits (once compression is implemented).
|
|
859
|
-
private readonly pendingAttachBatch = new BatchManager(64 * 1024);
|
|
860
|
-
private readonly pendingBatch = new BatchManager();
|
|
884
|
+
private readonly outbox: Outbox;
|
|
861
885
|
|
|
862
886
|
private readonly garbageCollector: IGarbageCollector;
|
|
863
887
|
|
|
864
|
-
// Local copy of incomplete received chunks.
|
|
865
|
-
private readonly chunkMap: Map<string, string[]>;
|
|
866
|
-
|
|
867
888
|
private readonly dataStores: DataStores;
|
|
889
|
+
private readonly remoteMessageProcessor: RemoteMessageProcessor;
|
|
868
890
|
|
|
869
891
|
/** The last message processed at the time of the last summary. */
|
|
870
892
|
private messageAtLastSummary: ISummaryMetadataMessage | undefined;
|
|
871
893
|
|
|
872
|
-
private get emptyBatch() {
|
|
873
|
-
return this.pendingBatch.empty && this.pendingAttachBatch.empty;
|
|
874
|
-
}
|
|
875
|
-
|
|
876
894
|
private get summarizer(): Summarizer {
|
|
877
895
|
assert(this._summarizer !== undefined, 0x257 /* "This is not summarizing container" */);
|
|
878
896
|
return this._summarizer;
|
|
@@ -943,7 +961,10 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
943
961
|
*/
|
|
944
962
|
private nextSummaryNumber: number;
|
|
945
963
|
|
|
946
|
-
|
|
964
|
+
/**
|
|
965
|
+
* @internal
|
|
966
|
+
*/
|
|
967
|
+
protected constructor(
|
|
947
968
|
private readonly context: IContainerContext,
|
|
948
969
|
private readonly registry: IFluidDataStoreRegistry,
|
|
949
970
|
metadata: IContainerRuntimeMetadata | undefined,
|
|
@@ -965,10 +986,31 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
965
986
|
},
|
|
966
987
|
) {
|
|
967
988
|
super();
|
|
989
|
+
|
|
990
|
+
let loadSummaryNumber: number;
|
|
991
|
+
// Get the container creation metadata. For new container, we initialize these. For existing containers,
|
|
992
|
+
// get the values from the metadata blob.
|
|
993
|
+
if (existing) {
|
|
994
|
+
this.createContainerMetadata = {
|
|
995
|
+
createContainerRuntimeVersion: metadata?.createContainerRuntimeVersion,
|
|
996
|
+
createContainerTimestamp: metadata?.createContainerTimestamp,
|
|
997
|
+
};
|
|
998
|
+
// summaryNumber was renamed from summaryCount. For older docs that haven't been opened for a long time,
|
|
999
|
+
// the count is reset to 0.
|
|
1000
|
+
loadSummaryNumber = metadata?.summaryNumber ?? 0;
|
|
1001
|
+
} else {
|
|
1002
|
+
this.createContainerMetadata = {
|
|
1003
|
+
createContainerRuntimeVersion: pkgVersion,
|
|
1004
|
+
createContainerTimestamp: Date.now(),
|
|
1005
|
+
};
|
|
1006
|
+
loadSummaryNumber = 0;
|
|
1007
|
+
}
|
|
1008
|
+
this.nextSummaryNumber = loadSummaryNumber + 1;
|
|
1009
|
+
|
|
968
1010
|
this.messageAtLastSummary = metadata?.message;
|
|
969
1011
|
|
|
970
1012
|
this._connected = this.context.connected;
|
|
971
|
-
this.
|
|
1013
|
+
this.remoteMessageProcessor = new RemoteMessageProcessor(new OpSplitter(chunks), new OpDecompressor());
|
|
972
1014
|
|
|
973
1015
|
this.handleContext = new ContainerFluidHandleContext("", this);
|
|
974
1016
|
|
|
@@ -993,6 +1035,14 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
993
1035
|
const pendingRuntimeState = context.pendingLocalState as IPendingRuntimeState | undefined;
|
|
994
1036
|
const baseSnapshot: ISnapshotTree | undefined = pendingRuntimeState?.baseSnapshot ?? context.baseSnapshot;
|
|
995
1037
|
|
|
1038
|
+
const maxSnapshotCacheDurationMs = this._storage?.policies?.maximumCacheDurationMs;
|
|
1039
|
+
if (maxSnapshotCacheDurationMs !== undefined && maxSnapshotCacheDurationMs > 5 * 24 * 60 * 60 * 1000) {
|
|
1040
|
+
// This is a runtime enforcement of what's already explicit in the policy's type itself,
|
|
1041
|
+
// which dictates the value is either undefined or exactly 5 days in ms.
|
|
1042
|
+
// As long as the actual value is less than 5 days, the assumptions GC makes here are valid.
|
|
1043
|
+
throw new UsageError("Driver's maximumCacheDurationMs policy cannot exceed 5 days");
|
|
1044
|
+
}
|
|
1045
|
+
|
|
996
1046
|
this.garbageCollector = GarbageCollector.create({
|
|
997
1047
|
runtime: this,
|
|
998
1048
|
gcOptions: this.runtimeOptions.gcOptions,
|
|
@@ -1000,6 +1050,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1000
1050
|
baseLogger: this.mc.logger,
|
|
1001
1051
|
existing,
|
|
1002
1052
|
metadata,
|
|
1053
|
+
createContainerMetadata: this.createContainerMetadata,
|
|
1003
1054
|
isSummarizerClient: this.context.clientDetails.type === summarizerClientType,
|
|
1004
1055
|
getNodePackagePath: async (nodePath: string) => this.getGCNodePackagePath(nodePath),
|
|
1005
1056
|
getLastSummaryTimestampMs: () => this.messageAtLastSummary?.timestamp,
|
|
@@ -1031,7 +1082,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1031
1082
|
);
|
|
1032
1083
|
|
|
1033
1084
|
if (baseSnapshot) {
|
|
1034
|
-
this.summarizerNode.
|
|
1085
|
+
this.summarizerNode.updateBaseSummaryState(baseSnapshot);
|
|
1035
1086
|
}
|
|
1036
1087
|
|
|
1037
1088
|
this.dataStores = new DataStores(
|
|
@@ -1060,7 +1111,6 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1060
1111
|
packagePath,
|
|
1061
1112
|
),
|
|
1062
1113
|
new Map<string, string>(dataStoreAliasMap),
|
|
1063
|
-
this.garbageCollector.writeDataAtRoot,
|
|
1064
1114
|
);
|
|
1065
1115
|
|
|
1066
1116
|
this.blobManager = new BlobManager(
|
|
@@ -1084,8 +1134,6 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1084
1134
|
ChildLogger.create(this.logger, "ScheduleManager"),
|
|
1085
1135
|
);
|
|
1086
1136
|
|
|
1087
|
-
this.deltaSender = this.deltaManager;
|
|
1088
|
-
|
|
1089
1137
|
this.pendingStateManager = new PendingStateManager(
|
|
1090
1138
|
{
|
|
1091
1139
|
applyStashedOp: this.applyStashedOp.bind(this),
|
|
@@ -1093,15 +1141,25 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1093
1141
|
close: this.closeFn,
|
|
1094
1142
|
connected: () => this.connected,
|
|
1095
1143
|
flush: this.flush.bind(this),
|
|
1096
|
-
flushMode: () => this.flushMode,
|
|
1097
1144
|
reSubmit: this.reSubmit.bind(this),
|
|
1098
|
-
|
|
1145
|
+
rollback: this.rollback.bind(this),
|
|
1146
|
+
orderSequentially: this.orderSequentially.bind(this),
|
|
1099
1147
|
},
|
|
1100
|
-
this._flushMode,
|
|
1101
1148
|
pendingRuntimeState?.pending);
|
|
1102
1149
|
|
|
1150
|
+
this.outbox = new Outbox({
|
|
1151
|
+
shouldSend: () => this.canSendOps(),
|
|
1152
|
+
pendingStateManager: this.pendingStateManager,
|
|
1153
|
+
containerContext: this.context,
|
|
1154
|
+
compressor: new OpCompressor(this.mc.logger),
|
|
1155
|
+
config: {
|
|
1156
|
+
compressionOptions: runtimeOptions.compressionOptions,
|
|
1157
|
+
maxBatchSizeInBytes: runtimeOptions.maxBatchSizeInBytes,
|
|
1158
|
+
},
|
|
1159
|
+
});
|
|
1160
|
+
|
|
1103
1161
|
this.context.quorum.on("removeMember", (clientId: string) => {
|
|
1104
|
-
this.
|
|
1162
|
+
this.remoteMessageProcessor.clearPartialMessagesFor(clientId);
|
|
1105
1163
|
});
|
|
1106
1164
|
|
|
1107
1165
|
this.summaryCollection = new SummaryCollection(this.deltaManager, this.logger);
|
|
@@ -1150,7 +1208,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1150
1208
|
// if summaries are enabled and we are not the summarizer client.
|
|
1151
1209
|
const defaultAction = () => {
|
|
1152
1210
|
if (this.summaryCollection.opsSinceLastAck > this.maxOpsSinceLastSummary) {
|
|
1153
|
-
this.logger.
|
|
1211
|
+
this.logger.sendTelemetryEvent({ eventName: "SummaryStatus:Behind" });
|
|
1154
1212
|
// unregister default to no log on every op after falling behind
|
|
1155
1213
|
// and register summary ack handler to re-register this handler
|
|
1156
1214
|
// after successful summary
|
|
@@ -1217,26 +1275,6 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1217
1275
|
...getDeviceSpec(),
|
|
1218
1276
|
});
|
|
1219
1277
|
|
|
1220
|
-
let loadSummaryNumber: number;
|
|
1221
|
-
// Get the container creation metadata. For new container, we initialize these. For existing containers,
|
|
1222
|
-
// get the values from the metadata blob.
|
|
1223
|
-
if (existing) {
|
|
1224
|
-
this.createContainerMetadata = {
|
|
1225
|
-
createContainerRuntimeVersion: metadata?.createContainerRuntimeVersion,
|
|
1226
|
-
createContainerTimestamp: metadata?.createContainerTimestamp,
|
|
1227
|
-
};
|
|
1228
|
-
// summaryNumber was renamed from summaryCount. For older docs that haven't been opened for a long time,
|
|
1229
|
-
// the count is reset to 0.
|
|
1230
|
-
loadSummaryNumber = metadata?.summaryNumber ?? 0;
|
|
1231
|
-
} else {
|
|
1232
|
-
this.createContainerMetadata = {
|
|
1233
|
-
createContainerRuntimeVersion: pkgVersion,
|
|
1234
|
-
createContainerTimestamp: Date.now(),
|
|
1235
|
-
};
|
|
1236
|
-
loadSummaryNumber = 0;
|
|
1237
|
-
}
|
|
1238
|
-
this.nextSummaryNumber = loadSummaryNumber + 1;
|
|
1239
|
-
|
|
1240
1278
|
this.logger.sendTelemetryEvent({
|
|
1241
1279
|
eventName: "ContainerLoadStats",
|
|
1242
1280
|
...this.createContainerMetadata,
|
|
@@ -1251,6 +1289,14 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1251
1289
|
BindBatchTracker(this, this.logger);
|
|
1252
1290
|
}
|
|
1253
1291
|
|
|
1292
|
+
/**
|
|
1293
|
+
* Initializes the state from the base snapshot this container runtime loaded from.
|
|
1294
|
+
*/
|
|
1295
|
+
private async initializeBaseState(): Promise<void> {
|
|
1296
|
+
await this.initializeBaseSnapshotBlobs();
|
|
1297
|
+
await this.garbageCollector.initializeBaseState();
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1254
1300
|
public dispose(error?: Error): void {
|
|
1255
1301
|
if (this._disposed) {
|
|
1256
1302
|
return;
|
|
@@ -1359,10 +1405,13 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1359
1405
|
const wait = typeof request.headers?.[RuntimeHeaders.wait] === "boolean"
|
|
1360
1406
|
? request.headers?.[RuntimeHeaders.wait]
|
|
1361
1407
|
: true;
|
|
1408
|
+
const viaHandle = typeof request.headers?.[RuntimeHeaders.viaHandle] === "boolean"
|
|
1409
|
+
? request.headers?.[RuntimeHeaders.viaHandle]
|
|
1410
|
+
: false;
|
|
1362
1411
|
|
|
1363
1412
|
await this.dataStores.waitIfPendingAlias(id);
|
|
1364
1413
|
const internalId = this.internalId(id);
|
|
1365
|
-
const dataStoreContext = await this.dataStores.getDataStore(internalId, wait);
|
|
1414
|
+
const dataStoreContext = await this.dataStores.getDataStore(internalId, wait, viaHandle);
|
|
1366
1415
|
|
|
1367
1416
|
/**
|
|
1368
1417
|
* If GC should run and this an external app request with "externalRequest" header, we need to return
|
|
@@ -1411,7 +1460,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1411
1460
|
addBlobToSummary(summaryTree, metadataBlobName, JSON.stringify(metadata));
|
|
1412
1461
|
}
|
|
1413
1462
|
|
|
1414
|
-
|
|
1463
|
+
protected addContainerStateToSummary(
|
|
1415
1464
|
summaryTree: ISummaryTreeWithStats,
|
|
1416
1465
|
fullTree: boolean,
|
|
1417
1466
|
trackState: boolean,
|
|
@@ -1419,8 +1468,8 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1419
1468
|
) {
|
|
1420
1469
|
this.addMetadataToSummary(summaryTree);
|
|
1421
1470
|
|
|
1422
|
-
if (this.
|
|
1423
|
-
const content = JSON.stringify([...this.
|
|
1471
|
+
if (this.remoteMessageProcessor.partialMessages.size > 0) {
|
|
1472
|
+
const content = JSON.stringify([...this.remoteMessageProcessor.partialMessages]);
|
|
1424
1473
|
addBlobToSummary(summaryTree, chunksBlobName, content);
|
|
1425
1474
|
}
|
|
1426
1475
|
|
|
@@ -1441,11 +1490,9 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1441
1490
|
addTreeToSummary(summaryTree, blobsTreeName, blobManagerSummary);
|
|
1442
1491
|
}
|
|
1443
1492
|
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
addSummarizeResultToSummary(summaryTree, gcTreeKey, gcSummary);
|
|
1448
|
-
}
|
|
1493
|
+
const gcSummary = this.garbageCollector.summarize(fullTree, trackState, telemetryContext);
|
|
1494
|
+
if (gcSummary !== undefined) {
|
|
1495
|
+
addSummarizeResultToSummary(summaryTree, gcTreeKey, gcSummary);
|
|
1449
1496
|
}
|
|
1450
1497
|
}
|
|
1451
1498
|
|
|
@@ -1617,37 +1664,26 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1617
1664
|
public process(messageArg: ISequencedDocumentMessage, local: boolean) {
|
|
1618
1665
|
this.verifyNotClosed();
|
|
1619
1666
|
|
|
1620
|
-
// Do shallow copy of message, as methods below will modify it.
|
|
1621
|
-
// There might be multiple container instances receiving same message
|
|
1622
|
-
// We do not need to make deep copy, as each layer will just replace message.content itself,
|
|
1623
|
-
// but would not modify contents details
|
|
1624
|
-
let message = { ...messageArg };
|
|
1625
|
-
|
|
1626
|
-
// back-compat: ADO #1385: eventually should become unconditional, but only for runtime messages!
|
|
1627
|
-
// System message may have no contents, or in some cases (mostly for back-compat) they may have actual objects.
|
|
1628
|
-
// Old ops may contain empty string (I assume noops).
|
|
1629
|
-
if (typeof message.contents === "string" && message.contents !== "") {
|
|
1630
|
-
message.contents = JSON.parse(message.contents);
|
|
1631
|
-
}
|
|
1632
|
-
|
|
1633
|
-
// Caveat: This will return false for runtime message in very old format, that are used in snapshot tests
|
|
1634
|
-
// This format was not shipped to production workflows.
|
|
1635
|
-
const runtimeMessage = unpackRuntimeMessage(message);
|
|
1636
|
-
|
|
1637
1667
|
if (this.mc.config.getBoolean("enableOfflineLoad") ?? this.runtimeOptions.enableOfflineLoad) {
|
|
1638
1668
|
this.savedOps.push(messageArg);
|
|
1639
1669
|
}
|
|
1640
1670
|
|
|
1671
|
+
|
|
1672
|
+
// Whether or not the message is actually a runtime message.
|
|
1673
|
+
// It may be a legacy runtime message (ie already unpacked and ContainerMessageType)
|
|
1674
|
+
// or something different, like a system message.
|
|
1675
|
+
const runtimeMessage = messageArg.type === MessageType.Operation;
|
|
1676
|
+
|
|
1677
|
+
// Do shallow copy of message, as the processing flow will modify it.
|
|
1678
|
+
const messageCopy = { ...messageArg };
|
|
1679
|
+
const message = this.remoteMessageProcessor.process(messageCopy);
|
|
1680
|
+
|
|
1641
1681
|
// Surround the actual processing of the operation with messages to the schedule manager indicating
|
|
1642
1682
|
// the beginning and end. This allows it to emit appropriate events and/or pause the processing of new
|
|
1643
1683
|
// messages once a batch has been fully processed.
|
|
1644
1684
|
this.scheduleManager.beforeOpProcessing(message);
|
|
1645
1685
|
|
|
1646
1686
|
try {
|
|
1647
|
-
// Chunk processing must come first given that we will transform the message to the unchunked version
|
|
1648
|
-
// once all pieces are available
|
|
1649
|
-
message = this.processRemoteChunkedMessage(message);
|
|
1650
|
-
|
|
1651
1687
|
let localOpMetadata: unknown;
|
|
1652
1688
|
if (local && runtimeMessage) {
|
|
1653
1689
|
localOpMetadata = this.pendingStateManager.processPendingLocalMessage(message);
|
|
@@ -1767,143 +1803,35 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1767
1803
|
private async getRootDataStoreChannel(id: string, wait = true): Promise<IFluidDataStoreChannel> {
|
|
1768
1804
|
await this.dataStores.waitIfPendingAlias(id);
|
|
1769
1805
|
const internalId = this.internalId(id);
|
|
1770
|
-
const context = await this.dataStores.getDataStore(internalId, wait);
|
|
1806
|
+
const context = await this.dataStores.getDataStore(internalId, wait, false /* viaHandle */);
|
|
1771
1807
|
assert(await context.isRoot(), 0x12b /* "did not get root data store" */);
|
|
1772
1808
|
return context.realize();
|
|
1773
1809
|
}
|
|
1774
1810
|
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
this.mc.logger.sendTelemetryEvent({
|
|
1781
|
-
eventName: "FlushMode Updated",
|
|
1782
|
-
old: this._flushMode,
|
|
1783
|
-
new: mode,
|
|
1784
|
-
});
|
|
1785
|
-
|
|
1786
|
-
// Flush any pending batches if switching to immediate
|
|
1787
|
-
if (mode === FlushMode.Immediate) {
|
|
1788
|
-
this.flush();
|
|
1789
|
-
}
|
|
1790
|
-
|
|
1791
|
-
this._flushMode = mode;
|
|
1792
|
-
|
|
1793
|
-
// Let the PendingStateManager know that FlushMode has been updated.
|
|
1794
|
-
this.pendingStateManager.onFlushModeUpdated(mode);
|
|
1795
|
-
}
|
|
1796
|
-
|
|
1797
|
-
public flush(): void {
|
|
1811
|
+
/**
|
|
1812
|
+
* Flush the pending ops manually.
|
|
1813
|
+
* This method is expected to be called at the end of a batch.
|
|
1814
|
+
*/
|
|
1815
|
+
private flush(): void {
|
|
1798
1816
|
assert(this._orderSequentiallyCalls === 0,
|
|
1799
1817
|
0x24c /* "Cannot call `flush()` from `orderSequentially`'s callback" */);
|
|
1800
1818
|
|
|
1801
|
-
this.
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
assert(this.emptyBatch, 0x3cf /* reentrancy */);
|
|
1819
|
+
this.outbox.flush();
|
|
1820
|
+
assert(this.outbox.isEmpty, 0x3cf /* reentrancy */);
|
|
1805
1821
|
}
|
|
1806
1822
|
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
if (length > 1) {
|
|
1811
|
-
batch[0].metadata = { ...batch[0].metadata, batch: true };
|
|
1812
|
-
batch[length - 1].metadata = { ...batch[length - 1].metadata, batch: false };
|
|
1813
|
-
|
|
1814
|
-
// This assert fires for the following reason (there might be more cases like that):
|
|
1815
|
-
// AgentScheduler will send ops in response to ConsensusRegisterCollection's "atomicChanged" event handler,
|
|
1816
|
-
// i.e. in the middle of op processing!
|
|
1817
|
-
// Sending ops while processing ops is not good idea - it's not defined when
|
|
1818
|
-
// referenceSequenceNumber changes in op processing sequence (at the beginning or end of op processing),
|
|
1819
|
-
// If we send ops in response to processing multiple ops, then we for sure hit this assert!
|
|
1820
|
-
// Tracked via ADO #1834
|
|
1821
|
-
// assert(batch[0].referenceSequenceNumber === batch[length - 1].referenceSequenceNumber,
|
|
1822
|
-
// "Batch should be generated synchronously, without processing ops in the middle!");
|
|
1823
|
-
}
|
|
1824
|
-
|
|
1825
|
-
let clientSequenceNumber: number = -1;
|
|
1826
|
-
|
|
1827
|
-
// Did we disconnect in the middle of turn-based batch?
|
|
1828
|
-
// If so, do nothing, as pending state manager will resubmit it correctly on reconnect.
|
|
1829
|
-
if (this.canSendOps()) {
|
|
1830
|
-
if (this.context.submitBatchFn !== undefined) {
|
|
1831
|
-
const batchToSend: IBatchMessage[] = [];
|
|
1832
|
-
for (const message of batch) {
|
|
1833
|
-
batchToSend.push({ contents: message.contents, metadata: message.metadata });
|
|
1834
|
-
}
|
|
1835
|
-
// returns clientSequenceNumber of last message in a batch
|
|
1836
|
-
clientSequenceNumber = this.context.submitBatchFn(batchToSend);
|
|
1837
|
-
} else {
|
|
1838
|
-
// Legacy path - supporting old loader versions. Can be removed only when LTS moves above
|
|
1839
|
-
// version that has support for batches (submitBatchFn)
|
|
1840
|
-
for (const message of batch) {
|
|
1841
|
-
clientSequenceNumber = this.context.submitFn(
|
|
1842
|
-
MessageType.Operation,
|
|
1843
|
-
message.deserializedContent,
|
|
1844
|
-
true, // batch
|
|
1845
|
-
message.metadata);
|
|
1846
|
-
}
|
|
1847
|
-
|
|
1848
|
-
this.deltaSender.flush();
|
|
1849
|
-
}
|
|
1850
|
-
|
|
1851
|
-
// Convert from clientSequenceNumber of last message in the batch to clientSequenceNumber of first message.
|
|
1852
|
-
clientSequenceNumber -= batch.length - 1;
|
|
1853
|
-
assert(clientSequenceNumber >= 0, 0x3d0 /* clientSequenceNumber can't be negative */);
|
|
1854
|
-
}
|
|
1855
|
-
|
|
1856
|
-
// Let the PendingStateManager know that a message was submitted.
|
|
1857
|
-
// In future, need to shift toward keeping batch as a whole!
|
|
1858
|
-
for (const message of batch) {
|
|
1859
|
-
this.pendingStateManager.onSubmitMessage(
|
|
1860
|
-
message.deserializedContent.type,
|
|
1861
|
-
clientSequenceNumber,
|
|
1862
|
-
message.referenceSequenceNumber,
|
|
1863
|
-
message.deserializedContent.contents,
|
|
1864
|
-
message.localOpMetadata,
|
|
1865
|
-
message.metadata,
|
|
1866
|
-
);
|
|
1867
|
-
clientSequenceNumber++;
|
|
1868
|
-
}
|
|
1869
|
-
|
|
1870
|
-
this.pendingStateManager.onFlush();
|
|
1871
|
-
}
|
|
1872
|
-
|
|
1873
|
-
public orderSequentially(callback: () => void): void {
|
|
1874
|
-
// If flush mode is already TurnBased we are either
|
|
1875
|
-
// nested in another orderSequentially, or
|
|
1876
|
-
// the app is flushing manually, in which
|
|
1877
|
-
// case this invocation doesn't own
|
|
1878
|
-
// flushing.
|
|
1879
|
-
if (this.flushMode === FlushMode.TurnBased) {
|
|
1880
|
-
this.trackOrderSequentiallyCalls(callback);
|
|
1881
|
-
return;
|
|
1882
|
-
}
|
|
1883
|
-
|
|
1884
|
-
const savedFlushMode = this.flushMode;
|
|
1885
|
-
this.setFlushMode(FlushMode.TurnBased);
|
|
1886
|
-
|
|
1887
|
-
try {
|
|
1888
|
-
this.trackOrderSequentiallyCalls(callback);
|
|
1889
|
-
this.flush();
|
|
1890
|
-
} finally {
|
|
1891
|
-
this.setFlushMode(savedFlushMode);
|
|
1892
|
-
}
|
|
1893
|
-
}
|
|
1894
|
-
|
|
1895
|
-
private trackOrderSequentiallyCalls(callback: () => void): void {
|
|
1896
|
-
let checkpoint: { rollback: (action: (message: BatchMessage) => void) => void; } | undefined;
|
|
1823
|
+
public orderSequentially<T>(callback: () => T): T {
|
|
1824
|
+
let checkpoint: IBatchCheckpoint | undefined;
|
|
1825
|
+
let result: T;
|
|
1897
1826
|
if (this.mc.config.getBoolean("Fluid.ContainerRuntime.EnableRollback")) {
|
|
1898
1827
|
// Note: we are not touching this.pendingAttachBatch here, for two reasons:
|
|
1899
1828
|
// 1. It would not help, as we flush attach ops as they become available.
|
|
1900
1829
|
// 2. There is no way to undo process of data store creation.
|
|
1901
|
-
checkpoint = this.
|
|
1830
|
+
checkpoint = this.outbox.checkpoint().mainBatch;
|
|
1902
1831
|
}
|
|
1903
|
-
|
|
1904
1832
|
try {
|
|
1905
1833
|
this._orderSequentiallyCalls++;
|
|
1906
|
-
callback();
|
|
1834
|
+
result = callback();
|
|
1907
1835
|
} catch (error) {
|
|
1908
1836
|
if (checkpoint) {
|
|
1909
1837
|
// This will throw and close the container if rollback fails
|
|
@@ -1931,6 +1859,11 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1931
1859
|
} finally {
|
|
1932
1860
|
this._orderSequentiallyCalls--;
|
|
1933
1861
|
}
|
|
1862
|
+
|
|
1863
|
+
if (this.flushMode === FlushMode.Immediate && this._orderSequentiallyCalls === 0) {
|
|
1864
|
+
this.flush();
|
|
1865
|
+
}
|
|
1866
|
+
return result;
|
|
1934
1867
|
}
|
|
1935
1868
|
|
|
1936
1869
|
public async createDataStore(pkg: string | string[]): Promise<IDataStore> {
|
|
@@ -1980,6 +1913,13 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1980
1913
|
return this.connected && !this.deltaManager.readOnlyInfo.readonly;
|
|
1981
1914
|
}
|
|
1982
1915
|
|
|
1916
|
+
/**
|
|
1917
|
+
* Are we in the middle of batching ops together?
|
|
1918
|
+
*/
|
|
1919
|
+
private currentlyBatching() {
|
|
1920
|
+
return this.flushMode === FlushMode.TurnBased || this._orderSequentiallyCalls !== 0;
|
|
1921
|
+
}
|
|
1922
|
+
|
|
1983
1923
|
public getQuorum(): IQuorumClients {
|
|
1984
1924
|
return this.context.quorum;
|
|
1985
1925
|
}
|
|
@@ -2192,31 +2132,35 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2192
2132
|
* Implementation of IGarbageCollectionRuntime::updateUsedRoutes.
|
|
2193
2133
|
* After GC has run, called to notify this container's nodes of routes that are used in it.
|
|
2194
2134
|
* @param usedRoutes - The routes that are used in all nodes in this Container.
|
|
2195
|
-
* @param gcTimestamp - The time when GC was run that generated these used routes. If any node node becomes
|
|
2196
|
-
* unreferenced as part of this GC run, this should be used to update the time when it happens.
|
|
2197
2135
|
*/
|
|
2198
|
-
public updateUsedRoutes(usedRoutes: string[]
|
|
2136
|
+
public updateUsedRoutes(usedRoutes: string[]) {
|
|
2199
2137
|
// Update our summarizer node's used routes. Updating used routes in summarizer node before
|
|
2200
2138
|
// summarizing is required and asserted by the the summarizer node. We are the root and are
|
|
2201
2139
|
// always referenced, so the used routes is only self-route (empty string).
|
|
2202
2140
|
this.summarizerNode.updateUsedRoutes([""]);
|
|
2203
2141
|
|
|
2142
|
+
const blobManagerUsedRoutes: string[] = [];
|
|
2204
2143
|
const dataStoreUsedRoutes: string[] = [];
|
|
2205
2144
|
for (const route of usedRoutes) {
|
|
2206
|
-
if (
|
|
2145
|
+
if (this.isBlobPath(route)) {
|
|
2146
|
+
blobManagerUsedRoutes.push(route);
|
|
2147
|
+
} else {
|
|
2207
2148
|
dataStoreUsedRoutes.push(route);
|
|
2208
2149
|
}
|
|
2209
2150
|
}
|
|
2210
2151
|
|
|
2211
|
-
|
|
2152
|
+
this.blobManager.updateUsedRoutes(blobManagerUsedRoutes);
|
|
2153
|
+
this.dataStores.updateUsedRoutes(dataStoreUsedRoutes);
|
|
2212
2154
|
}
|
|
2213
2155
|
|
|
2214
2156
|
/**
|
|
2215
|
-
*
|
|
2216
|
-
*
|
|
2217
|
-
* @param unusedRoutes - The routes that are unused in all data stores in this Container.
|
|
2157
|
+
* This is called to update objects whose routes are unused. The unused objects are either deleted or marked as
|
|
2158
|
+
* tombstones.
|
|
2159
|
+
* @param unusedRoutes - The routes that are unused in all data stores and attachment blobs in this Container.
|
|
2160
|
+
* @param tombstone - if true, the objects corresponding to unused routes are marked tombstones. Otherwise, they
|
|
2161
|
+
* are deleted.
|
|
2218
2162
|
*/
|
|
2219
|
-
public
|
|
2163
|
+
public updateUnusedRoutes(unusedRoutes: string[], tombstone: boolean) {
|
|
2220
2164
|
const blobManagerUnusedRoutes: string[] = [];
|
|
2221
2165
|
const dataStoreUnusedRoutes: string[] = [];
|
|
2222
2166
|
for (const route of unusedRoutes) {
|
|
@@ -2227,8 +2171,8 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2227
2171
|
}
|
|
2228
2172
|
}
|
|
2229
2173
|
|
|
2230
|
-
this.blobManager.
|
|
2231
|
-
this.dataStores.
|
|
2174
|
+
this.blobManager.updateUnusedRoutes(blobManagerUnusedRoutes, tombstone);
|
|
2175
|
+
this.dataStores.updateUnusedRoutes(dataStoreUnusedRoutes, tombstone);
|
|
2232
2176
|
}
|
|
2233
2177
|
|
|
2234
2178
|
/**
|
|
@@ -2326,7 +2270,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2326
2270
|
},
|
|
2327
2271
|
);
|
|
2328
2272
|
|
|
2329
|
-
assert(this.
|
|
2273
|
+
assert(this.outbox.isEmpty, 0x3d1 /* Can't trigger summary in the middle of a batch */);
|
|
2330
2274
|
|
|
2331
2275
|
let latestSnapshotVersionId: string | undefined;
|
|
2332
2276
|
if (refreshLatestAck) {
|
|
@@ -2335,20 +2279,9 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2335
2279
|
const latestSnapshotRefSeq = latestSnapshotInfo.latestSnapshotRefSeq;
|
|
2336
2280
|
latestSnapshotVersionId = latestSnapshotInfo.latestSnapshotVersionId;
|
|
2337
2281
|
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
summaryNumberLogger,
|
|
2342
|
-
{
|
|
2343
|
-
eventName: "WaitingForSeq",
|
|
2344
|
-
lastSequenceNumber: this.deltaManager.lastSequenceNumber,
|
|
2345
|
-
targetSequenceNumber: latestSnapshotRefSeq,
|
|
2346
|
-
lastKnownSeqNumber: this.deltaManager.lastKnownSeqNumber,
|
|
2347
|
-
},
|
|
2348
|
-
async () => waitForSeq(this.deltaManager, latestSnapshotRefSeq),
|
|
2349
|
-
{ start: true, end: true, cancel: "error" }, // definitely want start event
|
|
2350
|
-
);
|
|
2351
|
-
}
|
|
2282
|
+
// We might need to catch up to the latest summary's reference sequence number before pausing.
|
|
2283
|
+
await this.waitForDeltaManagerToCatchup(latestSnapshotRefSeq,
|
|
2284
|
+
summaryNumberLogger);
|
|
2352
2285
|
}
|
|
2353
2286
|
|
|
2354
2287
|
try {
|
|
@@ -2547,45 +2480,8 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2547
2480
|
}
|
|
2548
2481
|
}
|
|
2549
2482
|
|
|
2550
|
-
private processRemoteChunkedMessage(message: ISequencedDocumentMessage) {
|
|
2551
|
-
if (message.type !== ContainerMessageType.ChunkedOp) {
|
|
2552
|
-
return message;
|
|
2553
|
-
}
|
|
2554
|
-
|
|
2555
|
-
const clientId = message.clientId;
|
|
2556
|
-
const chunkedContent = message.contents as IChunkedOp;
|
|
2557
|
-
this.addChunk(clientId, chunkedContent);
|
|
2558
|
-
if (chunkedContent.chunkId === chunkedContent.totalChunks) {
|
|
2559
|
-
const newMessage = { ...message };
|
|
2560
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
2561
|
-
const serializedContent = this.chunkMap.get(clientId)!.join("");
|
|
2562
|
-
newMessage.contents = JSON.parse(serializedContent);
|
|
2563
|
-
newMessage.type = chunkedContent.originalType;
|
|
2564
|
-
this.clearPartialChunks(clientId);
|
|
2565
|
-
return newMessage;
|
|
2566
|
-
}
|
|
2567
|
-
return message;
|
|
2568
|
-
}
|
|
2569
|
-
|
|
2570
|
-
private addChunk(clientId: string, chunkedContent: IChunkedOp) {
|
|
2571
|
-
let map = this.chunkMap.get(clientId);
|
|
2572
|
-
if (map === undefined) {
|
|
2573
|
-
map = [];
|
|
2574
|
-
this.chunkMap.set(clientId, map);
|
|
2575
|
-
}
|
|
2576
|
-
assert(chunkedContent.chunkId === map.length + 1,
|
|
2577
|
-
0x131 /* "Mismatch between new chunkId and expected chunkMap" */); // 1-based indexing
|
|
2578
|
-
map.push(chunkedContent.contents);
|
|
2579
|
-
}
|
|
2580
|
-
|
|
2581
|
-
private clearPartialChunks(clientId: string) {
|
|
2582
|
-
if (this.chunkMap.has(clientId)) {
|
|
2583
|
-
this.chunkMap.delete(clientId);
|
|
2584
|
-
}
|
|
2585
|
-
}
|
|
2586
|
-
|
|
2587
2483
|
private hasPendingMessages() {
|
|
2588
|
-
return this.pendingStateManager.hasPendingMessages() || !this.
|
|
2484
|
+
return this.pendingStateManager.hasPendingMessages() || !this.outbox.isEmpty;
|
|
2589
2485
|
}
|
|
2590
2486
|
|
|
2591
2487
|
private updateDocumentDirtyState(dirty: boolean) {
|
|
@@ -2648,7 +2544,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2648
2544
|
const serializedContent = JSON.stringify(deserializedContent);
|
|
2649
2545
|
|
|
2650
2546
|
if (this.deltaManager.readOnlyInfo.readonly) {
|
|
2651
|
-
this.logger.
|
|
2547
|
+
this.logger.sendTelemetryEvent({ eventName: "SubmitOpInReadonly", connected: this.connected });
|
|
2652
2548
|
}
|
|
2653
2549
|
|
|
2654
2550
|
const message: BatchMessage = {
|
|
@@ -2680,47 +2576,23 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2680
2576
|
// issue than sending.
|
|
2681
2577
|
// Please note that this does not change file format, so it can be disabled in the future if this
|
|
2682
2578
|
// optimization no longer makes sense (for example, batch compression may make it less appealing).
|
|
2683
|
-
if (type === ContainerMessageType.Attach &&
|
|
2579
|
+
if (this.currentlyBatching() && type === ContainerMessageType.Attach &&
|
|
2684
2580
|
this.mc.config.getBoolean("Fluid.ContainerRuntime.disableAttachOpReorder") !== true) {
|
|
2685
|
-
|
|
2686
|
-
// BatchManager has two limits - soft limit & hard limit. Soft limit is only engaged
|
|
2687
|
-
// when queue is not empty.
|
|
2688
|
-
// Flush queue & retry. Failure on retry would mean - single message is bigger than hard limit
|
|
2689
|
-
this.flushBatch(this.pendingAttachBatch.popBatch());
|
|
2690
|
-
if (!this.pendingAttachBatch.push(message)) {
|
|
2691
|
-
throw new GenericError(
|
|
2692
|
-
"BatchTooLarge",
|
|
2693
|
-
/* error */ undefined,
|
|
2694
|
-
{
|
|
2695
|
-
opSize: message.contents.length,
|
|
2696
|
-
count: this.pendingAttachBatch.length,
|
|
2697
|
-
limit: this.pendingAttachBatch.limit,
|
|
2698
|
-
});
|
|
2699
|
-
}
|
|
2700
|
-
}
|
|
2581
|
+
this.outbox.submitAttach(message);
|
|
2701
2582
|
} else {
|
|
2702
|
-
|
|
2703
|
-
throw new GenericError(
|
|
2704
|
-
"BatchTooLarge",
|
|
2705
|
-
/* error */ undefined,
|
|
2706
|
-
{
|
|
2707
|
-
opSize: message.contents.length,
|
|
2708
|
-
count: this.pendingBatch.length,
|
|
2709
|
-
limit: this.pendingBatch.limit,
|
|
2710
|
-
});
|
|
2711
|
-
}
|
|
2583
|
+
this.outbox.submit(message);
|
|
2712
2584
|
}
|
|
2713
2585
|
|
|
2714
|
-
if (this.
|
|
2586
|
+
if (!this.currentlyBatching()) {
|
|
2715
2587
|
this.flush();
|
|
2716
|
-
} else if (!this.
|
|
2717
|
-
this.
|
|
2588
|
+
} else if (!this.flushMicroTaskExists) {
|
|
2589
|
+
this.flushMicroTaskExists = true;
|
|
2718
2590
|
// Queue a microtask to detect the end of the turn and force a flush.
|
|
2719
2591
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
2720
2592
|
Promise.resolve().then(() => {
|
|
2721
|
-
this.
|
|
2593
|
+
this.flushMicroTaskExists = false;
|
|
2722
2594
|
this.flush();
|
|
2723
|
-
});
|
|
2595
|
+
}).catch((error) => { this.closeFn(error as GenericError) });
|
|
2724
2596
|
}
|
|
2725
2597
|
} catch (error) {
|
|
2726
2598
|
this.closeFn(error as GenericError);
|
|
@@ -2737,7 +2609,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2737
2609
|
assert(this.connected, 0x133 /* "Container disconnected when trying to submit system message" */);
|
|
2738
2610
|
|
|
2739
2611
|
// System message should not be sent in the middle of the batch.
|
|
2740
|
-
assert(this.
|
|
2612
|
+
assert(this.outbox.isEmpty, 0x3d4 /* System op in the middle of a batch */);
|
|
2741
2613
|
|
|
2742
2614
|
// back-compat: ADO #1385: Make this call unconditional in the future
|
|
2743
2615
|
return this.context.submitSummaryFn !== undefined
|
|
@@ -2809,28 +2681,61 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2809
2681
|
}
|
|
2810
2682
|
}
|
|
2811
2683
|
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
proposalHandle: string | undefined,
|
|
2815
|
-
ackHandle: string,
|
|
2816
|
-
summaryRefSeq: number,
|
|
2684
|
+
private async waitForDeltaManagerToCatchup(
|
|
2685
|
+
latestSnapshotRefSeq: number,
|
|
2817
2686
|
summaryLogger: ITelemetryLogger,
|
|
2818
|
-
) {
|
|
2687
|
+
): Promise<void> {
|
|
2688
|
+
if (latestSnapshotRefSeq > this.deltaManager.lastSequenceNumber) {
|
|
2689
|
+
// We need to catch up to the latest summary's reference sequence number before proceeding.
|
|
2690
|
+
await PerformanceEvent.timedExecAsync(
|
|
2691
|
+
summaryLogger,
|
|
2692
|
+
{
|
|
2693
|
+
eventName: "WaitingForSeq",
|
|
2694
|
+
lastSequenceNumber: this.deltaManager.lastSequenceNumber,
|
|
2695
|
+
targetSequenceNumber: latestSnapshotRefSeq,
|
|
2696
|
+
lastKnownSeqNumber: this.deltaManager.lastKnownSeqNumber,
|
|
2697
|
+
},
|
|
2698
|
+
async () => waitForSeq(this.deltaManager, latestSnapshotRefSeq),
|
|
2699
|
+
{ start: true, end: true, cancel: "error" }, // definitely want start event
|
|
2700
|
+
);
|
|
2701
|
+
}
|
|
2702
|
+
}
|
|
2703
|
+
|
|
2704
|
+
/** Implementation of ISummarizerInternalsProvider.refreshLatestSummaryAck */
|
|
2705
|
+
public async refreshLatestSummaryAck(options: IRefreshSummaryAckOptions) {
|
|
2706
|
+
const { proposalHandle, ackHandle, summaryRefSeq, summaryLogger } = options;
|
|
2819
2707
|
const readAndParseBlob = async <T>(id: string) => readAndParse<T>(this.storage, id);
|
|
2820
2708
|
// The call to fetch the snapshot is very expensive and not always needed.
|
|
2821
2709
|
// It should only be done by the summarizerNode, if required.
|
|
2710
|
+
// When fetching from storage we will always get the latest version and do not use the ackHandle.
|
|
2822
2711
|
const snapshotTreeFetcher = async () => {
|
|
2823
2712
|
const fetchResult = await this.fetchSnapshotFromStorage(
|
|
2824
|
-
|
|
2713
|
+
null,
|
|
2825
2714
|
summaryLogger,
|
|
2826
2715
|
{
|
|
2827
2716
|
eventName: "RefreshLatestSummaryGetSnapshot",
|
|
2828
2717
|
ackHandle,
|
|
2829
2718
|
summaryRefSeq,
|
|
2830
|
-
fetchLatest:
|
|
2719
|
+
fetchLatest: true,
|
|
2831
2720
|
});
|
|
2721
|
+
|
|
2722
|
+
const latestSnapshotRefSeq = await seqFromTree(fetchResult.snapshotTree, readAndParseBlob);
|
|
2723
|
+
summaryLogger.sendTelemetryEvent(
|
|
2724
|
+
{
|
|
2725
|
+
eventName: "LatestSummaryRetrieved",
|
|
2726
|
+
ackHandle,
|
|
2727
|
+
lastSequenceNumber: latestSnapshotRefSeq,
|
|
2728
|
+
targetSequenceNumber: summaryRefSeq,
|
|
2729
|
+
});
|
|
2730
|
+
|
|
2731
|
+
// In case we had to retrieve the latest snapshot and it is different than summaryRefSeq,
|
|
2732
|
+
// wait for the delta manager to catch up before refreshing the latest Summary.
|
|
2733
|
+
await this.waitForDeltaManagerToCatchup(latestSnapshotRefSeq,
|
|
2734
|
+
summaryLogger);
|
|
2735
|
+
|
|
2832
2736
|
return fetchResult.snapshotTree;
|
|
2833
2737
|
};
|
|
2738
|
+
|
|
2834
2739
|
const result = await this.summarizerNode.refreshLatestSummary(
|
|
2835
2740
|
proposalHandle,
|
|
2836
2741
|
summaryRefSeq,
|
|
@@ -2912,7 +2817,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2912
2817
|
}
|
|
2913
2818
|
}
|
|
2914
2819
|
|
|
2915
|
-
|
|
2820
|
+
private async initializeBaseSnapshotBlobs(): Promise<void> {
|
|
2916
2821
|
if (!(this.mc.config.getBoolean("enableOfflineLoad") ?? this.runtimeOptions.enableOfflineLoad) ||
|
|
2917
2822
|
this.attachState !== AttachState.Attached || this.context.pendingLocalState) {
|
|
2918
2823
|
return;
|
|
@@ -2931,6 +2836,10 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2931
2836
|
// to close current batch.
|
|
2932
2837
|
this.flush();
|
|
2933
2838
|
|
|
2839
|
+
if (this._orderSequentiallyCalls !== 0) {
|
|
2840
|
+
throw new UsageError("can't get state during orderSequentially");
|
|
2841
|
+
}
|
|
2842
|
+
|
|
2934
2843
|
const previousPendingState = this.context.pendingLocalState as IPendingRuntimeState | undefined;
|
|
2935
2844
|
if (previousPendingState) {
|
|
2936
2845
|
return {
|
|
@@ -3036,6 +2945,9 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
3036
2945
|
throw new UsageError(`Summary heuristic configuration property "${prop}" cannot be less than 0`);
|
|
3037
2946
|
}
|
|
3038
2947
|
}
|
|
2948
|
+
if (configuration.minIdleTime > configuration.maxIdleTime) {
|
|
2949
|
+
throw new UsageError(`"minIdleTime" [${configuration.minIdleTime}] cannot be greater than "maxIdleTime" [${configuration.maxIdleTime}]`);
|
|
2950
|
+
}
|
|
3039
2951
|
}
|
|
3040
2952
|
}
|
|
3041
2953
|
|
|
@@ -3050,11 +2962,16 @@ const waitForSeq = async (
|
|
|
3050
2962
|
// TODO: remove cast to any when actual event is determined
|
|
3051
2963
|
deltaManager.on("closed" as any, reject);
|
|
3052
2964
|
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
2965
|
+
// If we already reached target sequence number, simply resolve the promise.
|
|
2966
|
+
if (deltaManager.lastSequenceNumber >= targetSeq) {
|
|
2967
|
+
resolve();
|
|
2968
|
+
} else {
|
|
2969
|
+
const handleOp = (message: Pick<ISequencedDocumentMessage, "sequenceNumber">) => {
|
|
2970
|
+
if (message.sequenceNumber >= targetSeq) {
|
|
2971
|
+
resolve();
|
|
2972
|
+
deltaManager.off("op", handleOp);
|
|
2973
|
+
}
|
|
2974
|
+
};
|
|
2975
|
+
deltaManager.on("op", handleOp);
|
|
2976
|
+
}
|
|
3060
2977
|
});
|