@fluidframework/container-runtime 2.0.0-dev.1.4.5.105745 → 2.0.0-dev.2.2.0.111723
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/batchManager.d.ts +11 -6
- package/dist/batchManager.d.ts.map +1 -1
- package/dist/batchManager.js +23 -13
- package/dist/batchManager.js.map +1 -1
- package/dist/containerRuntime.d.ts +74 -20
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +190 -137
- 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 +50 -20
- package/dist/dataStores.js.map +1 -1
- package/dist/garbageCollection.d.ts +36 -19
- package/dist/garbageCollection.d.ts.map +1 -1
- package/dist/garbageCollection.js +207 -121
- package/dist/garbageCollection.js.map +1 -1
- package/dist/gcSweepReadyUsageDetection.d.ts.map +1 -1
- package/dist/gcSweepReadyUsageDetection.js +3 -12
- package/dist/gcSweepReadyUsageDetection.js.map +1 -1
- package/dist/index.d.ts +4 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -5
- package/dist/index.js.map +1 -1
- package/dist/opCompressor.d.ts +18 -0
- package/dist/opCompressor.d.ts.map +1 -0
- package/dist/opCompressor.js +50 -0
- package/dist/opCompressor.js.map +1 -0
- package/dist/opDecompressor.d.ts +20 -0
- package/dist/opDecompressor.d.ts.map +1 -0
- package/dist/opDecompressor.js +72 -0
- package/dist/opDecompressor.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.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/batchManager.d.ts +11 -6
- package/lib/batchManager.d.ts.map +1 -1
- package/lib/batchManager.js +23 -13
- package/lib/batchManager.js.map +1 -1
- package/lib/containerRuntime.d.ts +74 -20
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +189 -136
- 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 -23
- package/lib/dataStores.js.map +1 -1
- package/lib/garbageCollection.d.ts +36 -19
- package/lib/garbageCollection.d.ts.map +1 -1
- package/lib/garbageCollection.js +208 -122
- package/lib/garbageCollection.js.map +1 -1
- package/lib/gcSweepReadyUsageDetection.d.ts.map +1 -1
- package/lib/gcSweepReadyUsageDetection.js +3 -12
- package/lib/gcSweepReadyUsageDetection.js.map +1 -1
- package/lib/index.d.ts +4 -6
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +2 -4
- package/lib/index.js.map +1 -1
- package/lib/opCompressor.d.ts +18 -0
- package/lib/opCompressor.d.ts.map +1 -0
- package/lib/opCompressor.js +46 -0
- package/lib/opCompressor.js.map +1 -0
- package/lib/opDecompressor.d.ts +20 -0
- package/lib/opDecompressor.d.ts.map +1 -0
- package/lib/opDecompressor.js +68 -0
- package/lib/opDecompressor.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.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 +37 -63
- package/prettier.config.cjs +8 -0
- package/src/batchManager.ts +32 -15
- package/src/containerRuntime.ts +260 -156
- package/src/dataStore.ts +13 -1
- package/src/dataStoreContext.ts +100 -76
- package/src/dataStoreContexts.ts +1 -1
- package/src/dataStores.ts +61 -23
- package/src/garbageCollection.ts +257 -126
- package/src/gcSweepReadyUsageDetection.ts +2 -10
- package/src/index.ts +4 -4
- package/src/opCompressor.ts +59 -0
- package/src/opDecompressor.ts +82 -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 +4 -2
- package/src/summaryGenerator.ts +3 -2
- package/src/summaryManager.ts +18 -7
package/src/containerRuntime.ts
CHANGED
|
@@ -147,6 +147,7 @@ import {
|
|
|
147
147
|
ISummarizerInternalsProvider,
|
|
148
148
|
ISummarizerOptions,
|
|
149
149
|
ISummarizerRuntime,
|
|
150
|
+
IRefreshSummaryAckOptions,
|
|
150
151
|
} from "./summarizerTypes";
|
|
151
152
|
import { formExponentialFn, Throttler } from "./throttler";
|
|
152
153
|
import { RunWhileConnectedCoordinator } from "./runWhileConnectedCoordinator";
|
|
@@ -166,6 +167,7 @@ import {
|
|
|
166
167
|
import { BindBatchTracker } from "./batchTracker";
|
|
167
168
|
import { ISerializedBaseSnapshotBlobs, SerializedSnapshotStorage } from "./serializedSnapshotStorage";
|
|
168
169
|
import { ScheduleManager } from "./scheduleManager";
|
|
170
|
+
import { OpDecompressor } from "./opDecompressor";
|
|
169
171
|
|
|
170
172
|
export enum ContainerMessageType {
|
|
171
173
|
// An op to be delivered to store
|
|
@@ -209,6 +211,7 @@ export interface ISummaryBaseConfiguration {
|
|
|
209
211
|
initialSummarizerDelayMs: number;
|
|
210
212
|
|
|
211
213
|
/**
|
|
214
|
+
* @deprecated
|
|
212
215
|
* Flag that will enable changing elected summarizer client after maxOpsSinceLastSummary.
|
|
213
216
|
* This defaults to false (disabled) and must be explicitly set to true to enable.
|
|
214
217
|
*/
|
|
@@ -229,11 +232,6 @@ export interface ISummaryBaseConfiguration {
|
|
|
229
232
|
|
|
230
233
|
export interface ISummaryConfigurationHeuristics extends ISummaryBaseConfiguration {
|
|
231
234
|
state: "enabled";
|
|
232
|
-
/**
|
|
233
|
-
* @deprecated Please move all implementations to {@link ISummaryConfigurationHeuristics.minIdleTime} and
|
|
234
|
-
* {@link ISummaryConfigurationHeuristics.maxIdleTime} instead.
|
|
235
|
-
*/
|
|
236
|
-
idleTime?: number;
|
|
237
235
|
/**
|
|
238
236
|
* Defines the maximum allowed time, since the last received Ack, before running the summary
|
|
239
237
|
* with reason maxTime.
|
|
@@ -279,6 +277,17 @@ export interface ISummaryConfigurationHeuristics extends ISummaryBaseConfigurati
|
|
|
279
277
|
* For example: (multiplier) * (number of non-runtime ops) = weighted number of non-runtime ops
|
|
280
278
|
*/
|
|
281
279
|
nonRuntimeOpWeight: number;
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Number of ops since last summary needed before a non-runtime op can trigger running summary heuristics.
|
|
283
|
+
*
|
|
284
|
+
* Note: Any runtime ops sent before the threshold is reached will trigger heuristics normally.
|
|
285
|
+
* This threshold ONLY applies to non-runtime ops triggering summaries.
|
|
286
|
+
*
|
|
287
|
+
* For example: Say the threshold is 20. Sending 19 non-runtime ops will not trigger any heuristic checks.
|
|
288
|
+
* Sending the 20th non-runtime op will trigger the heuristic checks for summarizing.
|
|
289
|
+
*/
|
|
290
|
+
nonRuntimeHeuristicThreshold?: number;
|
|
282
291
|
}
|
|
283
292
|
|
|
284
293
|
export interface ISummaryConfigurationDisableSummarizer {
|
|
@@ -318,6 +327,8 @@ export const DefaultSummaryConfiguration: ISummaryConfiguration = {
|
|
|
318
327
|
nonRuntimeOpWeight: 0.1,
|
|
319
328
|
|
|
320
329
|
runtimeOpWeight: 1.0,
|
|
330
|
+
|
|
331
|
+
nonRuntimeHeuristicThreshold: 20,
|
|
321
332
|
};
|
|
322
333
|
|
|
323
334
|
export interface IGCRuntimeOptions {
|
|
@@ -416,6 +427,22 @@ export interface ISummaryRuntimeOptions {
|
|
|
416
427
|
summarizerOptions?: Readonly<Partial<ISummarizerOptions>>;
|
|
417
428
|
}
|
|
418
429
|
|
|
430
|
+
/**
|
|
431
|
+
* Options for op compression.
|
|
432
|
+
* @experimental - Not ready for use
|
|
433
|
+
*/
|
|
434
|
+
export interface ICompressionRuntimeOptions {
|
|
435
|
+
/**
|
|
436
|
+
* The minimum size the batch's payload must exceed before the batch's contents will be compressed.
|
|
437
|
+
*/
|
|
438
|
+
readonly minimumBatchSizeInBytes: number;
|
|
439
|
+
|
|
440
|
+
/**
|
|
441
|
+
* The compression algorithm that will be used to compress the op.
|
|
442
|
+
*/
|
|
443
|
+
readonly compressionAlgorithm: CompressionAlgorithms;
|
|
444
|
+
}
|
|
445
|
+
|
|
419
446
|
/**
|
|
420
447
|
* Options for container runtime.
|
|
421
448
|
*/
|
|
@@ -442,6 +469,22 @@ export interface IContainerRuntimeOptions {
|
|
|
442
469
|
* Save enough runtime state to be able to serialize upon request and load to the same state in a new container.
|
|
443
470
|
*/
|
|
444
471
|
readonly enableOfflineLoad?: boolean;
|
|
472
|
+
/**
|
|
473
|
+
* Enables the runtime to compress ops. Compression is disabled when undefined.
|
|
474
|
+
* @experimental Not ready for use.
|
|
475
|
+
*/
|
|
476
|
+
readonly compressionOptions?: ICompressionRuntimeOptions;
|
|
477
|
+
/**
|
|
478
|
+
* If specified, when in FlushMode.TurnBased, if the size of the ops between JS turns exceeds this value,
|
|
479
|
+
* an error will be thrown and the container will close.
|
|
480
|
+
*
|
|
481
|
+
* If unspecified, the limit is 950 * 1024.
|
|
482
|
+
*
|
|
483
|
+
* 'Infinity' will disable any limit.
|
|
484
|
+
*
|
|
485
|
+
* @experimental This config should be driven by the connection with the service and will be moved in the future.
|
|
486
|
+
*/
|
|
487
|
+
readonly maxBatchSizeInBytes?: number;
|
|
445
488
|
}
|
|
446
489
|
|
|
447
490
|
/**
|
|
@@ -467,6 +510,13 @@ export enum RuntimeHeaders {
|
|
|
467
510
|
viaHandle = "viaHandle",
|
|
468
511
|
}
|
|
469
512
|
|
|
513
|
+
/**
|
|
514
|
+
* Available compression algorithms for op compression.
|
|
515
|
+
*/
|
|
516
|
+
export enum CompressionAlgorithms {
|
|
517
|
+
lz4 = "lz4",
|
|
518
|
+
}
|
|
519
|
+
|
|
470
520
|
/**
|
|
471
521
|
* @deprecated
|
|
472
522
|
* Untagged logger is unsupported going forward. There are old loaders with old ContainerContexts that only
|
|
@@ -513,6 +563,12 @@ const maxConsecutiveReconnectsKey = "Fluid.ContainerRuntime.MaxConsecutiveReconn
|
|
|
513
563
|
|
|
514
564
|
const defaultFlushMode = FlushMode.TurnBased;
|
|
515
565
|
|
|
566
|
+
// The actual limit is 1Mb (socket.io and Kafka limits)
|
|
567
|
+
// We can't estimate it fully, as we
|
|
568
|
+
// - do not know what properties relay service will add
|
|
569
|
+
// - we do not stringify final op, thus we do not know how much escaping will be added.
|
|
570
|
+
const defaultMaxBatchSizeInBytes = 950 * 1024;
|
|
571
|
+
|
|
516
572
|
/**
|
|
517
573
|
* @deprecated - use ContainerRuntimeMessage instead
|
|
518
574
|
*/
|
|
@@ -530,16 +586,13 @@ export enum RuntimeMessage {
|
|
|
530
586
|
* @deprecated - please use version in driver-utils
|
|
531
587
|
*/
|
|
532
588
|
export function isRuntimeMessage(message: ISequencedDocumentMessage): boolean {
|
|
533
|
-
|
|
534
|
-
return true;
|
|
535
|
-
}
|
|
536
|
-
return false;
|
|
589
|
+
return (Object.values(RuntimeMessage) as string[]).includes(message.type);
|
|
537
590
|
}
|
|
538
591
|
|
|
539
592
|
/**
|
|
540
593
|
* Unpacks runtime messages
|
|
541
594
|
*
|
|
542
|
-
* @remarks This API makes no promises regarding backward-
|
|
595
|
+
* @remarks This API makes no promises regarding backward-compatibility. This is internal API.
|
|
543
596
|
* @param message - message (as it observed in storage / service)
|
|
544
597
|
* @returns unpacked runtime message
|
|
545
598
|
*
|
|
@@ -553,7 +606,6 @@ export function unpackRuntimeMessage(message: ISequencedDocumentMessage) {
|
|
|
553
606
|
} else {
|
|
554
607
|
// new format
|
|
555
608
|
const innerContents = message.contents as ContainerRuntimeMessage;
|
|
556
|
-
assert(innerContents.type !== undefined, 0x121 /* "Undefined inner contents type!" */);
|
|
557
609
|
message.type = innerContents.type;
|
|
558
610
|
message.contents = innerContents.contents;
|
|
559
611
|
}
|
|
@@ -635,6 +687,9 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
635
687
|
loadSequenceNumberVerification = "close",
|
|
636
688
|
flushMode = defaultFlushMode,
|
|
637
689
|
enableOfflineLoad = false,
|
|
690
|
+
compressionOptions = { minimumBatchSizeInBytes: Number.POSITIVE_INFINITY,
|
|
691
|
+
compressionAlgorithm: CompressionAlgorithms.lz4 },
|
|
692
|
+
maxBatchSizeInBytes = defaultMaxBatchSizeInBytes,
|
|
638
693
|
} = runtimeOptions;
|
|
639
694
|
|
|
640
695
|
const pendingRuntimeState = context.pendingLocalState as IPendingRuntimeState | undefined;
|
|
@@ -710,6 +765,8 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
710
765
|
loadSequenceNumberVerification,
|
|
711
766
|
flushMode,
|
|
712
767
|
enableOfflineLoad,
|
|
768
|
+
compressionOptions,
|
|
769
|
+
maxBatchSizeInBytes,
|
|
713
770
|
},
|
|
714
771
|
containerScope,
|
|
715
772
|
logger,
|
|
@@ -725,7 +782,8 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
725
782
|
pendingRuntimeState.savedOps = [];
|
|
726
783
|
}
|
|
727
784
|
|
|
728
|
-
|
|
785
|
+
// Initialize the base state of the runtime before it's returned.
|
|
786
|
+
await runtime.initializeBaseState();
|
|
729
787
|
|
|
730
788
|
return runtime;
|
|
731
789
|
}
|
|
@@ -787,6 +845,9 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
787
845
|
|
|
788
846
|
// internal logger for ContainerRuntime. Use this.logger for stores, summaries, etc.
|
|
789
847
|
private readonly mc: MonitoringContext;
|
|
848
|
+
|
|
849
|
+
private readonly opDecompressor: OpDecompressor = new OpDecompressor();
|
|
850
|
+
|
|
790
851
|
private readonly summarizerClientElection?: SummarizerClientElection;
|
|
791
852
|
/**
|
|
792
853
|
* summaryManager will only be created if this client is permitted to spawn a summarizing client
|
|
@@ -802,8 +863,8 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
802
863
|
private readonly defaultMaxConsecutiveReconnects = 7;
|
|
803
864
|
|
|
804
865
|
private _orderSequentiallyCalls: number = 0;
|
|
805
|
-
private _flushMode: FlushMode;
|
|
806
|
-
private
|
|
866
|
+
private readonly _flushMode: FlushMode;
|
|
867
|
+
private flushMicroTaskExists = false;
|
|
807
868
|
|
|
808
869
|
private _connected: boolean;
|
|
809
870
|
|
|
@@ -851,13 +912,8 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
851
912
|
private readonly scheduleManager: ScheduleManager;
|
|
852
913
|
private readonly blobManager: BlobManager;
|
|
853
914
|
private readonly pendingStateManager: PendingStateManager;
|
|
854
|
-
|
|
855
|
-
|
|
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();
|
|
915
|
+
private readonly pendingAttachBatch: BatchManager;
|
|
916
|
+
private readonly pendingBatch: BatchManager;
|
|
861
917
|
|
|
862
918
|
private readonly garbageCollector: IGarbageCollector;
|
|
863
919
|
|
|
@@ -965,6 +1021,27 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
965
1021
|
},
|
|
966
1022
|
) {
|
|
967
1023
|
super();
|
|
1024
|
+
|
|
1025
|
+
let loadSummaryNumber: number;
|
|
1026
|
+
// Get the container creation metadata. For new container, we initialize these. For existing containers,
|
|
1027
|
+
// get the values from the metadata blob.
|
|
1028
|
+
if (existing) {
|
|
1029
|
+
this.createContainerMetadata = {
|
|
1030
|
+
createContainerRuntimeVersion: metadata?.createContainerRuntimeVersion,
|
|
1031
|
+
createContainerTimestamp: metadata?.createContainerTimestamp,
|
|
1032
|
+
};
|
|
1033
|
+
// summaryNumber was renamed from summaryCount. For older docs that haven't been opened for a long time,
|
|
1034
|
+
// the count is reset to 0.
|
|
1035
|
+
loadSummaryNumber = metadata?.summaryNumber ?? 0;
|
|
1036
|
+
} else {
|
|
1037
|
+
this.createContainerMetadata = {
|
|
1038
|
+
createContainerRuntimeVersion: pkgVersion,
|
|
1039
|
+
createContainerTimestamp: Date.now(),
|
|
1040
|
+
};
|
|
1041
|
+
loadSummaryNumber = 0;
|
|
1042
|
+
}
|
|
1043
|
+
this.nextSummaryNumber = loadSummaryNumber + 1;
|
|
1044
|
+
|
|
968
1045
|
this.messageAtLastSummary = metadata?.message;
|
|
969
1046
|
|
|
970
1047
|
this._connected = this.context.connected;
|
|
@@ -990,9 +1067,32 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
990
1067
|
|
|
991
1068
|
this._flushMode = runtimeOptions.flushMode;
|
|
992
1069
|
|
|
1070
|
+
// Provide lower soft limit - we want to have some number of ops to get efficiency in compression
|
|
1071
|
+
// & bandwidth usage, but at the same time we want to send these ops sooner, to reduce overall
|
|
1072
|
+
// latency of processing a batch.
|
|
1073
|
+
// So there is some ballance here, that depends on compression algorithm and its efficiency working with smaller
|
|
1074
|
+
// payloads. That number represents final (compressed) bits (once compression is implemented).
|
|
1075
|
+
this.pendingAttachBatch = new BatchManager(this.mc.logger, {
|
|
1076
|
+
hardLimit: runtimeOptions.maxBatchSizeInBytes,
|
|
1077
|
+
softLimit: 64 * 1024,
|
|
1078
|
+
compressionOptions: runtimeOptions.compressionOptions
|
|
1079
|
+
});
|
|
1080
|
+
this.pendingBatch = new BatchManager(this.mc.logger, {
|
|
1081
|
+
hardLimit: runtimeOptions.maxBatchSizeInBytes,
|
|
1082
|
+
compressionOptions: runtimeOptions.compressionOptions
|
|
1083
|
+
});
|
|
1084
|
+
|
|
993
1085
|
const pendingRuntimeState = context.pendingLocalState as IPendingRuntimeState | undefined;
|
|
994
1086
|
const baseSnapshot: ISnapshotTree | undefined = pendingRuntimeState?.baseSnapshot ?? context.baseSnapshot;
|
|
995
1087
|
|
|
1088
|
+
const maxSnapshotCacheDurationMs = this._storage?.policies?.maximumCacheDurationMs;
|
|
1089
|
+
if (maxSnapshotCacheDurationMs !== undefined && maxSnapshotCacheDurationMs > 5 * 24 * 60 * 60 * 1000) {
|
|
1090
|
+
// This is a runtime enforcement of what's already explicit in the policy's type itself,
|
|
1091
|
+
// which dictates the value is either undefined or exactly 5 days in ms.
|
|
1092
|
+
// As long as the actual value is less than 5 days, the assumptions GC makes here are valid.
|
|
1093
|
+
throw new UsageError("Driver's maximumCacheDurationMs policy cannot exceed 5 days");
|
|
1094
|
+
}
|
|
1095
|
+
|
|
996
1096
|
this.garbageCollector = GarbageCollector.create({
|
|
997
1097
|
runtime: this,
|
|
998
1098
|
gcOptions: this.runtimeOptions.gcOptions,
|
|
@@ -1000,6 +1100,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1000
1100
|
baseLogger: this.mc.logger,
|
|
1001
1101
|
existing,
|
|
1002
1102
|
metadata,
|
|
1103
|
+
createContainerMetadata: this.createContainerMetadata,
|
|
1003
1104
|
isSummarizerClient: this.context.clientDetails.type === summarizerClientType,
|
|
1004
1105
|
getNodePackagePath: async (nodePath: string) => this.getGCNodePackagePath(nodePath),
|
|
1005
1106
|
getLastSummaryTimestampMs: () => this.messageAtLastSummary?.timestamp,
|
|
@@ -1031,7 +1132,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1031
1132
|
);
|
|
1032
1133
|
|
|
1033
1134
|
if (baseSnapshot) {
|
|
1034
|
-
this.summarizerNode.
|
|
1135
|
+
this.summarizerNode.updateBaseSummaryState(baseSnapshot);
|
|
1035
1136
|
}
|
|
1036
1137
|
|
|
1037
1138
|
this.dataStores = new DataStores(
|
|
@@ -1060,7 +1161,6 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1060
1161
|
packagePath,
|
|
1061
1162
|
),
|
|
1062
1163
|
new Map<string, string>(dataStoreAliasMap),
|
|
1063
|
-
this.garbageCollector.writeDataAtRoot,
|
|
1064
1164
|
);
|
|
1065
1165
|
|
|
1066
1166
|
this.blobManager = new BlobManager(
|
|
@@ -1093,11 +1193,10 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1093
1193
|
close: this.closeFn,
|
|
1094
1194
|
connected: () => this.connected,
|
|
1095
1195
|
flush: this.flush.bind(this),
|
|
1096
|
-
flushMode: () => this.flushMode,
|
|
1097
1196
|
reSubmit: this.reSubmit.bind(this),
|
|
1098
|
-
|
|
1197
|
+
rollback: this.rollback.bind(this),
|
|
1198
|
+
orderSequentially: this.orderSequentially.bind(this),
|
|
1099
1199
|
},
|
|
1100
|
-
this._flushMode,
|
|
1101
1200
|
pendingRuntimeState?.pending);
|
|
1102
1201
|
|
|
1103
1202
|
this.context.quorum.on("removeMember", (clientId: string) => {
|
|
@@ -1150,7 +1249,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1150
1249
|
// if summaries are enabled and we are not the summarizer client.
|
|
1151
1250
|
const defaultAction = () => {
|
|
1152
1251
|
if (this.summaryCollection.opsSinceLastAck > this.maxOpsSinceLastSummary) {
|
|
1153
|
-
this.logger.
|
|
1252
|
+
this.logger.sendTelemetryEvent({ eventName: "SummaryStatus:Behind" });
|
|
1154
1253
|
// unregister default to no log on every op after falling behind
|
|
1155
1254
|
// and register summary ack handler to re-register this handler
|
|
1156
1255
|
// after successful summary
|
|
@@ -1217,26 +1316,6 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1217
1316
|
...getDeviceSpec(),
|
|
1218
1317
|
});
|
|
1219
1318
|
|
|
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
1319
|
this.logger.sendTelemetryEvent({
|
|
1241
1320
|
eventName: "ContainerLoadStats",
|
|
1242
1321
|
...this.createContainerMetadata,
|
|
@@ -1251,6 +1330,14 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1251
1330
|
BindBatchTracker(this, this.logger);
|
|
1252
1331
|
}
|
|
1253
1332
|
|
|
1333
|
+
/**
|
|
1334
|
+
* Initializes the state from the base snapshot this container runtime loaded from.
|
|
1335
|
+
*/
|
|
1336
|
+
private async initializeBaseState(): Promise<void> {
|
|
1337
|
+
await this.initializeBaseSnapshotBlobs();
|
|
1338
|
+
await this.garbageCollector.initializeBaseState();
|
|
1339
|
+
}
|
|
1340
|
+
|
|
1254
1341
|
public dispose(error?: Error): void {
|
|
1255
1342
|
if (this._disposed) {
|
|
1256
1343
|
return;
|
|
@@ -1359,10 +1446,13 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1359
1446
|
const wait = typeof request.headers?.[RuntimeHeaders.wait] === "boolean"
|
|
1360
1447
|
? request.headers?.[RuntimeHeaders.wait]
|
|
1361
1448
|
: true;
|
|
1449
|
+
const viaHandle = typeof request.headers?.[RuntimeHeaders.viaHandle] === "boolean"
|
|
1450
|
+
? request.headers?.[RuntimeHeaders.viaHandle]
|
|
1451
|
+
: false;
|
|
1362
1452
|
|
|
1363
1453
|
await this.dataStores.waitIfPendingAlias(id);
|
|
1364
1454
|
const internalId = this.internalId(id);
|
|
1365
|
-
const dataStoreContext = await this.dataStores.getDataStore(internalId, wait);
|
|
1455
|
+
const dataStoreContext = await this.dataStores.getDataStore(internalId, wait, viaHandle);
|
|
1366
1456
|
|
|
1367
1457
|
/**
|
|
1368
1458
|
* If GC should run and this an external app request with "externalRequest" header, we need to return
|
|
@@ -1441,11 +1531,9 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1441
1531
|
addTreeToSummary(summaryTree, blobsTreeName, blobManagerSummary);
|
|
1442
1532
|
}
|
|
1443
1533
|
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
addSummarizeResultToSummary(summaryTree, gcTreeKey, gcSummary);
|
|
1448
|
-
}
|
|
1534
|
+
const gcSummary = this.garbageCollector.summarize(fullTree, trackState, telemetryContext);
|
|
1535
|
+
if (gcSummary !== undefined) {
|
|
1536
|
+
addSummarizeResultToSummary(summaryTree, gcTreeKey, gcSummary);
|
|
1449
1537
|
}
|
|
1450
1538
|
}
|
|
1451
1539
|
|
|
@@ -1630,6 +1718,8 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1630
1718
|
message.contents = JSON.parse(message.contents);
|
|
1631
1719
|
}
|
|
1632
1720
|
|
|
1721
|
+
message = this.opDecompressor.processMessage(message);
|
|
1722
|
+
|
|
1633
1723
|
// Caveat: This will return false for runtime message in very old format, that are used in snapshot tests
|
|
1634
1724
|
// This format was not shipped to production workflows.
|
|
1635
1725
|
const runtimeMessage = unpackRuntimeMessage(message);
|
|
@@ -1767,34 +1857,16 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1767
1857
|
private async getRootDataStoreChannel(id: string, wait = true): Promise<IFluidDataStoreChannel> {
|
|
1768
1858
|
await this.dataStores.waitIfPendingAlias(id);
|
|
1769
1859
|
const internalId = this.internalId(id);
|
|
1770
|
-
const context = await this.dataStores.getDataStore(internalId, wait);
|
|
1860
|
+
const context = await this.dataStores.getDataStore(internalId, wait, false /* viaHandle */);
|
|
1771
1861
|
assert(await context.isRoot(), 0x12b /* "did not get root data store" */);
|
|
1772
1862
|
return context.realize();
|
|
1773
1863
|
}
|
|
1774
1864
|
|
|
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 {
|
|
1865
|
+
/**
|
|
1866
|
+
* Flush the pending ops manually.
|
|
1867
|
+
* This method is expected to be called at the end of a batch.
|
|
1868
|
+
*/
|
|
1869
|
+
private flush(): void {
|
|
1798
1870
|
assert(this._orderSequentiallyCalls === 0,
|
|
1799
1871
|
0x24c /* "Cannot call `flush()` from `orderSequentially`'s callback" */);
|
|
1800
1872
|
|
|
@@ -1829,15 +1901,22 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1829
1901
|
if (this.canSendOps()) {
|
|
1830
1902
|
if (this.context.submitBatchFn !== undefined) {
|
|
1831
1903
|
const batchToSend: IBatchMessage[] = [];
|
|
1904
|
+
|
|
1832
1905
|
for (const message of batch) {
|
|
1833
1906
|
batchToSend.push({ contents: message.contents, metadata: message.metadata });
|
|
1834
1907
|
}
|
|
1908
|
+
|
|
1835
1909
|
// returns clientSequenceNumber of last message in a batch
|
|
1836
1910
|
clientSequenceNumber = this.context.submitBatchFn(batchToSend);
|
|
1837
1911
|
} else {
|
|
1838
1912
|
// Legacy path - supporting old loader versions. Can be removed only when LTS moves above
|
|
1839
1913
|
// version that has support for batches (submitBatchFn)
|
|
1840
1914
|
for (const message of batch) {
|
|
1915
|
+
// Legacy path doesn't support compressed payloads and will submit uncompressed payload anyways
|
|
1916
|
+
if (message.metadata?.compressed) {
|
|
1917
|
+
delete message.metadata.compressed;
|
|
1918
|
+
}
|
|
1919
|
+
|
|
1841
1920
|
clientSequenceNumber = this.context.submitFn(
|
|
1842
1921
|
MessageType.Operation,
|
|
1843
1922
|
message.deserializedContent,
|
|
@@ -1871,36 +1950,14 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1871
1950
|
}
|
|
1872
1951
|
|
|
1873
1952
|
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
1953
|
let checkpoint: { rollback: (action: (message: BatchMessage) => void) => void; } | undefined;
|
|
1954
|
+
|
|
1897
1955
|
if (this.mc.config.getBoolean("Fluid.ContainerRuntime.EnableRollback")) {
|
|
1898
1956
|
// Note: we are not touching this.pendingAttachBatch here, for two reasons:
|
|
1899
1957
|
// 1. It would not help, as we flush attach ops as they become available.
|
|
1900
1958
|
// 2. There is no way to undo process of data store creation.
|
|
1901
1959
|
checkpoint = this.pendingBatch.checkpoint();
|
|
1902
1960
|
}
|
|
1903
|
-
|
|
1904
1961
|
try {
|
|
1905
1962
|
this._orderSequentiallyCalls++;
|
|
1906
1963
|
callback();
|
|
@@ -1931,6 +1988,10 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1931
1988
|
} finally {
|
|
1932
1989
|
this._orderSequentiallyCalls--;
|
|
1933
1990
|
}
|
|
1991
|
+
|
|
1992
|
+
if (this.flushMode === FlushMode.Immediate && this._orderSequentiallyCalls === 0) {
|
|
1993
|
+
this.flush();
|
|
1994
|
+
}
|
|
1934
1995
|
}
|
|
1935
1996
|
|
|
1936
1997
|
public async createDataStore(pkg: string | string[]): Promise<IDataStore> {
|
|
@@ -1980,6 +2041,13 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1980
2041
|
return this.connected && !this.deltaManager.readOnlyInfo.readonly;
|
|
1981
2042
|
}
|
|
1982
2043
|
|
|
2044
|
+
/**
|
|
2045
|
+
* Are we in the middle of batching ops together?
|
|
2046
|
+
*/
|
|
2047
|
+
private currentlyBatching() {
|
|
2048
|
+
return this.flushMode === FlushMode.TurnBased || this._orderSequentiallyCalls !== 0;
|
|
2049
|
+
}
|
|
2050
|
+
|
|
1983
2051
|
public getQuorum(): IQuorumClients {
|
|
1984
2052
|
return this.context.quorum;
|
|
1985
2053
|
}
|
|
@@ -2192,10 +2260,8 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2192
2260
|
* Implementation of IGarbageCollectionRuntime::updateUsedRoutes.
|
|
2193
2261
|
* After GC has run, called to notify this container's nodes of routes that are used in it.
|
|
2194
2262
|
* @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
2263
|
*/
|
|
2198
|
-
public updateUsedRoutes(usedRoutes: string[]
|
|
2264
|
+
public updateUsedRoutes(usedRoutes: string[]) {
|
|
2199
2265
|
// Update our summarizer node's used routes. Updating used routes in summarizer node before
|
|
2200
2266
|
// summarizing is required and asserted by the the summarizer node. We are the root and are
|
|
2201
2267
|
// always referenced, so the used routes is only self-route (empty string).
|
|
@@ -2208,15 +2274,17 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2208
2274
|
}
|
|
2209
2275
|
}
|
|
2210
2276
|
|
|
2211
|
-
return this.dataStores.updateUsedRoutes(dataStoreUsedRoutes
|
|
2277
|
+
return this.dataStores.updateUsedRoutes(dataStoreUsedRoutes);
|
|
2212
2278
|
}
|
|
2213
2279
|
|
|
2214
2280
|
/**
|
|
2215
|
-
*
|
|
2216
|
-
*
|
|
2281
|
+
* This is called to update objects whose routes are unused. The unused objects are either deleted or marked as
|
|
2282
|
+
* tombstones.
|
|
2217
2283
|
* @param unusedRoutes - The routes that are unused in all data stores in this Container.
|
|
2284
|
+
* @param tombstone - if true, the objects corresponding to unused routes are marked tombstones. Otherwise, they
|
|
2285
|
+
* are deleted.
|
|
2218
2286
|
*/
|
|
2219
|
-
public
|
|
2287
|
+
public updateUnusedRoutes(unusedRoutes: string[], tombstone: boolean) {
|
|
2220
2288
|
const blobManagerUnusedRoutes: string[] = [];
|
|
2221
2289
|
const dataStoreUnusedRoutes: string[] = [];
|
|
2222
2290
|
for (const route of unusedRoutes) {
|
|
@@ -2227,8 +2295,11 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2227
2295
|
}
|
|
2228
2296
|
}
|
|
2229
2297
|
|
|
2230
|
-
|
|
2231
|
-
|
|
2298
|
+
// Todo: Add tombstone for attachment blobs. For now, we ignore attachment blobs that should be tombstoned.
|
|
2299
|
+
if (!tombstone) {
|
|
2300
|
+
this.blobManager.deleteUnusedRoutes(blobManagerUnusedRoutes);
|
|
2301
|
+
}
|
|
2302
|
+
this.dataStores.updateUnusedRoutes(dataStoreUnusedRoutes, tombstone);
|
|
2232
2303
|
}
|
|
2233
2304
|
|
|
2234
2305
|
/**
|
|
@@ -2335,20 +2406,9 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2335
2406
|
const latestSnapshotRefSeq = latestSnapshotInfo.latestSnapshotRefSeq;
|
|
2336
2407
|
latestSnapshotVersionId = latestSnapshotInfo.latestSnapshotVersionId;
|
|
2337
2408
|
|
|
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
|
-
}
|
|
2409
|
+
// We might need to catch up to the latest summary's reference sequence number before pausing.
|
|
2410
|
+
await this.waitForDeltaManagerToCatchup(latestSnapshotRefSeq,
|
|
2411
|
+
summaryNumberLogger);
|
|
2352
2412
|
}
|
|
2353
2413
|
|
|
2354
2414
|
try {
|
|
@@ -2680,8 +2740,8 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2680
2740
|
// issue than sending.
|
|
2681
2741
|
// Please note that this does not change file format, so it can be disabled in the future if this
|
|
2682
2742
|
// optimization no longer makes sense (for example, batch compression may make it less appealing).
|
|
2683
|
-
if (type === ContainerMessageType.Attach &&
|
|
2684
|
-
this.mc.config.getBoolean("Fluid.ContainerRuntime.
|
|
2743
|
+
if (this.currentlyBatching() && type === ContainerMessageType.Attach &&
|
|
2744
|
+
this.mc.config.getBoolean("Fluid.ContainerRuntime.enableAttachOpReorder") === true) {
|
|
2685
2745
|
if (!this.pendingAttachBatch.push(message)) {
|
|
2686
2746
|
// BatchManager has two limits - soft limit & hard limit. Soft limit is only engaged
|
|
2687
2747
|
// when queue is not empty.
|
|
@@ -2692,9 +2752,9 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2692
2752
|
"BatchTooLarge",
|
|
2693
2753
|
/* error */ undefined,
|
|
2694
2754
|
{
|
|
2695
|
-
opSize: message.contents
|
|
2755
|
+
opSize: (message.contents?.length) ?? 0,
|
|
2696
2756
|
count: this.pendingAttachBatch.length,
|
|
2697
|
-
limit: this.pendingAttachBatch.
|
|
2757
|
+
limit: this.pendingAttachBatch.options.hardLimit,
|
|
2698
2758
|
});
|
|
2699
2759
|
}
|
|
2700
2760
|
}
|
|
@@ -2704,23 +2764,22 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2704
2764
|
"BatchTooLarge",
|
|
2705
2765
|
/* error */ undefined,
|
|
2706
2766
|
{
|
|
2707
|
-
opSize: message.contents
|
|
2767
|
+
opSize: (message.contents?.length) ?? 0,
|
|
2708
2768
|
count: this.pendingBatch.length,
|
|
2709
|
-
limit: this.pendingBatch.
|
|
2769
|
+
limit: this.pendingBatch.options.hardLimit,
|
|
2710
2770
|
});
|
|
2711
2771
|
}
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
if (this._flushMode !== FlushMode.TurnBased) {
|
|
2715
|
-
this.flush();
|
|
2716
|
-
} else if (!this.flushTrigger) {
|
|
2717
|
-
this.flushTrigger = true;
|
|
2718
|
-
// Queue a microtask to detect the end of the turn and force a flush.
|
|
2719
|
-
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
2720
|
-
Promise.resolve().then(() => {
|
|
2721
|
-
this.flushTrigger = false;
|
|
2772
|
+
if (!this.currentlyBatching()) {
|
|
2722
2773
|
this.flush();
|
|
2723
|
-
})
|
|
2774
|
+
} else if (!this.flushMicroTaskExists) {
|
|
2775
|
+
this.flushMicroTaskExists = true;
|
|
2776
|
+
// Queue a microtask to detect the end of the turn and force a flush.
|
|
2777
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
2778
|
+
Promise.resolve().then(() => {
|
|
2779
|
+
this.flushMicroTaskExists = false;
|
|
2780
|
+
this.flush();
|
|
2781
|
+
});
|
|
2782
|
+
}
|
|
2724
2783
|
}
|
|
2725
2784
|
} catch (error) {
|
|
2726
2785
|
this.closeFn(error as GenericError);
|
|
@@ -2809,28 +2868,61 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2809
2868
|
}
|
|
2810
2869
|
}
|
|
2811
2870
|
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
proposalHandle: string | undefined,
|
|
2815
|
-
ackHandle: string,
|
|
2816
|
-
summaryRefSeq: number,
|
|
2871
|
+
private async waitForDeltaManagerToCatchup(
|
|
2872
|
+
latestSnapshotRefSeq: number,
|
|
2817
2873
|
summaryLogger: ITelemetryLogger,
|
|
2818
|
-
) {
|
|
2874
|
+
): Promise<void> {
|
|
2875
|
+
if (latestSnapshotRefSeq > this.deltaManager.lastSequenceNumber) {
|
|
2876
|
+
// We need to catch up to the latest summary's reference sequence number before proceeding.
|
|
2877
|
+
await PerformanceEvent.timedExecAsync(
|
|
2878
|
+
summaryLogger,
|
|
2879
|
+
{
|
|
2880
|
+
eventName: "WaitingForSeq",
|
|
2881
|
+
lastSequenceNumber: this.deltaManager.lastSequenceNumber,
|
|
2882
|
+
targetSequenceNumber: latestSnapshotRefSeq,
|
|
2883
|
+
lastKnownSeqNumber: this.deltaManager.lastKnownSeqNumber,
|
|
2884
|
+
},
|
|
2885
|
+
async () => waitForSeq(this.deltaManager, latestSnapshotRefSeq),
|
|
2886
|
+
{ start: true, end: true, cancel: "error" }, // definitely want start event
|
|
2887
|
+
);
|
|
2888
|
+
}
|
|
2889
|
+
}
|
|
2890
|
+
|
|
2891
|
+
/** Implementation of ISummarizerInternalsProvider.refreshLatestSummaryAck */
|
|
2892
|
+
public async refreshLatestSummaryAck(options: IRefreshSummaryAckOptions) {
|
|
2893
|
+
const { proposalHandle, ackHandle, summaryRefSeq, summaryLogger } = options;
|
|
2819
2894
|
const readAndParseBlob = async <T>(id: string) => readAndParse<T>(this.storage, id);
|
|
2820
2895
|
// The call to fetch the snapshot is very expensive and not always needed.
|
|
2821
2896
|
// It should only be done by the summarizerNode, if required.
|
|
2897
|
+
// When fetching from storage we will always get the latest version and do not use the ackHandle.
|
|
2822
2898
|
const snapshotTreeFetcher = async () => {
|
|
2823
2899
|
const fetchResult = await this.fetchSnapshotFromStorage(
|
|
2824
|
-
|
|
2900
|
+
null,
|
|
2825
2901
|
summaryLogger,
|
|
2826
2902
|
{
|
|
2827
2903
|
eventName: "RefreshLatestSummaryGetSnapshot",
|
|
2828
2904
|
ackHandle,
|
|
2829
2905
|
summaryRefSeq,
|
|
2830
|
-
fetchLatest:
|
|
2906
|
+
fetchLatest: true,
|
|
2831
2907
|
});
|
|
2908
|
+
|
|
2909
|
+
const latestSnapshotRefSeq = await seqFromTree(fetchResult.snapshotTree, readAndParseBlob);
|
|
2910
|
+
summaryLogger.sendTelemetryEvent(
|
|
2911
|
+
{
|
|
2912
|
+
eventName: "LatestSummaryRetrieved",
|
|
2913
|
+
ackHandle,
|
|
2914
|
+
lastSequenceNumber: latestSnapshotRefSeq,
|
|
2915
|
+
targetSequenceNumber: summaryRefSeq,
|
|
2916
|
+
});
|
|
2917
|
+
|
|
2918
|
+
// In case we had to retrieve the latest snapshot and it is different than summaryRefSeq,
|
|
2919
|
+
// wait for the delta manager to catch up before refreshing the latest Summary.
|
|
2920
|
+
await this.waitForDeltaManagerToCatchup(latestSnapshotRefSeq,
|
|
2921
|
+
summaryLogger);
|
|
2922
|
+
|
|
2832
2923
|
return fetchResult.snapshotTree;
|
|
2833
2924
|
};
|
|
2925
|
+
|
|
2834
2926
|
const result = await this.summarizerNode.refreshLatestSummary(
|
|
2835
2927
|
proposalHandle,
|
|
2836
2928
|
summaryRefSeq,
|
|
@@ -2912,7 +3004,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2912
3004
|
}
|
|
2913
3005
|
}
|
|
2914
3006
|
|
|
2915
|
-
|
|
3007
|
+
private async initializeBaseSnapshotBlobs(): Promise<void> {
|
|
2916
3008
|
if (!(this.mc.config.getBoolean("enableOfflineLoad") ?? this.runtimeOptions.enableOfflineLoad) ||
|
|
2917
3009
|
this.attachState !== AttachState.Attached || this.context.pendingLocalState) {
|
|
2918
3010
|
return;
|
|
@@ -2931,6 +3023,10 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2931
3023
|
// to close current batch.
|
|
2932
3024
|
this.flush();
|
|
2933
3025
|
|
|
3026
|
+
if (this._orderSequentiallyCalls !== 0) {
|
|
3027
|
+
throw new UsageError("can't get state during orderSequentially");
|
|
3028
|
+
}
|
|
3029
|
+
|
|
2934
3030
|
const previousPendingState = this.context.pendingLocalState as IPendingRuntimeState | undefined;
|
|
2935
3031
|
if (previousPendingState) {
|
|
2936
3032
|
return {
|
|
@@ -3036,6 +3132,9 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
3036
3132
|
throw new UsageError(`Summary heuristic configuration property "${prop}" cannot be less than 0`);
|
|
3037
3133
|
}
|
|
3038
3134
|
}
|
|
3135
|
+
if (configuration.minIdleTime > configuration.maxIdleTime) {
|
|
3136
|
+
throw new UsageError(`"minIdleTime" [${configuration.minIdleTime}] cannot be greater than "maxIdleTime" [${configuration.maxIdleTime}]`);
|
|
3137
|
+
}
|
|
3039
3138
|
}
|
|
3040
3139
|
}
|
|
3041
3140
|
|
|
@@ -3050,11 +3149,16 @@ const waitForSeq = async (
|
|
|
3050
3149
|
// TODO: remove cast to any when actual event is determined
|
|
3051
3150
|
deltaManager.on("closed" as any, reject);
|
|
3052
3151
|
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3152
|
+
// If we already reached target sequence number, simply resolve the promise.
|
|
3153
|
+
if (deltaManager.lastSequenceNumber >= targetSeq) {
|
|
3154
|
+
resolve();
|
|
3155
|
+
} else {
|
|
3156
|
+
const handleOp = (message: Pick<ISequencedDocumentMessage, "sequenceNumber">) => {
|
|
3157
|
+
if (message.sequenceNumber >= targetSeq) {
|
|
3158
|
+
resolve();
|
|
3159
|
+
deltaManager.off("op", handleOp);
|
|
3160
|
+
}
|
|
3161
|
+
};
|
|
3162
|
+
deltaManager.on("op", handleOp);
|
|
3163
|
+
}
|
|
3060
3164
|
});
|