@fluidframework/container-runtime 2.1.0 → 2.2.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 +30 -0
- package/README.md +2 -2
- package/api-report/container-runtime.legacy.alpha.api.md +4 -3
- package/container-runtime.test-files.tar +0 -0
- package/dist/batchTracker.d.ts.map +1 -1
- package/dist/batchTracker.js.map +1 -1
- package/dist/blobManager/blobManager.d.ts.map +1 -1
- package/dist/blobManager/blobManager.js +9 -0
- package/dist/blobManager/blobManager.js.map +1 -1
- package/dist/channelCollection.d.ts +0 -14
- package/dist/channelCollection.d.ts.map +1 -1
- package/dist/channelCollection.js +2 -12
- package/dist/channelCollection.js.map +1 -1
- package/dist/containerRuntime.d.ts +34 -6
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +176 -81
- package/dist/containerRuntime.js.map +1 -1
- package/dist/dataStoreContext.d.ts +9 -18
- package/dist/dataStoreContext.d.ts.map +1 -1
- package/dist/dataStoreContext.js +40 -78
- package/dist/dataStoreContext.js.map +1 -1
- package/dist/gc/garbageCollection.d.ts +0 -6
- package/dist/gc/garbageCollection.d.ts.map +1 -1
- package/dist/gc/garbageCollection.js +23 -66
- package/dist/gc/garbageCollection.js.map +1 -1
- package/dist/gc/gcConfigs.d.ts.map +1 -1
- package/dist/gc/gcConfigs.js +11 -34
- package/dist/gc/gcConfigs.js.map +1 -1
- package/dist/gc/gcDefinitions.d.ts +9 -52
- package/dist/gc/gcDefinitions.d.ts.map +1 -1
- package/dist/gc/gcDefinitions.js +3 -23
- package/dist/gc/gcDefinitions.js.map +1 -1
- package/dist/gc/gcHelpers.d.ts.map +1 -1
- package/dist/gc/gcHelpers.js +2 -6
- package/dist/gc/gcHelpers.js.map +1 -1
- package/dist/gc/gcSummaryStateTracker.d.ts +1 -1
- package/dist/gc/gcSummaryStateTracker.d.ts.map +1 -1
- package/dist/gc/gcSummaryStateTracker.js +4 -8
- package/dist/gc/gcSummaryStateTracker.js.map +1 -1
- package/dist/gc/gcTelemetry.d.ts +1 -9
- package/dist/gc/gcTelemetry.d.ts.map +1 -1
- package/dist/gc/gcTelemetry.js +3 -25
- 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 +2 -7
- package/dist/gc/index.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -2
- package/dist/index.js.map +1 -1
- package/dist/messageTypes.d.ts +6 -5
- package/dist/messageTypes.d.ts.map +1 -1
- package/dist/messageTypes.js.map +1 -1
- package/dist/metadata.d.ts +9 -1
- package/dist/metadata.d.ts.map +1 -1
- package/dist/metadata.js +6 -1
- package/dist/metadata.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.map +1 -1
- package/dist/opLifecycle/opGroupingManager.d.ts +8 -0
- package/dist/opLifecycle/opGroupingManager.d.ts.map +1 -1
- package/dist/opLifecycle/opGroupingManager.js +34 -2
- package/dist/opLifecycle/opGroupingManager.js.map +1 -1
- package/dist/opLifecycle/outbox.d.ts +1 -0
- package/dist/opLifecycle/outbox.d.ts.map +1 -1
- package/dist/opLifecycle/outbox.js +20 -0
- package/dist/opLifecycle/outbox.js.map +1 -1
- package/dist/opLifecycle/remoteMessageProcessor.d.ts +34 -15
- package/dist/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
- package/dist/opLifecycle/remoteMessageProcessor.js +59 -46
- 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 +28 -27
- package/dist/pendingStateManager.d.ts.map +1 -1
- package/dist/pendingStateManager.js +143 -112
- package/dist/pendingStateManager.js.map +1 -1
- package/dist/summary/summarizerNode/summarizerNode.d.ts.map +1 -1
- package/dist/summary/summarizerNode/summarizerNode.js +5 -1
- package/dist/summary/summarizerNode/summarizerNode.js.map +1 -1
- package/dist/summary/summarizerNode/summarizerNodeWithGc.d.ts +3 -4
- package/dist/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
- package/dist/summary/summarizerNode/summarizerNodeWithGc.js +16 -15
- package/dist/summary/summarizerNode/summarizerNodeWithGc.js.map +1 -1
- package/lib/batchTracker.d.ts.map +1 -1
- package/lib/batchTracker.js.map +1 -1
- package/lib/blobManager/blobManager.d.ts.map +1 -1
- package/lib/blobManager/blobManager.js +9 -0
- package/lib/blobManager/blobManager.js.map +1 -1
- package/lib/channelCollection.d.ts +0 -14
- package/lib/channelCollection.d.ts.map +1 -1
- package/lib/channelCollection.js +2 -11
- package/lib/channelCollection.js.map +1 -1
- package/lib/containerRuntime.d.ts +34 -6
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +176 -81
- package/lib/containerRuntime.js.map +1 -1
- package/lib/dataStoreContext.d.ts +9 -18
- package/lib/dataStoreContext.d.ts.map +1 -1
- package/lib/dataStoreContext.js +27 -65
- package/lib/dataStoreContext.js.map +1 -1
- package/lib/gc/garbageCollection.d.ts +0 -6
- package/lib/gc/garbageCollection.d.ts.map +1 -1
- package/lib/gc/garbageCollection.js +25 -68
- package/lib/gc/garbageCollection.js.map +1 -1
- package/lib/gc/gcConfigs.d.ts.map +1 -1
- package/lib/gc/gcConfigs.js +12 -35
- package/lib/gc/gcConfigs.js.map +1 -1
- package/lib/gc/gcDefinitions.d.ts +9 -52
- package/lib/gc/gcDefinitions.d.ts.map +1 -1
- package/lib/gc/gcDefinitions.js +2 -22
- package/lib/gc/gcDefinitions.js.map +1 -1
- package/lib/gc/gcHelpers.d.ts.map +1 -1
- package/lib/gc/gcHelpers.js +2 -6
- package/lib/gc/gcHelpers.js.map +1 -1
- package/lib/gc/gcSummaryStateTracker.d.ts +1 -1
- package/lib/gc/gcSummaryStateTracker.d.ts.map +1 -1
- package/lib/gc/gcSummaryStateTracker.js +4 -8
- package/lib/gc/gcSummaryStateTracker.js.map +1 -1
- package/lib/gc/gcTelemetry.d.ts +1 -9
- package/lib/gc/gcTelemetry.d.ts.map +1 -1
- package/lib/gc/gcTelemetry.js +3 -24
- 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 +1 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -1
- package/lib/index.js.map +1 -1
- package/lib/messageTypes.d.ts +6 -5
- package/lib/messageTypes.d.ts.map +1 -1
- package/lib/messageTypes.js.map +1 -1
- package/lib/metadata.d.ts +9 -1
- package/lib/metadata.d.ts.map +1 -1
- package/lib/metadata.js +4 -0
- package/lib/metadata.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.map +1 -1
- package/lib/opLifecycle/opGroupingManager.d.ts +8 -0
- package/lib/opLifecycle/opGroupingManager.d.ts.map +1 -1
- package/lib/opLifecycle/opGroupingManager.js +34 -2
- package/lib/opLifecycle/opGroupingManager.js.map +1 -1
- package/lib/opLifecycle/outbox.d.ts +1 -0
- package/lib/opLifecycle/outbox.d.ts.map +1 -1
- package/lib/opLifecycle/outbox.js +20 -0
- package/lib/opLifecycle/outbox.js.map +1 -1
- package/lib/opLifecycle/remoteMessageProcessor.d.ts +34 -15
- package/lib/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
- package/lib/opLifecycle/remoteMessageProcessor.js +59 -46
- 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 +28 -27
- package/lib/pendingStateManager.d.ts.map +1 -1
- package/lib/pendingStateManager.js +144 -113
- package/lib/pendingStateManager.js.map +1 -1
- package/lib/summary/summarizerNode/summarizerNode.d.ts.map +1 -1
- package/lib/summary/summarizerNode/summarizerNode.js +5 -1
- package/lib/summary/summarizerNode/summarizerNode.js.map +1 -1
- package/lib/summary/summarizerNode/summarizerNodeWithGc.d.ts +3 -4
- package/lib/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
- package/lib/summary/summarizerNode/summarizerNodeWithGc.js +16 -15
- package/lib/summary/summarizerNode/summarizerNodeWithGc.js.map +1 -1
- package/package.json +22 -31
- package/src/batchTracker.ts +4 -2
- package/src/blobManager/blobManager.ts +9 -0
- package/src/channelCollection.ts +2 -11
- package/src/containerRuntime.ts +211 -109
- package/src/dataStoreContext.ts +29 -93
- package/src/gc/garbageCollection.ts +26 -79
- package/src/gc/gcConfigs.ts +12 -45
- package/src/gc/gcDefinitions.ts +10 -55
- package/src/gc/gcHelpers.ts +10 -8
- package/src/gc/gcSummaryStateTracker.ts +6 -9
- package/src/gc/gcTelemetry.ts +3 -38
- package/src/gc/index.ts +2 -6
- package/src/index.ts +0 -1
- package/src/messageTypes.ts +12 -11
- package/src/metadata.ts +16 -2
- package/src/opLifecycle/index.ts +1 -0
- package/src/opLifecycle/opGroupingManager.ts +42 -3
- package/src/opLifecycle/outbox.ts +30 -0
- package/src/opLifecycle/remoteMessageProcessor.ts +98 -62
- package/src/packageVersion.ts +1 -1
- package/src/pendingStateManager.ts +199 -177
- package/src/summary/README.md +31 -28
- package/src/summary/summarizerNode/summarizerNode.ts +6 -1
- package/src/summary/summarizerNode/summarizerNodeWithGc.ts +20 -43
- package/src/summary/summaryFormats.md +25 -22
package/src/containerRuntime.ts
CHANGED
|
@@ -162,7 +162,7 @@ import {
|
|
|
162
162
|
ContainerRuntimeGCMessage,
|
|
163
163
|
type ContainerRuntimeIdAllocationMessage,
|
|
164
164
|
type InboundSequencedContainerRuntimeMessage,
|
|
165
|
-
type
|
|
165
|
+
type InboundSequencedNonContainerRuntimeMessage,
|
|
166
166
|
type LocalContainerRuntimeMessage,
|
|
167
167
|
type OutboundContainerRuntimeMessage,
|
|
168
168
|
type UnknownContainerRuntimeMessage,
|
|
@@ -180,6 +180,7 @@ import {
|
|
|
180
180
|
OpSplitter,
|
|
181
181
|
Outbox,
|
|
182
182
|
RemoteMessageProcessor,
|
|
183
|
+
type InboundBatch,
|
|
183
184
|
} from "./opLifecycle/index.js";
|
|
184
185
|
import { pkgVersion } from "./packageVersion.js";
|
|
185
186
|
import {
|
|
@@ -522,6 +523,9 @@ export const TombstoneResponseHeaderKey = "isTombstoned";
|
|
|
522
523
|
* Inactive error responses will have this header set to true
|
|
523
524
|
* @legacy
|
|
524
525
|
* @alpha
|
|
526
|
+
*
|
|
527
|
+
* @deprecated this header is deprecated and will be removed in the future. The functionality corresponding
|
|
528
|
+
* to this was experimental and is no longer supported.
|
|
525
529
|
*/
|
|
526
530
|
export const InactiveResponseHeaderKey = "isInactive";
|
|
527
531
|
|
|
@@ -533,7 +537,6 @@ export interface RuntimeHeaderData {
|
|
|
533
537
|
wait?: boolean;
|
|
534
538
|
viaHandle?: boolean;
|
|
535
539
|
allowTombstone?: boolean;
|
|
536
|
-
allowInactive?: boolean;
|
|
537
540
|
}
|
|
538
541
|
|
|
539
542
|
/** Default values for Runtime Headers */
|
|
@@ -541,7 +544,6 @@ export const defaultRuntimeHeaderData: Required<RuntimeHeaderData> = {
|
|
|
541
544
|
wait: true,
|
|
542
545
|
viaHandle: false,
|
|
543
546
|
allowTombstone: false,
|
|
544
|
-
allowInactive: false,
|
|
545
547
|
};
|
|
546
548
|
|
|
547
549
|
/**
|
|
@@ -629,6 +631,7 @@ export const defaultPendingOpsRetryDelayMs = 1000;
|
|
|
629
631
|
const defaultCloseSummarizerDelayMs = 5000; // 5 seconds
|
|
630
632
|
|
|
631
633
|
/**
|
|
634
|
+
* Checks whether a message.type is one of the values in ContainerMessageType
|
|
632
635
|
* @deprecated please use version in driver-utils
|
|
633
636
|
* @internal
|
|
634
637
|
*/
|
|
@@ -688,9 +691,7 @@ export const makeLegacySendBatchFn =
|
|
|
688
691
|
/** Helper type for type constraints passed through several functions.
|
|
689
692
|
* local - Did this client send the op?
|
|
690
693
|
* savedOp - Is this op being replayed after being serialized (having been sequenced previously)
|
|
691
|
-
*
|
|
692
|
-
* message - The unpacked message. Likely a TypedContainerRuntimeMessage, but could also be a system op
|
|
693
|
-
* modernRuntimeMessage - Does this appear like a current TypedContainerRuntimeMessage?
|
|
694
|
+
* localOpMetadata - Metadata maintained locally for a local op.
|
|
694
695
|
*/
|
|
695
696
|
type MessageWithContext = {
|
|
696
697
|
local: boolean;
|
|
@@ -699,14 +700,13 @@ type MessageWithContext = {
|
|
|
699
700
|
} & (
|
|
700
701
|
| {
|
|
701
702
|
message: InboundSequencedContainerRuntimeMessage;
|
|
702
|
-
|
|
703
|
+
isRuntimeMessage: true;
|
|
703
704
|
}
|
|
704
705
|
| {
|
|
705
|
-
message:
|
|
706
|
-
|
|
706
|
+
message: InboundSequencedNonContainerRuntimeMessage;
|
|
707
|
+
isRuntimeMessage: false;
|
|
707
708
|
}
|
|
708
709
|
);
|
|
709
|
-
|
|
710
710
|
const summarizerRequestUrl = "_summarizer";
|
|
711
711
|
|
|
712
712
|
/**
|
|
@@ -761,6 +761,23 @@ function lastMessageFromMetadata(metadata: IContainerRuntimeMetadata | undefined
|
|
|
761
761
|
: metadata?.message;
|
|
762
762
|
}
|
|
763
763
|
|
|
764
|
+
/**
|
|
765
|
+
* There is some ancient back-compat code that we'd like to instrument
|
|
766
|
+
* to understand if/when it is hit.
|
|
767
|
+
* We only want to log this once, to avoid spamming telemetry if we are wrong and these cases are hit commonly.
|
|
768
|
+
*/
|
|
769
|
+
let getSingleUseLegacyLogCallback = (logger: ITelemetryLoggerExt, type: string) => {
|
|
770
|
+
// We only want to log this once per ContainerRuntime instance, to avoid spamming telemetry.
|
|
771
|
+
getSingleUseLegacyLogCallback = () => () => {};
|
|
772
|
+
|
|
773
|
+
return (codePath: string) => {
|
|
774
|
+
logger.sendTelemetryEvent({
|
|
775
|
+
eventName: "LegacyMessageFormat",
|
|
776
|
+
details: { codePath, type },
|
|
777
|
+
});
|
|
778
|
+
};
|
|
779
|
+
};
|
|
780
|
+
|
|
764
781
|
/**
|
|
765
782
|
* Represents the runtime of the container. Contains helper functions/state of the container.
|
|
766
783
|
* It will define the store level mappings.
|
|
@@ -1219,6 +1236,7 @@ export class ContainerRuntime
|
|
|
1219
1236
|
|
|
1220
1237
|
private _orderSequentiallyCalls: number = 0;
|
|
1221
1238
|
private readonly _flushMode: FlushMode;
|
|
1239
|
+
private readonly offlineEnabled: boolean;
|
|
1222
1240
|
private flushTaskExists = false;
|
|
1223
1241
|
|
|
1224
1242
|
private _connected: boolean;
|
|
@@ -1330,14 +1348,22 @@ export class ContainerRuntime
|
|
|
1330
1348
|
*/
|
|
1331
1349
|
private nextSummaryNumber: number;
|
|
1332
1350
|
|
|
1333
|
-
/**
|
|
1351
|
+
/**
|
|
1352
|
+
* If false, loading or using a Tombstoned object should merely log, not fail.
|
|
1353
|
+
* @deprecated NOT SUPPORTED - hardcoded to return false since it's deprecated.
|
|
1354
|
+
*/
|
|
1355
|
+
// eslint-disable-next-line @typescript-eslint/class-literal-property-style
|
|
1334
1356
|
public get gcTombstoneEnforcementAllowed(): boolean {
|
|
1335
|
-
return
|
|
1357
|
+
return false;
|
|
1336
1358
|
}
|
|
1337
1359
|
|
|
1338
|
-
/**
|
|
1360
|
+
/**
|
|
1361
|
+
* If true, throw an error when a tombstone data store is used.
|
|
1362
|
+
* @deprecated NOT SUPPORTED - hardcoded to return false since it's deprecated.
|
|
1363
|
+
*/
|
|
1364
|
+
// eslint-disable-next-line @typescript-eslint/class-literal-property-style
|
|
1339
1365
|
public get gcThrowOnTombstoneUsage(): boolean {
|
|
1340
|
-
return
|
|
1366
|
+
return false;
|
|
1341
1367
|
}
|
|
1342
1368
|
|
|
1343
1369
|
/**
|
|
@@ -1547,7 +1573,6 @@ export class ContainerRuntime
|
|
|
1547
1573
|
{
|
|
1548
1574
|
applyStashedOp: this.applyStashedOp.bind(this),
|
|
1549
1575
|
clientId: () => this.clientId,
|
|
1550
|
-
close: this.closeFn,
|
|
1551
1576
|
connected: () => this.connected,
|
|
1552
1577
|
reSubmitBatch: this.reSubmitBatch.bind(this),
|
|
1553
1578
|
isActiveConnection: () => this.innerDeltaManager.active,
|
|
@@ -1602,6 +1627,14 @@ export class ContainerRuntime
|
|
|
1602
1627
|
} else {
|
|
1603
1628
|
this._flushMode = runtimeOptions.flushMode;
|
|
1604
1629
|
}
|
|
1630
|
+
this.offlineEnabled =
|
|
1631
|
+
this.mc.config.getBoolean("Fluid.Container.enableOfflineLoad") ?? false;
|
|
1632
|
+
|
|
1633
|
+
if (this.offlineEnabled && this._flushMode !== FlushMode.TurnBased) {
|
|
1634
|
+
const error = new UsageError("Offline mode is only supported in turn-based mode");
|
|
1635
|
+
this.closeFn(error);
|
|
1636
|
+
throw error;
|
|
1637
|
+
}
|
|
1605
1638
|
|
|
1606
1639
|
if (context.attachState === AttachState.Attached) {
|
|
1607
1640
|
const maxSnapshotCacheDurationMs = this._storage?.policies?.maximumCacheDurationMs;
|
|
@@ -2609,73 +2642,92 @@ export class ContainerRuntime
|
|
|
2609
2642
|
await this.pendingStateManager.applyStashedOpsAt(message.sequenceNumber);
|
|
2610
2643
|
}
|
|
2611
2644
|
|
|
2612
|
-
|
|
2645
|
+
/**
|
|
2646
|
+
* Processes the op.
|
|
2647
|
+
* @param messageCopy - Sequenced message for a distributed document.
|
|
2648
|
+
* @param local - true if the message was originally generated by the client receiving it.
|
|
2649
|
+
*/
|
|
2650
|
+
public process({ ...messageCopy }: ISequencedDocumentMessage, local: boolean) {
|
|
2651
|
+
// spread operator above ensure we make a shallow copy of message, as the processing flow will modify it.
|
|
2652
|
+
// There might be multiple container instances receiving the same message.
|
|
2653
|
+
|
|
2613
2654
|
this.verifyNotClosed();
|
|
2614
2655
|
|
|
2615
2656
|
// Whether or not the message appears to be a runtime message from an up-to-date client.
|
|
2616
2657
|
// It may be a legacy runtime message (ie already unpacked and ContainerMessageType)
|
|
2617
2658
|
// or something different, like a system message.
|
|
2618
|
-
const
|
|
2619
|
-
|
|
2620
|
-
const
|
|
2659
|
+
const hasModernRuntimeMessageEnvelope = messageCopy.type === MessageType.Operation;
|
|
2660
|
+
const savedOp = (messageCopy.metadata as ISavedOpMetadata)?.savedOp;
|
|
2661
|
+
const logLegacyCase = getSingleUseLegacyLogCallback(this.logger, messageCopy.type);
|
|
2621
2662
|
|
|
2622
|
-
// There is some ancient back-compat code that we'd like to instrument
|
|
2623
|
-
// to understand if/when it is hit.
|
|
2624
|
-
const logLegacyCase = (codePath: string) =>
|
|
2625
|
-
this.logger.sendTelemetryEvent({
|
|
2626
|
-
eventName: "LegacyMessageFormat",
|
|
2627
|
-
details: { codePath, type: messageArg.type },
|
|
2628
|
-
});
|
|
2629
|
-
|
|
2630
|
-
// Do shallow copy of message, as the processing flow will modify it.
|
|
2631
|
-
// There might be multiple container instances receiving the same message.
|
|
2632
|
-
// We do not need to make a deep copy. Each layer will just replace message.contents itself,
|
|
2633
|
-
// but will not modify the contents object (likely it will replace it on the message).
|
|
2634
|
-
const messageCopy = { ...messageArg };
|
|
2635
2663
|
// We expect runtime messages to have JSON contents - deserialize it in place.
|
|
2636
|
-
ensureContentsDeserialized(messageCopy,
|
|
2637
|
-
if (
|
|
2638
|
-
|
|
2639
|
-
|
|
2664
|
+
ensureContentsDeserialized(messageCopy, hasModernRuntimeMessageEnvelope, logLegacyCase);
|
|
2665
|
+
if (hasModernRuntimeMessageEnvelope) {
|
|
2666
|
+
// If the message has the modern message envelope, then process it here.
|
|
2667
|
+
// Here we unpack the message (decompress, unchunk, and/or ungroup) into a batch of messages with ContainerMessageType
|
|
2668
|
+
const inboundBatch = this.remoteMessageProcessor.process(messageCopy, logLegacyCase);
|
|
2669
|
+
if (inboundBatch === undefined) {
|
|
2640
2670
|
// This means the incoming message is an incomplete part of a message or batch
|
|
2641
2671
|
// and we need to process more messages before the rest of the system can understand it.
|
|
2642
2672
|
return;
|
|
2643
2673
|
}
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
const
|
|
2647
|
-
|
|
2648
|
-
localOpMetadata: unknown;
|
|
2649
|
-
}[] = local
|
|
2650
|
-
? this.pendingStateManager.processPendingLocalBatch(batch, batchStartCsn)
|
|
2651
|
-
: batch.map((message) => ({ message, localOpMetadata: undefined }));
|
|
2652
|
-
messages.forEach(({ message, localOpMetadata }) => {
|
|
2653
|
-
const msg: MessageWithContext = {
|
|
2654
|
-
message,
|
|
2655
|
-
local,
|
|
2656
|
-
modernRuntimeMessage,
|
|
2657
|
-
savedOp,
|
|
2658
|
-
localOpMetadata,
|
|
2659
|
-
};
|
|
2660
|
-
this.ensureNoDataModelChanges(() => this.processCore(msg));
|
|
2661
|
-
});
|
|
2662
|
-
} else {
|
|
2663
|
-
const msg: MessageWithContext = {
|
|
2664
|
-
message: messageCopy as InboundSequencedContainerRuntimeMessageOrSystemMessage,
|
|
2674
|
+
|
|
2675
|
+
// Reach out to PendingStateManager to zip localOpMetadata into the message list if it's a local batch
|
|
2676
|
+
const messagesWithPendingState = this.pendingStateManager.processInboundBatch(
|
|
2677
|
+
inboundBatch,
|
|
2665
2678
|
local,
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2679
|
+
);
|
|
2680
|
+
if (messagesWithPendingState.length > 0) {
|
|
2681
|
+
messagesWithPendingState.forEach(({ message, localOpMetadata }) => {
|
|
2682
|
+
const msg: MessageWithContext = {
|
|
2683
|
+
message,
|
|
2684
|
+
local,
|
|
2685
|
+
isRuntimeMessage: true,
|
|
2686
|
+
savedOp,
|
|
2687
|
+
localOpMetadata,
|
|
2688
|
+
};
|
|
2689
|
+
this.ensureNoDataModelChanges(() => this.processRuntimeMessage(msg));
|
|
2690
|
+
});
|
|
2691
|
+
} else {
|
|
2692
|
+
this.ensureNoDataModelChanges(() => this.processEmptyBatch(inboundBatch, local));
|
|
2693
|
+
}
|
|
2694
|
+
} else {
|
|
2695
|
+
// Check if message.type is one of values in ContainerMessageType
|
|
2696
|
+
// eslint-disable-next-line import/no-deprecated
|
|
2697
|
+
if (isRuntimeMessage(messageCopy)) {
|
|
2698
|
+
// Legacy op received
|
|
2699
|
+
this.ensureNoDataModelChanges(() =>
|
|
2700
|
+
this.processRuntimeMessage({
|
|
2701
|
+
message: messageCopy as InboundSequencedContainerRuntimeMessage,
|
|
2702
|
+
local,
|
|
2703
|
+
isRuntimeMessage: true,
|
|
2704
|
+
savedOp,
|
|
2705
|
+
}),
|
|
2706
|
+
);
|
|
2707
|
+
} else {
|
|
2708
|
+
// A non container runtime message (like other system ops - join, ack, leave, nack etc.)
|
|
2709
|
+
this.ensureNoDataModelChanges(() =>
|
|
2710
|
+
this.observeNonRuntimeMessage({
|
|
2711
|
+
message: messageCopy as InboundSequencedNonContainerRuntimeMessage,
|
|
2712
|
+
local,
|
|
2713
|
+
isRuntimeMessage: false,
|
|
2714
|
+
savedOp,
|
|
2715
|
+
}),
|
|
2716
|
+
);
|
|
2717
|
+
}
|
|
2670
2718
|
}
|
|
2671
2719
|
}
|
|
2672
2720
|
|
|
2673
2721
|
private _processedClientSequenceNumber: number | undefined;
|
|
2674
2722
|
|
|
2675
2723
|
/**
|
|
2676
|
-
*
|
|
2724
|
+
* Processes messages that are intended for the runtime layer to process.
|
|
2725
|
+
* It redirects the message to the correct subsystem for processing, and implement other side effects
|
|
2726
|
+
* @param messageWithContext - message to process with additional context and isRuntimeMessage prop as true
|
|
2677
2727
|
*/
|
|
2678
|
-
private
|
|
2728
|
+
private processRuntimeMessage(
|
|
2729
|
+
messageWithContext: MessageWithContext & { isRuntimeMessage: true },
|
|
2730
|
+
) {
|
|
2679
2731
|
const { message, local, localOpMetadata } = messageWithContext;
|
|
2680
2732
|
|
|
2681
2733
|
// Intercept to reduce minimum sequence number to the delta manager's minimum sequence number.
|
|
@@ -2710,7 +2762,75 @@ export class ContainerRuntime
|
|
|
2710
2762
|
|
|
2711
2763
|
this.validateAndProcessRuntimeMessage(messageWithContext, localOpMetadata);
|
|
2712
2764
|
|
|
2713
|
-
this.emit("op", message, messageWithContext.
|
|
2765
|
+
this.emit("op", message, messageWithContext.isRuntimeMessage);
|
|
2766
|
+
|
|
2767
|
+
this.scheduleManager.afterOpProcessing(undefined, message);
|
|
2768
|
+
|
|
2769
|
+
if (local) {
|
|
2770
|
+
// If we have processed a local op, this means that the container is
|
|
2771
|
+
// making progress and we can reset the counter for how many times
|
|
2772
|
+
// we have consecutively replayed the pending states
|
|
2773
|
+
this.resetReconnectCount();
|
|
2774
|
+
}
|
|
2775
|
+
} catch (e) {
|
|
2776
|
+
this.scheduleManager.afterOpProcessing(e, message);
|
|
2777
|
+
throw e;
|
|
2778
|
+
}
|
|
2779
|
+
}
|
|
2780
|
+
|
|
2781
|
+
/**
|
|
2782
|
+
* Process an empty batch, which will execute expected actions while processing even if there are no messages.
|
|
2783
|
+
* This is a separate function because the processCore function expects at least one message to process.
|
|
2784
|
+
* It is expected to happen only when the outbox produces an empty batch due to a resubmit flow.
|
|
2785
|
+
*/
|
|
2786
|
+
private processEmptyBatch(emptyBatch: InboundBatch, local: boolean) {
|
|
2787
|
+
const { emptyBatchSequenceNumber: sequenceNumber, batchStartCsn } = emptyBatch;
|
|
2788
|
+
assert(sequenceNumber !== undefined, 0x9fa /* emptyBatchSequenceNumber must be defined */);
|
|
2789
|
+
this.emit("batchBegin", { sequenceNumber });
|
|
2790
|
+
this._processedClientSequenceNumber = batchStartCsn;
|
|
2791
|
+
if (!this.hasPendingMessages()) {
|
|
2792
|
+
this.updateDocumentDirtyState(false);
|
|
2793
|
+
}
|
|
2794
|
+
this.emit("batchEnd", undefined, { sequenceNumber });
|
|
2795
|
+
if (local) {
|
|
2796
|
+
this.resetReconnectCount();
|
|
2797
|
+
}
|
|
2798
|
+
}
|
|
2799
|
+
|
|
2800
|
+
/**
|
|
2801
|
+
* Observes messages that are not intended for the runtime layer, updating/notifying Runtime systems as needed.
|
|
2802
|
+
* @param messageWithContext - non-runtime messages to process with additional context and isRuntimeMessage prop as false
|
|
2803
|
+
*/
|
|
2804
|
+
private observeNonRuntimeMessage(
|
|
2805
|
+
messageWithContext: MessageWithContext & { isRuntimeMessage: false },
|
|
2806
|
+
) {
|
|
2807
|
+
const { message, local } = messageWithContext;
|
|
2808
|
+
|
|
2809
|
+
// Intercept to reduce minimum sequence number to the delta manager's minimum sequence number.
|
|
2810
|
+
// Sequence numbers are not guaranteed to follow any sort of order. Re-entrancy is one of those situations
|
|
2811
|
+
if (
|
|
2812
|
+
this.deltaManager.minimumSequenceNumber <
|
|
2813
|
+
messageWithContext.message.minimumSequenceNumber
|
|
2814
|
+
) {
|
|
2815
|
+
messageWithContext.message.minimumSequenceNumber =
|
|
2816
|
+
this.deltaManager.minimumSequenceNumber;
|
|
2817
|
+
}
|
|
2818
|
+
|
|
2819
|
+
// Surround the actual processing of the operation with messages to the schedule manager indicating
|
|
2820
|
+
// the beginning and end. This allows it to emit appropriate events and/or pause the processing of new
|
|
2821
|
+
// messages once a batch has been fully processed.
|
|
2822
|
+
this.scheduleManager.beforeOpProcessing(message);
|
|
2823
|
+
|
|
2824
|
+
this._processedClientSequenceNumber = message.clientSequenceNumber;
|
|
2825
|
+
|
|
2826
|
+
try {
|
|
2827
|
+
// If there are no more pending messages after processing a local message,
|
|
2828
|
+
// the document is no longer dirty.
|
|
2829
|
+
if (!this.hasPendingMessages()) {
|
|
2830
|
+
this.updateDocumentDirtyState(false);
|
|
2831
|
+
}
|
|
2832
|
+
|
|
2833
|
+
this.emit("op", message, messageWithContext.isRuntimeMessage);
|
|
2714
2834
|
|
|
2715
2835
|
this.scheduleManager.afterOpProcessing(undefined, message);
|
|
2716
2836
|
|
|
@@ -2725,33 +2845,33 @@ export class ContainerRuntime
|
|
|
2725
2845
|
throw e;
|
|
2726
2846
|
}
|
|
2727
2847
|
}
|
|
2848
|
+
|
|
2728
2849
|
/**
|
|
2729
2850
|
* Assuming the given message is also a TypedContainerRuntimeMessage,
|
|
2730
2851
|
* checks its type and dispatches the message to the appropriate handler in the runtime.
|
|
2731
2852
|
* Throws a DataProcessingError if the message looks like but doesn't conform to a known TypedContainerRuntimeMessage type.
|
|
2732
2853
|
*/
|
|
2733
2854
|
private validateAndProcessRuntimeMessage(
|
|
2734
|
-
messageWithContext: MessageWithContext,
|
|
2855
|
+
messageWithContext: MessageWithContext & { isRuntimeMessage: true },
|
|
2735
2856
|
localOpMetadata: unknown,
|
|
2736
2857
|
): void {
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
switch (messageWithContext.message.type) {
|
|
2858
|
+
const { local, message, savedOp } = messageWithContext;
|
|
2859
|
+
switch (message.type) {
|
|
2740
2860
|
case ContainerMessageType.Attach:
|
|
2741
2861
|
case ContainerMessageType.Alias:
|
|
2742
2862
|
case ContainerMessageType.FluidDataStoreOp:
|
|
2743
|
-
this.channelCollection.process(
|
|
2863
|
+
this.channelCollection.process(message, local, localOpMetadata);
|
|
2744
2864
|
break;
|
|
2745
2865
|
case ContainerMessageType.BlobAttach:
|
|
2746
|
-
this.blobManager.processBlobAttachOp(
|
|
2866
|
+
this.blobManager.processBlobAttachOp(message, local);
|
|
2747
2867
|
break;
|
|
2748
2868
|
case ContainerMessageType.IdAllocation:
|
|
2749
2869
|
// Don't re-finalize the range if we're processing a "savedOp" in
|
|
2750
2870
|
// stashed ops flow. The compressor is stashed with these ops already processed.
|
|
2751
2871
|
// That said, in idCompressorMode === "delayed", we might not serialize ID compressor, and
|
|
2752
2872
|
// thus we need to process all the ops.
|
|
2753
|
-
if (!(this.skipSavedCompressorOps &&
|
|
2754
|
-
const range =
|
|
2873
|
+
if (!(this.skipSavedCompressorOps && savedOp === true)) {
|
|
2874
|
+
const range = message.contents;
|
|
2755
2875
|
// Some other client turned on the id compressor. If we have not turned it on,
|
|
2756
2876
|
// put it in a pending queue and delay finalization.
|
|
2757
2877
|
if (this._idCompressor === undefined) {
|
|
@@ -2770,11 +2890,7 @@ export class ContainerRuntime
|
|
|
2770
2890
|
}
|
|
2771
2891
|
break;
|
|
2772
2892
|
case ContainerMessageType.GC:
|
|
2773
|
-
this.garbageCollector.processMessage(
|
|
2774
|
-
messageWithContext.message,
|
|
2775
|
-
messageWithContext.message.timestamp,
|
|
2776
|
-
local,
|
|
2777
|
-
);
|
|
2893
|
+
this.garbageCollector.processMessage(message, message.timestamp, local);
|
|
2778
2894
|
break;
|
|
2779
2895
|
case ContainerMessageType.ChunkedOp:
|
|
2780
2896
|
// From observability POV, we should not exppse the rest of the system (including "op" events on object) to these messages.
|
|
@@ -2784,23 +2900,14 @@ export class ContainerRuntime
|
|
|
2784
2900
|
break;
|
|
2785
2901
|
case ContainerMessageType.DocumentSchemaChange:
|
|
2786
2902
|
this.documentsSchemaController.processDocumentSchemaOp(
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2903
|
+
message.contents,
|
|
2904
|
+
local,
|
|
2905
|
+
message.sequenceNumber,
|
|
2790
2906
|
);
|
|
2791
2907
|
break;
|
|
2792
2908
|
default: {
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
if (!messageWithContext.modernRuntimeMessage) {
|
|
2796
|
-
return;
|
|
2797
|
-
}
|
|
2798
|
-
|
|
2799
|
-
const compatBehavior = messageWithContext.message.compatDetails?.behavior;
|
|
2800
|
-
if (
|
|
2801
|
-
!compatBehaviorAllowsMessageType(messageWithContext.message.type, compatBehavior)
|
|
2802
|
-
) {
|
|
2803
|
-
const { message } = messageWithContext;
|
|
2909
|
+
const compatBehavior = message.compatDetails?.behavior;
|
|
2910
|
+
if (!compatBehaviorAllowsMessageType(message.type, compatBehavior)) {
|
|
2804
2911
|
const error = DataProcessingError.create(
|
|
2805
2912
|
// Former assert 0x3ce
|
|
2806
2913
|
"Runtime message of unknown type",
|
|
@@ -3982,8 +4089,6 @@ export class ContainerRuntime
|
|
|
3982
4089
|
0x93f /* metadata */,
|
|
3983
4090
|
);
|
|
3984
4091
|
|
|
3985
|
-
const serializedContent = JSON.stringify(containerRuntimeMessage);
|
|
3986
|
-
|
|
3987
4092
|
// Note that the real (non-proxy) delta manager is used here to get the readonly info. This is because
|
|
3988
4093
|
// container runtime's ability to submit ops depend on the actual readonly state of the delta manager.
|
|
3989
4094
|
if (this.innerDeltaManager.readOnlyInfo.readonly) {
|
|
@@ -3998,12 +4103,6 @@ export class ContainerRuntime
|
|
|
3998
4103
|
type !== ContainerMessageType.IdAllocation,
|
|
3999
4104
|
0x9a5 /* IdAllocation should be submitted directly to outbox. */,
|
|
4000
4105
|
);
|
|
4001
|
-
const message: BatchMessage = {
|
|
4002
|
-
contents: serializedContent,
|
|
4003
|
-
metadata,
|
|
4004
|
-
localOpMetadata,
|
|
4005
|
-
referenceSequenceNumber: this.deltaManager.lastSequenceNumber,
|
|
4006
|
-
};
|
|
4007
4106
|
|
|
4008
4107
|
try {
|
|
4009
4108
|
this.submitIdAllocationOpIfNeeded(false);
|
|
@@ -4011,19 +4110,19 @@ export class ContainerRuntime
|
|
|
4011
4110
|
// Allow document schema controller to send a message if it needs to propose change in document schema.
|
|
4012
4111
|
// If it needs to send a message, it will call provided callback with payload of such message and rely
|
|
4013
4112
|
// on this callback to do actual sending.
|
|
4014
|
-
const
|
|
4015
|
-
if (
|
|
4113
|
+
const schemaChangeMessage = this.documentsSchemaController.maybeSendSchemaMessage();
|
|
4114
|
+
if (schemaChangeMessage) {
|
|
4016
4115
|
this.logger.sendTelemetryEvent({
|
|
4017
4116
|
eventName: "SchemaChangeProposal",
|
|
4018
|
-
refSeq:
|
|
4019
|
-
version:
|
|
4020
|
-
newRuntimeSchema: JSON.stringify(
|
|
4117
|
+
refSeq: schemaChangeMessage.refSeq,
|
|
4118
|
+
version: schemaChangeMessage.version,
|
|
4119
|
+
newRuntimeSchema: JSON.stringify(schemaChangeMessage.runtime),
|
|
4021
4120
|
sessionRuntimeSchema: JSON.stringify(this.sessionSchema),
|
|
4022
4121
|
oldRuntimeSchema: JSON.stringify(this.metadata?.documentSchema?.runtime),
|
|
4023
4122
|
});
|
|
4024
4123
|
const msg: ContainerRuntimeDocumentSchemaMessage = {
|
|
4025
4124
|
type: ContainerMessageType.DocumentSchemaChange,
|
|
4026
|
-
contents,
|
|
4125
|
+
contents: schemaChangeMessage,
|
|
4027
4126
|
};
|
|
4028
4127
|
this.outbox.submit({
|
|
4029
4128
|
contents: JSON.stringify(msg),
|
|
@@ -4031,6 +4130,12 @@ export class ContainerRuntime
|
|
|
4031
4130
|
});
|
|
4032
4131
|
}
|
|
4033
4132
|
|
|
4133
|
+
const message: BatchMessage = {
|
|
4134
|
+
contents: JSON.stringify(containerRuntimeMessage) /* serialized content */,
|
|
4135
|
+
metadata,
|
|
4136
|
+
localOpMetadata,
|
|
4137
|
+
referenceSequenceNumber: this.deltaManager.lastSequenceNumber,
|
|
4138
|
+
};
|
|
4034
4139
|
if (type === ContainerMessageType.BlobAttach) {
|
|
4035
4140
|
// BlobAttach ops must have their metadata visible and cannot be grouped (see opGroupingManager.ts)
|
|
4036
4141
|
this.outbox.submitBlobAttach(message);
|
|
@@ -4130,10 +4235,7 @@ export class ContainerRuntime
|
|
|
4130
4235
|
|
|
4131
4236
|
// Only include Batch ID if "Offline Load" feature is enabled
|
|
4132
4237
|
// It's only needed to identify batches across container forks arising from misuse of offline load.
|
|
4133
|
-
|
|
4134
|
-
this.mc.config.getBoolean("Fluid.Container.enableOfflineLoad") ?? false;
|
|
4135
|
-
|
|
4136
|
-
this.flush(includeBatchId ? batchId : undefined);
|
|
4238
|
+
this.flush(this.offlineEnabled ? batchId : undefined);
|
|
4137
4239
|
}
|
|
4138
4240
|
|
|
4139
4241
|
private reSubmit(message: PendingMessageResubmitData) {
|