@fluidframework/container-runtime 2.0.0-dev-rc.5.0.0.271262 → 2.0.0-dev-rc.5.0.0.272251
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/api-extractor/api-extractor-lint-bundle.json +5 -0
- package/api-extractor/api-extractor-lint-legacy.cjs.json +5 -0
- package/api-extractor/api-extractor-lint-legacy.esm.json +5 -0
- package/api-extractor/api-extractor-lint-public.cjs.json +5 -0
- package/api-extractor/api-extractor-lint-public.esm.json +5 -0
- package/api-report/container-runtime.alpha.api.md +1 -1
- package/container-runtime.test-files.tar +0 -0
- package/dist/batchTracker.js +1 -5
- package/dist/batchTracker.js.map +1 -1
- package/dist/blobManager.js +14 -39
- package/dist/blobManager.js.map +1 -1
- package/dist/channelCollection.d.ts +12 -2
- package/dist/channelCollection.d.ts.map +1 -1
- package/dist/channelCollection.js +95 -110
- package/dist/channelCollection.js.map +1 -1
- package/dist/connectionTelemetry.js +14 -32
- package/dist/connectionTelemetry.js.map +1 -1
- package/dist/containerHandleContext.js +0 -4
- package/dist/containerHandleContext.js.map +1 -1
- package/dist/containerRuntime.d.ts +2 -1
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +51 -142
- package/dist/containerRuntime.js.map +1 -1
- package/dist/dataStore.js +1 -9
- package/dist/dataStore.js.map +1 -1
- package/dist/dataStoreContext.d.ts +2 -1
- package/dist/dataStoreContext.d.ts.map +1 -1
- package/dist/dataStoreContext.js +102 -143
- package/dist/dataStoreContext.js.map +1 -1
- package/dist/dataStoreContexts.js +28 -29
- package/dist/dataStoreContexts.js.map +1 -1
- package/dist/dataStoreRegistry.js +0 -1
- package/dist/dataStoreRegistry.js.map +1 -1
- package/dist/deltaManagerProxies.js +28 -33
- package/dist/deltaManagerProxies.js.map +1 -1
- package/dist/deltaScheduler.js +9 -13
- package/dist/deltaScheduler.js.map +1 -1
- package/dist/error.js +1 -2
- package/dist/error.js.map +1 -1
- package/dist/gc/garbageCollection.d.ts +4 -2
- package/dist/gc/garbageCollection.d.ts.map +1 -1
- package/dist/gc/garbageCollection.js +25 -43
- package/dist/gc/garbageCollection.js.map +1 -1
- package/dist/gc/gcDefinitions.d.ts +7 -4
- package/dist/gc/gcDefinitions.d.ts.map +1 -1
- package/dist/gc/gcDefinitions.js.map +1 -1
- package/dist/gc/gcSummaryStateTracker.js +14 -19
- package/dist/gc/gcSummaryStateTracker.js.map +1 -1
- package/dist/gc/gcTelemetry.d.ts +1 -1
- package/dist/gc/gcTelemetry.d.ts.map +1 -1
- package/dist/gc/gcTelemetry.js +5 -17
- package/dist/gc/gcTelemetry.js.map +1 -1
- package/dist/gc/gcUnreferencedStateTracker.js +1 -12
- package/dist/gc/gcUnreferencedStateTracker.js.map +1 -1
- package/dist/opLifecycle/batchManager.js +3 -5
- package/dist/opLifecycle/batchManager.js.map +1 -1
- package/dist/opLifecycle/opCompressor.js +0 -1
- package/dist/opLifecycle/opCompressor.js.map +1 -1
- package/dist/opLifecycle/opDecompressor.js +4 -6
- package/dist/opLifecycle/opDecompressor.js.map +1 -1
- package/dist/opLifecycle/opGroupingManager.js +1 -3
- package/dist/opLifecycle/opGroupingManager.js.map +1 -1
- package/dist/opLifecycle/opSplitter.js +0 -6
- package/dist/opLifecycle/opSplitter.js.map +1 -1
- package/dist/opLifecycle/outbox.js +10 -15
- package/dist/opLifecycle/outbox.js.map +1 -1
- package/dist/opLifecycle/remoteMessageProcessor.js +0 -3
- 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.js +14 -20
- package/dist/pendingStateManager.js.map +1 -1
- package/dist/scheduleManager.js +4 -15
- package/dist/scheduleManager.js.map +1 -1
- package/dist/storageServiceWithAttachBlobs.js +0 -1
- package/dist/storageServiceWithAttachBlobs.js.map +1 -1
- package/dist/summary/documentSchema.js +1 -17
- package/dist/summary/documentSchema.js.map +1 -1
- package/dist/summary/orderedClientElection.js +11 -19
- package/dist/summary/orderedClientElection.js.map +1 -1
- package/dist/summary/runWhileConnectedCoordinator.js +2 -4
- package/dist/summary/runWhileConnectedCoordinator.js.map +1 -1
- package/dist/summary/runningSummarizer.js +38 -56
- package/dist/summary/runningSummarizer.js.map +1 -1
- package/dist/summary/summarizer.js +8 -17
- package/dist/summary/summarizer.js.map +1 -1
- package/dist/summary/summarizerClientElection.js +7 -18
- package/dist/summary/summarizerClientElection.js.map +1 -1
- package/dist/summary/summarizerHeuristics.js +25 -30
- package/dist/summary/summarizerHeuristics.js.map +1 -1
- package/dist/summary/summarizerNode/summarizerNode.js +3 -12
- package/dist/summary/summarizerNode/summarizerNode.js.map +1 -1
- package/dist/summary/summarizerNode/summarizerNodeUtils.js +0 -2
- package/dist/summary/summarizerNode/summarizerNodeUtils.js.map +1 -1
- package/dist/summary/summarizerNode/summarizerNodeWithGc.js +6 -20
- package/dist/summary/summarizerNode/summarizerNodeWithGc.js.map +1 -1
- package/dist/summary/summaryCollection.js +11 -23
- package/dist/summary/summaryCollection.js.map +1 -1
- package/dist/summary/summaryGenerator.js +5 -12
- package/dist/summary/summaryGenerator.js.map +1 -1
- package/dist/summary/summaryManager.js +48 -58
- package/dist/summary/summaryManager.js.map +1 -1
- package/dist/throttler.js +1 -4
- package/dist/throttler.js.map +1 -1
- package/lib/batchTracker.js +1 -5
- package/lib/batchTracker.js.map +1 -1
- package/lib/blobManager.js +14 -39
- package/lib/blobManager.js.map +1 -1
- package/lib/channelCollection.d.ts +12 -2
- package/lib/channelCollection.d.ts.map +1 -1
- package/lib/channelCollection.js +95 -110
- package/lib/channelCollection.js.map +1 -1
- package/lib/connectionTelemetry.js +14 -32
- package/lib/connectionTelemetry.js.map +1 -1
- package/lib/containerHandleContext.js +0 -4
- package/lib/containerHandleContext.js.map +1 -1
- package/lib/containerRuntime.d.ts +2 -1
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +52 -143
- package/lib/containerRuntime.js.map +1 -1
- package/lib/dataStore.js +1 -9
- package/lib/dataStore.js.map +1 -1
- package/lib/dataStoreContext.d.ts +2 -1
- package/lib/dataStoreContext.d.ts.map +1 -1
- package/lib/dataStoreContext.js +102 -143
- package/lib/dataStoreContext.js.map +1 -1
- package/lib/dataStoreContexts.js +28 -29
- package/lib/dataStoreContexts.js.map +1 -1
- package/lib/dataStoreRegistry.js +0 -1
- package/lib/dataStoreRegistry.js.map +1 -1
- package/lib/deltaManagerProxies.js +28 -33
- package/lib/deltaManagerProxies.js.map +1 -1
- package/lib/deltaScheduler.js +9 -13
- package/lib/deltaScheduler.js.map +1 -1
- package/lib/error.js +1 -2
- package/lib/error.js.map +1 -1
- package/lib/gc/garbageCollection.d.ts +4 -2
- package/lib/gc/garbageCollection.d.ts.map +1 -1
- package/lib/gc/garbageCollection.js +25 -43
- package/lib/gc/garbageCollection.js.map +1 -1
- package/lib/gc/gcDefinitions.d.ts +7 -4
- package/lib/gc/gcDefinitions.d.ts.map +1 -1
- package/lib/gc/gcDefinitions.js.map +1 -1
- package/lib/gc/gcSummaryStateTracker.js +14 -19
- package/lib/gc/gcSummaryStateTracker.js.map +1 -1
- package/lib/gc/gcTelemetry.d.ts +1 -1
- package/lib/gc/gcTelemetry.d.ts.map +1 -1
- package/lib/gc/gcTelemetry.js +5 -17
- package/lib/gc/gcTelemetry.js.map +1 -1
- package/lib/gc/gcUnreferencedStateTracker.js +1 -12
- package/lib/gc/gcUnreferencedStateTracker.js.map +1 -1
- package/lib/opLifecycle/batchManager.js +3 -5
- package/lib/opLifecycle/batchManager.js.map +1 -1
- package/lib/opLifecycle/opCompressor.js +0 -1
- package/lib/opLifecycle/opCompressor.js.map +1 -1
- package/lib/opLifecycle/opDecompressor.js +4 -6
- package/lib/opLifecycle/opDecompressor.js.map +1 -1
- package/lib/opLifecycle/opGroupingManager.js +1 -3
- package/lib/opLifecycle/opGroupingManager.js.map +1 -1
- package/lib/opLifecycle/opSplitter.js +0 -6
- package/lib/opLifecycle/opSplitter.js.map +1 -1
- package/lib/opLifecycle/outbox.js +10 -15
- package/lib/opLifecycle/outbox.js.map +1 -1
- package/lib/opLifecycle/remoteMessageProcessor.js +0 -3
- 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.js +14 -20
- package/lib/pendingStateManager.js.map +1 -1
- package/lib/scheduleManager.js +4 -15
- package/lib/scheduleManager.js.map +1 -1
- package/lib/storageServiceWithAttachBlobs.js +0 -1
- package/lib/storageServiceWithAttachBlobs.js.map +1 -1
- package/lib/summary/documentSchema.js +1 -17
- package/lib/summary/documentSchema.js.map +1 -1
- package/lib/summary/orderedClientElection.js +11 -19
- package/lib/summary/orderedClientElection.js.map +1 -1
- package/lib/summary/runWhileConnectedCoordinator.js +2 -4
- package/lib/summary/runWhileConnectedCoordinator.js.map +1 -1
- package/lib/summary/runningSummarizer.js +38 -56
- package/lib/summary/runningSummarizer.js.map +1 -1
- package/lib/summary/summarizer.js +8 -17
- package/lib/summary/summarizer.js.map +1 -1
- package/lib/summary/summarizerClientElection.js +7 -18
- package/lib/summary/summarizerClientElection.js.map +1 -1
- package/lib/summary/summarizerHeuristics.js +25 -30
- package/lib/summary/summarizerHeuristics.js.map +1 -1
- package/lib/summary/summarizerNode/summarizerNode.js +3 -12
- package/lib/summary/summarizerNode/summarizerNode.js.map +1 -1
- package/lib/summary/summarizerNode/summarizerNodeUtils.js +0 -2
- package/lib/summary/summarizerNode/summarizerNodeUtils.js.map +1 -1
- package/lib/summary/summarizerNode/summarizerNodeWithGc.js +6 -20
- package/lib/summary/summarizerNode/summarizerNodeWithGc.js.map +1 -1
- package/lib/summary/summaryCollection.js +11 -23
- package/lib/summary/summaryCollection.js.map +1 -1
- package/lib/summary/summaryGenerator.js +5 -12
- package/lib/summary/summaryGenerator.js.map +1 -1
- package/lib/summary/summaryManager.js +48 -58
- package/lib/summary/summaryManager.js.map +1 -1
- package/lib/throttler.js +1 -4
- package/lib/throttler.js.map +1 -1
- package/package.json +27 -18
- package/src/channelCollection.ts +133 -123
- package/src/containerRuntime.ts +31 -4
- package/src/dataStoreContext.ts +3 -2
- package/src/gc/garbageCollection.ts +24 -7
- package/src/gc/gcDefinitions.ts +16 -4
- package/src/gc/gcTelemetry.ts +1 -7
- package/src/packageVersion.ts +1 -1
- package/tsdoc.json +4 -0
package/src/channelCollection.ts
CHANGED
|
@@ -400,7 +400,7 @@ export class ChannelCollection implements IFluidDataStoreChannel, IDisposable {
|
|
|
400
400
|
const foundGCData = processAttachMessageGCData(attachMessage.snapshot, (nodeId, toPath) => {
|
|
401
401
|
// nodeId is the relative path under the node being attached. Always starts with "/", but no trailing "/" after an id
|
|
402
402
|
const fromPath = `/${attachMessage.id}${nodeId === "/" ? "" : nodeId}`;
|
|
403
|
-
this.parentContext.addedGCOutboundRoute(fromPath, toPath);
|
|
403
|
+
this.parentContext.addedGCOutboundRoute(fromPath, toPath, message.timestamp);
|
|
404
404
|
});
|
|
405
405
|
|
|
406
406
|
// Only log once per container to avoid noise/cost.
|
|
@@ -495,13 +495,18 @@ export class ChannelCollection implements IFluidDataStoreChannel, IDisposable {
|
|
|
495
495
|
const aliasResult = this.processAliasMessageCore(
|
|
496
496
|
aliasMessage.internalId,
|
|
497
497
|
aliasMessage.alias,
|
|
498
|
+
message.timestamp,
|
|
498
499
|
);
|
|
499
500
|
if (local) {
|
|
500
501
|
resolve(aliasResult);
|
|
501
502
|
}
|
|
502
503
|
}
|
|
503
504
|
|
|
504
|
-
public processAliasMessageCore(
|
|
505
|
+
public processAliasMessageCore(
|
|
506
|
+
internalId: string,
|
|
507
|
+
alias: string,
|
|
508
|
+
messageTimestampMs?: number,
|
|
509
|
+
): boolean {
|
|
505
510
|
if (this.alreadyProcessed(alias)) {
|
|
506
511
|
return false;
|
|
507
512
|
}
|
|
@@ -521,7 +526,11 @@ export class ChannelCollection implements IFluidDataStoreChannel, IDisposable {
|
|
|
521
526
|
return false;
|
|
522
527
|
}
|
|
523
528
|
|
|
524
|
-
this.
|
|
529
|
+
// If message timestamp doesn't exist, this is called in a detached container. Don't notify GC in that case
|
|
530
|
+
// because it doesn't run in detached container and doesn't need to know about this route.
|
|
531
|
+
if (messageTimestampMs) {
|
|
532
|
+
this.parentContext.addedGCOutboundRoute("/", `/${internalId}`, messageTimestampMs);
|
|
533
|
+
}
|
|
525
534
|
|
|
526
535
|
this.aliasMap.set(alias, context.id);
|
|
527
536
|
this.aliasedDataStores.add(context.id);
|
|
@@ -829,7 +838,11 @@ export class ChannelCollection implements IFluidDataStoreChannel, IDisposable {
|
|
|
829
838
|
envelope.address,
|
|
830
839
|
transformed.contents,
|
|
831
840
|
(fromPath: string, toPath: string) =>
|
|
832
|
-
this.parentContext.addedGCOutboundRoute(
|
|
841
|
+
this.parentContext.addedGCOutboundRoute(
|
|
842
|
+
fromPath,
|
|
843
|
+
toPath,
|
|
844
|
+
message.timestamp,
|
|
845
|
+
),
|
|
833
846
|
);
|
|
834
847
|
break;
|
|
835
848
|
}
|
|
@@ -1075,86 +1088,30 @@ export class ChannelCollection implements IFluidDataStoreChannel, IDisposable {
|
|
|
1075
1088
|
return this.contexts.size;
|
|
1076
1089
|
}
|
|
1077
1090
|
|
|
1078
|
-
public async summarize(
|
|
1079
|
-
fullTree: boolean,
|
|
1080
|
-
trackState: boolean,
|
|
1081
|
-
telemetryContext?: ITelemetryContext,
|
|
1082
|
-
): Promise<ISummaryTreeWithStats> {
|
|
1083
|
-
const summaryBuilder = new SummaryTreeBuilder();
|
|
1084
|
-
|
|
1085
|
-
// Iterate over each store and ask it to snapshot
|
|
1086
|
-
await Promise.all(
|
|
1087
|
-
Array.from(this.contexts)
|
|
1088
|
-
.filter(([_, context]) => {
|
|
1089
|
-
// Summarizer works only with clients with no local changes. A data store in attaching
|
|
1090
|
-
// state indicates an op was sent to attach a local data store, and the the attach op
|
|
1091
|
-
// had not yet round tripped back to the client.
|
|
1092
|
-
if (context.attachState === AttachState.Attaching) {
|
|
1093
|
-
// Formerly assert 0x588
|
|
1094
|
-
const error = DataProcessingError.create(
|
|
1095
|
-
"Local data store detected in attaching state during summarize",
|
|
1096
|
-
"summarize",
|
|
1097
|
-
);
|
|
1098
|
-
throw error;
|
|
1099
|
-
}
|
|
1100
|
-
return context.attachState === AttachState.Attached;
|
|
1101
|
-
})
|
|
1102
|
-
.map(async ([contextId, context]) => {
|
|
1103
|
-
const contextSummary = await context.summarize(
|
|
1104
|
-
fullTree,
|
|
1105
|
-
trackState,
|
|
1106
|
-
telemetryContext,
|
|
1107
|
-
);
|
|
1108
|
-
summaryBuilder.addWithStats(contextId, contextSummary);
|
|
1109
|
-
}),
|
|
1110
|
-
);
|
|
1111
|
-
|
|
1112
|
-
return summaryBuilder.getSummaryTree();
|
|
1113
|
-
}
|
|
1114
|
-
|
|
1115
1091
|
/**
|
|
1116
1092
|
* Create a summary. Used when attaching or serializing a detached container.
|
|
1117
1093
|
*/
|
|
1118
1094
|
public getAttachSummary(telemetryContext?: ITelemetryContext): ISummaryTreeWithStats {
|
|
1119
1095
|
const builder = new SummaryTreeBuilder();
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
.map(([key, value]) => {
|
|
1140
|
-
let dataStoreSummary: ISummarizeResult;
|
|
1141
|
-
if (value.isLoaded) {
|
|
1142
|
-
dataStoreSummary = value.getAttachSummary(telemetryContext);
|
|
1143
|
-
} else {
|
|
1144
|
-
// If this data store is not yet loaded, then there should be no changes in the snapshot from
|
|
1145
|
-
// which it was created as it is detached container. So just use the previous snapshot.
|
|
1146
|
-
assert(
|
|
1147
|
-
!!this.baseSnapshot,
|
|
1148
|
-
0x166 /* "BaseSnapshot should be there as detached container loaded from snapshot" */,
|
|
1149
|
-
);
|
|
1150
|
-
dataStoreSummary = convertSnapshotTreeToSummaryTree(
|
|
1151
|
-
getSnapshotTree(this.baseSnapshot).trees[key],
|
|
1152
|
-
);
|
|
1153
|
-
}
|
|
1154
|
-
builder.addWithStats(key, dataStoreSummary);
|
|
1155
|
-
});
|
|
1156
|
-
} while (notBoundContextsLength !== this.contexts.notBoundLength());
|
|
1157
|
-
|
|
1096
|
+
this.visitLocalBoundContextsDuringAttach(
|
|
1097
|
+
(contextId: string, context: FluidDataStoreContext) => {
|
|
1098
|
+
let dataStoreSummary: ISummarizeResult;
|
|
1099
|
+
if (context.isLoaded) {
|
|
1100
|
+
dataStoreSummary = context.getAttachSummary(telemetryContext);
|
|
1101
|
+
} else {
|
|
1102
|
+
// If this data store is not yet loaded, then there should be no changes in the snapshot from
|
|
1103
|
+
// which it was created as it is detached container. So just use the previous snapshot.
|
|
1104
|
+
assert(
|
|
1105
|
+
!!this.baseSnapshot,
|
|
1106
|
+
0x166 /* "BaseSnapshot should be there as detached container loaded from snapshot" */,
|
|
1107
|
+
);
|
|
1108
|
+
dataStoreSummary = convertSnapshotTreeToSummaryTree(
|
|
1109
|
+
getSnapshotTree(this.baseSnapshot).trees[contextId],
|
|
1110
|
+
);
|
|
1111
|
+
}
|
|
1112
|
+
builder.addWithStats(contextId, dataStoreSummary);
|
|
1113
|
+
},
|
|
1114
|
+
);
|
|
1158
1115
|
return builder.getSummaryTree();
|
|
1159
1116
|
}
|
|
1160
1117
|
|
|
@@ -1163,31 +1120,100 @@ export class ChannelCollection implements IFluidDataStoreChannel, IDisposable {
|
|
|
1163
1120
|
*/
|
|
1164
1121
|
public getAttachGCData(telemetryContext?: ITelemetryContext): IGarbageCollectionData {
|
|
1165
1122
|
const builder = new GCDataBuilder();
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
([key, _]) =>
|
|
1175
|
-
// Take GC data of bounded data stores only.
|
|
1176
|
-
!this.contexts.isNotBound(key),
|
|
1177
|
-
)
|
|
1178
|
-
.map(([key, value]) => {
|
|
1179
|
-
const contextGCData = value.getAttachGCData(telemetryContext);
|
|
1180
|
-
// Prefix the child's id to the ids of its GC nodes so they can be identified as belonging to the child.
|
|
1181
|
-
// This also gradually builds the id of each node to be a path from the root.
|
|
1182
|
-
builder.prefixAndAddNodes(key, contextGCData.gcNodes);
|
|
1183
|
-
});
|
|
1184
|
-
} while (notBoundContextsLength !== this.contexts.notBoundLength());
|
|
1185
|
-
|
|
1123
|
+
this.visitLocalBoundContextsDuringAttach(
|
|
1124
|
+
(contextId: string, context: FluidDataStoreContext) => {
|
|
1125
|
+
const contextGCData = context.getAttachGCData(telemetryContext);
|
|
1126
|
+
// Prefix the child's id to the ids of its GC nodes so they can be identified as belonging to the child.
|
|
1127
|
+
// This also gradually builds the id of each node to be a path from the root.
|
|
1128
|
+
builder.prefixAndAddNodes(contextId, contextGCData.gcNodes);
|
|
1129
|
+
},
|
|
1130
|
+
);
|
|
1186
1131
|
// Get the outbound routes (aliased data stores) and add a GC node for this channel.
|
|
1187
1132
|
builder.addNode("/", Array.from(this.aliasedDataStores));
|
|
1188
1133
|
return builder.getGCData();
|
|
1189
1134
|
}
|
|
1190
1135
|
|
|
1136
|
+
/**
|
|
1137
|
+
* Helper method for preparing to attach this channel.
|
|
1138
|
+
* Runs the callback for each bound context to incorporate its data however the caller specifies
|
|
1139
|
+
*/
|
|
1140
|
+
private visitLocalBoundContextsDuringAttach(
|
|
1141
|
+
visitor: (contextId: string, context: FluidDataStoreContext) => void,
|
|
1142
|
+
): void {
|
|
1143
|
+
const visitedContexts = new Set<string>();
|
|
1144
|
+
let visitedLength = -1;
|
|
1145
|
+
let notBoundContextsLength = -1;
|
|
1146
|
+
while (
|
|
1147
|
+
visitedLength !== visitedContexts.size &&
|
|
1148
|
+
notBoundContextsLength !== this.contexts.notBoundLength()
|
|
1149
|
+
) {
|
|
1150
|
+
// detect changes in the visitedContexts set, as on visiting a context
|
|
1151
|
+
// it could could make contexts available by removing other contexts
|
|
1152
|
+
// from the not bound context list, so we need to ensure those get processed as well.
|
|
1153
|
+
// only once the loop can run with no new contexts added to the visitedContexts set do we
|
|
1154
|
+
// know for sure all possible contexts have been visited.
|
|
1155
|
+
visitedLength = visitedContexts.size;
|
|
1156
|
+
notBoundContextsLength = this.contexts.notBoundLength();
|
|
1157
|
+
for (const [contextId, context] of this.contexts) {
|
|
1158
|
+
if (
|
|
1159
|
+
!(
|
|
1160
|
+
visitedContexts.has(contextId) ||
|
|
1161
|
+
this.contexts.isNotBound(contextId) ||
|
|
1162
|
+
this.attachOpFiredForDataStore.has(contextId)
|
|
1163
|
+
)
|
|
1164
|
+
) {
|
|
1165
|
+
visitor(contextId, context);
|
|
1166
|
+
visitedContexts.add(contextId);
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
/**
|
|
1173
|
+
* Helper method for preparing to summarize this channel.
|
|
1174
|
+
* Runs the callback for each bound context to incorporate its data however the caller specifies
|
|
1175
|
+
*/
|
|
1176
|
+
private async visitContextsDuringSummary(
|
|
1177
|
+
visitor: (contextId: string, context: FluidDataStoreContext) => Promise<void>,
|
|
1178
|
+
): Promise<void> {
|
|
1179
|
+
for (const [contextId, context] of this.contexts) {
|
|
1180
|
+
// Summarizer client and hence GC works only with clients with no local changes. A data store in
|
|
1181
|
+
// attaching state indicates an op was sent to attach a local data store, and the the attach op
|
|
1182
|
+
// had not yet round tripped back to the client.
|
|
1183
|
+
// Formerly assert 0x589
|
|
1184
|
+
if (context.attachState === AttachState.Attaching) {
|
|
1185
|
+
const error = DataProcessingError.create(
|
|
1186
|
+
"Local data store detected in attaching state",
|
|
1187
|
+
"summarize/getGCData",
|
|
1188
|
+
);
|
|
1189
|
+
throw error;
|
|
1190
|
+
}
|
|
1191
|
+
|
|
1192
|
+
if (context.attachState === AttachState.Attached) {
|
|
1193
|
+
await visitor(contextId, context);
|
|
1194
|
+
}
|
|
1195
|
+
}
|
|
1196
|
+
}
|
|
1197
|
+
|
|
1198
|
+
public async summarize(
|
|
1199
|
+
fullTree: boolean,
|
|
1200
|
+
trackState: boolean,
|
|
1201
|
+
telemetryContext?: ITelemetryContext,
|
|
1202
|
+
): Promise<ISummaryTreeWithStats> {
|
|
1203
|
+
const summaryBuilder = new SummaryTreeBuilder();
|
|
1204
|
+
await this.visitContextsDuringSummary(
|
|
1205
|
+
async (contextId: string, context: FluidDataStoreContext) => {
|
|
1206
|
+
const contextSummary = await context.summarize(
|
|
1207
|
+
fullTree,
|
|
1208
|
+
trackState,
|
|
1209
|
+
telemetryContext,
|
|
1210
|
+
);
|
|
1211
|
+
summaryBuilder.addWithStats(contextId, contextSummary);
|
|
1212
|
+
},
|
|
1213
|
+
);
|
|
1214
|
+
return summaryBuilder.getSummaryTree();
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1191
1217
|
/**
|
|
1192
1218
|
* Generates data used for garbage collection. It does the following:
|
|
1193
1219
|
*
|
|
@@ -1203,30 +1229,13 @@ export class ChannelCollection implements IFluidDataStoreChannel, IDisposable {
|
|
|
1203
1229
|
*/
|
|
1204
1230
|
public async getGCData(fullGC: boolean = false): Promise<IGarbageCollectionData> {
|
|
1205
1231
|
const builder = new GCDataBuilder();
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
// Formerly assert 0x589
|
|
1214
|
-
if (context.attachState === AttachState.Attaching) {
|
|
1215
|
-
const error = DataProcessingError.create(
|
|
1216
|
-
"Local data store detected in attaching state while running GC",
|
|
1217
|
-
"getGCData",
|
|
1218
|
-
);
|
|
1219
|
-
throw error;
|
|
1220
|
-
}
|
|
1221
|
-
|
|
1222
|
-
return context.attachState === AttachState.Attached;
|
|
1223
|
-
})
|
|
1224
|
-
.map(async ([contextId, context]) => {
|
|
1225
|
-
const contextGCData = await context.getGCData(fullGC);
|
|
1226
|
-
// Prefix the child's id to the ids of its GC nodes so they can be identified as belonging to the child.
|
|
1227
|
-
// This also gradually builds the id of each node to be a path from the root.
|
|
1228
|
-
builder.prefixAndAddNodes(contextId, contextGCData.gcNodes);
|
|
1229
|
-
}),
|
|
1232
|
+
await this.visitContextsDuringSummary(
|
|
1233
|
+
async (contextId: string, context: FluidDataStoreContext) => {
|
|
1234
|
+
const contextGCData = await context.getGCData(fullGC);
|
|
1235
|
+
// Prefix the child's id to the ids of its GC nodes so they can be identified as belonging to the child.
|
|
1236
|
+
// This also gradually builds the id of each node to be a path from the root.
|
|
1237
|
+
builder.prefixAndAddNodes(contextId, contextGCData.gcNodes);
|
|
1238
|
+
},
|
|
1230
1239
|
);
|
|
1231
1240
|
|
|
1232
1241
|
// Get the outbound routes and add a GC node for this channel.
|
|
@@ -1432,6 +1441,7 @@ export class ChannelCollection implements IFluidDataStoreChannel, IDisposable {
|
|
|
1432
1441
|
packagePath: details.pkg,
|
|
1433
1442
|
request,
|
|
1434
1443
|
headerData,
|
|
1444
|
+
timestampMs: undefined, // This will be added by the parent context if needed.
|
|
1435
1445
|
});
|
|
1436
1446
|
|
|
1437
1447
|
const dataStore = await dataStoreContext.realize();
|
package/src/containerRuntime.ts
CHANGED
|
@@ -121,6 +121,7 @@ import {
|
|
|
121
121
|
loggerToMonitoringContext,
|
|
122
122
|
raiseConnectedEvent,
|
|
123
123
|
wrapError,
|
|
124
|
+
tagCodeArtifacts,
|
|
124
125
|
} from "@fluidframework/telemetry-utils/internal";
|
|
125
126
|
import { v4 as uuid } from "uuid";
|
|
126
127
|
|
|
@@ -1663,7 +1664,11 @@ export class ContainerRuntime
|
|
|
1663
1664
|
snapshot,
|
|
1664
1665
|
parentContext,
|
|
1665
1666
|
this.mc.logger,
|
|
1666
|
-
(props) =>
|
|
1667
|
+
(props) =>
|
|
1668
|
+
this.garbageCollector.nodeUpdated({
|
|
1669
|
+
...props,
|
|
1670
|
+
timestampMs: props.timestampMs ?? this.getCurrentReferenceTimestampMs(),
|
|
1671
|
+
}),
|
|
1667
1672
|
(path: string) => this.garbageCollector.isNodeDeleted(path),
|
|
1668
1673
|
new Map<string, string>(dataStoreAliasMap),
|
|
1669
1674
|
async (runtime: ChannelCollection) => provideEntryPoint,
|
|
@@ -1689,6 +1694,7 @@ export class ContainerRuntime
|
|
|
1689
1694
|
this.garbageCollector.nodeUpdated({
|
|
1690
1695
|
node: { type: "Blob", path: blobPath },
|
|
1691
1696
|
reason: "Loaded",
|
|
1697
|
+
timestampMs: this.getCurrentReferenceTimestampMs(),
|
|
1692
1698
|
}),
|
|
1693
1699
|
isBlobDeleted: (blobPath: string) => this.garbageCollector.isNodeDeleted(blobPath),
|
|
1694
1700
|
runtime: this,
|
|
@@ -2715,7 +2721,11 @@ export class ContainerRuntime
|
|
|
2715
2721
|
}
|
|
2716
2722
|
break;
|
|
2717
2723
|
case ContainerMessageType.GC:
|
|
2718
|
-
this.garbageCollector.processMessage(
|
|
2724
|
+
this.garbageCollector.processMessage(
|
|
2725
|
+
messageWithContext.message,
|
|
2726
|
+
messageWithContext.message.timestamp,
|
|
2727
|
+
local,
|
|
2728
|
+
);
|
|
2719
2729
|
break;
|
|
2720
2730
|
case ContainerMessageType.ChunkedOp:
|
|
2721
2731
|
// From observability POV, we should not exppse the rest of the system (including "op" events on object) to these messages.
|
|
@@ -2949,6 +2959,7 @@ export class ContainerRuntime
|
|
|
2949
2959
|
node: { type: "DataStore", path: `/${internalId}` },
|
|
2950
2960
|
reason: "Loaded",
|
|
2951
2961
|
packagePath: context.packagePath,
|
|
2962
|
+
timestampMs: this.getCurrentReferenceTimestampMs(),
|
|
2952
2963
|
});
|
|
2953
2964
|
return channel.entryPoint;
|
|
2954
2965
|
}
|
|
@@ -3402,9 +3413,25 @@ export class ContainerRuntime
|
|
|
3402
3413
|
* all references added in the system.
|
|
3403
3414
|
* @param fromPath - The absolute path of the node that added the reference.
|
|
3404
3415
|
* @param toPath - The absolute path of the outbound node that is referenced.
|
|
3416
|
+
* @param messageTimestampMs - The timestamp of the message that added the reference.
|
|
3405
3417
|
*/
|
|
3406
|
-
public addedGCOutboundRoute(fromPath: string, toPath: string) {
|
|
3407
|
-
|
|
3418
|
+
public addedGCOutboundRoute(fromPath: string, toPath: string, messageTimestampMs?: number) {
|
|
3419
|
+
// This is always called when processing an op so messageTimestampMs should exist. Due to back-compat
|
|
3420
|
+
// across the data store runtime / container runtime boundary, this may be undefined and if so, get
|
|
3421
|
+
// the timestamp from the last processed message which should exist.
|
|
3422
|
+
// If a timestamp doesn't exist, log so we can learn about these cases and return.
|
|
3423
|
+
const timestampMs = messageTimestampMs ?? this.getCurrentReferenceTimestampMs();
|
|
3424
|
+
if (timestampMs === undefined) {
|
|
3425
|
+
this.mc.logger.sendTelemetryEvent({
|
|
3426
|
+
eventName: "NoTimestampInGCOutboundRoute",
|
|
3427
|
+
...tagCodeArtifacts({
|
|
3428
|
+
id: toPath,
|
|
3429
|
+
fromId: fromPath,
|
|
3430
|
+
}),
|
|
3431
|
+
});
|
|
3432
|
+
return;
|
|
3433
|
+
}
|
|
3434
|
+
this.garbageCollector.addedOutboundReference(fromPath, toPath, timestampMs);
|
|
3408
3435
|
}
|
|
3409
3436
|
|
|
3410
3437
|
/**
|
package/src/dataStoreContext.ts
CHANGED
|
@@ -719,9 +719,10 @@ export abstract class FluidDataStoreContext
|
|
|
719
719
|
*
|
|
720
720
|
* @param fromPath - The absolute path of the node that added the reference.
|
|
721
721
|
* @param toPath - The absolute path of the outbound node that is referenced.
|
|
722
|
+
* @param messageTimestampMs - The timestamp of the message that added the reference.
|
|
722
723
|
*/
|
|
723
|
-
public addedGCOutboundRoute(fromPath: string, toPath: string) {
|
|
724
|
-
this.parentContext.addedGCOutboundRoute(fromPath, toPath);
|
|
724
|
+
public addedGCOutboundRoute(fromPath: string, toPath: string, messageTimestampMs?: number) {
|
|
725
|
+
this.parentContext.addedGCOutboundRoute(fromPath, toPath, messageTimestampMs);
|
|
725
726
|
}
|
|
726
727
|
|
|
727
728
|
/**
|
|
@@ -892,9 +892,14 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
892
892
|
/**
|
|
893
893
|
* Process a GC message.
|
|
894
894
|
* @param message - The GC message from the container runtime.
|
|
895
|
+
* @param messageTimestampMs - The timestamp of the message.
|
|
895
896
|
* @param local - Whether it was send by this client.
|
|
896
897
|
*/
|
|
897
|
-
public processMessage(
|
|
898
|
+
public processMessage(
|
|
899
|
+
message: ContainerRuntimeGCMessage,
|
|
900
|
+
messageTimestampMs: number,
|
|
901
|
+
local: boolean,
|
|
902
|
+
) {
|
|
898
903
|
const gcMessageType = message.contents.type;
|
|
899
904
|
switch (gcMessageType) {
|
|
900
905
|
case GarbageCollectionMessageType.Sweep: {
|
|
@@ -909,7 +914,12 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
909
914
|
|
|
910
915
|
// Mark the node as referenced to ensure it isn't Swept
|
|
911
916
|
const tombstonedNodePath = message.contents.nodePath;
|
|
912
|
-
this.addedOutboundReference(
|
|
917
|
+
this.addedOutboundReference(
|
|
918
|
+
"/",
|
|
919
|
+
tombstonedNodePath,
|
|
920
|
+
messageTimestampMs,
|
|
921
|
+
true /* autorecovery */,
|
|
922
|
+
);
|
|
913
923
|
|
|
914
924
|
// In case the cause of the TombstoneLoaded event is incorrect GC Data (i.e. the object is actually reachable),
|
|
915
925
|
// do fullGC on the next run to get a chance to repair (in the likely case the bug is not deterministic)
|
|
@@ -990,7 +1000,9 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
990
1000
|
request,
|
|
991
1001
|
headerData,
|
|
992
1002
|
}: IGCNodeUpdatedProps) {
|
|
993
|
-
|
|
1003
|
+
// If there is no reference timestamp to work with, no ops have been processed after creation. If so, skip
|
|
1004
|
+
// logging as nothing interesting would have happened worth logging.
|
|
1005
|
+
if (!this.shouldRunGC || timestampMs === undefined) {
|
|
994
1006
|
return;
|
|
995
1007
|
}
|
|
996
1008
|
|
|
@@ -1005,8 +1017,7 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
1005
1017
|
this.telemetryTracker.nodeUsed(trackedId, {
|
|
1006
1018
|
id: fullPath,
|
|
1007
1019
|
usageType: reason,
|
|
1008
|
-
currentReferenceTimestampMs:
|
|
1009
|
-
timestampMs ?? this.runtime.getCurrentReferenceTimestampMs(),
|
|
1020
|
+
currentReferenceTimestampMs: timestampMs,
|
|
1010
1021
|
packagePath,
|
|
1011
1022
|
completedGCRuns: this.completedRuns,
|
|
1012
1023
|
isTombstoned,
|
|
@@ -1096,9 +1107,15 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
1096
1107
|
*
|
|
1097
1108
|
* @param fromNodePath - The node from which the reference is added.
|
|
1098
1109
|
* @param toNodePath - The node to which the reference is added.
|
|
1110
|
+
* @param timestampMs - The timestamp of the message that added the reference.
|
|
1099
1111
|
* @param autorecovery - This reference is added artificially, for autorecovery. Used for logging.
|
|
1100
1112
|
*/
|
|
1101
|
-
public addedOutboundReference(
|
|
1113
|
+
public addedOutboundReference(
|
|
1114
|
+
fromNodePath: string,
|
|
1115
|
+
toNodePath: string,
|
|
1116
|
+
timestampMs: number,
|
|
1117
|
+
autorecovery?: true,
|
|
1118
|
+
) {
|
|
1102
1119
|
if (!this.shouldRunGC) {
|
|
1103
1120
|
return;
|
|
1104
1121
|
}
|
|
@@ -1129,7 +1146,7 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
1129
1146
|
id: toNodePath,
|
|
1130
1147
|
fromId: fromNodePath,
|
|
1131
1148
|
usageType: "Revived",
|
|
1132
|
-
currentReferenceTimestampMs:
|
|
1149
|
+
currentReferenceTimestampMs: timestampMs,
|
|
1133
1150
|
packagePath: undefined,
|
|
1134
1151
|
completedGCRuns: this.completedRuns,
|
|
1135
1152
|
isTombstoned: this.tombstones.includes(trackedId),
|
package/src/gc/gcDefinitions.ts
CHANGED
|
@@ -365,9 +365,18 @@ export interface IGarbageCollector {
|
|
|
365
365
|
*/
|
|
366
366
|
nodeUpdated(props: IGCNodeUpdatedProps): void;
|
|
367
367
|
/** Called when a reference is added to a node. Used to identify nodes that were referenced between summaries. */
|
|
368
|
-
addedOutboundReference(
|
|
368
|
+
addedOutboundReference(
|
|
369
|
+
fromNodePath: string,
|
|
370
|
+
toNodePath: string,
|
|
371
|
+
timestampMs: number,
|
|
372
|
+
autorecovery?: true,
|
|
373
|
+
): void;
|
|
369
374
|
/** Called to process a garbage collection message. */
|
|
370
|
-
processMessage(
|
|
375
|
+
processMessage(
|
|
376
|
+
message: ContainerRuntimeGCMessage,
|
|
377
|
+
messageTimestampMs: number,
|
|
378
|
+
local: boolean,
|
|
379
|
+
): void;
|
|
371
380
|
/** Returns true if this node has been deleted by GC during sweep phase. */
|
|
372
381
|
isNodeDeleted(nodePath: string): boolean;
|
|
373
382
|
setConnectionState(connected: boolean, clientId?: string): void;
|
|
@@ -383,8 +392,11 @@ export interface IGCNodeUpdatedProps {
|
|
|
383
392
|
node: { type: (typeof GCNodeType)["DataStore" | "Blob"]; path: string };
|
|
384
393
|
/** Whether the node (or a subpath) was loaded or changed. */
|
|
385
394
|
reason: "Loaded" | "Changed";
|
|
386
|
-
/**
|
|
387
|
-
|
|
395
|
+
/**
|
|
396
|
+
* The op-based timestamp when the node changed. If the update is from receiving an op, this should
|
|
397
|
+
* be the timestamp of the op. If not, this should be the timestamp of the last op processed.
|
|
398
|
+
*/
|
|
399
|
+
timestampMs: number | undefined;
|
|
388
400
|
/** The package path of the node. This may not be available if the node hasn't been loaded yet */
|
|
389
401
|
packagePath?: readonly string[];
|
|
390
402
|
/** The original request for loads to preserve it in telemetry */
|
package/src/gc/gcTelemetry.ts
CHANGED
|
@@ -63,7 +63,7 @@ interface INodeUsageProps extends ICommonProps {
|
|
|
63
63
|
/** The full path (in GC Path format) to the node in question */
|
|
64
64
|
id: string;
|
|
65
65
|
/** Latest timestamp received from the server, as a baseline for computing GC state/age */
|
|
66
|
-
currentReferenceTimestampMs: number
|
|
66
|
+
currentReferenceTimestampMs: number;
|
|
67
67
|
/** The package path of the node. This may not be available if the node hasn't been loaded yet */
|
|
68
68
|
packagePath: readonly string[] | undefined;
|
|
69
69
|
/** In case of Revived - what node added the reference? */
|
|
@@ -165,12 +165,6 @@ export class GCTelemetryTracker {
|
|
|
165
165
|
...otherNodeUsageProps
|
|
166
166
|
}: INodeUsageProps,
|
|
167
167
|
) {
|
|
168
|
-
// If there is no reference timestamp to work with, no ops have been processed after creation. If so, skip
|
|
169
|
-
// logging as nothing interesting would have happened worth logging.
|
|
170
|
-
if (currentReferenceTimestampMs === undefined) {
|
|
171
|
-
return;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
168
|
// Note: For SubDataStore Load usage, trackedId will be the DataStore's id, not the full path in question.
|
|
175
169
|
// This is necessary because the SubDataStore path may be unrecognized by GC (if suited for a custom request handler)
|
|
176
170
|
const nodeStateTracker = this.getNodeStateTracker(trackedId);
|
package/src/packageVersion.ts
CHANGED
package/tsdoc.json
ADDED