@fluidframework/container-runtime 2.5.0-302463 → 2.5.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 +18 -0
- package/api-report/container-runtime.legacy.alpha.api.md +3 -1
- package/container-runtime.test-files.tar +0 -0
- package/dist/blobManager/blobManager.d.ts +3 -3
- package/dist/blobManager/blobManager.d.ts.map +1 -1
- package/dist/blobManager/blobManager.js +1 -1
- package/dist/blobManager/blobManager.js.map +1 -1
- package/dist/channelCollection.d.ts +20 -5
- package/dist/channelCollection.d.ts.map +1 -1
- package/dist/channelCollection.js +185 -129
- package/dist/channelCollection.js.map +1 -1
- package/dist/containerRuntime.d.ts +14 -4
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +138 -55
- package/dist/containerRuntime.js.map +1 -1
- package/dist/dataStoreContext.d.ts +15 -3
- package/dist/dataStoreContext.d.ts.map +1 -1
- package/dist/dataStoreContext.js +48 -19
- package/dist/dataStoreContext.js.map +1 -1
- package/dist/dataStoreContexts.d.ts.map +1 -1
- package/dist/dataStoreContexts.js +6 -14
- package/dist/dataStoreContexts.js.map +1 -1
- package/dist/gc/garbageCollection.d.ts +5 -6
- package/dist/gc/garbageCollection.d.ts.map +1 -1
- package/dist/gc/garbageCollection.js +23 -22
- package/dist/gc/garbageCollection.js.map +1 -1
- package/dist/gc/gcDefinitions.d.ts +2 -2
- package/dist/gc/gcDefinitions.d.ts.map +1 -1
- package/dist/gc/gcDefinitions.js.map +1 -1
- package/dist/opLifecycle/outbox.d.ts +3 -0
- package/dist/opLifecycle/outbox.d.ts.map +1 -1
- package/dist/opLifecycle/outbox.js +9 -0
- package/dist/opLifecycle/outbox.js.map +1 -1
- package/dist/opLifecycle/remoteMessageProcessor.d.ts +1 -0
- package/dist/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
- package/dist/opLifecycle/remoteMessageProcessor.js +2 -0
- 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/summary/documentSchema.d.ts +11 -0
- package/dist/summary/documentSchema.d.ts.map +1 -1
- package/dist/summary/documentSchema.js +43 -28
- package/dist/summary/documentSchema.js.map +1 -1
- package/lib/blobManager/blobManager.d.ts +3 -3
- package/lib/blobManager/blobManager.d.ts.map +1 -1
- package/lib/blobManager/blobManager.js +1 -1
- package/lib/blobManager/blobManager.js.map +1 -1
- package/lib/channelCollection.d.ts +20 -5
- package/lib/channelCollection.d.ts.map +1 -1
- package/lib/channelCollection.js +186 -130
- package/lib/channelCollection.js.map +1 -1
- package/lib/containerRuntime.d.ts +14 -4
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +137 -54
- package/lib/containerRuntime.js.map +1 -1
- package/lib/dataStoreContext.d.ts +15 -3
- package/lib/dataStoreContext.d.ts.map +1 -1
- package/lib/dataStoreContext.js +48 -19
- package/lib/dataStoreContext.js.map +1 -1
- package/lib/dataStoreContexts.d.ts.map +1 -1
- package/lib/dataStoreContexts.js +7 -15
- package/lib/dataStoreContexts.js.map +1 -1
- package/lib/gc/garbageCollection.d.ts +5 -6
- package/lib/gc/garbageCollection.d.ts.map +1 -1
- package/lib/gc/garbageCollection.js +23 -22
- package/lib/gc/garbageCollection.js.map +1 -1
- package/lib/gc/gcDefinitions.d.ts +2 -2
- package/lib/gc/gcDefinitions.d.ts.map +1 -1
- package/lib/gc/gcDefinitions.js.map +1 -1
- package/lib/opLifecycle/outbox.d.ts +3 -0
- package/lib/opLifecycle/outbox.d.ts.map +1 -1
- package/lib/opLifecycle/outbox.js +9 -0
- package/lib/opLifecycle/outbox.js.map +1 -1
- package/lib/opLifecycle/remoteMessageProcessor.d.ts +1 -0
- package/lib/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
- package/lib/opLifecycle/remoteMessageProcessor.js +2 -0
- 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/summary/documentSchema.d.ts +11 -0
- package/lib/summary/documentSchema.d.ts.map +1 -1
- package/lib/summary/documentSchema.js +43 -28
- package/lib/summary/documentSchema.js.map +1 -1
- package/package.json +23 -19
- package/src/blobManager/blobManager.ts +2 -2
- package/src/channelCollection.ts +234 -176
- package/src/containerRuntime.ts +179 -68
- package/src/dataStoreContext.ts +66 -23
- package/src/dataStoreContexts.ts +7 -20
- package/src/gc/garbageCollection.ts +32 -32
- package/src/gc/gcDefinitions.ts +3 -3
- package/src/opLifecycle/outbox.ts +12 -0
- package/src/opLifecycle/remoteMessageProcessor.ts +3 -0
- package/src/packageVersion.ts +1 -1
- package/src/summary/documentSchema.ts +56 -37
package/src/channelCollection.ts
CHANGED
|
@@ -39,11 +39,13 @@ import {
|
|
|
39
39
|
IFluidDataStoreRegistry,
|
|
40
40
|
IFluidParentContext,
|
|
41
41
|
ISummarizeResult,
|
|
42
|
-
InboundAttachMessage,
|
|
43
42
|
NamedFluidDataStoreRegistryEntries,
|
|
44
43
|
channelsTreeName,
|
|
45
44
|
IInboundSignalMessage,
|
|
46
45
|
gcDataBlobKey,
|
|
46
|
+
type IRuntimeMessagesContent,
|
|
47
|
+
type InboundAttachMessage,
|
|
48
|
+
type IRuntimeMessageCollection,
|
|
47
49
|
} from "@fluidframework/runtime-definitions/internal";
|
|
48
50
|
import {
|
|
49
51
|
GCDataBuilder,
|
|
@@ -69,12 +71,15 @@ import {
|
|
|
69
71
|
createChildMonitoringContext,
|
|
70
72
|
extractSafePropertiesFromMessage,
|
|
71
73
|
tagCodeArtifacts,
|
|
72
|
-
type IConfigProvider,
|
|
73
74
|
type ITelemetryPropertiesExt,
|
|
74
75
|
} from "@fluidframework/telemetry-utils/internal";
|
|
75
76
|
import { v4 as uuid } from "uuid";
|
|
76
77
|
|
|
77
|
-
import {
|
|
78
|
+
import {
|
|
79
|
+
DeletedResponseHeaderKey,
|
|
80
|
+
RuntimeHeaderData,
|
|
81
|
+
defaultRuntimeHeaderData,
|
|
82
|
+
} from "./containerRuntime.js";
|
|
78
83
|
import {
|
|
79
84
|
IDataStoreAliasMessage,
|
|
80
85
|
channelToDataStore,
|
|
@@ -124,18 +129,6 @@ interface FluidDataStoreMessage {
|
|
|
124
129
|
type: string;
|
|
125
130
|
}
|
|
126
131
|
|
|
127
|
-
function computeRuntimeHeaderData(
|
|
128
|
-
config: IConfigProvider,
|
|
129
|
-
data: Partial<RuntimeHeaderData>,
|
|
130
|
-
): Required<RuntimeHeaderData> {
|
|
131
|
-
return {
|
|
132
|
-
wait: config.getBoolean("Fluid.ContainerRuntime.WaitHeaderDefault") ?? true,
|
|
133
|
-
viaHandle: false,
|
|
134
|
-
allowTombstone: false,
|
|
135
|
-
...data,
|
|
136
|
-
};
|
|
137
|
-
}
|
|
138
|
-
|
|
139
132
|
/**
|
|
140
133
|
* Creates a shallow wrapper of {@link IFluidParentContext}. The wrapper can then have its methods overwritten as needed
|
|
141
134
|
*/
|
|
@@ -400,115 +393,119 @@ export class ChannelCollection implements IFluidDataStoreChannel, IDisposable {
|
|
|
400
393
|
this.parentContext.makeLocallyVisible();
|
|
401
394
|
}
|
|
402
395
|
|
|
403
|
-
private
|
|
404
|
-
const
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
);
|
|
415
|
-
|
|
416
|
-
// Only log once per container to avoid noise/cost.
|
|
417
|
-
// Allows longitudinal tracking of various state (e.g. foundGCData), and some sampled details
|
|
418
|
-
if (this.shouldSendAttachLog) {
|
|
419
|
-
this.shouldSendAttachLog = false;
|
|
420
|
-
this.mc.logger.sendTelemetryEvent({
|
|
421
|
-
eventName: "dataStoreAttachMessage_sampled",
|
|
422
|
-
...tagCodeArtifacts({ id: attachMessage.id, pkg: attachMessage.type }),
|
|
423
|
-
details: {
|
|
424
|
-
local,
|
|
425
|
-
snapshot: !!attachMessage.snapshot,
|
|
426
|
-
foundGCData,
|
|
396
|
+
private processAttachMessages(messageCollection: IRuntimeMessageCollection) {
|
|
397
|
+
const { envelope, messagesContent, local } = messageCollection;
|
|
398
|
+
for (const { contents } of messagesContent) {
|
|
399
|
+
const attachMessage = contents as InboundAttachMessage;
|
|
400
|
+
// We need to process the GC Data for both local and remote attach messages
|
|
401
|
+
const foundGCData = processAttachMessageGCData(
|
|
402
|
+
attachMessage.snapshot,
|
|
403
|
+
(nodeId, toPath) => {
|
|
404
|
+
// nodeId is the relative path under the node being attached. Always starts with "/", but no trailing "/" after an id
|
|
405
|
+
const fromPath = `/${attachMessage.id}${nodeId === "/" ? "" : nodeId}`;
|
|
406
|
+
this.parentContext.addedGCOutboundRoute(fromPath, toPath, envelope.timestamp);
|
|
427
407
|
},
|
|
428
|
-
...extractSafePropertiesFromMessage(message),
|
|
429
|
-
});
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
// The local object has already been attached
|
|
433
|
-
if (local) {
|
|
434
|
-
assert(
|
|
435
|
-
this.pendingAttach.has(attachMessage.id),
|
|
436
|
-
0x15e /* "Local object does not have matching attach message id" */,
|
|
437
408
|
);
|
|
438
|
-
this.contexts.get(attachMessage.id)?.setAttachState(AttachState.Attached);
|
|
439
|
-
this.pendingAttach.delete(attachMessage.id);
|
|
440
|
-
return;
|
|
441
|
-
}
|
|
442
409
|
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
410
|
+
// Only log once per container to avoid noise/cost.
|
|
411
|
+
// Allows longitudinal tracking of various state (e.g. foundGCData), and some sampled details
|
|
412
|
+
if (this.shouldSendAttachLog) {
|
|
413
|
+
this.shouldSendAttachLog = false;
|
|
414
|
+
this.mc.logger.sendTelemetryEvent({
|
|
415
|
+
eventName: "dataStoreAttachMessage_sampled",
|
|
416
|
+
...tagCodeArtifacts({ id: attachMessage.id, pkg: attachMessage.type }),
|
|
417
|
+
details: {
|
|
418
|
+
local,
|
|
419
|
+
snapshot: !!attachMessage.snapshot,
|
|
420
|
+
foundGCData,
|
|
421
|
+
},
|
|
422
|
+
...extractSafePropertiesFromMessage(envelope),
|
|
423
|
+
});
|
|
424
|
+
}
|
|
456
425
|
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
426
|
+
// The local object has already been attached
|
|
427
|
+
if (local) {
|
|
428
|
+
assert(
|
|
429
|
+
this.pendingAttach.has(attachMessage.id),
|
|
430
|
+
0x15e /* "Local object does not have matching attach message id" */,
|
|
431
|
+
);
|
|
432
|
+
this.contexts.get(attachMessage.id)?.setAttachState(AttachState.Attached);
|
|
433
|
+
this.pendingAttach.delete(attachMessage.id);
|
|
434
|
+
continue;
|
|
463
435
|
}
|
|
464
|
-
}
|
|
465
436
|
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
loadingGroupId: attachMessage.snapshot?.groupId,
|
|
476
|
-
createSummarizerNodeFn: this.parentContext.getCreateChildSummarizerNodeFn(
|
|
477
|
-
attachMessage.id,
|
|
478
|
-
{
|
|
479
|
-
type: CreateSummarizerNodeSource.FromAttach,
|
|
480
|
-
sequenceNumber: message.sequenceNumber,
|
|
481
|
-
snapshot: attachMessage.snapshot ?? {
|
|
482
|
-
entries: [createAttributesBlob(pkg, true /* isRootDataStore */)],
|
|
437
|
+
// If a non-local operation then go and create the object, otherwise mark it as officially attached.
|
|
438
|
+
if (this.alreadyProcessed(attachMessage.id)) {
|
|
439
|
+
// TODO: dataStoreId may require a different tag from PackageData #7488
|
|
440
|
+
const error = new DataCorruptionError(
|
|
441
|
+
// pre-0.58 error message: duplicateDataStoreCreatedWithExistingId
|
|
442
|
+
"Duplicate DataStore created with existing id",
|
|
443
|
+
{
|
|
444
|
+
...extractSafePropertiesFromMessage(envelope),
|
|
445
|
+
...tagCodeArtifacts({ dataStoreId: attachMessage.id }),
|
|
483
446
|
},
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
});
|
|
447
|
+
);
|
|
448
|
+
throw error;
|
|
449
|
+
}
|
|
488
450
|
|
|
489
|
-
|
|
490
|
-
|
|
451
|
+
const flatAttachBlobs = new Map<string, ArrayBufferLike>();
|
|
452
|
+
let snapshot: ISnapshotTree | ISnapshot | undefined;
|
|
453
|
+
if (attachMessage.snapshot) {
|
|
454
|
+
snapshot = buildSnapshotTree(attachMessage.snapshot.entries, flatAttachBlobs);
|
|
455
|
+
if (isInstanceOfISnapshot(this.baseSnapshot)) {
|
|
456
|
+
snapshot = { ...this.baseSnapshot, snapshotTree: snapshot };
|
|
457
|
+
}
|
|
458
|
+
}
|
|
491
459
|
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
460
|
+
// Include the type of attach message which is the pkg of the store to be
|
|
461
|
+
// used by RemoteFluidDataStoreContext in case it is not in the snapshot.
|
|
462
|
+
const pkg = [attachMessage.type];
|
|
463
|
+
const remoteFluidDataStoreContext = new RemoteFluidDataStoreContext({
|
|
464
|
+
id: attachMessage.id,
|
|
465
|
+
snapshot,
|
|
466
|
+
parentContext: this.wrapContextForInnerChannel(attachMessage.id),
|
|
467
|
+
storage: new StorageServiceWithAttachBlobs(
|
|
468
|
+
this.parentContext.storage,
|
|
469
|
+
flatAttachBlobs,
|
|
470
|
+
),
|
|
471
|
+
scope: this.parentContext.scope,
|
|
472
|
+
loadingGroupId: attachMessage.snapshot?.groupId,
|
|
473
|
+
createSummarizerNodeFn: this.parentContext.getCreateChildSummarizerNodeFn(
|
|
474
|
+
attachMessage.id,
|
|
475
|
+
{
|
|
476
|
+
type: CreateSummarizerNodeSource.FromAttach,
|
|
477
|
+
sequenceNumber: envelope.sequenceNumber,
|
|
478
|
+
snapshot: attachMessage.snapshot ?? {
|
|
479
|
+
entries: [createAttributesBlob(pkg, true /* isRootDataStore */)],
|
|
480
|
+
},
|
|
481
|
+
},
|
|
482
|
+
),
|
|
483
|
+
pkg,
|
|
501
484
|
});
|
|
485
|
+
|
|
486
|
+
this.contexts.addBoundOrRemoted(remoteFluidDataStoreContext);
|
|
502
487
|
}
|
|
488
|
+
}
|
|
503
489
|
|
|
504
|
-
|
|
505
|
-
const
|
|
506
|
-
|
|
507
|
-
aliasMessage
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
490
|
+
private processAliasMessages(messageCollection: IRuntimeMessageCollection): void {
|
|
491
|
+
const { envelope, messagesContent, local } = messageCollection;
|
|
492
|
+
for (const { contents, localOpMetadata } of messagesContent) {
|
|
493
|
+
const aliasMessage = contents as IDataStoreAliasMessage;
|
|
494
|
+
if (!isDataStoreAliasMessage(aliasMessage)) {
|
|
495
|
+
throw new DataCorruptionError("malformedDataStoreAliasMessage", {
|
|
496
|
+
...extractSafePropertiesFromMessage(envelope),
|
|
497
|
+
});
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
const resolve = localOpMetadata as PendingAliasResolve;
|
|
501
|
+
const aliasResult = this.processAliasMessageCore(
|
|
502
|
+
aliasMessage.internalId,
|
|
503
|
+
aliasMessage.alias,
|
|
504
|
+
envelope.timestamp,
|
|
505
|
+
);
|
|
506
|
+
if (local) {
|
|
507
|
+
resolve(aliasResult);
|
|
508
|
+
}
|
|
512
509
|
}
|
|
513
510
|
}
|
|
514
511
|
|
|
@@ -828,84 +825,145 @@ export class ChannelCollection implements IFluidDataStoreChannel, IDisposable {
|
|
|
828
825
|
}
|
|
829
826
|
}
|
|
830
827
|
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
) {
|
|
836
|
-
switch (
|
|
828
|
+
/**
|
|
829
|
+
* Process messages for this channel collection. The messages here are contiguous messages in a batch.
|
|
830
|
+
* @param messageCollection - The collection of messages to process.
|
|
831
|
+
*/
|
|
832
|
+
public processMessages(messageCollection: IRuntimeMessageCollection): void {
|
|
833
|
+
switch (messageCollection.envelope.type) {
|
|
834
|
+
case ContainerMessageType.FluidDataStoreOp:
|
|
835
|
+
this.processChannelMessages(messageCollection);
|
|
836
|
+
break;
|
|
837
837
|
case ContainerMessageType.Attach:
|
|
838
|
-
this.
|
|
839
|
-
|
|
838
|
+
this.processAttachMessages(messageCollection);
|
|
839
|
+
break;
|
|
840
840
|
case ContainerMessageType.Alias:
|
|
841
|
-
this.
|
|
842
|
-
return;
|
|
843
|
-
case ContainerMessageType.FluidDataStoreOp: {
|
|
844
|
-
const envelope = message.contents as IEnvelope;
|
|
845
|
-
const innerContents = envelope.contents as FluidDataStoreMessage;
|
|
846
|
-
const transformed = {
|
|
847
|
-
...message,
|
|
848
|
-
type: innerContents.type,
|
|
849
|
-
contents: innerContents.content,
|
|
850
|
-
};
|
|
851
|
-
|
|
852
|
-
this.processChannelOp(envelope.address, transformed, local, localMessageMetadata);
|
|
853
|
-
|
|
854
|
-
// Notify GC of any outbound references that were added by this op.
|
|
855
|
-
detectOutboundReferences(
|
|
856
|
-
envelope.address,
|
|
857
|
-
transformed.contents,
|
|
858
|
-
(fromPath: string, toPath: string) =>
|
|
859
|
-
this.parentContext.addedGCOutboundRoute(fromPath, toPath, message.timestamp),
|
|
860
|
-
);
|
|
841
|
+
this.processAliasMessages(messageCollection);
|
|
861
842
|
break;
|
|
862
|
-
}
|
|
863
843
|
default:
|
|
864
844
|
assert(false, 0x8e9 /* unreached */);
|
|
865
845
|
}
|
|
866
846
|
}
|
|
867
847
|
|
|
868
|
-
|
|
869
|
-
|
|
848
|
+
/**
|
|
849
|
+
* This is still here for back-compat purposes because channel collection implements
|
|
850
|
+
* IFluidDataStoreChannel. Once it is removed from the interface, this method can be removed.
|
|
851
|
+
* Container runtime calls `processMessages` instead.
|
|
852
|
+
*/
|
|
853
|
+
public process(
|
|
870
854
|
message: ISequencedDocumentMessage,
|
|
871
855
|
local: boolean,
|
|
872
|
-
|
|
856
|
+
localOpMetadata: unknown,
|
|
873
857
|
) {
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
// corruption in case a deleted data store accidentally submitted an op.
|
|
878
|
-
if (this.checkAndLogIfDeleted(address, context, "Changed", "processFluidDataStoreOp")) {
|
|
879
|
-
return;
|
|
880
|
-
}
|
|
881
|
-
|
|
882
|
-
if (context === undefined) {
|
|
883
|
-
// Former assert 0x162
|
|
884
|
-
throw DataProcessingError.create(
|
|
885
|
-
"No context for op",
|
|
886
|
-
"processFluidDataStoreOp",
|
|
887
|
-
message,
|
|
858
|
+
this.processMessages({
|
|
859
|
+
envelope: message,
|
|
860
|
+
messagesContent: [
|
|
888
861
|
{
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
contentType: typeof message.contents,
|
|
893
|
-
}),
|
|
894
|
-
...tagCodeArtifacts({ address }),
|
|
862
|
+
contents: message.contents,
|
|
863
|
+
localOpMetadata,
|
|
864
|
+
clientSequenceNumber: message.clientSequenceNumber,
|
|
895
865
|
},
|
|
866
|
+
],
|
|
867
|
+
local,
|
|
868
|
+
});
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
/**
|
|
872
|
+
* Process channel messages. The messages here are contiguous channel type messages in a batch. Bunch
|
|
873
|
+
* of contiguous messages for a data store should be sent to it together.
|
|
874
|
+
* @param messageCollection - The collection of messages to process.
|
|
875
|
+
*/
|
|
876
|
+
private processChannelMessages(messageCollection: IRuntimeMessageCollection): void {
|
|
877
|
+
const { messagesContent, local } = messageCollection;
|
|
878
|
+
let currentMessageState: { address: string; type: string } | undefined;
|
|
879
|
+
let currentMessagesContent: IRuntimeMessagesContent[] = [];
|
|
880
|
+
|
|
881
|
+
// Helper that sends the current bunch of messages to the data store. It validates that the data stores exists.
|
|
882
|
+
const sendBunchedMessages = () => {
|
|
883
|
+
// Current message state will be undefined for the first message in the list.
|
|
884
|
+
if (currentMessageState === undefined) {
|
|
885
|
+
return;
|
|
886
|
+
}
|
|
887
|
+
const currentContext = this.contexts.get(currentMessageState.address);
|
|
888
|
+
assert(!!currentContext, 0xa66 /* Context not found */);
|
|
889
|
+
|
|
890
|
+
currentContext.processMessages({
|
|
891
|
+
envelope: { ...messageCollection.envelope, type: currentMessageState.type },
|
|
892
|
+
messagesContent: currentMessagesContent,
|
|
893
|
+
local,
|
|
894
|
+
});
|
|
895
|
+
currentMessagesContent = [];
|
|
896
|
+
};
|
|
897
|
+
|
|
898
|
+
/**
|
|
899
|
+
* Bunch contiguous messages for the same data store and send them together.
|
|
900
|
+
* This is an optimization mainly for DDSes, where it can process a bunch of ops together. DDSes
|
|
901
|
+
* like merge tree or shared tree can process ops more efficiently when they are bunched together.
|
|
902
|
+
*/
|
|
903
|
+
for (const { contents, ...restOfMessagesContent } of messagesContent) {
|
|
904
|
+
const contentsEnvelope = contents as IEnvelope;
|
|
905
|
+
const address = contentsEnvelope.address;
|
|
906
|
+
const context = this.contexts.get(address);
|
|
907
|
+
|
|
908
|
+
// If the data store has been deleted, log an error and ignore this message. This helps prevent document
|
|
909
|
+
// corruption in case a deleted data store accidentally submitted an op.
|
|
910
|
+
if (this.checkAndLogIfDeleted(address, context, "Changed", "processFluidDataStoreOp")) {
|
|
911
|
+
continue;
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
if (context === undefined) {
|
|
915
|
+
// Former assert 0x162
|
|
916
|
+
throw DataProcessingError.create(
|
|
917
|
+
"No context for op",
|
|
918
|
+
"processFluidDataStoreOp",
|
|
919
|
+
messageCollection.envelope as ISequencedDocumentMessage,
|
|
920
|
+
{
|
|
921
|
+
local,
|
|
922
|
+
messageDetails: JSON.stringify({
|
|
923
|
+
type: messageCollection.envelope.type,
|
|
924
|
+
contentType: typeof contents,
|
|
925
|
+
}),
|
|
926
|
+
...tagCodeArtifacts({ address }),
|
|
927
|
+
},
|
|
928
|
+
);
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
const { type: contextType, content: contextContents } =
|
|
932
|
+
contentsEnvelope.contents as FluidDataStoreMessage;
|
|
933
|
+
// If the address or type of the message changes while processing the message, send the current bunch.
|
|
934
|
+
if (
|
|
935
|
+
currentMessageState?.address !== address ||
|
|
936
|
+
currentMessageState?.type !== contextType
|
|
937
|
+
) {
|
|
938
|
+
sendBunchedMessages();
|
|
939
|
+
}
|
|
940
|
+
currentMessagesContent.push({
|
|
941
|
+
contents: contextContents,
|
|
942
|
+
...restOfMessagesContent,
|
|
943
|
+
});
|
|
944
|
+
currentMessageState = { address, type: contextType };
|
|
945
|
+
|
|
946
|
+
// Notify that a GC node for the data store changed. This is used to detect if a deleted data store is
|
|
947
|
+
// being used.
|
|
948
|
+
this.gcNodeUpdated({
|
|
949
|
+
node: { type: "DataStore", path: `/${address}` },
|
|
950
|
+
reason: "Changed",
|
|
951
|
+
timestampMs: messageCollection.envelope.timestamp,
|
|
952
|
+
packagePath: context.isLoaded ? context.packagePath : undefined,
|
|
953
|
+
});
|
|
954
|
+
|
|
955
|
+
detectOutboundReferences(address, contextContents, (fromPath: string, toPath: string) =>
|
|
956
|
+
this.parentContext.addedGCOutboundRoute(
|
|
957
|
+
fromPath,
|
|
958
|
+
toPath,
|
|
959
|
+
messageCollection.envelope.timestamp,
|
|
960
|
+
),
|
|
896
961
|
);
|
|
897
962
|
}
|
|
898
963
|
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
// being used.
|
|
903
|
-
this.gcNodeUpdated({
|
|
904
|
-
node: { type: "DataStore", path: `/${address}` },
|
|
905
|
-
reason: "Changed",
|
|
906
|
-
timestampMs: message.timestamp,
|
|
907
|
-
packagePath: context.isLoaded ? context.packagePath : undefined,
|
|
908
|
-
});
|
|
964
|
+
// Process the last bunch of messages, if any. Note that there may not be any messages in case all of them are
|
|
965
|
+
// ignored because the data store is deleted.
|
|
966
|
+
sendBunchedMessages();
|
|
909
967
|
}
|
|
910
968
|
|
|
911
969
|
private async getDataStore(
|
|
@@ -913,7 +971,7 @@ export class ChannelCollection implements IFluidDataStoreChannel, IDisposable {
|
|
|
913
971
|
requestHeaderData: RuntimeHeaderData,
|
|
914
972
|
originalRequest: IRequest,
|
|
915
973
|
): Promise<IFluidDataStoreContextInternal> {
|
|
916
|
-
const headerData =
|
|
974
|
+
const headerData = { ...defaultRuntimeHeaderData, ...requestHeaderData };
|
|
917
975
|
if (
|
|
918
976
|
this.checkAndLogIfDeleted(
|
|
919
977
|
id,
|
|
@@ -961,7 +1019,7 @@ export class ChannelCollection implements IFluidDataStoreChannel, IDisposable {
|
|
|
961
1019
|
) {
|
|
962
1020
|
return undefined;
|
|
963
1021
|
}
|
|
964
|
-
const headerData =
|
|
1022
|
+
const headerData = { ...defaultRuntimeHeaderData, ...requestHeaderData };
|
|
965
1023
|
const context = await this.contexts.getBoundOrRemoted(id, headerData.wait);
|
|
966
1024
|
if (context === undefined) {
|
|
967
1025
|
return undefined;
|