@fluidframework/container-runtime 2.0.0-internal.6.3.3 → 2.0.0-internal.6.4.0
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/CHANGELOG.md +8 -0
- package/dist/blobManager.d.ts +3 -2
- package/dist/blobManager.d.ts.map +1 -1
- package/dist/blobManager.js +29 -25
- package/dist/blobManager.js.map +1 -1
- package/dist/containerRuntime.d.ts +14 -69
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +155 -184
- package/dist/containerRuntime.js.map +1 -1
- package/dist/dataStoreContext.d.ts.map +1 -1
- package/dist/dataStoreContext.js +3 -1
- package/dist/dataStoreContext.js.map +1 -1
- package/dist/dataStores.d.ts +1 -1
- package/dist/dataStores.js +3 -3
- package/dist/dataStores.js.map +1 -1
- package/dist/gc/garbageCollection.d.ts +6 -3
- package/dist/gc/garbageCollection.d.ts.map +1 -1
- package/dist/gc/garbageCollection.js +6 -3
- package/dist/gc/garbageCollection.js.map +1 -1
- package/dist/gc/gcDefinitions.d.ts +13 -2
- package/dist/gc/gcDefinitions.d.ts.map +1 -1
- package/dist/gc/gcDefinitions.js +14 -15
- package/dist/gc/gcDefinitions.js.map +1 -1
- package/dist/gc/gcHelpers.d.ts +0 -9
- package/dist/gc/gcHelpers.d.ts.map +1 -1
- package/dist/gc/gcHelpers.js +1 -13
- package/dist/gc/gcHelpers.js.map +1 -1
- package/dist/gc/gcTelemetry.d.ts.map +1 -1
- package/dist/gc/gcTelemetry.js +1 -4
- package/dist/gc/gcTelemetry.js.map +1 -1
- package/dist/gc/index.d.ts +2 -2
- package/dist/gc/index.d.ts.map +1 -1
- package/dist/gc/index.js +3 -4
- package/dist/gc/index.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/messageTypes.d.ts +134 -0
- package/dist/messageTypes.d.ts.map +1 -0
- package/dist/messageTypes.js +29 -0
- package/dist/messageTypes.js.map +1 -0
- package/dist/opLifecycle/definitions.d.ts +2 -1
- package/dist/opLifecycle/definitions.d.ts.map +1 -1
- package/dist/opLifecycle/definitions.js.map +1 -1
- package/dist/opLifecycle/opDecompressor.d.ts.map +1 -1
- package/dist/opLifecycle/opDecompressor.js +0 -4
- package/dist/opLifecycle/opDecompressor.js.map +1 -1
- package/dist/opLifecycle/opGroupingManager.d.ts.map +1 -1
- package/dist/opLifecycle/opGroupingManager.js +4 -2
- package/dist/opLifecycle/opGroupingManager.js.map +1 -1
- package/dist/opLifecycle/opSplitter.js +3 -3
- package/dist/opLifecycle/opSplitter.js.map +1 -1
- package/dist/opLifecycle/remoteMessageProcessor.d.ts +17 -3
- package/dist/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
- package/dist/opLifecycle/remoteMessageProcessor.js +38 -25
- 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 +2 -2
- package/dist/pendingStateManager.d.ts.map +1 -1
- package/dist/pendingStateManager.js +13 -6
- package/dist/pendingStateManager.js.map +1 -1
- package/dist/summary/runningSummarizer.d.ts +1 -1
- package/dist/summary/runningSummarizer.d.ts.map +1 -1
- package/dist/summary/runningSummarizer.js +4 -2
- package/dist/summary/runningSummarizer.js.map +1 -1
- package/dist/summary/summarizer.d.ts +2 -2
- package/dist/summary/summarizer.js +2 -2
- package/dist/summary/summarizer.js.map +1 -1
- package/dist/summary/summarizerNode/summarizerNode.d.ts +1 -1
- package/dist/summary/summarizerNode/summarizerNode.js +1 -1
- package/dist/summary/summarizerNode/summarizerNode.js.map +1 -1
- package/dist/summary/summarizerTypes.d.ts +2 -1
- package/dist/summary/summarizerTypes.d.ts.map +1 -1
- package/dist/summary/summarizerTypes.js.map +1 -1
- package/dist/summary/summaryFormat.d.ts +3 -0
- package/dist/summary/summaryFormat.d.ts.map +1 -1
- package/dist/summary/summaryFormat.js +3 -0
- package/dist/summary/summaryFormat.js.map +1 -1
- package/lib/blobManager.d.ts +3 -2
- package/lib/blobManager.d.ts.map +1 -1
- package/lib/blobManager.js +30 -26
- package/lib/blobManager.js.map +1 -1
- package/lib/containerRuntime.d.ts +14 -69
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +121 -150
- package/lib/containerRuntime.js.map +1 -1
- package/lib/dataStoreContext.d.ts.map +1 -1
- package/lib/dataStoreContext.js +3 -1
- package/lib/dataStoreContext.js.map +1 -1
- package/lib/dataStores.d.ts +1 -1
- package/lib/dataStores.js +4 -4
- package/lib/dataStores.js.map +1 -1
- package/lib/gc/garbageCollection.d.ts +6 -3
- package/lib/gc/garbageCollection.d.ts.map +1 -1
- package/lib/gc/garbageCollection.js +6 -3
- package/lib/gc/garbageCollection.js.map +1 -1
- package/lib/gc/gcDefinitions.d.ts +13 -2
- package/lib/gc/gcDefinitions.d.ts.map +1 -1
- package/lib/gc/gcDefinitions.js +13 -14
- package/lib/gc/gcDefinitions.js.map +1 -1
- package/lib/gc/gcHelpers.d.ts +0 -9
- package/lib/gc/gcHelpers.d.ts.map +1 -1
- package/lib/gc/gcHelpers.js +0 -11
- package/lib/gc/gcHelpers.js.map +1 -1
- package/lib/gc/gcTelemetry.d.ts.map +1 -1
- package/lib/gc/gcTelemetry.js +1 -4
- package/lib/gc/gcTelemetry.js.map +1 -1
- package/lib/gc/index.d.ts +2 -2
- package/lib/gc/index.d.ts.map +1 -1
- package/lib/gc/index.js +2 -2
- package/lib/gc/index.js.map +1 -1
- package/lib/index.d.ts +2 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +2 -1
- package/lib/index.js.map +1 -1
- package/lib/messageTypes.d.ts +134 -0
- package/lib/messageTypes.d.ts.map +1 -0
- package/lib/messageTypes.js +26 -0
- package/lib/messageTypes.js.map +1 -0
- package/lib/opLifecycle/definitions.d.ts +2 -1
- package/lib/opLifecycle/definitions.d.ts.map +1 -1
- package/lib/opLifecycle/definitions.js.map +1 -1
- package/lib/opLifecycle/opDecompressor.d.ts.map +1 -1
- package/lib/opLifecycle/opDecompressor.js +0 -4
- package/lib/opLifecycle/opDecompressor.js.map +1 -1
- package/lib/opLifecycle/opGroupingManager.d.ts.map +1 -1
- package/lib/opLifecycle/opGroupingManager.js +4 -2
- package/lib/opLifecycle/opGroupingManager.js.map +1 -1
- package/lib/opLifecycle/opSplitter.js +1 -1
- package/lib/opLifecycle/opSplitter.js.map +1 -1
- package/lib/opLifecycle/remoteMessageProcessor.d.ts +17 -3
- package/lib/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
- package/lib/opLifecycle/remoteMessageProcessor.js +37 -24
- 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 +2 -2
- package/lib/pendingStateManager.d.ts.map +1 -1
- package/lib/pendingStateManager.js +12 -5
- package/lib/pendingStateManager.js.map +1 -1
- package/lib/summary/runningSummarizer.d.ts +1 -1
- package/lib/summary/runningSummarizer.d.ts.map +1 -1
- package/lib/summary/runningSummarizer.js +4 -2
- package/lib/summary/runningSummarizer.js.map +1 -1
- package/lib/summary/summarizer.d.ts +2 -2
- package/lib/summary/summarizer.js +2 -2
- package/lib/summary/summarizer.js.map +1 -1
- package/lib/summary/summarizerNode/summarizerNode.d.ts +1 -1
- package/lib/summary/summarizerNode/summarizerNode.js +1 -1
- package/lib/summary/summarizerNode/summarizerNode.js.map +1 -1
- package/lib/summary/summarizerTypes.d.ts +2 -1
- package/lib/summary/summarizerTypes.d.ts.map +1 -1
- package/lib/summary/summarizerTypes.js.map +1 -1
- package/lib/summary/summaryFormat.d.ts +3 -0
- package/lib/summary/summaryFormat.d.ts.map +1 -1
- package/lib/summary/summaryFormat.js +3 -0
- package/lib/summary/summaryFormat.js.map +1 -1
- package/package.json +16 -16
- package/src/blobManager.ts +38 -28
- package/src/containerRuntime.ts +181 -245
- package/src/dataStoreContext.ts +3 -1
- package/src/dataStores.ts +4 -4
- package/src/gc/garbageCollection.md +53 -5
- package/src/gc/garbageCollection.ts +6 -3
- package/src/gc/gcDefinitions.ts +13 -14
- package/src/gc/gcEarlyAdoption.md +145 -0
- package/src/gc/gcHelpers.ts +0 -12
- package/src/gc/gcTelemetry.ts +1 -4
- package/src/gc/index.ts +2 -3
- package/src/index.ts +7 -4
- package/src/messageTypes.ts +225 -0
- package/src/opLifecycle/README.md +40 -40
- package/src/opLifecycle/definitions.ts +2 -1
- package/src/opLifecycle/opDecompressor.ts +0 -8
- package/src/opLifecycle/opGroupingManager.ts +7 -6
- package/src/opLifecycle/opSplitter.ts +2 -2
- package/src/opLifecycle/remoteMessageProcessor.ts +54 -33
- package/src/packageVersion.ts +1 -1
- package/src/pendingStateManager.ts +23 -6
- package/src/summary/runningSummarizer.ts +4 -2
- package/src/summary/summarizer.ts +2 -2
- package/src/summary/summarizerNode/summarizerNode.ts +1 -1
- package/src/summary/summarizerTypes.ts +2 -1
- package/src/summary/summaryFormat.ts +3 -0
package/src/containerRuntime.ts
CHANGED
|
@@ -88,7 +88,6 @@ import {
|
|
|
88
88
|
IIdCompressorCore,
|
|
89
89
|
IdCreationRange,
|
|
90
90
|
IdCreationRangeWithStashedState,
|
|
91
|
-
IAttachMessage,
|
|
92
91
|
} from "@fluidframework/runtime-definitions";
|
|
93
92
|
import {
|
|
94
93
|
addBlobToSummary,
|
|
@@ -184,92 +183,42 @@ import {
|
|
|
184
183
|
} from "./opLifecycle";
|
|
185
184
|
import { DeltaManagerSummarizerProxy } from "./deltaManagerSummarizerProxy";
|
|
186
185
|
import { IBatchMetadata } from "./metadata";
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
// Signifies that a blob has been attached and should not be garbage collected by storage
|
|
199
|
-
BlobAttach = "blobAttach",
|
|
200
|
-
|
|
201
|
-
// Ties our new clientId to our old one on reconnect
|
|
202
|
-
Rejoin = "rejoin",
|
|
203
|
-
|
|
204
|
-
// Sets the alias of a root data store
|
|
205
|
-
Alias = "alias",
|
|
206
|
-
|
|
207
|
-
/**
|
|
208
|
-
* An op containing an IdRange of Ids allocated using the runtime's IdCompressor since
|
|
209
|
-
* the last allocation op was sent.
|
|
210
|
-
* See the [IdCompressor README](./id-compressor/README.md) for more details.
|
|
211
|
-
*/
|
|
212
|
-
IdAllocation = "idAllocation",
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
/**
|
|
216
|
-
* How should an older client handle an unrecognized remote op type?
|
|
217
|
-
*
|
|
218
|
-
* @internal
|
|
219
|
-
*/
|
|
220
|
-
export type CompatModeBehavior =
|
|
221
|
-
/** Ignore the op. It won't be persisted if this client summarizes */
|
|
222
|
-
| "Ignore"
|
|
223
|
-
/** Fail processing immediately. (The container will close) */
|
|
224
|
-
| "FailToProcess";
|
|
225
|
-
|
|
226
|
-
/**
|
|
227
|
-
* All the info an older client would need to know how to handle an unrecognized remote op type
|
|
228
|
-
*
|
|
229
|
-
* @internal
|
|
230
|
-
*/
|
|
231
|
-
export interface IContainerRuntimeMessageCompatDetails {
|
|
232
|
-
/** How should an older client handle an unrecognized remote op type? */
|
|
233
|
-
behavior: CompatModeBehavior;
|
|
234
|
-
}
|
|
186
|
+
import {
|
|
187
|
+
ContainerMessageType,
|
|
188
|
+
type InboundSequencedContainerRuntimeMessage,
|
|
189
|
+
type InboundSequencedContainerRuntimeMessageOrSystemMessage,
|
|
190
|
+
type ContainerRuntimeIdAllocationMessage,
|
|
191
|
+
type LocalContainerRuntimeIdAllocationMessage,
|
|
192
|
+
type LocalContainerRuntimeMessage,
|
|
193
|
+
type OutboundContainerRuntimeMessage,
|
|
194
|
+
type UnknownContainerRuntimeMessage,
|
|
195
|
+
} from "./messageTypes";
|
|
235
196
|
|
|
236
197
|
/**
|
|
237
198
|
* Utility to implement compat behaviors given an unknown message type
|
|
238
199
|
* The parameters are typed to support compile-time enforcement of handling all known types/behaviors
|
|
239
200
|
*
|
|
240
|
-
* @param _unknownContainerRuntimeMessageType - Typed as
|
|
201
|
+
* @param _unknownContainerRuntimeMessageType - Typed as something unexpected, to ensure all known types have been
|
|
241
202
|
* handled before calling this function (e.g. in a switch statement).
|
|
242
203
|
* @param compatBehavior - Typed redundantly with CompatModeBehavior to ensure handling is added when updating that type
|
|
243
204
|
*/
|
|
244
205
|
function compatBehaviorAllowsMessageType(
|
|
245
|
-
_unknownContainerRuntimeMessageType:
|
|
206
|
+
_unknownContainerRuntimeMessageType: UnknownContainerRuntimeMessage["type"],
|
|
246
207
|
compatBehavior: "Ignore" | "FailToProcess" | undefined,
|
|
247
208
|
): boolean {
|
|
248
209
|
// undefined defaults to same behavior as "FailToProcess"
|
|
249
210
|
return compatBehavior === "Ignore";
|
|
250
211
|
}
|
|
251
212
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
/** Type of the op, within the ContainerRuntime's domain */
|
|
260
|
-
type: ContainerMessageType;
|
|
261
|
-
/** Domain-specific contents, interpreted according to the type */
|
|
262
|
-
contents: any;
|
|
263
|
-
/** Info describing how to handle this op in case the type is unrecognized (default: fail to process) */
|
|
264
|
-
compatDetails?: IContainerRuntimeMessageCompatDetails;
|
|
213
|
+
function prepareLocalContainerRuntimeIdAllocationMessageForTransit(
|
|
214
|
+
message: LocalContainerRuntimeIdAllocationMessage | ContainerRuntimeIdAllocationMessage,
|
|
215
|
+
): asserts message is ContainerRuntimeIdAllocationMessage {
|
|
216
|
+
// Remove the stashedState from the op if it's a stashed op
|
|
217
|
+
if ("stashedState" in message.contents) {
|
|
218
|
+
delete message.contents.stashedState;
|
|
219
|
+
}
|
|
265
220
|
}
|
|
266
221
|
|
|
267
|
-
/**
|
|
268
|
-
* An unpacked ISequencedDocumentMessage with the inner ContainerRuntimeMessage type/contents/etc
|
|
269
|
-
* promoted up to the outer object
|
|
270
|
-
*/
|
|
271
|
-
export type SequencedContainerRuntimeMessage = ISequencedDocumentMessage & ContainerRuntimeMessage;
|
|
272
|
-
|
|
273
222
|
export interface ISummaryBaseConfiguration {
|
|
274
223
|
/**
|
|
275
224
|
* Delay before first attempt to spawn summarizing container.
|
|
@@ -597,7 +546,7 @@ export const defaultPendingOpsRetryDelayMs = 1000;
|
|
|
597
546
|
const defaultCloseSummarizerDelayMs = 5000; // 5 seconds
|
|
598
547
|
|
|
599
548
|
/**
|
|
600
|
-
* @deprecated - use
|
|
549
|
+
* @deprecated - use ContainerRuntimeMessageType instead
|
|
601
550
|
*/
|
|
602
551
|
export enum RuntimeMessage {
|
|
603
552
|
FluidDataStoreOp = "component",
|
|
@@ -660,6 +609,23 @@ export const makeLegacySendBatchFn =
|
|
|
660
609
|
deltaManager.flush();
|
|
661
610
|
};
|
|
662
611
|
|
|
612
|
+
/** Helper type for type constraints passed through several functions.
|
|
613
|
+
* message - The unpacked message. Likely a TypedContainerRuntimeMessage, but could also be a system op
|
|
614
|
+
* modernRuntimeMessage - Does this appear like a current TypedContainerRuntimeMessage?
|
|
615
|
+
* local - Did this client send the op?
|
|
616
|
+
*/
|
|
617
|
+
type MessageWithContext =
|
|
618
|
+
| {
|
|
619
|
+
message: InboundSequencedContainerRuntimeMessage;
|
|
620
|
+
modernRuntimeMessage: true;
|
|
621
|
+
local: boolean;
|
|
622
|
+
}
|
|
623
|
+
| {
|
|
624
|
+
message: InboundSequencedContainerRuntimeMessageOrSystemMessage;
|
|
625
|
+
modernRuntimeMessage: false;
|
|
626
|
+
local: boolean;
|
|
627
|
+
};
|
|
628
|
+
|
|
663
629
|
/**
|
|
664
630
|
* Represents the runtime of the container. Contains helper functions/state of the container.
|
|
665
631
|
* It will define the store level mappings.
|
|
@@ -889,10 +855,9 @@ export class ContainerRuntime
|
|
|
889
855
|
initializeEntryPoint,
|
|
890
856
|
);
|
|
891
857
|
|
|
892
|
-
|
|
893
|
-
//
|
|
894
|
-
|
|
895
|
-
await runtime.pendingStateManager.applyStashedOpsAt(0);
|
|
858
|
+
// Apply stashed ops with a reference sequence number equal to the sequence number of the snapshot,
|
|
859
|
+
// or zero. This must be done before Container replays saved ops.
|
|
860
|
+
await runtime.pendingStateManager.applyStashedOpsAt(runtimeSequenceNumber ?? 0);
|
|
896
861
|
|
|
897
862
|
// Initialize the base state of the runtime before it's returned.
|
|
898
863
|
await runtime.initializeBaseState();
|
|
@@ -1638,35 +1603,6 @@ export class ContainerRuntime
|
|
|
1638
1603
|
}
|
|
1639
1604
|
}
|
|
1640
1605
|
|
|
1641
|
-
this.deltaManager.on("readonly", (readonly: boolean) => {
|
|
1642
|
-
// we accumulate ops while being in read-only state.
|
|
1643
|
-
// once user gets write permissions and we have active connection, flush all pending ops.
|
|
1644
|
-
// Note that the inner (non-proxy) delta manager is needed here to get the readonly information.
|
|
1645
|
-
assert(
|
|
1646
|
-
readonly === this.innerDeltaManager.readOnlyInfo.readonly,
|
|
1647
|
-
0x124 /* "inconsistent readonly property/event state" */,
|
|
1648
|
-
);
|
|
1649
|
-
|
|
1650
|
-
// We need to be very careful with when we (re)send pending ops, to ensure that we only send ops
|
|
1651
|
-
// when we either never send an op, or attempted to send it but we know for sure it was not
|
|
1652
|
-
// sequenced by server and will never be sequenced (i.e. was lost)
|
|
1653
|
-
// For loss of connection, we wait for our own "join" op and use it a a barrier to know all the
|
|
1654
|
-
// ops that made it from previous connection, before switching clientId and raising "connected" event
|
|
1655
|
-
// But with read-only permissions, if we transition between read-only and r/w states while on same
|
|
1656
|
-
// connection, then we have no good signal to tell us when it's safe to send ops we accumulated while
|
|
1657
|
-
// being in read-only state.
|
|
1658
|
-
// For that reason, we support getting to read-only state only when disconnected. This ensures that we
|
|
1659
|
-
// can rely on same safety mechanism and resend ops only when we establish new connection.
|
|
1660
|
-
// This is applicable for read-only permissions (event is raised before connection is properly registered),
|
|
1661
|
-
// but it's an extra requirement for Container.forceReadonly() API
|
|
1662
|
-
assert(
|
|
1663
|
-
!readonly || !this.connected,
|
|
1664
|
-
0x125 /* "Unsafe to transition to read-only state!" */,
|
|
1665
|
-
);
|
|
1666
|
-
|
|
1667
|
-
this.replayPendingStates();
|
|
1668
|
-
});
|
|
1669
|
-
|
|
1670
1606
|
// logging hardware telemetry
|
|
1671
1607
|
logger.sendTelemetryEvent({
|
|
1672
1608
|
eventName: "DeviceSpec",
|
|
@@ -1826,7 +1762,10 @@ export class ContainerRuntime
|
|
|
1826
1762
|
return this.dataStores.aliases.get(maybeAlias) ?? maybeAlias;
|
|
1827
1763
|
}
|
|
1828
1764
|
|
|
1829
|
-
private async getDataStoreFromRequest(
|
|
1765
|
+
private async getDataStoreFromRequest(
|
|
1766
|
+
id: string,
|
|
1767
|
+
request: IRequest,
|
|
1768
|
+
): Promise<IFluidDataStoreChannel> {
|
|
1830
1769
|
const headerData: RuntimeHeaderData = {};
|
|
1831
1770
|
if (typeof request.headers?.[RuntimeHeaders.wait] === "boolean") {
|
|
1832
1771
|
headerData.wait = request.headers[RuntimeHeaders.wait];
|
|
@@ -2015,28 +1954,28 @@ export class ContainerRuntime
|
|
|
2015
1954
|
* Parse an op's type and actual content from given serialized content
|
|
2016
1955
|
* ! Note: this format needs to be in-line with what is set in the "ContainerRuntime.submit(...)" method
|
|
2017
1956
|
*/
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
assert(type !== undefined, 0x6d6 /* incorrect op content format */);
|
|
2023
|
-
return
|
|
1957
|
+
// TODO: markfields: confirm Local- versus Outbound- ContainerRuntimeMessage typing
|
|
1958
|
+
private parseLocalOpContent(serializedContents?: string): LocalContainerRuntimeMessage {
|
|
1959
|
+
assert(serializedContents !== undefined, 0x6d5 /* content must be defined */);
|
|
1960
|
+
const message: LocalContainerRuntimeMessage = JSON.parse(serializedContents);
|
|
1961
|
+
assert(message.type !== undefined, 0x6d6 /* incorrect op content format */);
|
|
1962
|
+
return message;
|
|
2024
1963
|
}
|
|
2025
1964
|
|
|
2026
|
-
private async applyStashedOp(
|
|
1965
|
+
private async applyStashedOp(serializedOpContent: string): Promise<unknown> {
|
|
2027
1966
|
// Need to parse from string for back-compat
|
|
2028
|
-
const
|
|
2029
|
-
switch (type) {
|
|
1967
|
+
const opContents = this.parseLocalOpContent(serializedOpContent);
|
|
1968
|
+
switch (opContents.type) {
|
|
2030
1969
|
case ContainerMessageType.FluidDataStoreOp:
|
|
2031
|
-
return this.dataStores.applyStashedOp(contents
|
|
1970
|
+
return this.dataStores.applyStashedOp(opContents.contents);
|
|
2032
1971
|
case ContainerMessageType.Attach:
|
|
2033
|
-
return this.dataStores.applyStashedAttachOp(contents
|
|
1972
|
+
return this.dataStores.applyStashedAttachOp(opContents.contents);
|
|
2034
1973
|
case ContainerMessageType.IdAllocation:
|
|
2035
1974
|
assert(
|
|
2036
1975
|
this.idCompressor !== undefined,
|
|
2037
1976
|
0x67b /* IdCompressor should be defined if enabled */,
|
|
2038
1977
|
);
|
|
2039
|
-
return this.applyStashedIdAllocationOp(contents
|
|
1978
|
+
return this.applyStashedIdAllocationOp(opContents.contents);
|
|
2040
1979
|
case ContainerMessageType.Alias:
|
|
2041
1980
|
case ContainerMessageType.BlobAttach:
|
|
2042
1981
|
return;
|
|
@@ -2048,15 +1987,15 @@ export class ContainerRuntime
|
|
|
2048
1987
|
// This should be extremely rare for stashed ops.
|
|
2049
1988
|
// It would require a newer runtime stashing ops and then an older one applying them,
|
|
2050
1989
|
// e.g. if an app rolled back its container version
|
|
2051
|
-
const compatBehavior = compatDetails?.behavior;
|
|
2052
|
-
if (!compatBehaviorAllowsMessageType(type, compatBehavior)) {
|
|
1990
|
+
const compatBehavior = opContents.compatDetails?.behavior;
|
|
1991
|
+
if (!compatBehaviorAllowsMessageType(opContents.type, compatBehavior)) {
|
|
2053
1992
|
const error = DataProcessingError.create(
|
|
2054
1993
|
"Stashed runtime message of unknown type",
|
|
2055
1994
|
"applyStashedOp",
|
|
2056
1995
|
undefined /* sequencedMessage */,
|
|
2057
1996
|
{
|
|
2058
1997
|
messageDetails: JSON.stringify({
|
|
2059
|
-
type,
|
|
1998
|
+
type: opContents.type,
|
|
2060
1999
|
compatBehavior,
|
|
2061
2000
|
}),
|
|
2062
2001
|
},
|
|
@@ -2078,6 +2017,30 @@ export class ContainerRuntime
|
|
|
2078
2017
|
return;
|
|
2079
2018
|
}
|
|
2080
2019
|
|
|
2020
|
+
// If there are stashed blobs in the pending state, we need to delay
|
|
2021
|
+
// propagation of the "connected" event until we have uploaded them to
|
|
2022
|
+
// ensure we don't submit ops referencing a blob that has not been uploaded
|
|
2023
|
+
const connecting = connected && !this._connected;
|
|
2024
|
+
if (connecting && this.blobManager.hasPendingStashedBlobs()) {
|
|
2025
|
+
assert(
|
|
2026
|
+
!this.delayConnectClientId,
|
|
2027
|
+
0x791 /* Connect event delay must be canceled before subsequent connect event */,
|
|
2028
|
+
);
|
|
2029
|
+
assert(!!clientId, 0x792 /* Must have clientId when connecting */);
|
|
2030
|
+
this.delayConnectClientId = clientId;
|
|
2031
|
+
this.blobManager.processStashedChanges().then(
|
|
2032
|
+
() => {
|
|
2033
|
+
// make sure we didn't reconnect before the promise resolved
|
|
2034
|
+
if (this.delayConnectClientId === clientId && !this.disposed) {
|
|
2035
|
+
this.delayConnectClientId = undefined;
|
|
2036
|
+
this.setConnectionStateCore(connected, clientId);
|
|
2037
|
+
}
|
|
2038
|
+
},
|
|
2039
|
+
(error) => this.closeFn(error),
|
|
2040
|
+
);
|
|
2041
|
+
return;
|
|
2042
|
+
}
|
|
2043
|
+
|
|
2081
2044
|
this.setConnectionStateCore(connected, clientId);
|
|
2082
2045
|
}
|
|
2083
2046
|
|
|
@@ -2156,9 +2119,25 @@ export class ContainerRuntime
|
|
|
2156
2119
|
const modernRuntimeMessage = messageArg.type === MessageType.Operation;
|
|
2157
2120
|
|
|
2158
2121
|
// Do shallow copy of message, as the processing flow will modify it.
|
|
2122
|
+
// There might be multiple container instances receiving the same message.
|
|
2123
|
+
// We do not need to make a deep copy. Each layer will just replace message.contents itself,
|
|
2124
|
+
// but will not modify the contents object (likely it will replace it on the message).
|
|
2159
2125
|
const messageCopy = { ...messageArg };
|
|
2160
2126
|
for (const message of this.remoteMessageProcessor.process(messageCopy)) {
|
|
2161
|
-
|
|
2127
|
+
if (modernRuntimeMessage) {
|
|
2128
|
+
this.processCore({
|
|
2129
|
+
// Cast it since we expect it to be this based on modernRuntimeMessage computation above.
|
|
2130
|
+
// There is nothing really ensuring that anytime original message.type is Operation that
|
|
2131
|
+
// the result messages will be so. In the end modern bool being true only directs to
|
|
2132
|
+
// throw error if ultimately unrecognized without compat details saying otherwise.
|
|
2133
|
+
message: message as InboundSequencedContainerRuntimeMessage,
|
|
2134
|
+
local,
|
|
2135
|
+
modernRuntimeMessage,
|
|
2136
|
+
});
|
|
2137
|
+
} else {
|
|
2138
|
+
// Unrecognized message will be ignored.
|
|
2139
|
+
this.processCore({ message, local, modernRuntimeMessage });
|
|
2140
|
+
}
|
|
2162
2141
|
}
|
|
2163
2142
|
}
|
|
2164
2143
|
|
|
@@ -2166,15 +2145,9 @@ export class ContainerRuntime
|
|
|
2166
2145
|
|
|
2167
2146
|
/**
|
|
2168
2147
|
* Direct the message to the correct subsystem for processing, and implement other side effects
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
*/
|
|
2173
|
-
private processCore(
|
|
2174
|
-
message: ISequencedDocumentMessage | SequencedContainerRuntimeMessage,
|
|
2175
|
-
local: boolean,
|
|
2176
|
-
modernRuntimeMessage: boolean,
|
|
2177
|
-
) {
|
|
2148
|
+
*/
|
|
2149
|
+
private processCore(messageWithContext: MessageWithContext) {
|
|
2150
|
+
const { message, local } = messageWithContext;
|
|
2178
2151
|
// Surround the actual processing of the operation with messages to the schedule manager indicating
|
|
2179
2152
|
// the beginning and end. This allows it to emit appropriate events and/or pause the processing of new
|
|
2180
2153
|
// messages once a batch has been fully processed.
|
|
@@ -2184,9 +2157,13 @@ export class ContainerRuntime
|
|
|
2184
2157
|
|
|
2185
2158
|
try {
|
|
2186
2159
|
let localOpMetadata: unknown;
|
|
2187
|
-
if (
|
|
2160
|
+
if (
|
|
2161
|
+
local &&
|
|
2162
|
+
messageWithContext.modernRuntimeMessage &&
|
|
2163
|
+
message.type !== ContainerMessageType.ChunkedOp
|
|
2164
|
+
) {
|
|
2188
2165
|
localOpMetadata = this.pendingStateManager.processPendingLocalMessage(
|
|
2189
|
-
message
|
|
2166
|
+
messageWithContext.message,
|
|
2190
2167
|
);
|
|
2191
2168
|
}
|
|
2192
2169
|
|
|
@@ -2196,14 +2173,9 @@ export class ContainerRuntime
|
|
|
2196
2173
|
this.updateDocumentDirtyState(false);
|
|
2197
2174
|
}
|
|
2198
2175
|
|
|
2199
|
-
this.validateAndProcessRuntimeMessage(
|
|
2200
|
-
message,
|
|
2201
|
-
localOpMetadata,
|
|
2202
|
-
local,
|
|
2203
|
-
modernRuntimeMessage,
|
|
2204
|
-
);
|
|
2176
|
+
this.validateAndProcessRuntimeMessage(messageWithContext, localOpMetadata);
|
|
2205
2177
|
|
|
2206
|
-
this.emit("op", message, modernRuntimeMessage);
|
|
2178
|
+
this.emit("op", message, messageWithContext.modernRuntimeMessage);
|
|
2207
2179
|
|
|
2208
2180
|
this.scheduleManager.afterOpProcessing(undefined, message);
|
|
2209
2181
|
|
|
@@ -2219,39 +2191,43 @@ export class ContainerRuntime
|
|
|
2219
2191
|
}
|
|
2220
2192
|
}
|
|
2221
2193
|
/**
|
|
2222
|
-
* Assuming the given message is also a
|
|
2194
|
+
* Assuming the given message is also a TypedContainerRuntimeMessage,
|
|
2223
2195
|
* checks its type and dispatches the message to the appropriate handler in the runtime.
|
|
2224
|
-
* Throws a DataProcessingError if the message doesn't conform to
|
|
2196
|
+
* Throws a DataProcessingError if the message looks like but doesn't conform to a known TypedContainerRuntimeMessage type.
|
|
2225
2197
|
*/
|
|
2226
2198
|
private validateAndProcessRuntimeMessage(
|
|
2227
|
-
|
|
2199
|
+
messageWithContext: MessageWithContext,
|
|
2228
2200
|
localOpMetadata: unknown,
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
const { type: maybeContainerMessageType, compatDetails } =
|
|
2234
|
-
message as ContainerRuntimeMessage;
|
|
2235
|
-
|
|
2236
|
-
switch (maybeContainerMessageType) {
|
|
2201
|
+
): void {
|
|
2202
|
+
// TODO: destructure message and modernRuntimeMessage once using typescript 5.2.2+
|
|
2203
|
+
const { local } = messageWithContext;
|
|
2204
|
+
switch (messageWithContext.message.type) {
|
|
2237
2205
|
case ContainerMessageType.Attach:
|
|
2238
|
-
this.dataStores.processAttachMessage(message, local);
|
|
2206
|
+
this.dataStores.processAttachMessage(messageWithContext.message, local);
|
|
2239
2207
|
break;
|
|
2240
2208
|
case ContainerMessageType.Alias:
|
|
2241
|
-
this.dataStores.processAliasMessage(
|
|
2209
|
+
this.dataStores.processAliasMessage(
|
|
2210
|
+
messageWithContext.message,
|
|
2211
|
+
localOpMetadata,
|
|
2212
|
+
local,
|
|
2213
|
+
);
|
|
2242
2214
|
break;
|
|
2243
2215
|
case ContainerMessageType.FluidDataStoreOp:
|
|
2244
|
-
this.dataStores.processFluidDataStoreOp(
|
|
2216
|
+
this.dataStores.processFluidDataStoreOp(
|
|
2217
|
+
messageWithContext.message,
|
|
2218
|
+
local,
|
|
2219
|
+
localOpMetadata,
|
|
2220
|
+
);
|
|
2245
2221
|
break;
|
|
2246
2222
|
case ContainerMessageType.BlobAttach:
|
|
2247
|
-
this.blobManager.processBlobAttachOp(message, local);
|
|
2223
|
+
this.blobManager.processBlobAttachOp(messageWithContext.message, local);
|
|
2248
2224
|
break;
|
|
2249
2225
|
case ContainerMessageType.IdAllocation:
|
|
2250
2226
|
assert(
|
|
2251
2227
|
this.idCompressor !== undefined,
|
|
2252
2228
|
0x67c /* IdCompressor should be defined if enabled */,
|
|
2253
2229
|
);
|
|
2254
|
-
this.idCompressor.finalizeCreationRange(message.contents
|
|
2230
|
+
this.idCompressor.finalizeCreationRange(messageWithContext.message.contents);
|
|
2255
2231
|
break;
|
|
2256
2232
|
case ContainerMessageType.ChunkedOp:
|
|
2257
2233
|
case ContainerMessageType.Rejoin:
|
|
@@ -2259,12 +2235,18 @@ export class ContainerRuntime
|
|
|
2259
2235
|
default: {
|
|
2260
2236
|
// If we didn't necessarily expect a runtime message type, then no worries - just return
|
|
2261
2237
|
// e.g. this case applies to system ops, or legacy ops that would have fallen into the above cases anyway.
|
|
2262
|
-
if (!
|
|
2238
|
+
if (!messageWithContext.modernRuntimeMessage) {
|
|
2263
2239
|
return;
|
|
2264
2240
|
}
|
|
2265
2241
|
|
|
2266
|
-
const compatBehavior = compatDetails?.behavior;
|
|
2267
|
-
if (
|
|
2242
|
+
const compatBehavior = messageWithContext.message.compatDetails?.behavior;
|
|
2243
|
+
if (
|
|
2244
|
+
!compatBehaviorAllowsMessageType(
|
|
2245
|
+
messageWithContext.message.type,
|
|
2246
|
+
compatBehavior,
|
|
2247
|
+
)
|
|
2248
|
+
) {
|
|
2249
|
+
const { message } = messageWithContext;
|
|
2268
2250
|
const error = DataProcessingError.create(
|
|
2269
2251
|
// Former assert 0x3ce
|
|
2270
2252
|
"Runtime message of unknown type",
|
|
@@ -2434,8 +2416,8 @@ export class ContainerRuntime
|
|
|
2434
2416
|
/**
|
|
2435
2417
|
* Returns the aliased data store's entryPoint, given the alias.
|
|
2436
2418
|
* @param alias - The alias for the data store.
|
|
2437
|
-
* @returns
|
|
2438
|
-
* data store has been assigned the given alias.
|
|
2419
|
+
* @returns The data store's entry point ({@link @fluidframework/core-interfaces#IFluidHandle}) if it exists and is aliased.
|
|
2420
|
+
* Returns undefined if no data store has been assigned the given alias.
|
|
2439
2421
|
*/
|
|
2440
2422
|
public async getAliasedDataStoreEntryPoint(
|
|
2441
2423
|
alias: string,
|
|
@@ -2535,7 +2517,7 @@ export class ContainerRuntime
|
|
|
2535
2517
|
return this.dirtyContainer;
|
|
2536
2518
|
}
|
|
2537
2519
|
|
|
2538
|
-
private isContainerMessageDirtyable({ type, contents }:
|
|
2520
|
+
private isContainerMessageDirtyable({ type, contents }: OutboundContainerRuntimeMessage) {
|
|
2539
2521
|
// For legacy purposes, exclude the old built-in AgentScheduler from dirty consideration as a special-case.
|
|
2540
2522
|
// Ultimately we should have no special-cases from the ContainerRuntime's perspective.
|
|
2541
2523
|
if (type === ContainerMessageType.Attach) {
|
|
@@ -2797,7 +2779,7 @@ export class ContainerRuntime
|
|
|
2797
2779
|
/**
|
|
2798
2780
|
* After GC has run and identified nodes that are sweep ready, this is called to delete the sweep ready nodes.
|
|
2799
2781
|
* @param sweepReadyRoutes - The routes of nodes that are sweep ready and should be deleted.
|
|
2800
|
-
* @returns
|
|
2782
|
+
* @returns The routes of nodes that were deleted.
|
|
2801
2783
|
*/
|
|
2802
2784
|
public deleteSweepReadyNodes(sweepReadyRoutes: string[]): string[] {
|
|
2803
2785
|
const { dataStoreRoutes, blobManagerRoutes } =
|
|
@@ -2868,7 +2850,7 @@ export class ContainerRuntime
|
|
|
2868
2850
|
/**
|
|
2869
2851
|
* From a given list of routes, separate and return routes that belong to blob manager and data stores.
|
|
2870
2852
|
* @param routes - A list of routes that can belong to data stores or blob manager.
|
|
2871
|
-
* @returns
|
|
2853
|
+
* @returns Two route lists - One that contains routes for blob manager and another one that contains routes
|
|
2872
2854
|
* for data stores.
|
|
2873
2855
|
*/
|
|
2874
2856
|
private getDataStoreAndBlobManagerRoutes(routes: string[]) {
|
|
@@ -2937,19 +2919,15 @@ export class ContainerRuntime
|
|
|
2937
2919
|
|
|
2938
2920
|
assert(this.outbox.isEmpty, 0x3d1 /* Can't trigger summary in the middle of a batch */);
|
|
2939
2921
|
|
|
2922
|
+
// We close the summarizer and download a new snapshot and reload the container
|
|
2940
2923
|
let latestSnapshotVersionId: string | undefined;
|
|
2941
|
-
if (refreshLatestAck) {
|
|
2942
|
-
|
|
2924
|
+
if (refreshLatestAck === true) {
|
|
2925
|
+
return this.prefetchLatestSummaryThenClose(
|
|
2943
2926
|
createChildLogger({
|
|
2944
2927
|
logger: summaryNumberLogger,
|
|
2945
2928
|
properties: { all: { safeSummary: true } },
|
|
2946
2929
|
}),
|
|
2947
2930
|
);
|
|
2948
|
-
const latestSnapshotRefSeq = latestSnapshotInfo.latestSnapshotRefSeq;
|
|
2949
|
-
latestSnapshotVersionId = latestSnapshotInfo.latestSnapshotVersionId;
|
|
2950
|
-
|
|
2951
|
-
// We might need to catch up to the latest summary's reference sequence number before pausing.
|
|
2952
|
-
await this.waitForDeltaManagerToCatchup(latestSnapshotRefSeq, summaryNumberLogger);
|
|
2953
2931
|
}
|
|
2954
2932
|
|
|
2955
2933
|
// If there are pending (unacked ops), the summary will not be eventual consistent and it may even be
|
|
@@ -3397,7 +3375,7 @@ export class ContainerRuntime
|
|
|
3397
3375
|
}
|
|
3398
3376
|
|
|
3399
3377
|
if (idRange !== undefined) {
|
|
3400
|
-
const idAllocationMessage:
|
|
3378
|
+
const idAllocationMessage: ContainerRuntimeIdAllocationMessage = {
|
|
3401
3379
|
type: ContainerMessageType.IdAllocation,
|
|
3402
3380
|
contents: idRange,
|
|
3403
3381
|
};
|
|
@@ -3417,7 +3395,7 @@ export class ContainerRuntime
|
|
|
3417
3395
|
}
|
|
3418
3396
|
|
|
3419
3397
|
private submit(
|
|
3420
|
-
containerRuntimeMessage:
|
|
3398
|
+
containerRuntimeMessage: OutboundContainerRuntimeMessage,
|
|
3421
3399
|
localOpMetadata: unknown = undefined,
|
|
3422
3400
|
metadata: Record<string, unknown> | undefined = undefined,
|
|
3423
3401
|
): void {
|
|
@@ -3613,39 +3591,36 @@ export class ContainerRuntime
|
|
|
3613
3591
|
|
|
3614
3592
|
private reSubmit(message: IPendingBatchMessage) {
|
|
3615
3593
|
// Need to parse from string for back-compat
|
|
3616
|
-
const containerRuntimeMessage = this.
|
|
3594
|
+
const containerRuntimeMessage = this.parseLocalOpContent(message.content);
|
|
3617
3595
|
this.reSubmitCore(containerRuntimeMessage, message.localOpMetadata, message.opMetadata);
|
|
3618
3596
|
}
|
|
3619
3597
|
|
|
3620
3598
|
/**
|
|
3621
3599
|
* Finds the right store and asks it to resubmit the message. This typically happens when we
|
|
3622
3600
|
* reconnect and there are pending messages.
|
|
3623
|
-
* @param message - The original
|
|
3601
|
+
* @param message - The original LocalContainerRuntimeMessage.
|
|
3624
3602
|
* @param localOpMetadata - The local metadata associated with the original message.
|
|
3625
3603
|
*/
|
|
3626
3604
|
private reSubmitCore(
|
|
3627
|
-
message:
|
|
3605
|
+
message: LocalContainerRuntimeMessage,
|
|
3628
3606
|
localOpMetadata: unknown,
|
|
3629
3607
|
opMetadata: Record<string, unknown> | undefined,
|
|
3630
3608
|
) {
|
|
3631
|
-
const contents = message.contents;
|
|
3632
3609
|
switch (message.type) {
|
|
3633
3610
|
case ContainerMessageType.FluidDataStoreOp:
|
|
3634
3611
|
// For Operations, call resubmitDataStoreOp which will find the right store
|
|
3635
3612
|
// and trigger resubmission on it.
|
|
3636
|
-
this.dataStores.resubmitDataStoreOp(contents, localOpMetadata);
|
|
3613
|
+
this.dataStores.resubmitDataStoreOp(message.contents, localOpMetadata);
|
|
3637
3614
|
break;
|
|
3638
3615
|
case ContainerMessageType.Attach:
|
|
3639
3616
|
case ContainerMessageType.Alias:
|
|
3640
3617
|
this.submit(message, localOpMetadata);
|
|
3641
3618
|
break;
|
|
3642
|
-
case ContainerMessageType.IdAllocation:
|
|
3643
|
-
|
|
3644
|
-
if (contents.stashedState !== undefined) {
|
|
3645
|
-
delete contents.stashedState;
|
|
3646
|
-
}
|
|
3619
|
+
case ContainerMessageType.IdAllocation: {
|
|
3620
|
+
prepareLocalContainerRuntimeIdAllocationMessageForTransit(message);
|
|
3647
3621
|
this.submit(message, localOpMetadata);
|
|
3648
3622
|
break;
|
|
3623
|
+
}
|
|
3649
3624
|
case ContainerMessageType.ChunkedOp:
|
|
3650
3625
|
throw new Error(`chunkedOp not expected here`);
|
|
3651
3626
|
case ContainerMessageType.BlobAttach:
|
|
@@ -3684,7 +3659,7 @@ export class ContainerRuntime
|
|
|
3684
3659
|
|
|
3685
3660
|
private rollback(content: string | undefined, localOpMetadata: unknown) {
|
|
3686
3661
|
// Need to parse from string for back-compat
|
|
3687
|
-
const { type, contents } = this.
|
|
3662
|
+
const { type, contents } = this.parseLocalOpContent(content);
|
|
3688
3663
|
switch (type) {
|
|
3689
3664
|
case ContainerMessageType.FluidDataStoreOp:
|
|
3690
3665
|
// For operations, call rollbackDataStoreOp which will find the right store
|
|
@@ -3697,26 +3672,6 @@ export class ContainerRuntime
|
|
|
3697
3672
|
}
|
|
3698
3673
|
}
|
|
3699
3674
|
|
|
3700
|
-
private async waitForDeltaManagerToCatchup(
|
|
3701
|
-
latestSnapshotRefSeq: number,
|
|
3702
|
-
summaryLogger: ITelemetryLoggerExt,
|
|
3703
|
-
): Promise<void> {
|
|
3704
|
-
if (latestSnapshotRefSeq > this.deltaManager.lastSequenceNumber) {
|
|
3705
|
-
// We need to catch up to the latest summary's reference sequence number before proceeding.
|
|
3706
|
-
await PerformanceEvent.timedExecAsync(
|
|
3707
|
-
summaryLogger,
|
|
3708
|
-
{
|
|
3709
|
-
eventName: "WaitingForSeq",
|
|
3710
|
-
lastSequenceNumber: this.deltaManager.lastSequenceNumber,
|
|
3711
|
-
targetSequenceNumber: latestSnapshotRefSeq,
|
|
3712
|
-
lastKnownSeqNumber: this.deltaManager.lastKnownSeqNumber,
|
|
3713
|
-
},
|
|
3714
|
-
async () => waitForSeq(this.deltaManager, latestSnapshotRefSeq),
|
|
3715
|
-
{ start: true, end: true, cancel: "error" }, // definitely want start event
|
|
3716
|
-
);
|
|
3717
|
-
}
|
|
3718
|
-
}
|
|
3719
|
-
|
|
3720
3675
|
/** Implementation of ISummarizerInternalsProvider.refreshLatestSummaryAck */
|
|
3721
3676
|
public async refreshLatestSummaryAck(options: IRefreshSummaryAckOptions) {
|
|
3722
3677
|
const { proposalHandle, ackHandle, summaryRefSeq, summaryLogger } = options;
|
|
@@ -3780,16 +3735,19 @@ export class ContainerRuntime
|
|
|
3780
3735
|
}
|
|
3781
3736
|
|
|
3782
3737
|
/**
|
|
3783
|
-
* Fetches the latest snapshot from storage
|
|
3784
|
-
*
|
|
3738
|
+
* Fetches the latest snapshot from storage to refresh the cache as a performance optimization and closes the
|
|
3739
|
+
* summarizer to reload from new state.
|
|
3785
3740
|
* @param summaryLogger - logger to use when fetching snapshot from storage
|
|
3786
|
-
* @returns
|
|
3741
|
+
* @returns a generic summarization error
|
|
3787
3742
|
*/
|
|
3788
|
-
private async
|
|
3743
|
+
private async prefetchLatestSummaryThenClose(
|
|
3789
3744
|
summaryLogger: ITelemetryLoggerExt,
|
|
3790
|
-
): Promise<
|
|
3745
|
+
): Promise<IBaseSummarizeResult> {
|
|
3791
3746
|
const readAndParseBlob = async <T>(id: string) => readAndParse<T>(this.storage, id);
|
|
3792
|
-
|
|
3747
|
+
|
|
3748
|
+
// This is a performance optimization as the same parent is likely to be elected again, and would use its
|
|
3749
|
+
// cache to fetch the snapshot instead of the network.
|
|
3750
|
+
await this.fetchSnapshotFromStorage(
|
|
3793
3751
|
summaryLogger,
|
|
3794
3752
|
{
|
|
3795
3753
|
eventName: "RefreshLatestSummaryFromServerFetch",
|
|
@@ -3800,7 +3758,12 @@ export class ContainerRuntime
|
|
|
3800
3758
|
|
|
3801
3759
|
await this.closeStaleSummarizer("RefreshLatestSummaryFromServerFetch");
|
|
3802
3760
|
|
|
3803
|
-
return {
|
|
3761
|
+
return {
|
|
3762
|
+
stage: "base",
|
|
3763
|
+
error: "summary state stale - Unsupported option 'refreshLatestAck'",
|
|
3764
|
+
referenceSequenceNumber: this.deltaManager.lastSequenceNumber,
|
|
3765
|
+
minimumSequenceNumber: this.deltaManager.minimumSequenceNumber,
|
|
3766
|
+
};
|
|
3804
3767
|
}
|
|
3805
3768
|
|
|
3806
3769
|
private async closeStaleSummarizer(codePath: string): Promise<void> {
|
|
@@ -3853,7 +3816,7 @@ export class ContainerRuntime
|
|
|
3853
3816
|
const versions = await this.storage.getVersions(
|
|
3854
3817
|
versionId,
|
|
3855
3818
|
1,
|
|
3856
|
-
"
|
|
3819
|
+
"prefetchLatestSummaryBeforeClose",
|
|
3857
3820
|
versionId === null ? FetchSource.noCache : undefined,
|
|
3858
3821
|
);
|
|
3859
3822
|
assert(
|
|
@@ -3896,17 +3859,17 @@ export class ContainerRuntime
|
|
|
3896
3859
|
if (this._orderSequentiallyCalls !== 0) {
|
|
3897
3860
|
throw new UsageError("can't get state during orderSequentially");
|
|
3898
3861
|
}
|
|
3899
|
-
const pendingAttachmentBlobs = await this.blobManager.getPendingBlobs(
|
|
3900
|
-
waitBlobsToAttach,
|
|
3901
|
-
);
|
|
3902
|
-
const pending = this.pendingStateManager.getLocalState();
|
|
3903
|
-
if (!pendingAttachmentBlobs && !this.hasPendingMessages()) {
|
|
3904
|
-
return; // no pending state to save
|
|
3905
|
-
}
|
|
3906
3862
|
// Flush pending batch.
|
|
3907
3863
|
// getPendingLocalState() is only exposed through Container.closeAndGetPendingLocalState(), so it's safe
|
|
3908
3864
|
// to close current batch.
|
|
3909
3865
|
this.flush();
|
|
3866
|
+
const pendingAttachmentBlobs = waitBlobsToAttach
|
|
3867
|
+
? await this.blobManager.attachAndGetPendingBlobs()
|
|
3868
|
+
: undefined;
|
|
3869
|
+
const pending = this.pendingStateManager.getLocalState();
|
|
3870
|
+
if (!pendingAttachmentBlobs && !this.hasPendingMessages()) {
|
|
3871
|
+
return; // no pending state to save
|
|
3872
|
+
}
|
|
3910
3873
|
|
|
3911
3874
|
const pendingState: IPendingRuntimeState = {
|
|
3912
3875
|
pending,
|
|
@@ -4003,30 +3966,3 @@ export class ContainerRuntime
|
|
|
4003
3966
|
return killSwitch !== true && this.runtimeOptions.enableGroupedBatching;
|
|
4004
3967
|
}
|
|
4005
3968
|
}
|
|
4006
|
-
|
|
4007
|
-
/**
|
|
4008
|
-
* Wait for a specific sequence number. Promise should resolve when we reach that number,
|
|
4009
|
-
* or reject if closed.
|
|
4010
|
-
*/
|
|
4011
|
-
const waitForSeq = async (
|
|
4012
|
-
deltaManager: IDeltaManager<Pick<ISequencedDocumentMessage, "sequenceNumber">, unknown>,
|
|
4013
|
-
targetSeq: number,
|
|
4014
|
-
): Promise<void> =>
|
|
4015
|
-
new Promise<void>((resolve, reject) => {
|
|
4016
|
-
// TODO: remove cast to any when actual event is determined
|
|
4017
|
-
deltaManager.on("closed" as any, reject);
|
|
4018
|
-
deltaManager.on("disposed" as any, reject);
|
|
4019
|
-
|
|
4020
|
-
// If we already reached target sequence number, simply resolve the promise.
|
|
4021
|
-
if (deltaManager.lastSequenceNumber >= targetSeq) {
|
|
4022
|
-
resolve();
|
|
4023
|
-
} else {
|
|
4024
|
-
const handleOp = (message: Pick<ISequencedDocumentMessage, "sequenceNumber">) => {
|
|
4025
|
-
if (message.sequenceNumber >= targetSeq) {
|
|
4026
|
-
resolve();
|
|
4027
|
-
deltaManager.off("op", handleOp);
|
|
4028
|
-
}
|
|
4029
|
-
};
|
|
4030
|
-
deltaManager.on("op", handleOp);
|
|
4031
|
-
}
|
|
4032
|
-
});
|