@fluidframework/container-runtime 2.1.0-276326 → 2.1.0-281041
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/README.md +74 -21
- package/api-extractor/api-extractor.current.json +5 -0
- package/api-extractor/api-extractor.legacy.json +1 -1
- package/api-extractor.json +1 -1
- package/api-report/container-runtime.legacy.public.api.md +9 -0
- package/container-runtime.test-files.tar +0 -0
- package/dist/{blobManager.d.ts → blobManager/blobManager.d.ts} +19 -29
- package/dist/blobManager/blobManager.d.ts.map +1 -0
- package/dist/{blobManager.js → blobManager/blobManager.js} +42 -83
- package/dist/blobManager/blobManager.js.map +1 -0
- package/dist/blobManager/blobManagerSnapSum.d.ts +30 -0
- package/dist/blobManager/blobManagerSnapSum.d.ts.map +1 -0
- package/dist/blobManager/blobManagerSnapSum.js +82 -0
- package/dist/blobManager/blobManagerSnapSum.js.map +1 -0
- package/dist/blobManager/index.d.ts +7 -0
- package/dist/blobManager/index.d.ts.map +1 -0
- package/dist/blobManager/index.js +16 -0
- package/dist/blobManager/index.js.map +1 -0
- package/dist/channelCollection.d.ts +1 -1
- package/dist/channelCollection.d.ts.map +1 -1
- package/dist/channelCollection.js +40 -8
- package/dist/channelCollection.js.map +1 -1
- package/dist/containerRuntime.d.ts +15 -10
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +199 -162
- package/dist/containerRuntime.js.map +1 -1
- package/dist/dataStoreContext.d.ts +5 -0
- package/dist/dataStoreContext.d.ts.map +1 -1
- package/dist/dataStoreContext.js +16 -5
- package/dist/dataStoreContext.js.map +1 -1
- package/dist/gc/garbageCollection.d.ts +1 -1
- package/dist/gc/garbageCollection.d.ts.map +1 -1
- package/dist/gc/garbageCollection.js +16 -10
- package/dist/gc/garbageCollection.js.map +1 -1
- package/dist/gc/gcDefinitions.d.ts +4 -2
- package/dist/gc/gcDefinitions.d.ts.map +1 -1
- package/dist/gc/gcDefinitions.js.map +1 -1
- package/dist/gc/gcHelpers.d.ts.map +1 -1
- package/dist/gc/gcHelpers.js +12 -0
- package/dist/gc/gcHelpers.js.map +1 -1
- package/dist/gc/gcTelemetry.d.ts +3 -2
- package/dist/gc/gcTelemetry.d.ts.map +1 -1
- package/dist/gc/gcTelemetry.js +6 -6
- package/dist/gc/gcTelemetry.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/legacy.d.ts +1 -1
- package/dist/metadata.d.ts +7 -1
- package/dist/metadata.d.ts.map +1 -1
- package/dist/metadata.js +6 -0
- package/dist/metadata.js.map +1 -1
- package/dist/opLifecycle/batchManager.d.ts +8 -1
- package/dist/opLifecycle/batchManager.d.ts.map +1 -1
- package/dist/opLifecycle/batchManager.js +37 -16
- package/dist/opLifecycle/batchManager.js.map +1 -1
- package/dist/opLifecycle/definitions.d.ts +1 -1
- package/dist/opLifecycle/definitions.d.ts.map +1 -1
- package/dist/opLifecycle/definitions.js.map +1 -1
- package/dist/opLifecycle/index.d.ts +1 -1
- package/dist/opLifecycle/index.d.ts.map +1 -1
- package/dist/opLifecycle/index.js +2 -1
- package/dist/opLifecycle/index.js.map +1 -1
- package/dist/opLifecycle/opCompressor.d.ts.map +1 -1
- package/dist/opLifecycle/opCompressor.js +12 -8
- package/dist/opLifecycle/opCompressor.js.map +1 -1
- package/dist/opLifecycle/opGroupingManager.d.ts.map +1 -1
- package/dist/opLifecycle/opGroupingManager.js +14 -11
- package/dist/opLifecycle/opGroupingManager.js.map +1 -1
- package/dist/opLifecycle/opSplitter.d.ts.map +1 -1
- package/dist/opLifecycle/opSplitter.js +11 -6
- package/dist/opLifecycle/opSplitter.js.map +1 -1
- package/dist/opLifecycle/outbox.d.ts +22 -6
- package/dist/opLifecycle/outbox.d.ts.map +1 -1
- package/dist/opLifecycle/outbox.js +43 -21
- package/dist/opLifecycle/outbox.js.map +1 -1
- package/dist/opLifecycle/remoteMessageProcessor.d.ts +22 -6
- package/dist/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
- package/dist/opLifecycle/remoteMessageProcessor.js +59 -9
- package/dist/opLifecycle/remoteMessageProcessor.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/pendingStateManager.d.ts +39 -13
- package/dist/pendingStateManager.d.ts.map +1 -1
- package/dist/pendingStateManager.js +98 -33
- package/dist/pendingStateManager.js.map +1 -1
- package/dist/public.d.ts +1 -1
- package/dist/scheduleManager.js +4 -0
- package/dist/scheduleManager.js.map +1 -1
- package/dist/summary/index.d.ts +1 -1
- package/dist/summary/index.d.ts.map +1 -1
- package/dist/summary/index.js +1 -2
- package/dist/summary/index.js.map +1 -1
- package/dist/summary/summarizerNode/summarizerNodeUtils.d.ts.map +1 -1
- package/dist/summary/summarizerNode/summarizerNodeUtils.js +2 -0
- package/dist/summary/summarizerNode/summarizerNodeUtils.js.map +1 -1
- package/dist/summary/summaryFormat.d.ts +0 -1
- package/dist/summary/summaryFormat.d.ts.map +1 -1
- package/dist/summary/summaryFormat.js +7 -4
- package/dist/summary/summaryFormat.js.map +1 -1
- package/internal.d.ts +1 -1
- package/legacy.d.ts +1 -1
- package/lib/{blobManager.d.ts → blobManager/blobManager.d.ts} +19 -29
- package/lib/blobManager/blobManager.d.ts.map +1 -0
- package/lib/{blobManager.js → blobManager/blobManager.js} +40 -83
- package/lib/blobManager/blobManager.js.map +1 -0
- package/lib/blobManager/blobManagerSnapSum.d.ts +30 -0
- package/lib/blobManager/blobManagerSnapSum.d.ts.map +1 -0
- package/lib/blobManager/blobManagerSnapSum.js +75 -0
- package/lib/blobManager/blobManagerSnapSum.js.map +1 -0
- package/lib/blobManager/index.d.ts +7 -0
- package/lib/blobManager/index.d.ts.map +1 -0
- package/lib/blobManager/index.js +7 -0
- package/lib/blobManager/index.js.map +1 -0
- package/lib/channelCollection.d.ts +1 -1
- package/lib/channelCollection.d.ts.map +1 -1
- package/lib/channelCollection.js +40 -8
- package/lib/channelCollection.js.map +1 -1
- package/lib/containerRuntime.d.ts +15 -10
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +149 -112
- package/lib/containerRuntime.js.map +1 -1
- package/lib/dataStoreContext.d.ts +5 -0
- package/lib/dataStoreContext.d.ts.map +1 -1
- package/lib/dataStoreContext.js +17 -6
- package/lib/dataStoreContext.js.map +1 -1
- package/lib/gc/garbageCollection.d.ts +1 -1
- package/lib/gc/garbageCollection.d.ts.map +1 -1
- package/lib/gc/garbageCollection.js +16 -10
- package/lib/gc/garbageCollection.js.map +1 -1
- package/lib/gc/gcDefinitions.d.ts +4 -2
- package/lib/gc/gcDefinitions.d.ts.map +1 -1
- package/lib/gc/gcDefinitions.js.map +1 -1
- package/lib/gc/gcHelpers.d.ts.map +1 -1
- package/lib/gc/gcHelpers.js +12 -0
- package/lib/gc/gcHelpers.js.map +1 -1
- package/lib/gc/gcTelemetry.d.ts +3 -2
- package/lib/gc/gcTelemetry.d.ts.map +1 -1
- package/lib/gc/gcTelemetry.js +6 -6
- package/lib/gc/gcTelemetry.js.map +1 -1
- package/lib/index.d.ts +1 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js.map +1 -1
- package/lib/legacy.d.ts +1 -1
- package/lib/metadata.d.ts +7 -1
- package/lib/metadata.d.ts.map +1 -1
- package/lib/metadata.js +4 -1
- package/lib/metadata.js.map +1 -1
- package/lib/opLifecycle/batchManager.d.ts +8 -1
- package/lib/opLifecycle/batchManager.d.ts.map +1 -1
- package/lib/opLifecycle/batchManager.js +35 -15
- package/lib/opLifecycle/batchManager.js.map +1 -1
- package/lib/opLifecycle/definitions.d.ts +1 -1
- package/lib/opLifecycle/definitions.d.ts.map +1 -1
- package/lib/opLifecycle/definitions.js.map +1 -1
- package/lib/opLifecycle/index.d.ts +1 -1
- package/lib/opLifecycle/index.d.ts.map +1 -1
- package/lib/opLifecycle/index.js +1 -1
- package/lib/opLifecycle/index.js.map +1 -1
- package/lib/opLifecycle/opCompressor.d.ts.map +1 -1
- package/lib/opLifecycle/opCompressor.js +12 -8
- package/lib/opLifecycle/opCompressor.js.map +1 -1
- package/lib/opLifecycle/opGroupingManager.d.ts.map +1 -1
- package/lib/opLifecycle/opGroupingManager.js +14 -11
- package/lib/opLifecycle/opGroupingManager.js.map +1 -1
- package/lib/opLifecycle/opSplitter.d.ts.map +1 -1
- package/lib/opLifecycle/opSplitter.js +11 -6
- package/lib/opLifecycle/opSplitter.js.map +1 -1
- package/lib/opLifecycle/outbox.d.ts +22 -6
- package/lib/opLifecycle/outbox.d.ts.map +1 -1
- package/lib/opLifecycle/outbox.js +44 -22
- package/lib/opLifecycle/outbox.js.map +1 -1
- package/lib/opLifecycle/remoteMessageProcessor.d.ts +22 -6
- package/lib/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
- package/lib/opLifecycle/remoteMessageProcessor.js +57 -7
- package/lib/opLifecycle/remoteMessageProcessor.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/pendingStateManager.d.ts +39 -13
- package/lib/pendingStateManager.d.ts.map +1 -1
- package/lib/pendingStateManager.js +99 -34
- package/lib/pendingStateManager.js.map +1 -1
- package/lib/public.d.ts +1 -1
- package/lib/scheduleManager.js +4 -0
- package/lib/scheduleManager.js.map +1 -1
- package/lib/summary/index.d.ts +1 -1
- package/lib/summary/index.d.ts.map +1 -1
- package/lib/summary/index.js +1 -1
- package/lib/summary/index.js.map +1 -1
- package/lib/summary/summarizerNode/summarizerNodeUtils.d.ts.map +1 -1
- package/lib/summary/summarizerNode/summarizerNodeUtils.js +2 -0
- package/lib/summary/summarizerNode/summarizerNodeUtils.js.map +1 -1
- package/lib/summary/summaryFormat.d.ts +0 -1
- package/lib/summary/summaryFormat.d.ts.map +1 -1
- package/lib/summary/summaryFormat.js +5 -2
- package/lib/summary/summaryFormat.js.map +1 -1
- package/package.json +49 -34
- package/src/{blobManager.ts → blobManager/blobManager.ts} +57 -123
- package/src/blobManager/blobManagerSnapSum.ts +133 -0
- package/src/blobManager/index.ts +19 -0
- package/src/channelCollection.ts +48 -11
- package/src/containerRuntime.ts +213 -158
- package/src/dataStoreContext.ts +30 -6
- package/src/gc/garbageCollection.ts +17 -12
- package/src/gc/gcDefinitions.ts +7 -2
- package/src/gc/gcHelpers.ts +18 -6
- package/src/gc/gcTelemetry.ts +20 -8
- package/src/index.ts +1 -1
- package/src/metadata.ts +11 -1
- package/src/opLifecycle/README.md +0 -8
- package/src/opLifecycle/batchManager.ts +46 -16
- package/src/opLifecycle/definitions.ts +1 -1
- package/src/opLifecycle/index.ts +8 -1
- package/src/opLifecycle/opCompressor.ts +12 -8
- package/src/opLifecycle/opGroupingManager.ts +14 -11
- package/src/opLifecycle/opSplitter.ts +10 -6
- package/src/opLifecycle/outbox.ts +64 -26
- package/src/opLifecycle/remoteMessageProcessor.ts +84 -11
- package/src/packageVersion.ts +1 -1
- package/src/pendingStateManager.ts +177 -60
- package/src/scheduleManager.ts +6 -2
- package/src/summary/README.md +81 -0
- package/src/summary/index.ts +0 -1
- package/src/summary/summarizerNode/summarizerNodeUtils.ts +3 -1
- package/src/summary/summaryFormat.ts +4 -2
- package/src/summary/summaryFormats.md +69 -8
- package/tsconfig.json +0 -1
- package/dist/blobManager.d.ts.map +0 -1
- package/dist/blobManager.js.map +0 -1
- package/lib/blobManager.d.ts.map +0 -1
- package/lib/blobManager.js.map +0 -1
- package/src/summary/images/appTree.png +0 -0
- package/src/summary/images/protocolAndAppTree.png +0 -0
- package/src/summary/images/summaryTree.png +0 -0
package/src/containerRuntime.ts
CHANGED
|
@@ -61,6 +61,7 @@ import {
|
|
|
61
61
|
MessageType,
|
|
62
62
|
ISequencedDocumentMessage,
|
|
63
63
|
ISignalMessage,
|
|
64
|
+
type ISummaryContext,
|
|
64
65
|
} from "@fluidframework/driver-definitions/internal";
|
|
65
66
|
import { readAndParse } from "@fluidframework/driver-utils/internal";
|
|
66
67
|
import type { IIdCompressor } from "@fluidframework/id-compressor";
|
|
@@ -91,7 +92,6 @@ import {
|
|
|
91
92
|
} from "@fluidframework/runtime-definitions/internal";
|
|
92
93
|
import {
|
|
93
94
|
GCDataBuilder,
|
|
94
|
-
ReadAndParseBlob,
|
|
95
95
|
RequestParser,
|
|
96
96
|
TelemetryContext,
|
|
97
97
|
addBlobToSummary,
|
|
@@ -126,7 +126,15 @@ import {
|
|
|
126
126
|
import { v4 as uuid } from "uuid";
|
|
127
127
|
|
|
128
128
|
import { BindBatchTracker } from "./batchTracker.js";
|
|
129
|
-
import {
|
|
129
|
+
import {
|
|
130
|
+
BlobManager,
|
|
131
|
+
IPendingBlobs,
|
|
132
|
+
blobManagerBasePath,
|
|
133
|
+
blobsTreeName,
|
|
134
|
+
isBlobPath,
|
|
135
|
+
loadBlobManagerLoadInfo,
|
|
136
|
+
type IBlobManagerLoadInfo,
|
|
137
|
+
} from "./blobManager/index.js";
|
|
130
138
|
import {
|
|
131
139
|
ChannelCollection,
|
|
132
140
|
getSummaryForDatastores,
|
|
@@ -161,6 +169,7 @@ import {
|
|
|
161
169
|
} from "./messageTypes.js";
|
|
162
170
|
import { IBatchMetadata, ISavedOpMetadata } from "./metadata.js";
|
|
163
171
|
import {
|
|
172
|
+
BatchId,
|
|
164
173
|
BatchMessage,
|
|
165
174
|
IBatch,
|
|
166
175
|
IBatchCheckpoint,
|
|
@@ -173,7 +182,7 @@ import {
|
|
|
173
182
|
} from "./opLifecycle/index.js";
|
|
174
183
|
import { pkgVersion } from "./packageVersion.js";
|
|
175
184
|
import {
|
|
176
|
-
|
|
185
|
+
PendingMessageResubmitData,
|
|
177
186
|
IPendingLocalState,
|
|
178
187
|
PendingStateManager,
|
|
179
188
|
} from "./pendingStateManager.js";
|
|
@@ -211,7 +220,6 @@ import {
|
|
|
211
220
|
SummaryCollection,
|
|
212
221
|
SummaryManager,
|
|
213
222
|
aliasBlobName,
|
|
214
|
-
blobsTreeName,
|
|
215
223
|
chunksBlobName,
|
|
216
224
|
createRootSummarizerNodeWithGC,
|
|
217
225
|
electedSummarizerBlobName,
|
|
@@ -661,7 +669,7 @@ export const makeLegacySendBatchFn =
|
|
|
661
669
|
(batch: IBatch) => {
|
|
662
670
|
// Default to negative one to match Container.submitBatch behavior
|
|
663
671
|
let clientSequenceNumber: number = -1;
|
|
664
|
-
for (const message of batch.
|
|
672
|
+
for (const message of batch.messages) {
|
|
665
673
|
clientSequenceNumber = submitFn(
|
|
666
674
|
MessageType.Operation,
|
|
667
675
|
// For back-compat (submitFn only works on deserialized content)
|
|
@@ -677,23 +685,26 @@ export const makeLegacySendBatchFn =
|
|
|
677
685
|
};
|
|
678
686
|
|
|
679
687
|
/** Helper type for type constraints passed through several functions.
|
|
688
|
+
* local - Did this client send the op?
|
|
689
|
+
* savedOp - Is this op being replayed after being serialized (having been sequenced previously)
|
|
690
|
+
* batchStartCsn - The clientSequenceNumber given on submit to the start of this batch
|
|
680
691
|
* message - The unpacked message. Likely a TypedContainerRuntimeMessage, but could also be a system op
|
|
681
692
|
* modernRuntimeMessage - Does this appear like a current TypedContainerRuntimeMessage?
|
|
682
|
-
* local - Did this client send the op?
|
|
683
693
|
*/
|
|
684
|
-
type MessageWithContext =
|
|
694
|
+
type MessageWithContext = {
|
|
695
|
+
local: boolean;
|
|
696
|
+
savedOp?: boolean;
|
|
697
|
+
localOpMetadata?: unknown;
|
|
698
|
+
} & (
|
|
685
699
|
| {
|
|
686
700
|
message: InboundSequencedContainerRuntimeMessage;
|
|
687
701
|
modernRuntimeMessage: true;
|
|
688
|
-
local: boolean;
|
|
689
|
-
savedOp?: boolean;
|
|
690
702
|
}
|
|
691
703
|
| {
|
|
692
704
|
message: InboundSequencedContainerRuntimeMessageOrSystemMessage;
|
|
693
705
|
modernRuntimeMessage: false;
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
};
|
|
706
|
+
}
|
|
707
|
+
);
|
|
697
708
|
|
|
698
709
|
const summarizerRequestUrl = "_summarizer";
|
|
699
710
|
|
|
@@ -857,18 +868,7 @@ export class ContainerRuntime
|
|
|
857
868
|
]);
|
|
858
869
|
|
|
859
870
|
// read snapshot blobs needed for BlobManager to load
|
|
860
|
-
const blobManagerSnapshot = await
|
|
861
|
-
context.baseSnapshot?.trees[blobsTreeName],
|
|
862
|
-
async (id) => {
|
|
863
|
-
// IContainerContext storage api return type still has undefined in 0.39 package version.
|
|
864
|
-
// So once we release 0.40 container-defn package we can remove this check.
|
|
865
|
-
assert(
|
|
866
|
-
context.storage !== undefined,
|
|
867
|
-
0x256 /* "storage undefined in attached container" */,
|
|
868
|
-
);
|
|
869
|
-
return readAndParse(context.storage, id);
|
|
870
|
-
},
|
|
871
|
-
);
|
|
871
|
+
const blobManagerSnapshot = await loadBlobManagerLoadInfo(context);
|
|
872
872
|
|
|
873
873
|
const messageAtLastSummary = lastMessageFromMetadata(metadata);
|
|
874
874
|
|
|
@@ -989,11 +989,7 @@ export class ContainerRuntime
|
|
|
989
989
|
}
|
|
990
990
|
};
|
|
991
991
|
|
|
992
|
-
const disableCompression = mc.config.getBoolean(
|
|
993
|
-
"Fluid.ContainerRuntime.CompressionDisabled",
|
|
994
|
-
);
|
|
995
992
|
const compressionLz4 =
|
|
996
|
-
disableCompression !== true &&
|
|
997
993
|
compressionOptions.minimumBatchSizeInBytes !== Infinity &&
|
|
998
994
|
compressionOptions.compressionAlgorithm === "lz4";
|
|
999
995
|
|
|
@@ -1013,9 +1009,7 @@ export class ContainerRuntime
|
|
|
1013
1009
|
},
|
|
1014
1010
|
);
|
|
1015
1011
|
|
|
1016
|
-
const featureGatesForTelemetry: Record<string, boolean | number | undefined> = {
|
|
1017
|
-
disableCompression,
|
|
1018
|
-
};
|
|
1012
|
+
const featureGatesForTelemetry: Record<string, boolean | number | undefined> = {};
|
|
1019
1013
|
|
|
1020
1014
|
const runtime = new containerRuntimeCtor(
|
|
1021
1015
|
context,
|
|
@@ -1361,6 +1355,13 @@ export class ContainerRuntime
|
|
|
1361
1355
|
*/
|
|
1362
1356
|
private readonly loadedFromVersionId: string | undefined;
|
|
1363
1357
|
|
|
1358
|
+
private readonly isSnapshotInstanceOfISnapshot: boolean | undefined;
|
|
1359
|
+
|
|
1360
|
+
/**
|
|
1361
|
+
* The summary context of the last acked summary. The properties from this as used when uploading a summary.
|
|
1362
|
+
*/
|
|
1363
|
+
private lastAckedSummaryContext: ISummaryContext | undefined;
|
|
1364
|
+
|
|
1364
1365
|
/**
|
|
1365
1366
|
* It a cache for holding mapping for loading groupIds with its snapshot from the service. Add expiry policy of 1 minute.
|
|
1366
1367
|
* Starting with 1 min and based on recorded usage we can tweak it later on.
|
|
@@ -1513,9 +1514,6 @@ export class ContainerRuntime
|
|
|
1513
1514
|
this.disableAttachReorder = this.mc.config.getBoolean(
|
|
1514
1515
|
"Fluid.ContainerRuntime.disableAttachOpReorder",
|
|
1515
1516
|
);
|
|
1516
|
-
const disableChunking = this.mc.config.getBoolean(
|
|
1517
|
-
"Fluid.ContainerRuntime.CompressionChunkingDisabled",
|
|
1518
|
-
);
|
|
1519
1517
|
|
|
1520
1518
|
const opGroupingManager = new OpGroupingManager(
|
|
1521
1519
|
{
|
|
@@ -1532,7 +1530,7 @@ export class ContainerRuntime
|
|
|
1532
1530
|
const opSplitter = new OpSplitter(
|
|
1533
1531
|
chunks,
|
|
1534
1532
|
this.submitBatchFn,
|
|
1535
|
-
|
|
1533
|
+
runtimeOptions.chunkSizeInBytes,
|
|
1536
1534
|
runtimeOptions.maxBatchSizeInBytes,
|
|
1537
1535
|
this.mc.logger,
|
|
1538
1536
|
);
|
|
@@ -1550,10 +1548,6 @@ export class ContainerRuntime
|
|
|
1550
1548
|
clientId: () => this.clientId,
|
|
1551
1549
|
close: this.closeFn,
|
|
1552
1550
|
connected: () => this.connected,
|
|
1553
|
-
reSubmit: (message: IPendingBatchMessage) => {
|
|
1554
|
-
this.reSubmit(message);
|
|
1555
|
-
this.flush();
|
|
1556
|
-
},
|
|
1557
1551
|
reSubmitBatch: this.reSubmitBatch.bind(this),
|
|
1558
1552
|
isActiveConnection: () => this.innerDeltaManager.active,
|
|
1559
1553
|
isAttached: () => this.attachState !== AttachState.Detached,
|
|
@@ -1666,6 +1660,10 @@ export class ContainerRuntime
|
|
|
1666
1660
|
|
|
1667
1661
|
const parentContext = wrapContext(this);
|
|
1668
1662
|
|
|
1663
|
+
if (snapshotWithContents !== undefined) {
|
|
1664
|
+
this.isSnapshotInstanceOfISnapshot = true;
|
|
1665
|
+
}
|
|
1666
|
+
|
|
1669
1667
|
// Due to a mismatch between different layers in terms of
|
|
1670
1668
|
// what is the interface of passing signals, we need the
|
|
1671
1669
|
// downstream stores to wrap the signal.
|
|
@@ -1922,7 +1920,6 @@ export class ContainerRuntime
|
|
|
1922
1920
|
sessionRuntimeSchema: JSON.stringify(this.sessionSchema),
|
|
1923
1921
|
featureGates: JSON.stringify({
|
|
1924
1922
|
...featureGatesForTelemetry,
|
|
1925
|
-
disableChunking,
|
|
1926
1923
|
disableAttachReorder: this.disableAttachReorder,
|
|
1927
1924
|
disablePartialFlush,
|
|
1928
1925
|
closeSummarizerDelayOverride,
|
|
@@ -2168,9 +2165,13 @@ export class ContainerRuntime
|
|
|
2168
2165
|
let childTree = snapshotTree;
|
|
2169
2166
|
for (const part of pathParts) {
|
|
2170
2167
|
if (hasIsolatedChannels) {
|
|
2171
|
-
|
|
2168
|
+
// TODO Why are we non null asserting here
|
|
2169
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
2170
|
+
childTree = childTree.trees[channelsTreeName]!;
|
|
2172
2171
|
}
|
|
2173
|
-
|
|
2172
|
+
// TODO Why are we non null asserting here
|
|
2173
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
2174
|
+
childTree = childTree.trees[part]!;
|
|
2174
2175
|
}
|
|
2175
2176
|
return childTree;
|
|
2176
2177
|
}
|
|
@@ -2221,8 +2222,10 @@ export class ContainerRuntime
|
|
|
2221
2222
|
return this.resolveHandle(requestParser.createSubRequest(1));
|
|
2222
2223
|
}
|
|
2223
2224
|
|
|
2224
|
-
if (id ===
|
|
2225
|
-
|
|
2225
|
+
if (id === blobManagerBasePath && requestParser.isLeaf(2)) {
|
|
2226
|
+
// TODO why are we non null asserting here?
|
|
2227
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
2228
|
+
const blob = await this.blobManager.getBlob(requestParser.pathParts[1]!);
|
|
2226
2229
|
return blob
|
|
2227
2230
|
? {
|
|
2228
2231
|
status: 200,
|
|
@@ -2619,26 +2622,38 @@ export class ContainerRuntime
|
|
|
2619
2622
|
// but will not modify the contents object (likely it will replace it on the message).
|
|
2620
2623
|
const messageCopy = { ...messageArg };
|
|
2621
2624
|
const savedOp = (messageCopy.metadata as ISavedOpMetadata)?.savedOp;
|
|
2622
|
-
|
|
2623
|
-
const
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
:
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2625
|
+
if (modernRuntimeMessage) {
|
|
2626
|
+
const processResult = this.remoteMessageProcessor.process(messageCopy);
|
|
2627
|
+
if (processResult === undefined) {
|
|
2628
|
+
// This means the incoming message is an incomplete part of a message or batch
|
|
2629
|
+
// and we need to process more messages before the rest of the system can understand it.
|
|
2630
|
+
return;
|
|
2631
|
+
}
|
|
2632
|
+
const batchStartCsn = processResult.batchStartCsn;
|
|
2633
|
+
const batch = processResult.messages;
|
|
2634
|
+
const messages: {
|
|
2635
|
+
message: InboundSequencedContainerRuntimeMessage;
|
|
2636
|
+
localOpMetadata: unknown;
|
|
2637
|
+
}[] = local
|
|
2638
|
+
? this.pendingStateManager.processPendingLocalBatch(batch, batchStartCsn)
|
|
2639
|
+
: batch.map((message) => ({ message, localOpMetadata: undefined }));
|
|
2640
|
+
messages.forEach(({ message, localOpMetadata }) => {
|
|
2641
|
+
const msg: MessageWithContext = {
|
|
2642
|
+
message,
|
|
2643
|
+
local,
|
|
2644
|
+
modernRuntimeMessage,
|
|
2645
|
+
savedOp,
|
|
2646
|
+
localOpMetadata,
|
|
2647
|
+
};
|
|
2648
|
+
this.ensureNoDataModelChanges(() => this.processCore(msg));
|
|
2649
|
+
});
|
|
2650
|
+
} else {
|
|
2651
|
+
const msg: MessageWithContext = {
|
|
2652
|
+
message: messageCopy as InboundSequencedContainerRuntimeMessageOrSystemMessage,
|
|
2653
|
+
local,
|
|
2654
|
+
modernRuntimeMessage,
|
|
2655
|
+
savedOp,
|
|
2656
|
+
};
|
|
2642
2657
|
this.ensureNoDataModelChanges(() => this.processCore(msg));
|
|
2643
2658
|
}
|
|
2644
2659
|
}
|
|
@@ -2649,7 +2664,7 @@ export class ContainerRuntime
|
|
|
2649
2664
|
* Direct the message to the correct subsystem for processing, and implement other side effects
|
|
2650
2665
|
*/
|
|
2651
2666
|
private processCore(messageWithContext: MessageWithContext) {
|
|
2652
|
-
const { message, local } = messageWithContext;
|
|
2667
|
+
const { message, local, localOpMetadata } = messageWithContext;
|
|
2653
2668
|
|
|
2654
2669
|
// Intercept to reduce minimum sequence number to the delta manager's minimum sequence number.
|
|
2655
2670
|
// Sequence numbers are not guaranteed to follow any sort of order. Re-entrancy is one of those situations
|
|
@@ -2669,22 +2684,12 @@ export class ContainerRuntime
|
|
|
2669
2684
|
this._processedClientSequenceNumber = message.clientSequenceNumber;
|
|
2670
2685
|
|
|
2671
2686
|
try {
|
|
2672
|
-
//
|
|
2673
|
-
// These calls should be made for all but chunked ops:
|
|
2674
|
-
// 1) this.pendingStateManager.processPendingLocalMessage() below
|
|
2675
|
-
// 2) this.resetReconnectCount() below
|
|
2687
|
+
// RemoteMessageProcessor would have already reconstituted Chunked Ops into the original op type
|
|
2676
2688
|
assert(
|
|
2677
2689
|
message.type !== ContainerMessageType.ChunkedOp,
|
|
2678
2690
|
0x93b /* we should never get here with chunked ops */,
|
|
2679
2691
|
);
|
|
2680
2692
|
|
|
2681
|
-
let localOpMetadata: unknown;
|
|
2682
|
-
if (local && messageWithContext.modernRuntimeMessage) {
|
|
2683
|
-
localOpMetadata = this.pendingStateManager.processPendingLocalMessage(
|
|
2684
|
-
messageWithContext.message,
|
|
2685
|
-
);
|
|
2686
|
-
}
|
|
2687
|
-
|
|
2688
2693
|
// If there are no more pending messages after processing a local message,
|
|
2689
2694
|
// the document is no longer dirty.
|
|
2690
2695
|
if (!this.hasPendingMessages()) {
|
|
@@ -2880,14 +2885,16 @@ export class ContainerRuntime
|
|
|
2880
2885
|
/**
|
|
2881
2886
|
* Flush the pending ops manually.
|
|
2882
2887
|
* This method is expected to be called at the end of a batch.
|
|
2888
|
+
* @param resubmittingBatchId - If defined, indicates this is a resubmission of a batch
|
|
2889
|
+
* with the given Batch ID, which must be preserved
|
|
2883
2890
|
*/
|
|
2884
|
-
private flush(): void {
|
|
2891
|
+
private flush(resubmittingBatchId?: BatchId): void {
|
|
2885
2892
|
assert(
|
|
2886
2893
|
this._orderSequentiallyCalls === 0,
|
|
2887
2894
|
0x24c /* "Cannot call `flush()` from `orderSequentially`'s callback" */,
|
|
2888
2895
|
);
|
|
2889
2896
|
|
|
2890
|
-
this.outbox.flush();
|
|
2897
|
+
this.outbox.flush(resubmittingBatchId);
|
|
2891
2898
|
assert(this.outbox.isEmpty, 0x3cf /* reentrancy */);
|
|
2892
2899
|
}
|
|
2893
2900
|
|
|
@@ -2901,7 +2908,7 @@ export class ContainerRuntime
|
|
|
2901
2908
|
// Note: we are not touching any batches other than mainBatch here, for two reasons:
|
|
2902
2909
|
// 1. It would not help, as other batches are flushed independently from main batch.
|
|
2903
2910
|
// 2. There is no way to undo process of data store creation, blob creation, ID compressor ops, or other things tracked by other batches.
|
|
2904
|
-
checkpoint = this.outbox.
|
|
2911
|
+
checkpoint = this.outbox.getBatchCheckpoints().mainBatch;
|
|
2905
2912
|
}
|
|
2906
2913
|
try {
|
|
2907
2914
|
this._orderSequentiallyCalls++;
|
|
@@ -3044,7 +3051,7 @@ export class ContainerRuntime
|
|
|
3044
3051
|
}
|
|
3045
3052
|
|
|
3046
3053
|
/**
|
|
3047
|
-
*
|
|
3054
|
+
* Typically ops are batched and later flushed together, but in some cases we want to flush immediately.
|
|
3048
3055
|
*/
|
|
3049
3056
|
private currentlyBatching() {
|
|
3050
3057
|
return this.flushMode !== FlushMode.Immediate || this._orderSequentiallyCalls !== 0;
|
|
@@ -3360,7 +3367,7 @@ export class ContainerRuntime
|
|
|
3360
3367
|
* blob manager.
|
|
3361
3368
|
*/
|
|
3362
3369
|
public getNodeType(nodePath: string): GCNodeType {
|
|
3363
|
-
if (
|
|
3370
|
+
if (isBlobPath(nodePath)) {
|
|
3364
3371
|
return GCNodeType.Blob;
|
|
3365
3372
|
}
|
|
3366
3373
|
return this.channelCollection.getGCNodeType(nodePath) ?? GCNodeType.Other;
|
|
@@ -3379,7 +3386,7 @@ export class ContainerRuntime
|
|
|
3379
3386
|
|
|
3380
3387
|
switch (this.getNodeType(nodePath)) {
|
|
3381
3388
|
case GCNodeType.Blob:
|
|
3382
|
-
return [
|
|
3389
|
+
return [blobManagerBasePath];
|
|
3383
3390
|
case GCNodeType.DataStore:
|
|
3384
3391
|
case GCNodeType.SubDataStore:
|
|
3385
3392
|
return this.channelCollection.getDataStorePackagePath(nodePath);
|
|
@@ -3388,17 +3395,6 @@ export class ContainerRuntime
|
|
|
3388
3395
|
}
|
|
3389
3396
|
}
|
|
3390
3397
|
|
|
3391
|
-
/**
|
|
3392
|
-
* Returns whether a given path is for attachment blobs that are in the format - "/BlobManager.basePath/...".
|
|
3393
|
-
*/
|
|
3394
|
-
private isBlobPath(path: string): boolean {
|
|
3395
|
-
const pathParts = path.split("/");
|
|
3396
|
-
if (pathParts.length < 2 || pathParts[1] !== BlobManager.basePath) {
|
|
3397
|
-
return false;
|
|
3398
|
-
}
|
|
3399
|
-
return true;
|
|
3400
|
-
}
|
|
3401
|
-
|
|
3402
3398
|
/**
|
|
3403
3399
|
* From a given list of routes, separate and return routes that belong to blob manager and data stores.
|
|
3404
3400
|
* @param routes - A list of routes that can belong to data stores or blob manager.
|
|
@@ -3409,7 +3405,7 @@ export class ContainerRuntime
|
|
|
3409
3405
|
const blobManagerRoutes: string[] = [];
|
|
3410
3406
|
const dataStoreRoutes: string[] = [];
|
|
3411
3407
|
for (const route of routes) {
|
|
3412
|
-
if (
|
|
3408
|
+
if (isBlobPath(route)) {
|
|
3413
3409
|
blobManagerRoutes.push(route);
|
|
3414
3410
|
} else {
|
|
3415
3411
|
dataStoreRoutes.push(route);
|
|
@@ -3555,7 +3551,7 @@ export class ContainerRuntime
|
|
|
3555
3551
|
summaryRefSeqNum = this.deltaManager.lastSequenceNumber;
|
|
3556
3552
|
const minimumSequenceNumber = this.deltaManager.minimumSequenceNumber;
|
|
3557
3553
|
const message = `Summary @${summaryRefSeqNum}:${this.deltaManager.minimumSequenceNumber}`;
|
|
3558
|
-
const
|
|
3554
|
+
const lastAckedContext = this.lastAckedSummaryContext;
|
|
3559
3555
|
|
|
3560
3556
|
const startSummaryResult = this.summarizerNode.startSummary(
|
|
3561
3557
|
summaryRefSeqNum,
|
|
@@ -3622,10 +3618,10 @@ export class ContainerRuntime
|
|
|
3622
3618
|
0x395 /* it's one and the same thing */,
|
|
3623
3619
|
);
|
|
3624
3620
|
|
|
3625
|
-
if (
|
|
3621
|
+
if (lastAckedContext !== this.lastAckedSummaryContext) {
|
|
3626
3622
|
return {
|
|
3627
3623
|
continue: false,
|
|
3628
|
-
error: `Last summary changed while summarizing. ${this.
|
|
3624
|
+
error: `Last summary changed while summarizing. ${this.lastAckedSummaryContext} !== ${lastAckedContext}`,
|
|
3629
3625
|
};
|
|
3630
3626
|
}
|
|
3631
3627
|
return { continue: true };
|
|
@@ -3697,7 +3693,9 @@ export class ContainerRuntime
|
|
|
3697
3693
|
// Counting dataStores and handles
|
|
3698
3694
|
// Because handles are unchanged dataStores in the current logic,
|
|
3699
3695
|
// summarized dataStore count is total dataStore count minus handle count
|
|
3700
|
-
|
|
3696
|
+
// TODO why are we non null asserting here
|
|
3697
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
3698
|
+
const dataStoreTree = summaryTree.tree[channelsTreeName]!;
|
|
3701
3699
|
|
|
3702
3700
|
assert(dataStoreTree.type === SummaryType.Tree, 0x1fc /* "summary is not a tree" */);
|
|
3703
3701
|
const handleCount = Object.values(dataStoreTree.tree).filter(
|
|
@@ -3733,18 +3731,11 @@ export class ContainerRuntime
|
|
|
3733
3731
|
};
|
|
3734
3732
|
}
|
|
3735
3733
|
|
|
3736
|
-
const summaryContext =
|
|
3737
|
-
|
|
3738
|
-
|
|
3739
|
-
|
|
3740
|
-
|
|
3741
|
-
referenceSequenceNumber: summaryRefSeqNum,
|
|
3742
|
-
}
|
|
3743
|
-
: {
|
|
3744
|
-
proposalHandle: lastAck.summaryOp.contents.handle,
|
|
3745
|
-
ackHandle: lastAck.summaryAck.contents.handle,
|
|
3746
|
-
referenceSequenceNumber: summaryRefSeqNum,
|
|
3747
|
-
};
|
|
3734
|
+
const summaryContext: ISummaryContext = {
|
|
3735
|
+
proposalHandle: this.lastAckedSummaryContext?.proposalHandle ?? undefined,
|
|
3736
|
+
ackHandle: this.lastAckedSummaryContext?.ackHandle ?? this.loadedFromVersionId,
|
|
3737
|
+
referenceSequenceNumber: summaryRefSeqNum,
|
|
3738
|
+
};
|
|
3748
3739
|
|
|
3749
3740
|
let handle: string;
|
|
3750
3741
|
try {
|
|
@@ -4035,7 +4026,9 @@ export class ContainerRuntime
|
|
|
4035
4026
|
this.outbox.submit(message);
|
|
4036
4027
|
}
|
|
4037
4028
|
|
|
4038
|
-
if
|
|
4029
|
+
// Note: Technically, the system "always" batches - if this case is true we'll just have a single-message batch.
|
|
4030
|
+
const flushImmediatelyOnSubmit = !this.currentlyBatching();
|
|
4031
|
+
if (flushImmediatelyOnSubmit) {
|
|
4039
4032
|
this.flush();
|
|
4040
4033
|
} else {
|
|
4041
4034
|
this.scheduleFlush();
|
|
@@ -4116,16 +4109,22 @@ export class ContainerRuntime
|
|
|
4116
4109
|
}
|
|
4117
4110
|
}
|
|
4118
4111
|
|
|
4119
|
-
private reSubmitBatch(batch:
|
|
4112
|
+
private reSubmitBatch(batch: PendingMessageResubmitData[], batchId: BatchId) {
|
|
4120
4113
|
this.orderSequentially(() => {
|
|
4121
4114
|
for (const message of batch) {
|
|
4122
4115
|
this.reSubmit(message);
|
|
4123
4116
|
}
|
|
4124
4117
|
});
|
|
4125
|
-
|
|
4118
|
+
|
|
4119
|
+
// Only include Batch ID if "Offline Load" feature is enabled
|
|
4120
|
+
// It's only needed to identify batches across container forks arising from misuse of offline load.
|
|
4121
|
+
const includeBatchId =
|
|
4122
|
+
this.mc.config.getBoolean("Fluid.Container.enableOfflineLoad") ?? false;
|
|
4123
|
+
|
|
4124
|
+
this.flush(includeBatchId ? batchId : undefined);
|
|
4126
4125
|
}
|
|
4127
4126
|
|
|
4128
|
-
private reSubmit(message:
|
|
4127
|
+
private reSubmit(message: PendingMessageResubmitData) {
|
|
4129
4128
|
// Need to parse from string for back-compat
|
|
4130
4129
|
const containerRuntimeMessage = this.parseLocalOpContent(message.content);
|
|
4131
4130
|
this.reSubmitCore(containerRuntimeMessage, message.localOpMetadata, message.opMetadata);
|
|
@@ -4228,84 +4227,140 @@ export class ContainerRuntime
|
|
|
4228
4227
|
const { proposalHandle, ackHandle, summaryRefSeq, summaryLogger } = options;
|
|
4229
4228
|
// proposalHandle is always passed from RunningSummarizer.
|
|
4230
4229
|
assert(proposalHandle !== undefined, 0x766 /* proposalHandle should be available */);
|
|
4231
|
-
const readAndParseBlob = async <T>(id: string) => readAndParse<T>(this.storage, id);
|
|
4232
4230
|
const result = await this.summarizerNode.refreshLatestSummary(
|
|
4233
4231
|
proposalHandle,
|
|
4234
4232
|
summaryRefSeq,
|
|
4235
4233
|
);
|
|
4236
4234
|
|
|
4235
|
+
/* eslint-disable jsdoc/check-indentation */
|
|
4237
4236
|
/**
|
|
4238
|
-
*
|
|
4239
|
-
*
|
|
4240
|
-
*
|
|
4241
|
-
*
|
|
4237
|
+
* If the snapshot corresponding to the ack is not tracked by this client, it was submitted by another client.
|
|
4238
|
+
* Take action as per the following scenarios:
|
|
4239
|
+
* 1. If that snapshot is older than the one tracked by this client, ignore the ack because only the latest
|
|
4240
|
+
* snapshot is tracked.
|
|
4241
|
+
* 2. If that snapshot is newer, attempt to fetch the latest snapshot and do one of the following:
|
|
4242
|
+
* 2.1. If the fetched snapshot is same or newer than the one for which ack was received, close this client.
|
|
4243
|
+
* The next summarizer client will likely start from this snapshot and get out of this state. Fetching
|
|
4244
|
+
* the snapshot updates the cache for this client so if it's re-elected as summarizer, this will prevent
|
|
4245
|
+
* any thrashing.
|
|
4246
|
+
* 2.2. If the fetched snapshot is older than the one for which ack was received, ignore the ack. This can
|
|
4247
|
+
* happen in scenarios where the snapshot for the ack was lost in storage (in scenarios like DB rollback,
|
|
4248
|
+
* etc.) but the summary ack is still there because it's tracked a different service. In such cases,
|
|
4249
|
+
* ignoring the ack is the correct thing to do because the latest snapshot in storage is not the one for
|
|
4250
|
+
* the ack but is still the one tracked by this client. If we were to close the summarizer like in the
|
|
4251
|
+
* previous scenario, it will result in this document stuck in this state in a loop.
|
|
4242
4252
|
*/
|
|
4243
|
-
|
|
4244
|
-
|
|
4245
|
-
|
|
4246
|
-
|
|
4247
|
-
|
|
4248
|
-
ackHandle,
|
|
4249
|
-
targetSequenceNumber: summaryRefSeq,
|
|
4250
|
-
},
|
|
4251
|
-
readAndParseBlob,
|
|
4252
|
-
);
|
|
4253
|
+
/* eslint-enable jsdoc/check-indentation */
|
|
4254
|
+
if (!result.isSummaryTracked) {
|
|
4255
|
+
if (result.isSummaryNewer) {
|
|
4256
|
+
await this.fetchLatestSnapshotAndMaybeClose(summaryRefSeq, ackHandle, summaryLogger);
|
|
4257
|
+
}
|
|
4253
4258
|
return;
|
|
4254
4259
|
}
|
|
4255
4260
|
|
|
4256
4261
|
// Notify the garbage collector so it can update its latest summary state.
|
|
4257
4262
|
await this.garbageCollector.refreshLatestSummary(result);
|
|
4263
|
+
|
|
4264
|
+
// If we here, the ack was tracked by this client. Update the summary context of the last ack.
|
|
4265
|
+
this.lastAckedSummaryContext = {
|
|
4266
|
+
proposalHandle,
|
|
4267
|
+
ackHandle,
|
|
4268
|
+
referenceSequenceNumber: summaryRefSeq,
|
|
4269
|
+
};
|
|
4258
4270
|
}
|
|
4259
4271
|
|
|
4260
4272
|
/**
|
|
4261
|
-
* Fetches the latest snapshot from storage
|
|
4262
|
-
*
|
|
4263
|
-
*
|
|
4273
|
+
* Fetches the latest snapshot from storage. If the fetched snapshot is same or newer than the one for which ack
|
|
4274
|
+
* was received, close this client. Fetching the snapshot will update the cache for this client so if it's
|
|
4275
|
+
* re-elected as summarizer, this will prevent any thrashing.
|
|
4276
|
+
* If the fetched snapshot is older than the one for which ack was received, ignore the ack and return. This can
|
|
4277
|
+
* happen in scenarios where the snapshot for the ack was lost in storage in scenarios like DB rollback, etc.
|
|
4264
4278
|
*/
|
|
4265
|
-
private async
|
|
4279
|
+
private async fetchLatestSnapshotAndMaybeClose(
|
|
4280
|
+
targetRefSeq: number,
|
|
4281
|
+
targetAckHandle: string,
|
|
4266
4282
|
logger: ITelemetryLoggerExt,
|
|
4267
|
-
event: ITelemetryGenericEventExt,
|
|
4268
|
-
readAndParseBlob: ReadAndParseBlob,
|
|
4269
4283
|
) {
|
|
4270
|
-
await PerformanceEvent.timedExecAsync(
|
|
4284
|
+
const fetchedSnapshotRefSeq = await PerformanceEvent.timedExecAsync(
|
|
4271
4285
|
logger,
|
|
4272
|
-
|
|
4286
|
+
{ eventName: "RefreshLatestSummaryAckFetch" },
|
|
4273
4287
|
async (perfEvent: {
|
|
4274
4288
|
end: (arg0: {
|
|
4275
|
-
|
|
4276
|
-
|
|
4277
|
-
|
|
4278
|
-
|
|
4289
|
+
details: {
|
|
4290
|
+
getVersionDuration?: number | undefined;
|
|
4291
|
+
getSnapshotDuration?: number | undefined;
|
|
4292
|
+
snapshotRefSeq?: number | undefined;
|
|
4293
|
+
snapshotVersion?: string | undefined;
|
|
4294
|
+
newerSnapshotPresent?: boolean | undefined;
|
|
4295
|
+
targetRefSeq?: number | undefined;
|
|
4296
|
+
targetAckHandle?: string | undefined;
|
|
4297
|
+
};
|
|
4279
4298
|
}) => void;
|
|
4280
4299
|
}) => {
|
|
4281
|
-
const
|
|
4300
|
+
const props: {
|
|
4282
4301
|
getVersionDuration?: number;
|
|
4283
4302
|
getSnapshotDuration?: number;
|
|
4284
4303
|
snapshotRefSeq?: number;
|
|
4285
4304
|
snapshotVersion?: string;
|
|
4286
|
-
|
|
4305
|
+
newerSnapshotPresent?: boolean | undefined;
|
|
4306
|
+
targetRefSeq?: number | undefined;
|
|
4307
|
+
targetAckHandle?: string | undefined;
|
|
4308
|
+
} = { targetRefSeq, targetAckHandle };
|
|
4287
4309
|
const trace = Trace.start();
|
|
4288
4310
|
|
|
4289
|
-
|
|
4290
|
-
|
|
4291
|
-
|
|
4292
|
-
|
|
4293
|
-
|
|
4294
|
-
|
|
4295
|
-
|
|
4296
|
-
|
|
4311
|
+
let snapshotTree: ISnapshotTree | null;
|
|
4312
|
+
const scenarioName = "RefreshLatestSummaryAckFetch";
|
|
4313
|
+
// If loader supplied us the ISnapshot when loading, the new getSnapshotApi is supported and feature gate is ON, then use the
|
|
4314
|
+
// new API, otherwise it will reduce the service performance because the service will need to recalculate the full snapshot
|
|
4315
|
+
// in case previously getSnapshotApi was used and now we use the getVersions API.
|
|
4316
|
+
if (
|
|
4317
|
+
this.isSnapshotInstanceOfISnapshot &&
|
|
4318
|
+
this.storage.getSnapshot !== undefined &&
|
|
4319
|
+
this.mc.config.getBoolean("Fluid.Container.UseLoadingGroupIdForSnapshotFetch2") ===
|
|
4320
|
+
true
|
|
4321
|
+
) {
|
|
4322
|
+
const snapshot = await this.storage.getSnapshot({
|
|
4323
|
+
scenarioName,
|
|
4324
|
+
fetchSource: FetchSource.noCache,
|
|
4325
|
+
});
|
|
4326
|
+
const id = snapshot.snapshotTree.id;
|
|
4327
|
+
assert(id !== undefined, "id of the fetched snapshot should be defined");
|
|
4328
|
+
props.snapshotVersion = id;
|
|
4329
|
+
snapshotTree = snapshot.snapshotTree;
|
|
4330
|
+
} else {
|
|
4331
|
+
const versions = await this.storage.getVersions(
|
|
4332
|
+
null,
|
|
4333
|
+
1,
|
|
4334
|
+
scenarioName,
|
|
4335
|
+
FetchSource.noCache,
|
|
4336
|
+
);
|
|
4337
|
+
assert(
|
|
4338
|
+
!!versions && !!versions[0],
|
|
4339
|
+
0x137 /* "Failed to get version from storage" */,
|
|
4340
|
+
);
|
|
4341
|
+
snapshotTree = await this.storage.getSnapshotTree(versions[0]);
|
|
4342
|
+
assert(!!snapshotTree, 0x138 /* "Failed to get snapshot from storage" */);
|
|
4343
|
+
props.snapshotVersion = versions[0].id;
|
|
4344
|
+
}
|
|
4297
4345
|
|
|
4298
|
-
|
|
4299
|
-
|
|
4300
|
-
|
|
4301
|
-
|
|
4302
|
-
|
|
4303
|
-
stats.snapshotVersion = versions[0].id;
|
|
4346
|
+
props.getSnapshotDuration = trace.trace().duration;
|
|
4347
|
+
const readAndParseBlob = async <T>(id: string) => readAndParse<T>(this.storage, id);
|
|
4348
|
+
const snapshotRefSeq = await seqFromTree(snapshotTree, readAndParseBlob);
|
|
4349
|
+
props.snapshotRefSeq = snapshotRefSeq;
|
|
4350
|
+
props.newerSnapshotPresent = snapshotRefSeq >= targetRefSeq;
|
|
4304
4351
|
|
|
4305
|
-
perfEvent.end(
|
|
4352
|
+
perfEvent.end({ details: props });
|
|
4353
|
+
return snapshotRefSeq;
|
|
4306
4354
|
},
|
|
4307
4355
|
);
|
|
4308
4356
|
|
|
4357
|
+
// If the snapshot that was fetched is older than the target snapshot, return. The summarizer will not be closed
|
|
4358
|
+
// because the snapshot is likely deleted from storage and it so, closing the summarizer will result in the
|
|
4359
|
+
// document being stuck in this state.
|
|
4360
|
+
if (fetchedSnapshotRefSeq < targetRefSeq) {
|
|
4361
|
+
return;
|
|
4362
|
+
}
|
|
4363
|
+
|
|
4309
4364
|
await delay(this.closeSummarizerDelayMs);
|
|
4310
4365
|
this._summarizer?.stop("latestSummaryStateStale");
|
|
4311
4366
|
this.disposeFn();
|