@fluidframework/container-runtime 2.1.0-281041 → 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 +34 -0
- package/README.md +6 -6
- 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 +177 -74
- 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/batchManager.d.ts.map +1 -1
- package/dist/opLifecycle/batchManager.js +1 -1
- package/dist/opLifecycle/batchManager.js.map +1 -1
- package/dist/opLifecycle/index.d.ts +1 -1
- package/dist/opLifecycle/index.d.ts.map +1 -1
- package/dist/opLifecycle/index.js +2 -1
- package/dist/opLifecycle/index.js.map +1 -1
- package/dist/opLifecycle/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 +21 -1
- package/dist/opLifecycle/outbox.js.map +1 -1
- package/dist/opLifecycle/remoteMessageProcessor.d.ts +35 -14
- package/dist/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
- package/dist/opLifecycle/remoteMessageProcessor.js +71 -46
- package/dist/opLifecycle/remoteMessageProcessor.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.d.ts.map +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 +178 -75
- 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/batchManager.d.ts.map +1 -1
- package/lib/opLifecycle/batchManager.js +1 -1
- package/lib/opLifecycle/batchManager.js.map +1 -1
- package/lib/opLifecycle/index.d.ts +1 -1
- package/lib/opLifecycle/index.d.ts.map +1 -1
- package/lib/opLifecycle/index.js +1 -1
- package/lib/opLifecycle/index.js.map +1 -1
- package/lib/opLifecycle/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 +21 -1
- package/lib/opLifecycle/outbox.js.map +1 -1
- package/lib/opLifecycle/remoteMessageProcessor.d.ts +35 -14
- package/lib/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
- package/lib/opLifecycle/remoteMessageProcessor.js +69 -45
- package/lib/opLifecycle/remoteMessageProcessor.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.d.ts.map +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 +23 -32
- package/src/batchTracker.ts +4 -2
- package/src/blobManager/blobManager.ts +9 -0
- package/src/channelCollection.ts +2 -11
- package/src/containerRuntime.ts +214 -100
- 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/batchManager.ts +4 -1
- package/src/opLifecycle/index.ts +6 -1
- package/src/opLifecycle/opGroupingManager.ts +42 -3
- package/src/opLifecycle/outbox.ts +31 -1
- package/src/opLifecycle/remoteMessageProcessor.ts +116 -57
- 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,
|
|
@@ -171,6 +171,7 @@ import { IBatchMetadata, ISavedOpMetadata } from "./metadata.js";
|
|
|
171
171
|
import {
|
|
172
172
|
BatchId,
|
|
173
173
|
BatchMessage,
|
|
174
|
+
ensureContentsDeserialized,
|
|
174
175
|
IBatch,
|
|
175
176
|
IBatchCheckpoint,
|
|
176
177
|
OpCompressor,
|
|
@@ -179,6 +180,7 @@ import {
|
|
|
179
180
|
OpSplitter,
|
|
180
181
|
Outbox,
|
|
181
182
|
RemoteMessageProcessor,
|
|
183
|
+
type InboundBatch,
|
|
182
184
|
} from "./opLifecycle/index.js";
|
|
183
185
|
import { pkgVersion } from "./packageVersion.js";
|
|
184
186
|
import {
|
|
@@ -521,6 +523,9 @@ export const TombstoneResponseHeaderKey = "isTombstoned";
|
|
|
521
523
|
* Inactive error responses will have this header set to true
|
|
522
524
|
* @legacy
|
|
523
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.
|
|
524
529
|
*/
|
|
525
530
|
export const InactiveResponseHeaderKey = "isInactive";
|
|
526
531
|
|
|
@@ -532,7 +537,6 @@ export interface RuntimeHeaderData {
|
|
|
532
537
|
wait?: boolean;
|
|
533
538
|
viaHandle?: boolean;
|
|
534
539
|
allowTombstone?: boolean;
|
|
535
|
-
allowInactive?: boolean;
|
|
536
540
|
}
|
|
537
541
|
|
|
538
542
|
/** Default values for Runtime Headers */
|
|
@@ -540,7 +544,6 @@ export const defaultRuntimeHeaderData: Required<RuntimeHeaderData> = {
|
|
|
540
544
|
wait: true,
|
|
541
545
|
viaHandle: false,
|
|
542
546
|
allowTombstone: false,
|
|
543
|
-
allowInactive: false,
|
|
544
547
|
};
|
|
545
548
|
|
|
546
549
|
/**
|
|
@@ -628,6 +631,7 @@ export const defaultPendingOpsRetryDelayMs = 1000;
|
|
|
628
631
|
const defaultCloseSummarizerDelayMs = 5000; // 5 seconds
|
|
629
632
|
|
|
630
633
|
/**
|
|
634
|
+
* Checks whether a message.type is one of the values in ContainerMessageType
|
|
631
635
|
* @deprecated please use version in driver-utils
|
|
632
636
|
* @internal
|
|
633
637
|
*/
|
|
@@ -687,9 +691,7 @@ export const makeLegacySendBatchFn =
|
|
|
687
691
|
/** Helper type for type constraints passed through several functions.
|
|
688
692
|
* local - Did this client send the op?
|
|
689
693
|
* savedOp - Is this op being replayed after being serialized (having been sequenced previously)
|
|
690
|
-
*
|
|
691
|
-
* message - The unpacked message. Likely a TypedContainerRuntimeMessage, but could also be a system op
|
|
692
|
-
* modernRuntimeMessage - Does this appear like a current TypedContainerRuntimeMessage?
|
|
694
|
+
* localOpMetadata - Metadata maintained locally for a local op.
|
|
693
695
|
*/
|
|
694
696
|
type MessageWithContext = {
|
|
695
697
|
local: boolean;
|
|
@@ -698,14 +700,13 @@ type MessageWithContext = {
|
|
|
698
700
|
} & (
|
|
699
701
|
| {
|
|
700
702
|
message: InboundSequencedContainerRuntimeMessage;
|
|
701
|
-
|
|
703
|
+
isRuntimeMessage: true;
|
|
702
704
|
}
|
|
703
705
|
| {
|
|
704
|
-
message:
|
|
705
|
-
|
|
706
|
+
message: InboundSequencedNonContainerRuntimeMessage;
|
|
707
|
+
isRuntimeMessage: false;
|
|
706
708
|
}
|
|
707
709
|
);
|
|
708
|
-
|
|
709
710
|
const summarizerRequestUrl = "_summarizer";
|
|
710
711
|
|
|
711
712
|
/**
|
|
@@ -760,6 +761,23 @@ function lastMessageFromMetadata(metadata: IContainerRuntimeMetadata | undefined
|
|
|
760
761
|
: metadata?.message;
|
|
761
762
|
}
|
|
762
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
|
+
|
|
763
781
|
/**
|
|
764
782
|
* Represents the runtime of the container. Contains helper functions/state of the container.
|
|
765
783
|
* It will define the store level mappings.
|
|
@@ -1218,6 +1236,7 @@ export class ContainerRuntime
|
|
|
1218
1236
|
|
|
1219
1237
|
private _orderSequentiallyCalls: number = 0;
|
|
1220
1238
|
private readonly _flushMode: FlushMode;
|
|
1239
|
+
private readonly offlineEnabled: boolean;
|
|
1221
1240
|
private flushTaskExists = false;
|
|
1222
1241
|
|
|
1223
1242
|
private _connected: boolean;
|
|
@@ -1329,14 +1348,22 @@ export class ContainerRuntime
|
|
|
1329
1348
|
*/
|
|
1330
1349
|
private nextSummaryNumber: number;
|
|
1331
1350
|
|
|
1332
|
-
/**
|
|
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
|
|
1333
1356
|
public get gcTombstoneEnforcementAllowed(): boolean {
|
|
1334
|
-
return
|
|
1357
|
+
return false;
|
|
1335
1358
|
}
|
|
1336
1359
|
|
|
1337
|
-
/**
|
|
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
|
|
1338
1365
|
public get gcThrowOnTombstoneUsage(): boolean {
|
|
1339
|
-
return
|
|
1366
|
+
return false;
|
|
1340
1367
|
}
|
|
1341
1368
|
|
|
1342
1369
|
/**
|
|
@@ -1546,7 +1573,6 @@ export class ContainerRuntime
|
|
|
1546
1573
|
{
|
|
1547
1574
|
applyStashedOp: this.applyStashedOp.bind(this),
|
|
1548
1575
|
clientId: () => this.clientId,
|
|
1549
|
-
close: this.closeFn,
|
|
1550
1576
|
connected: () => this.connected,
|
|
1551
1577
|
reSubmitBatch: this.reSubmitBatch.bind(this),
|
|
1552
1578
|
isActiveConnection: () => this.innerDeltaManager.active,
|
|
@@ -1601,6 +1627,14 @@ export class ContainerRuntime
|
|
|
1601
1627
|
} else {
|
|
1602
1628
|
this._flushMode = runtimeOptions.flushMode;
|
|
1603
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
|
+
}
|
|
1604
1638
|
|
|
1605
1639
|
if (context.attachState === AttachState.Attached) {
|
|
1606
1640
|
const maxSnapshotCacheDurationMs = this._storage?.policies?.maximumCacheDurationMs;
|
|
@@ -2608,62 +2642,92 @@ export class ContainerRuntime
|
|
|
2608
2642
|
await this.pendingStateManager.applyStashedOpsAt(message.sequenceNumber);
|
|
2609
2643
|
}
|
|
2610
2644
|
|
|
2611
|
-
|
|
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
|
+
|
|
2612
2654
|
this.verifyNotClosed();
|
|
2613
2655
|
|
|
2614
2656
|
// Whether or not the message appears to be a runtime message from an up-to-date client.
|
|
2615
2657
|
// It may be a legacy runtime message (ie already unpacked and ContainerMessageType)
|
|
2616
2658
|
// or something different, like a system message.
|
|
2617
|
-
const
|
|
2618
|
-
|
|
2619
|
-
// Do shallow copy of message, as the processing flow will modify it.
|
|
2620
|
-
// There might be multiple container instances receiving the same message.
|
|
2621
|
-
// We do not need to make a deep copy. Each layer will just replace message.contents itself,
|
|
2622
|
-
// but will not modify the contents object (likely it will replace it on the message).
|
|
2623
|
-
const messageCopy = { ...messageArg };
|
|
2659
|
+
const hasModernRuntimeMessageEnvelope = messageCopy.type === MessageType.Operation;
|
|
2624
2660
|
const savedOp = (messageCopy.metadata as ISavedOpMetadata)?.savedOp;
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2661
|
+
const logLegacyCase = getSingleUseLegacyLogCallback(this.logger, messageCopy.type);
|
|
2662
|
+
|
|
2663
|
+
// We expect runtime messages to have JSON contents - deserialize it in place.
|
|
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) {
|
|
2628
2670
|
// This means the incoming message is an incomplete part of a message or batch
|
|
2629
2671
|
// and we need to process more messages before the rest of the system can understand it.
|
|
2630
2672
|
return;
|
|
2631
2673
|
}
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
const
|
|
2635
|
-
|
|
2636
|
-
localOpMetadata: unknown;
|
|
2637
|
-
}[] = local
|
|
2638
|
-
? this.pendingStateManager.processPendingLocalBatch(batch, batchStartCsn)
|
|
2639
|
-
: batch.map((message) => ({ message, localOpMetadata: undefined }));
|
|
2640
|
-
messages.forEach(({ message, localOpMetadata }) => {
|
|
2641
|
-
const msg: MessageWithContext = {
|
|
2642
|
-
message,
|
|
2643
|
-
local,
|
|
2644
|
-
modernRuntimeMessage,
|
|
2645
|
-
savedOp,
|
|
2646
|
-
localOpMetadata,
|
|
2647
|
-
};
|
|
2648
|
-
this.ensureNoDataModelChanges(() => this.processCore(msg));
|
|
2649
|
-
});
|
|
2650
|
-
} else {
|
|
2651
|
-
const msg: MessageWithContext = {
|
|
2652
|
-
message: messageCopy as InboundSequencedContainerRuntimeMessageOrSystemMessage,
|
|
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,
|
|
2653
2678
|
local,
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
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
|
+
}
|
|
2658
2718
|
}
|
|
2659
2719
|
}
|
|
2660
2720
|
|
|
2661
2721
|
private _processedClientSequenceNumber: number | undefined;
|
|
2662
2722
|
|
|
2663
2723
|
/**
|
|
2664
|
-
*
|
|
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
|
|
2665
2727
|
*/
|
|
2666
|
-
private
|
|
2728
|
+
private processRuntimeMessage(
|
|
2729
|
+
messageWithContext: MessageWithContext & { isRuntimeMessage: true },
|
|
2730
|
+
) {
|
|
2667
2731
|
const { message, local, localOpMetadata } = messageWithContext;
|
|
2668
2732
|
|
|
2669
2733
|
// Intercept to reduce minimum sequence number to the delta manager's minimum sequence number.
|
|
@@ -2698,7 +2762,75 @@ export class ContainerRuntime
|
|
|
2698
2762
|
|
|
2699
2763
|
this.validateAndProcessRuntimeMessage(messageWithContext, localOpMetadata);
|
|
2700
2764
|
|
|
2701
|
-
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);
|
|
2702
2834
|
|
|
2703
2835
|
this.scheduleManager.afterOpProcessing(undefined, message);
|
|
2704
2836
|
|
|
@@ -2713,33 +2845,33 @@ export class ContainerRuntime
|
|
|
2713
2845
|
throw e;
|
|
2714
2846
|
}
|
|
2715
2847
|
}
|
|
2848
|
+
|
|
2716
2849
|
/**
|
|
2717
2850
|
* Assuming the given message is also a TypedContainerRuntimeMessage,
|
|
2718
2851
|
* checks its type and dispatches the message to the appropriate handler in the runtime.
|
|
2719
2852
|
* Throws a DataProcessingError if the message looks like but doesn't conform to a known TypedContainerRuntimeMessage type.
|
|
2720
2853
|
*/
|
|
2721
2854
|
private validateAndProcessRuntimeMessage(
|
|
2722
|
-
messageWithContext: MessageWithContext,
|
|
2855
|
+
messageWithContext: MessageWithContext & { isRuntimeMessage: true },
|
|
2723
2856
|
localOpMetadata: unknown,
|
|
2724
2857
|
): void {
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
switch (messageWithContext.message.type) {
|
|
2858
|
+
const { local, message, savedOp } = messageWithContext;
|
|
2859
|
+
switch (message.type) {
|
|
2728
2860
|
case ContainerMessageType.Attach:
|
|
2729
2861
|
case ContainerMessageType.Alias:
|
|
2730
2862
|
case ContainerMessageType.FluidDataStoreOp:
|
|
2731
|
-
this.channelCollection.process(
|
|
2863
|
+
this.channelCollection.process(message, local, localOpMetadata);
|
|
2732
2864
|
break;
|
|
2733
2865
|
case ContainerMessageType.BlobAttach:
|
|
2734
|
-
this.blobManager.processBlobAttachOp(
|
|
2866
|
+
this.blobManager.processBlobAttachOp(message, local);
|
|
2735
2867
|
break;
|
|
2736
2868
|
case ContainerMessageType.IdAllocation:
|
|
2737
2869
|
// Don't re-finalize the range if we're processing a "savedOp" in
|
|
2738
2870
|
// stashed ops flow. The compressor is stashed with these ops already processed.
|
|
2739
2871
|
// That said, in idCompressorMode === "delayed", we might not serialize ID compressor, and
|
|
2740
2872
|
// thus we need to process all the ops.
|
|
2741
|
-
if (!(this.skipSavedCompressorOps &&
|
|
2742
|
-
const range =
|
|
2873
|
+
if (!(this.skipSavedCompressorOps && savedOp === true)) {
|
|
2874
|
+
const range = message.contents;
|
|
2743
2875
|
// Some other client turned on the id compressor. If we have not turned it on,
|
|
2744
2876
|
// put it in a pending queue and delay finalization.
|
|
2745
2877
|
if (this._idCompressor === undefined) {
|
|
@@ -2758,11 +2890,7 @@ export class ContainerRuntime
|
|
|
2758
2890
|
}
|
|
2759
2891
|
break;
|
|
2760
2892
|
case ContainerMessageType.GC:
|
|
2761
|
-
this.garbageCollector.processMessage(
|
|
2762
|
-
messageWithContext.message,
|
|
2763
|
-
messageWithContext.message.timestamp,
|
|
2764
|
-
local,
|
|
2765
|
-
);
|
|
2893
|
+
this.garbageCollector.processMessage(message, message.timestamp, local);
|
|
2766
2894
|
break;
|
|
2767
2895
|
case ContainerMessageType.ChunkedOp:
|
|
2768
2896
|
// From observability POV, we should not exppse the rest of the system (including "op" events on object) to these messages.
|
|
@@ -2772,23 +2900,14 @@ export class ContainerRuntime
|
|
|
2772
2900
|
break;
|
|
2773
2901
|
case ContainerMessageType.DocumentSchemaChange:
|
|
2774
2902
|
this.documentsSchemaController.processDocumentSchemaOp(
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2903
|
+
message.contents,
|
|
2904
|
+
local,
|
|
2905
|
+
message.sequenceNumber,
|
|
2778
2906
|
);
|
|
2779
2907
|
break;
|
|
2780
2908
|
default: {
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
if (!messageWithContext.modernRuntimeMessage) {
|
|
2784
|
-
return;
|
|
2785
|
-
}
|
|
2786
|
-
|
|
2787
|
-
const compatBehavior = messageWithContext.message.compatDetails?.behavior;
|
|
2788
|
-
if (
|
|
2789
|
-
!compatBehaviorAllowsMessageType(messageWithContext.message.type, compatBehavior)
|
|
2790
|
-
) {
|
|
2791
|
-
const { message } = messageWithContext;
|
|
2909
|
+
const compatBehavior = message.compatDetails?.behavior;
|
|
2910
|
+
if (!compatBehaviorAllowsMessageType(message.type, compatBehavior)) {
|
|
2792
2911
|
const error = DataProcessingError.create(
|
|
2793
2912
|
// Former assert 0x3ce
|
|
2794
2913
|
"Runtime message of unknown type",
|
|
@@ -3970,8 +4089,6 @@ export class ContainerRuntime
|
|
|
3970
4089
|
0x93f /* metadata */,
|
|
3971
4090
|
);
|
|
3972
4091
|
|
|
3973
|
-
const serializedContent = JSON.stringify(containerRuntimeMessage);
|
|
3974
|
-
|
|
3975
4092
|
// Note that the real (non-proxy) delta manager is used here to get the readonly info. This is because
|
|
3976
4093
|
// container runtime's ability to submit ops depend on the actual readonly state of the delta manager.
|
|
3977
4094
|
if (this.innerDeltaManager.readOnlyInfo.readonly) {
|
|
@@ -3986,12 +4103,6 @@ export class ContainerRuntime
|
|
|
3986
4103
|
type !== ContainerMessageType.IdAllocation,
|
|
3987
4104
|
0x9a5 /* IdAllocation should be submitted directly to outbox. */,
|
|
3988
4105
|
);
|
|
3989
|
-
const message: BatchMessage = {
|
|
3990
|
-
contents: serializedContent,
|
|
3991
|
-
metadata,
|
|
3992
|
-
localOpMetadata,
|
|
3993
|
-
referenceSequenceNumber: this.deltaManager.lastSequenceNumber,
|
|
3994
|
-
};
|
|
3995
4106
|
|
|
3996
4107
|
try {
|
|
3997
4108
|
this.submitIdAllocationOpIfNeeded(false);
|
|
@@ -3999,19 +4110,19 @@ export class ContainerRuntime
|
|
|
3999
4110
|
// Allow document schema controller to send a message if it needs to propose change in document schema.
|
|
4000
4111
|
// If it needs to send a message, it will call provided callback with payload of such message and rely
|
|
4001
4112
|
// on this callback to do actual sending.
|
|
4002
|
-
const
|
|
4003
|
-
if (
|
|
4113
|
+
const schemaChangeMessage = this.documentsSchemaController.maybeSendSchemaMessage();
|
|
4114
|
+
if (schemaChangeMessage) {
|
|
4004
4115
|
this.logger.sendTelemetryEvent({
|
|
4005
4116
|
eventName: "SchemaChangeProposal",
|
|
4006
|
-
refSeq:
|
|
4007
|
-
version:
|
|
4008
|
-
newRuntimeSchema: JSON.stringify(
|
|
4117
|
+
refSeq: schemaChangeMessage.refSeq,
|
|
4118
|
+
version: schemaChangeMessage.version,
|
|
4119
|
+
newRuntimeSchema: JSON.stringify(schemaChangeMessage.runtime),
|
|
4009
4120
|
sessionRuntimeSchema: JSON.stringify(this.sessionSchema),
|
|
4010
4121
|
oldRuntimeSchema: JSON.stringify(this.metadata?.documentSchema?.runtime),
|
|
4011
4122
|
});
|
|
4012
4123
|
const msg: ContainerRuntimeDocumentSchemaMessage = {
|
|
4013
4124
|
type: ContainerMessageType.DocumentSchemaChange,
|
|
4014
|
-
contents,
|
|
4125
|
+
contents: schemaChangeMessage,
|
|
4015
4126
|
};
|
|
4016
4127
|
this.outbox.submit({
|
|
4017
4128
|
contents: JSON.stringify(msg),
|
|
@@ -4019,6 +4130,12 @@ export class ContainerRuntime
|
|
|
4019
4130
|
});
|
|
4020
4131
|
}
|
|
4021
4132
|
|
|
4133
|
+
const message: BatchMessage = {
|
|
4134
|
+
contents: JSON.stringify(containerRuntimeMessage) /* serialized content */,
|
|
4135
|
+
metadata,
|
|
4136
|
+
localOpMetadata,
|
|
4137
|
+
referenceSequenceNumber: this.deltaManager.lastSequenceNumber,
|
|
4138
|
+
};
|
|
4022
4139
|
if (type === ContainerMessageType.BlobAttach) {
|
|
4023
4140
|
// BlobAttach ops must have their metadata visible and cannot be grouped (see opGroupingManager.ts)
|
|
4024
4141
|
this.outbox.submitBlobAttach(message);
|
|
@@ -4118,10 +4235,7 @@ export class ContainerRuntime
|
|
|
4118
4235
|
|
|
4119
4236
|
// Only include Batch ID if "Offline Load" feature is enabled
|
|
4120
4237
|
// It's only needed to identify batches across container forks arising from misuse of offline load.
|
|
4121
|
-
|
|
4122
|
-
this.mc.config.getBoolean("Fluid.Container.enableOfflineLoad") ?? false;
|
|
4123
|
-
|
|
4124
|
-
this.flush(includeBatchId ? batchId : undefined);
|
|
4238
|
+
this.flush(this.offlineEnabled ? batchId : undefined);
|
|
4125
4239
|
}
|
|
4126
4240
|
|
|
4127
4241
|
private reSubmit(message: PendingMessageResubmitData) {
|
|
@@ -4324,7 +4438,7 @@ export class ContainerRuntime
|
|
|
4324
4438
|
fetchSource: FetchSource.noCache,
|
|
4325
4439
|
});
|
|
4326
4440
|
const id = snapshot.snapshotTree.id;
|
|
4327
|
-
assert(id !== undefined,
|
|
4441
|
+
assert(id !== undefined, 0x9d0 /* id of the fetched snapshot should be defined */);
|
|
4328
4442
|
props.snapshotVersion = id;
|
|
4329
4443
|
snapshotTree = snapshot.snapshotTree;
|
|
4330
4444
|
} else {
|