@fluidframework/container-runtime 2.1.1 → 2.2.1
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 +181 -90
- 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 +38 -19
- package/dist/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
- package/dist/opLifecycle/remoteMessageProcessor.js +67 -43
- 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 +33 -22
- package/dist/pendingStateManager.d.ts.map +1 -1
- package/dist/pendingStateManager.js +148 -105
- 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 +181 -90
- 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 +38 -19
- package/lib/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
- package/lib/opLifecycle/remoteMessageProcessor.js +67 -43
- 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 +33 -22
- package/lib/pendingStateManager.d.ts.map +1 -1
- package/lib/pendingStateManager.js +149 -106
- 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 +21 -21
- package/src/batchTracker.ts +4 -2
- package/src/blobManager/blobManager.ts +9 -0
- package/src/channelCollection.ts +2 -11
- package/src/containerRuntime.ts +216 -121
- 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 +110 -56
- package/src/packageVersion.ts +1 -1
- package/src/pendingStateManager.ts +209 -168
- 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,25 +691,22 @@ 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;
|
|
697
698
|
savedOp?: boolean;
|
|
698
|
-
|
|
699
|
+
localOpMetadata?: unknown;
|
|
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;
|
|
@@ -1331,14 +1349,22 @@ export class ContainerRuntime
|
|
|
1331
1349
|
*/
|
|
1332
1350
|
private nextSummaryNumber: number;
|
|
1333
1351
|
|
|
1334
|
-
/**
|
|
1352
|
+
/**
|
|
1353
|
+
* If false, loading or using a Tombstoned object should merely log, not fail.
|
|
1354
|
+
* @deprecated NOT SUPPORTED - hardcoded to return false since it's deprecated.
|
|
1355
|
+
*/
|
|
1356
|
+
// eslint-disable-next-line @typescript-eslint/class-literal-property-style
|
|
1335
1357
|
public get gcTombstoneEnforcementAllowed(): boolean {
|
|
1336
|
-
return
|
|
1358
|
+
return false;
|
|
1337
1359
|
}
|
|
1338
1360
|
|
|
1339
|
-
/**
|
|
1361
|
+
/**
|
|
1362
|
+
* If true, throw an error when a tombstone data store is used.
|
|
1363
|
+
* @deprecated NOT SUPPORTED - hardcoded to return false since it's deprecated.
|
|
1364
|
+
*/
|
|
1365
|
+
// eslint-disable-next-line @typescript-eslint/class-literal-property-style
|
|
1340
1366
|
public get gcThrowOnTombstoneUsage(): boolean {
|
|
1341
|
-
return
|
|
1367
|
+
return false;
|
|
1342
1368
|
}
|
|
1343
1369
|
|
|
1344
1370
|
/**
|
|
@@ -1548,7 +1574,6 @@ export class ContainerRuntime
|
|
|
1548
1574
|
{
|
|
1549
1575
|
applyStashedOp: this.applyStashedOp.bind(this),
|
|
1550
1576
|
clientId: () => this.clientId,
|
|
1551
|
-
close: this.closeFn,
|
|
1552
1577
|
connected: () => this.connected,
|
|
1553
1578
|
reSubmitBatch: this.reSubmitBatch.bind(this),
|
|
1554
1579
|
isActiveConnection: () => this.innerDeltaManager.active,
|
|
@@ -1603,6 +1628,14 @@ export class ContainerRuntime
|
|
|
1603
1628
|
} else {
|
|
1604
1629
|
this._flushMode = runtimeOptions.flushMode;
|
|
1605
1630
|
}
|
|
1631
|
+
this.offlineEnabled =
|
|
1632
|
+
this.mc.config.getBoolean("Fluid.Container.enableOfflineLoad") ?? false;
|
|
1633
|
+
|
|
1634
|
+
if (this.offlineEnabled && this._flushMode !== FlushMode.TurnBased) {
|
|
1635
|
+
const error = new UsageError("Offline mode is only supported in turn-based mode");
|
|
1636
|
+
this.closeFn(error);
|
|
1637
|
+
throw error;
|
|
1638
|
+
}
|
|
1606
1639
|
|
|
1607
1640
|
if (context.attachState === AttachState.Attached) {
|
|
1608
1641
|
const maxSnapshotCacheDurationMs = this._storage?.policies?.maximumCacheDurationMs;
|
|
@@ -2610,70 +2643,93 @@ export class ContainerRuntime
|
|
|
2610
2643
|
await this.pendingStateManager.applyStashedOpsAt(message.sequenceNumber);
|
|
2611
2644
|
}
|
|
2612
2645
|
|
|
2613
|
-
|
|
2646
|
+
/**
|
|
2647
|
+
* Processes the op.
|
|
2648
|
+
* @param messageCopy - Sequenced message for a distributed document.
|
|
2649
|
+
* @param local - true if the message was originally generated by the client receiving it.
|
|
2650
|
+
*/
|
|
2651
|
+
public process({ ...messageCopy }: ISequencedDocumentMessage, local: boolean) {
|
|
2652
|
+
// spread operator above ensure we make a shallow copy of message, as the processing flow will modify it.
|
|
2653
|
+
// There might be multiple container instances receiving the same message.
|
|
2654
|
+
|
|
2614
2655
|
this.verifyNotClosed();
|
|
2615
2656
|
|
|
2616
2657
|
// Whether or not the message appears to be a runtime message from an up-to-date client.
|
|
2617
2658
|
// It may be a legacy runtime message (ie already unpacked and ContainerMessageType)
|
|
2618
2659
|
// or something different, like a system message.
|
|
2619
|
-
const
|
|
2620
|
-
|
|
2621
|
-
const
|
|
2622
|
-
|
|
2623
|
-
// There is some ancient back-compat code that we'd like to instrument
|
|
2624
|
-
// to understand if/when it is hit.
|
|
2625
|
-
const logLegacyCase = (codePath: string) =>
|
|
2626
|
-
this.logger.sendTelemetryEvent({
|
|
2627
|
-
eventName: "LegacyMessageFormat",
|
|
2628
|
-
details: { codePath, type: messageArg.type },
|
|
2629
|
-
});
|
|
2660
|
+
const hasModernRuntimeMessageEnvelope = messageCopy.type === MessageType.Operation;
|
|
2661
|
+
const savedOp = (messageCopy.metadata as ISavedOpMetadata)?.savedOp;
|
|
2662
|
+
const logLegacyCase = getSingleUseLegacyLogCallback(this.logger, messageCopy.type);
|
|
2630
2663
|
|
|
2631
|
-
// Do shallow copy of message, as the processing flow will modify it.
|
|
2632
|
-
// There might be multiple container instances receiving the same message.
|
|
2633
|
-
// We do not need to make a deep copy. Each layer will just replace message.contents itself,
|
|
2634
|
-
// but will not modify the contents object (likely it will replace it on the message).
|
|
2635
|
-
const messageCopy = { ...messageArg };
|
|
2636
2664
|
// We expect runtime messages to have JSON contents - deserialize it in place.
|
|
2637
|
-
ensureContentsDeserialized(messageCopy,
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
//
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
: // Unrecognized message will be ignored.
|
|
2657
|
-
{
|
|
2665
|
+
ensureContentsDeserialized(messageCopy, hasModernRuntimeMessageEnvelope, logLegacyCase);
|
|
2666
|
+
if (hasModernRuntimeMessageEnvelope) {
|
|
2667
|
+
// If the message has the modern message envelope, then process it here.
|
|
2668
|
+
// Here we unpack the message (decompress, unchunk, and/or ungroup) into a batch of messages with ContainerMessageType
|
|
2669
|
+
const inboundBatch = this.remoteMessageProcessor.process(messageCopy, logLegacyCase);
|
|
2670
|
+
if (inboundBatch === undefined) {
|
|
2671
|
+
// This means the incoming message is an incomplete part of a message or batch
|
|
2672
|
+
// and we need to process more messages before the rest of the system can understand it.
|
|
2673
|
+
return;
|
|
2674
|
+
}
|
|
2675
|
+
|
|
2676
|
+
// Reach out to PendingStateManager to zip localOpMetadata into the message list if it's a local batch
|
|
2677
|
+
const messagesWithPendingState = this.pendingStateManager.processInboundBatch(
|
|
2678
|
+
inboundBatch,
|
|
2679
|
+
local,
|
|
2680
|
+
);
|
|
2681
|
+
if (messagesWithPendingState.length > 0) {
|
|
2682
|
+
messagesWithPendingState.forEach(({ message, localOpMetadata }) => {
|
|
2683
|
+
const msg: MessageWithContext = {
|
|
2658
2684
|
message,
|
|
2659
2685
|
local,
|
|
2660
|
-
|
|
2661
|
-
|
|
2686
|
+
isRuntimeMessage: true,
|
|
2687
|
+
savedOp,
|
|
2688
|
+
localOpMetadata,
|
|
2662
2689
|
};
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2690
|
+
this.ensureNoDataModelChanges(() => this.processRuntimeMessage(msg));
|
|
2691
|
+
});
|
|
2692
|
+
} else {
|
|
2693
|
+
this.ensureNoDataModelChanges(() => this.processEmptyBatch(inboundBatch, local));
|
|
2694
|
+
}
|
|
2695
|
+
} else {
|
|
2696
|
+
// Check if message.type is one of values in ContainerMessageType
|
|
2697
|
+
// eslint-disable-next-line import/no-deprecated
|
|
2698
|
+
if (isRuntimeMessage(messageCopy)) {
|
|
2699
|
+
// Legacy op received
|
|
2700
|
+
this.ensureNoDataModelChanges(() =>
|
|
2701
|
+
this.processRuntimeMessage({
|
|
2702
|
+
message: messageCopy as InboundSequencedContainerRuntimeMessage,
|
|
2703
|
+
local,
|
|
2704
|
+
isRuntimeMessage: true,
|
|
2705
|
+
savedOp,
|
|
2706
|
+
}),
|
|
2707
|
+
);
|
|
2708
|
+
} else {
|
|
2709
|
+
// A non container runtime message (like other system ops - join, ack, leave, nack etc.)
|
|
2710
|
+
this.ensureNoDataModelChanges(() =>
|
|
2711
|
+
this.observeNonRuntimeMessage({
|
|
2712
|
+
message: messageCopy as InboundSequencedNonContainerRuntimeMessage,
|
|
2713
|
+
local,
|
|
2714
|
+
isRuntimeMessage: false,
|
|
2715
|
+
savedOp,
|
|
2716
|
+
}),
|
|
2717
|
+
);
|
|
2718
|
+
}
|
|
2667
2719
|
}
|
|
2668
2720
|
}
|
|
2669
2721
|
|
|
2670
2722
|
private _processedClientSequenceNumber: number | undefined;
|
|
2671
2723
|
|
|
2672
2724
|
/**
|
|
2673
|
-
*
|
|
2725
|
+
* Processes messages that are intended for the runtime layer to process.
|
|
2726
|
+
* It redirects the message to the correct subsystem for processing, and implement other side effects
|
|
2727
|
+
* @param messageWithContext - message to process with additional context and isRuntimeMessage prop as true
|
|
2674
2728
|
*/
|
|
2675
|
-
private
|
|
2676
|
-
|
|
2729
|
+
private processRuntimeMessage(
|
|
2730
|
+
messageWithContext: MessageWithContext & { isRuntimeMessage: true },
|
|
2731
|
+
) {
|
|
2732
|
+
const { message, local, localOpMetadata } = messageWithContext;
|
|
2677
2733
|
|
|
2678
2734
|
// Intercept to reduce minimum sequence number to the delta manager's minimum sequence number.
|
|
2679
2735
|
// Sequence numbers are not guaranteed to follow any sort of order. Re-entrancy is one of those situations
|
|
@@ -2694,23 +2750,12 @@ export class ContainerRuntime
|
|
|
2694
2750
|
this._processedClientSequenceNumber = message.clientSequenceNumber;
|
|
2695
2751
|
|
|
2696
2752
|
try {
|
|
2697
|
-
//
|
|
2698
|
-
// These calls should be made for all but chunked ops:
|
|
2699
|
-
// 1) this.pendingStateManager.processPendingLocalMessage() below
|
|
2700
|
-
// 2) this.resetReconnectCount() below
|
|
2753
|
+
// RemoteMessageProcessor would have already reconstituted Chunked Ops into the original op type
|
|
2701
2754
|
assert(
|
|
2702
2755
|
message.type !== ContainerMessageType.ChunkedOp,
|
|
2703
2756
|
0x93b /* we should never get here with chunked ops */,
|
|
2704
2757
|
);
|
|
2705
2758
|
|
|
2706
|
-
let localOpMetadata: unknown;
|
|
2707
|
-
if (local && messageWithContext.modernRuntimeMessage) {
|
|
2708
|
-
localOpMetadata = this.pendingStateManager.processPendingLocalMessage(
|
|
2709
|
-
messageWithContext.message,
|
|
2710
|
-
messageWithContext.batchStartCsn,
|
|
2711
|
-
);
|
|
2712
|
-
}
|
|
2713
|
-
|
|
2714
2759
|
// If there are no more pending messages after processing a local message,
|
|
2715
2760
|
// the document is no longer dirty.
|
|
2716
2761
|
if (!this.hasPendingMessages()) {
|
|
@@ -2719,7 +2764,7 @@ export class ContainerRuntime
|
|
|
2719
2764
|
|
|
2720
2765
|
this.validateAndProcessRuntimeMessage(messageWithContext, localOpMetadata);
|
|
2721
2766
|
|
|
2722
|
-
this.emit("op", message, messageWithContext.
|
|
2767
|
+
this.emit("op", message, messageWithContext.isRuntimeMessage);
|
|
2723
2768
|
|
|
2724
2769
|
this.scheduleManager.afterOpProcessing(undefined, message);
|
|
2725
2770
|
|
|
@@ -2734,33 +2779,101 @@ export class ContainerRuntime
|
|
|
2734
2779
|
throw e;
|
|
2735
2780
|
}
|
|
2736
2781
|
}
|
|
2782
|
+
|
|
2783
|
+
/**
|
|
2784
|
+
* Process an empty batch, which will execute expected actions while processing even if there are no messages.
|
|
2785
|
+
* This is a separate function because the processCore function expects at least one message to process.
|
|
2786
|
+
* It is expected to happen only when the outbox produces an empty batch due to a resubmit flow.
|
|
2787
|
+
*/
|
|
2788
|
+
private processEmptyBatch(emptyBatch: InboundBatch, local: boolean) {
|
|
2789
|
+
const { emptyBatchSequenceNumber: sequenceNumber, batchStartCsn } = emptyBatch;
|
|
2790
|
+
assert(sequenceNumber !== undefined, 0x9fa /* emptyBatchSequenceNumber must be defined */);
|
|
2791
|
+
this.emit("batchBegin", { sequenceNumber });
|
|
2792
|
+
this._processedClientSequenceNumber = batchStartCsn;
|
|
2793
|
+
if (!this.hasPendingMessages()) {
|
|
2794
|
+
this.updateDocumentDirtyState(false);
|
|
2795
|
+
}
|
|
2796
|
+
this.emit("batchEnd", undefined, { sequenceNumber });
|
|
2797
|
+
if (local) {
|
|
2798
|
+
this.resetReconnectCount();
|
|
2799
|
+
}
|
|
2800
|
+
}
|
|
2801
|
+
|
|
2802
|
+
/**
|
|
2803
|
+
* Observes messages that are not intended for the runtime layer, updating/notifying Runtime systems as needed.
|
|
2804
|
+
* @param messageWithContext - non-runtime messages to process with additional context and isRuntimeMessage prop as false
|
|
2805
|
+
*/
|
|
2806
|
+
private observeNonRuntimeMessage(
|
|
2807
|
+
messageWithContext: MessageWithContext & { isRuntimeMessage: false },
|
|
2808
|
+
) {
|
|
2809
|
+
const { message, local } = messageWithContext;
|
|
2810
|
+
|
|
2811
|
+
// Intercept to reduce minimum sequence number to the delta manager's minimum sequence number.
|
|
2812
|
+
// Sequence numbers are not guaranteed to follow any sort of order. Re-entrancy is one of those situations
|
|
2813
|
+
if (
|
|
2814
|
+
this.deltaManager.minimumSequenceNumber <
|
|
2815
|
+
messageWithContext.message.minimumSequenceNumber
|
|
2816
|
+
) {
|
|
2817
|
+
messageWithContext.message.minimumSequenceNumber =
|
|
2818
|
+
this.deltaManager.minimumSequenceNumber;
|
|
2819
|
+
}
|
|
2820
|
+
|
|
2821
|
+
// Surround the actual processing of the operation with messages to the schedule manager indicating
|
|
2822
|
+
// the beginning and end. This allows it to emit appropriate events and/or pause the processing of new
|
|
2823
|
+
// messages once a batch has been fully processed.
|
|
2824
|
+
this.scheduleManager.beforeOpProcessing(message);
|
|
2825
|
+
|
|
2826
|
+
this._processedClientSequenceNumber = message.clientSequenceNumber;
|
|
2827
|
+
|
|
2828
|
+
try {
|
|
2829
|
+
// If there are no more pending messages after processing a local message,
|
|
2830
|
+
// the document is no longer dirty.
|
|
2831
|
+
if (!this.hasPendingMessages()) {
|
|
2832
|
+
this.updateDocumentDirtyState(false);
|
|
2833
|
+
}
|
|
2834
|
+
|
|
2835
|
+
this.emit("op", message, messageWithContext.isRuntimeMessage);
|
|
2836
|
+
|
|
2837
|
+
this.scheduleManager.afterOpProcessing(undefined, message);
|
|
2838
|
+
|
|
2839
|
+
if (local) {
|
|
2840
|
+
// If we have processed a local op, this means that the container is
|
|
2841
|
+
// making progress and we can reset the counter for how many times
|
|
2842
|
+
// we have consecutively replayed the pending states
|
|
2843
|
+
this.resetReconnectCount();
|
|
2844
|
+
}
|
|
2845
|
+
} catch (e) {
|
|
2846
|
+
this.scheduleManager.afterOpProcessing(e, message);
|
|
2847
|
+
throw e;
|
|
2848
|
+
}
|
|
2849
|
+
}
|
|
2850
|
+
|
|
2737
2851
|
/**
|
|
2738
2852
|
* Assuming the given message is also a TypedContainerRuntimeMessage,
|
|
2739
2853
|
* checks its type and dispatches the message to the appropriate handler in the runtime.
|
|
2740
2854
|
* Throws a DataProcessingError if the message looks like but doesn't conform to a known TypedContainerRuntimeMessage type.
|
|
2741
2855
|
*/
|
|
2742
2856
|
private validateAndProcessRuntimeMessage(
|
|
2743
|
-
messageWithContext: MessageWithContext,
|
|
2857
|
+
messageWithContext: MessageWithContext & { isRuntimeMessage: true },
|
|
2744
2858
|
localOpMetadata: unknown,
|
|
2745
2859
|
): void {
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
switch (messageWithContext.message.type) {
|
|
2860
|
+
const { local, message, savedOp } = messageWithContext;
|
|
2861
|
+
switch (message.type) {
|
|
2749
2862
|
case ContainerMessageType.Attach:
|
|
2750
2863
|
case ContainerMessageType.Alias:
|
|
2751
2864
|
case ContainerMessageType.FluidDataStoreOp:
|
|
2752
|
-
this.channelCollection.process(
|
|
2865
|
+
this.channelCollection.process(message, local, localOpMetadata);
|
|
2753
2866
|
break;
|
|
2754
2867
|
case ContainerMessageType.BlobAttach:
|
|
2755
|
-
this.blobManager.processBlobAttachOp(
|
|
2868
|
+
this.blobManager.processBlobAttachOp(message, local);
|
|
2756
2869
|
break;
|
|
2757
2870
|
case ContainerMessageType.IdAllocation:
|
|
2758
2871
|
// Don't re-finalize the range if we're processing a "savedOp" in
|
|
2759
2872
|
// stashed ops flow. The compressor is stashed with these ops already processed.
|
|
2760
2873
|
// That said, in idCompressorMode === "delayed", we might not serialize ID compressor, and
|
|
2761
2874
|
// thus we need to process all the ops.
|
|
2762
|
-
if (!(this.skipSavedCompressorOps &&
|
|
2763
|
-
const range =
|
|
2875
|
+
if (!(this.skipSavedCompressorOps && savedOp === true)) {
|
|
2876
|
+
const range = message.contents;
|
|
2764
2877
|
// Some other client turned on the id compressor. If we have not turned it on,
|
|
2765
2878
|
// put it in a pending queue and delay finalization.
|
|
2766
2879
|
if (this._idCompressor === undefined) {
|
|
@@ -2779,11 +2892,7 @@ export class ContainerRuntime
|
|
|
2779
2892
|
}
|
|
2780
2893
|
break;
|
|
2781
2894
|
case ContainerMessageType.GC:
|
|
2782
|
-
this.garbageCollector.processMessage(
|
|
2783
|
-
messageWithContext.message,
|
|
2784
|
-
messageWithContext.message.timestamp,
|
|
2785
|
-
local,
|
|
2786
|
-
);
|
|
2895
|
+
this.garbageCollector.processMessage(message, message.timestamp, local);
|
|
2787
2896
|
break;
|
|
2788
2897
|
case ContainerMessageType.ChunkedOp:
|
|
2789
2898
|
// From observability POV, we should not exppse the rest of the system (including "op" events on object) to these messages.
|
|
@@ -2793,23 +2902,14 @@ export class ContainerRuntime
|
|
|
2793
2902
|
break;
|
|
2794
2903
|
case ContainerMessageType.DocumentSchemaChange:
|
|
2795
2904
|
this.documentsSchemaController.processDocumentSchemaOp(
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2905
|
+
message.contents,
|
|
2906
|
+
local,
|
|
2907
|
+
message.sequenceNumber,
|
|
2799
2908
|
);
|
|
2800
2909
|
break;
|
|
2801
2910
|
default: {
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
if (!messageWithContext.modernRuntimeMessage) {
|
|
2805
|
-
return;
|
|
2806
|
-
}
|
|
2807
|
-
|
|
2808
|
-
const compatBehavior = messageWithContext.message.compatDetails?.behavior;
|
|
2809
|
-
if (
|
|
2810
|
-
!compatBehaviorAllowsMessageType(messageWithContext.message.type, compatBehavior)
|
|
2811
|
-
) {
|
|
2812
|
-
const { message } = messageWithContext;
|
|
2911
|
+
const compatBehavior = message.compatDetails?.behavior;
|
|
2912
|
+
if (!compatBehaviorAllowsMessageType(message.type, compatBehavior)) {
|
|
2813
2913
|
const error = DataProcessingError.create(
|
|
2814
2914
|
// Former assert 0x3ce
|
|
2815
2915
|
"Runtime message of unknown type",
|
|
@@ -3991,8 +4091,6 @@ export class ContainerRuntime
|
|
|
3991
4091
|
0x93f /* metadata */,
|
|
3992
4092
|
);
|
|
3993
4093
|
|
|
3994
|
-
const serializedContent = JSON.stringify(containerRuntimeMessage);
|
|
3995
|
-
|
|
3996
4094
|
// Note that the real (non-proxy) delta manager is used here to get the readonly info. This is because
|
|
3997
4095
|
// container runtime's ability to submit ops depend on the actual readonly state of the delta manager.
|
|
3998
4096
|
if (this.innerDeltaManager.readOnlyInfo.readonly) {
|
|
@@ -4007,12 +4105,6 @@ export class ContainerRuntime
|
|
|
4007
4105
|
type !== ContainerMessageType.IdAllocation,
|
|
4008
4106
|
0x9a5 /* IdAllocation should be submitted directly to outbox. */,
|
|
4009
4107
|
);
|
|
4010
|
-
const message: BatchMessage = {
|
|
4011
|
-
contents: serializedContent,
|
|
4012
|
-
metadata,
|
|
4013
|
-
localOpMetadata,
|
|
4014
|
-
referenceSequenceNumber: this.deltaManager.lastSequenceNumber,
|
|
4015
|
-
};
|
|
4016
4108
|
|
|
4017
4109
|
try {
|
|
4018
4110
|
this.submitIdAllocationOpIfNeeded(false);
|
|
@@ -4020,19 +4112,19 @@ export class ContainerRuntime
|
|
|
4020
4112
|
// Allow document schema controller to send a message if it needs to propose change in document schema.
|
|
4021
4113
|
// If it needs to send a message, it will call provided callback with payload of such message and rely
|
|
4022
4114
|
// on this callback to do actual sending.
|
|
4023
|
-
const
|
|
4024
|
-
if (
|
|
4115
|
+
const schemaChangeMessage = this.documentsSchemaController.maybeSendSchemaMessage();
|
|
4116
|
+
if (schemaChangeMessage) {
|
|
4025
4117
|
this.logger.sendTelemetryEvent({
|
|
4026
4118
|
eventName: "SchemaChangeProposal",
|
|
4027
|
-
refSeq:
|
|
4028
|
-
version:
|
|
4029
|
-
newRuntimeSchema: JSON.stringify(
|
|
4119
|
+
refSeq: schemaChangeMessage.refSeq,
|
|
4120
|
+
version: schemaChangeMessage.version,
|
|
4121
|
+
newRuntimeSchema: JSON.stringify(schemaChangeMessage.runtime),
|
|
4030
4122
|
sessionRuntimeSchema: JSON.stringify(this.sessionSchema),
|
|
4031
4123
|
oldRuntimeSchema: JSON.stringify(this.metadata?.documentSchema?.runtime),
|
|
4032
4124
|
});
|
|
4033
4125
|
const msg: ContainerRuntimeDocumentSchemaMessage = {
|
|
4034
4126
|
type: ContainerMessageType.DocumentSchemaChange,
|
|
4035
|
-
contents,
|
|
4127
|
+
contents: schemaChangeMessage,
|
|
4036
4128
|
};
|
|
4037
4129
|
this.outbox.submit({
|
|
4038
4130
|
contents: JSON.stringify(msg),
|
|
@@ -4040,6 +4132,12 @@ export class ContainerRuntime
|
|
|
4040
4132
|
});
|
|
4041
4133
|
}
|
|
4042
4134
|
|
|
4135
|
+
const message: BatchMessage = {
|
|
4136
|
+
contents: JSON.stringify(containerRuntimeMessage) /* serialized content */,
|
|
4137
|
+
metadata,
|
|
4138
|
+
localOpMetadata,
|
|
4139
|
+
referenceSequenceNumber: this.deltaManager.lastSequenceNumber,
|
|
4140
|
+
};
|
|
4043
4141
|
if (type === ContainerMessageType.BlobAttach) {
|
|
4044
4142
|
// BlobAttach ops must have their metadata visible and cannot be grouped (see opGroupingManager.ts)
|
|
4045
4143
|
this.outbox.submitBlobAttach(message);
|
|
@@ -4139,10 +4237,7 @@ export class ContainerRuntime
|
|
|
4139
4237
|
|
|
4140
4238
|
// Only include Batch ID if "Offline Load" feature is enabled
|
|
4141
4239
|
// It's only needed to identify batches across container forks arising from misuse of offline load.
|
|
4142
|
-
|
|
4143
|
-
this.mc.config.getBoolean("Fluid.Container.enableOfflineLoad") ?? false;
|
|
4144
|
-
|
|
4145
|
-
this.flush(includeBatchId ? batchId : undefined);
|
|
4240
|
+
this.flush(this.offlineEnabled ? batchId : undefined);
|
|
4146
4241
|
}
|
|
4147
4242
|
|
|
4148
4243
|
private reSubmit(message: PendingMessageResubmitData) {
|