@fluidframework/container-runtime 2.0.0-internal.7.4.6 → 2.0.0-internal.7.4.7
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/dist/blobManager.d.ts.map +1 -1
- package/dist/blobManager.js +0 -5
- package/dist/blobManager.js.map +1 -1
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +0 -3
- package/dist/containerRuntime.js.map +1 -1
- package/dist/dataStores.d.ts +1 -1
- package/dist/dataStores.d.ts.map +1 -1
- package/dist/dataStores.js +2 -5
- package/dist/dataStores.js.map +1 -1
- package/dist/gc/garbageCollection.d.ts +15 -14
- package/dist/gc/garbageCollection.d.ts.map +1 -1
- package/dist/gc/garbageCollection.js +115 -125
- package/dist/gc/garbageCollection.js.map +1 -1
- package/dist/gc/gcConfigs.d.ts.map +1 -1
- package/dist/gc/gcConfigs.js +10 -3
- package/dist/gc/gcConfigs.js.map +1 -1
- package/dist/gc/gcDefinitions.d.ts +11 -8
- package/dist/gc/gcDefinitions.d.ts.map +1 -1
- package/dist/gc/gcDefinitions.js +7 -4
- package/dist/gc/gcDefinitions.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 +3 -0
- package/dist/gc/gcSummaryStateTracker.js.map +1 -1
- package/dist/gc/gcTelemetry.d.ts.map +1 -1
- package/dist/gc/gcTelemetry.js +4 -14
- package/dist/gc/gcTelemetry.js.map +1 -1
- package/dist/gc/index.d.ts +1 -1
- package/dist/gc/index.d.ts.map +1 -1
- package/dist/gc/index.js +2 -2
- package/dist/gc/index.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/lib/blobManager.d.ts.map +1 -1
- package/lib/blobManager.js +0 -5
- package/lib/blobManager.js.map +1 -1
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +0 -3
- package/lib/containerRuntime.js.map +1 -1
- package/lib/dataStores.d.ts +1 -1
- package/lib/dataStores.d.ts.map +1 -1
- package/lib/dataStores.js +2 -5
- package/lib/dataStores.js.map +1 -1
- package/lib/gc/garbageCollection.d.ts +15 -14
- package/lib/gc/garbageCollection.d.ts.map +1 -1
- package/lib/gc/garbageCollection.js +116 -126
- package/lib/gc/garbageCollection.js.map +1 -1
- package/lib/gc/gcConfigs.d.ts.map +1 -1
- package/lib/gc/gcConfigs.js +11 -4
- package/lib/gc/gcConfigs.js.map +1 -1
- package/lib/gc/gcDefinitions.d.ts +11 -8
- package/lib/gc/gcDefinitions.d.ts.map +1 -1
- package/lib/gc/gcDefinitions.js +6 -3
- package/lib/gc/gcDefinitions.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 +3 -0
- package/lib/gc/gcSummaryStateTracker.js.map +1 -1
- package/lib/gc/gcTelemetry.d.ts.map +1 -1
- package/lib/gc/gcTelemetry.js +4 -14
- package/lib/gc/gcTelemetry.js.map +1 -1
- package/lib/gc/index.d.ts +1 -1
- package/lib/gc/index.d.ts.map +1 -1
- package/lib/gc/index.js +1 -1
- package/lib/gc/index.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/package.json +16 -16
- package/src/blobManager.ts +0 -6
- package/src/containerRuntime.ts +0 -3
- package/src/dataStores.ts +2 -5
- package/src/gc/garbageCollection.ts +136 -142
- package/src/gc/gcConfigs.ts +13 -3
- package/src/gc/gcDefinitions.ts +12 -8
- package/src/gc/gcSummaryStateTracker.ts +5 -1
- package/src/gc/gcTelemetry.ts +4 -13
- package/src/gc/index.ts +1 -1
- package/src/packageVersion.ts +1 -1
|
@@ -2,10 +2,11 @@
|
|
|
2
2
|
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
|
-
import { LazyPromise, Timer } from "@fluidframework/core-utils";
|
|
5
|
+
import { assert, LazyPromise, Timer } from "@fluidframework/core-utils";
|
|
6
6
|
import { gcTreeKey, } from "@fluidframework/runtime-definitions";
|
|
7
7
|
import { createResponseError, responseToException } from "@fluidframework/runtime-utils";
|
|
8
8
|
import { createChildLogger, createChildMonitoringContext, DataProcessingError, PerformanceEvent, } from "@fluidframework/telemetry-utils";
|
|
9
|
+
import { BlobManager } from "../blobManager";
|
|
9
10
|
import { InactiveResponseHeaderKey, TombstoneResponseHeaderKey, } from "../containerRuntime";
|
|
10
11
|
import { ClientSessionExpiredError } from "../error";
|
|
11
12
|
import { ContainerMessageType } from "../messageTypes";
|
|
@@ -80,7 +81,6 @@ export class GarbageCollector {
|
|
|
80
81
|
this.isSummarizerClient = createParams.isSummarizerClient;
|
|
81
82
|
this.getNodePackagePath = createParams.getNodePackagePath;
|
|
82
83
|
this.getLastSummaryTimestampMs = createParams.getLastSummaryTimestampMs;
|
|
83
|
-
this.activeConnection = createParams.activeConnection;
|
|
84
84
|
this.submitMessage = createParams.submitMessage;
|
|
85
85
|
const baseSnapshot = createParams.baseSnapshot;
|
|
86
86
|
const readAndParseBlob = createParams.readAndParseBlob;
|
|
@@ -141,28 +141,14 @@ export class GarbageCollector {
|
|
|
141
141
|
}
|
|
142
142
|
});
|
|
143
143
|
/**
|
|
144
|
-
* Set up the initializer which initializes the GC state from the data in base snapshot.
|
|
145
|
-
*
|
|
146
|
-
*
|
|
144
|
+
* Set up the initializer which initializes the GC state from the data in base snapshot. It sets up GC data
|
|
145
|
+
* from the base GC state and starts tracking the state of unreferenced nodes.
|
|
146
|
+
*
|
|
147
|
+
* Must only be called if there is a current reference timestamp.
|
|
147
148
|
*/
|
|
148
149
|
this.initializeGCStateFromBaseSnapshotP = new LazyPromise(async () => {
|
|
149
|
-
/**
|
|
150
|
-
* If there is no current reference timestamp, skip initialization. We need the current timestamp to track
|
|
151
|
-
* how long objects have been unreferenced and if they can be deleted.
|
|
152
|
-
*
|
|
153
|
-
* Note that the only scenario where there is no reference timestamp is when no ops have ever been processed
|
|
154
|
-
* for this container and it is in read mode. In this scenario, there is no point in running GC anyway
|
|
155
|
-
* because references in the container do not change without any ops, i.e., there is nothing to collect.
|
|
156
|
-
*/
|
|
157
150
|
const currentReferenceTimestampMs = this.runtime.getCurrentReferenceTimestampMs();
|
|
158
|
-
|
|
159
|
-
// Log an event so we can evaluate how often we run into this scenario.
|
|
160
|
-
this.mc.logger.sendErrorEvent({
|
|
161
|
-
eventName: "GarbageCollectorInitializedWithoutTimestamp",
|
|
162
|
-
gcConfigs: JSON.stringify(this.configs),
|
|
163
|
-
});
|
|
164
|
-
return;
|
|
165
|
-
}
|
|
151
|
+
assert(currentReferenceTimestampMs !== undefined, "Trying to initialize GC state without current timestamp");
|
|
166
152
|
/**
|
|
167
153
|
* The base snapshot data will not be present if the container is loaded from:
|
|
168
154
|
* 1. The first summary created by the detached container.
|
|
@@ -170,11 +156,20 @@ export class GarbageCollector {
|
|
|
170
156
|
* 3. A summary that was generated before GC even existed.
|
|
171
157
|
*/
|
|
172
158
|
const baseSnapshotData = await this.baseSnapshotDataP;
|
|
173
|
-
|
|
159
|
+
this.summaryStateTracker.initializeBaseState(baseSnapshotData);
|
|
160
|
+
if (baseSnapshotData?.gcState === undefined) {
|
|
174
161
|
return;
|
|
175
162
|
}
|
|
176
|
-
|
|
177
|
-
|
|
163
|
+
// Update unreferenced state tracking as per the GC state in the snapshot data and update gcDataFromLastRun
|
|
164
|
+
// to the GC data from the snapshot data.
|
|
165
|
+
const gcNodes = {};
|
|
166
|
+
for (const [nodeId, nodeData] of Object.entries(baseSnapshotData.gcState.gcNodes)) {
|
|
167
|
+
if (nodeData.unreferencedTimestampMs !== undefined) {
|
|
168
|
+
this.unreferencedNodesState.set(nodeId, new UnreferencedStateTracker(nodeData.unreferencedTimestampMs, this.configs.inactiveTimeoutMs, currentReferenceTimestampMs, this.configs.sweepTimeoutMs, this.configs.sweepGracePeriodMs));
|
|
169
|
+
}
|
|
170
|
+
gcNodes[nodeId] = Array.from(nodeData.outboundRoutes);
|
|
171
|
+
}
|
|
172
|
+
this.gcDataFromLastRun = { gcNodes };
|
|
178
173
|
});
|
|
179
174
|
// Get the GC details from the GC state in the base summary. This is returned in getBaseGCDetails which is
|
|
180
175
|
// used to initialize the GC state of all the nodes in the container.
|
|
@@ -203,8 +198,10 @@ export class GarbageCollector {
|
|
|
203
198
|
});
|
|
204
199
|
}
|
|
205
200
|
/**
|
|
206
|
-
* Called during container initialization.
|
|
207
|
-
*
|
|
201
|
+
* Called during container initialization. Initializes the tombstone and deleted nodes state from the base snapshot.
|
|
202
|
+
* Also, initializes the GC state including unreferenced nodes tracking if a current reference timestamp exists.
|
|
203
|
+
* Note that if there is any GC state in the base snapshot, then there will definitely be a reference timestamp
|
|
204
|
+
* to work with - The GC state would have been generated using a timestamp which is part of the snapshot.
|
|
208
205
|
*/
|
|
209
206
|
async initializeBaseState() {
|
|
210
207
|
const baseSnapshotData = await this.baseSnapshotDataP;
|
|
@@ -229,98 +226,51 @@ export class GarbageCollector {
|
|
|
229
226
|
this.tombstones = Array.from(baseSnapshotData.tombstones);
|
|
230
227
|
this.runtime.updateTombstonedRoutes(this.tombstones);
|
|
231
228
|
}
|
|
229
|
+
await this.initializeOrUpdateGCState();
|
|
232
230
|
}
|
|
233
231
|
/**
|
|
234
|
-
*
|
|
235
|
-
*
|
|
236
|
-
* @param snapshotData - The snapshot data to update state from. If this is undefined, all GC state and tracking
|
|
237
|
-
* is reset.
|
|
238
|
-
* @param currentReferenceTimestampMs - The current reference timestamp for marking unreferenced nodes' unreferenced
|
|
239
|
-
* timestamp.
|
|
232
|
+
* Initialize the GC state if not already initialized. If GC state is already initialized, update the unreferenced
|
|
233
|
+
* state tracking as per the current reference timestamp.
|
|
240
234
|
*/
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
* its refreshing state from a summary that happened at seq#900. In this case, there may be references between
|
|
246
|
-
* seq#901 and seq#1000 that we don't want to reset.
|
|
247
|
-
* Unfortunately, there is no way to track the seq# of ops that add references, so we choose to not reset any
|
|
248
|
-
* references here. This should be fine because, in the worst case, we may end up updating the unreferenced
|
|
249
|
-
* timestamp of a node which will delay its deletion. Although not ideal, this will only happen in rare
|
|
250
|
-
* scenarios, so it should be okay.
|
|
251
|
-
*/
|
|
252
|
-
// Clear all existing unreferenced state tracking.
|
|
253
|
-
for (const [, nodeStateTracker] of this.unreferencedNodesState) {
|
|
254
|
-
nodeStateTracker.stopTracking();
|
|
255
|
-
}
|
|
256
|
-
this.unreferencedNodesState.clear();
|
|
257
|
-
// If running sweep, the tombstone state represents the list of nodes that have been deleted during sweep.
|
|
258
|
-
// If running in tombstone mode, the tombstone state represents the list of nodes that have been marked as
|
|
259
|
-
// tombstones.
|
|
260
|
-
// If this call is because we are refreshing from a snapshot due to an ack, it is likely that the GC state
|
|
261
|
-
// in the snapshot is newer than this client's. And so, the deleted / tombstone nodes need to be updated.
|
|
262
|
-
if (this.configs.shouldRunSweep) {
|
|
263
|
-
const snapshotDeletedNodes = snapshotData?.deletedNodes
|
|
264
|
-
? new Set(snapshotData.deletedNodes)
|
|
265
|
-
: undefined;
|
|
266
|
-
// If the snapshot contains deleted nodes that are not yet deleted by this client, ask the runtime to
|
|
267
|
-
// delete them.
|
|
268
|
-
if (snapshotDeletedNodes !== undefined) {
|
|
269
|
-
const newDeletedNodes = [];
|
|
270
|
-
for (const nodeId of snapshotDeletedNodes) {
|
|
271
|
-
if (!this.deletedNodes.has(nodeId)) {
|
|
272
|
-
newDeletedNodes.push(nodeId);
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
if (newDeletedNodes.length > 0) {
|
|
276
|
-
// Call container runtime to delete these nodes and add deleted nodes to this.deletedNodes.
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
else if (this.configs.tombstoneMode) {
|
|
281
|
-
// The snapshot may contain more or fewer tombstone nodes than this client. Update tombstone state and
|
|
282
|
-
// notify the runtime to update its state as well.
|
|
283
|
-
this.tombstones = snapshotData?.tombstones ? Array.from(snapshotData.tombstones) : [];
|
|
284
|
-
this.runtime.updateTombstonedRoutes(this.tombstones);
|
|
235
|
+
async initializeOrUpdateGCState() {
|
|
236
|
+
const currentReferenceTimestampMs = this.runtime.getCurrentReferenceTimestampMs();
|
|
237
|
+
if (currentReferenceTimestampMs === undefined) {
|
|
238
|
+
return;
|
|
285
239
|
}
|
|
286
|
-
// If
|
|
287
|
-
if (
|
|
288
|
-
this.
|
|
240
|
+
// If the GC state hasn't been initialized yet, initialize it and return.
|
|
241
|
+
if (this.gcDataFromLastRun === undefined) {
|
|
242
|
+
await this.initializeGCStateFromBaseSnapshotP;
|
|
289
243
|
return;
|
|
290
244
|
}
|
|
291
|
-
//
|
|
292
|
-
//
|
|
293
|
-
const
|
|
294
|
-
|
|
295
|
-
if (nodeData.unreferencedTimestampMs !== undefined) {
|
|
296
|
-
this.unreferencedNodesState.set(nodeId, new UnreferencedStateTracker(nodeData.unreferencedTimestampMs, this.configs.inactiveTimeoutMs, currentReferenceTimestampMs, this.configs.sweepTimeoutMs, this.configs.sweepGracePeriodMs));
|
|
297
|
-
}
|
|
298
|
-
gcNodes[nodeId] = Array.from(nodeData.outboundRoutes);
|
|
245
|
+
// If the GC state has been initialized, update the tracking of unreferenced nodes as per the current
|
|
246
|
+
// reference timestamp.
|
|
247
|
+
for (const [, nodeStateTracker] of this.unreferencedNodesState) {
|
|
248
|
+
nodeStateTracker.updateTracking(currentReferenceTimestampMs);
|
|
299
249
|
}
|
|
300
|
-
this.gcDataFromLastRun = { gcNodes };
|
|
301
250
|
}
|
|
302
251
|
/**
|
|
303
252
|
* Called when the connection state of the runtime changes, i.e., it connects or disconnects. GC subscribes to this
|
|
304
|
-
* to initialize the
|
|
253
|
+
* to initialize or update the unreference state tracking.
|
|
305
254
|
* @param connected - Whether the runtime connected / disconnected.
|
|
306
255
|
* @param clientId - The clientId of this runtime.
|
|
307
256
|
*/
|
|
308
257
|
setConnectionState(connected, clientId) {
|
|
309
258
|
/**
|
|
310
|
-
*
|
|
311
|
-
*
|
|
312
|
-
* reference timestamp that will be used to update the state of unreferenced nodes. Also, all trailing ops which
|
|
313
|
-
* could affect the GC state will have been processed.
|
|
259
|
+
* When the client connects (or reconnects), attempt to initialize or update the GC state. This will keep
|
|
260
|
+
* the unreferenced state tracking updated as per the reference timestamp at the time of connection.
|
|
314
261
|
*
|
|
315
|
-
*
|
|
316
|
-
*
|
|
317
|
-
*
|
|
318
|
-
*
|
|
319
|
-
* Ideally, this initialization should only be done for summarizer client. However, we are currently rolling out
|
|
320
|
-
* sweep in phases and we want to track when inactive and sweep-ready objects are used in any client.
|
|
262
|
+
* During GC initialization and during connections in read mode, it is possible that either no ops are
|
|
263
|
+
* processed or only trailing ops are processed. This means that the GC state is not initialized or initialized
|
|
264
|
+
* with an older reference timestamp. So, doing this on every connection will keep the unreferenced state
|
|
265
|
+
* tracking up-to-date.
|
|
321
266
|
*/
|
|
322
|
-
if (
|
|
323
|
-
this.
|
|
267
|
+
if (connected && this.configs.shouldRunGC) {
|
|
268
|
+
this.initializeOrUpdateGCState().catch((error) => {
|
|
269
|
+
this.mc.logger.sendErrorEvent({
|
|
270
|
+
eventName: "GCInitializationOrUpdateFailed",
|
|
271
|
+
gcConfigs: JSON.stringify(this.configs),
|
|
272
|
+
}, error);
|
|
273
|
+
});
|
|
324
274
|
}
|
|
325
275
|
}
|
|
326
276
|
/**
|
|
@@ -377,8 +327,11 @@ export class GarbageCollector {
|
|
|
377
327
|
const gcStats = await this.runGC(fullGC, currentReferenceTimestampMs, logger);
|
|
378
328
|
event.end({
|
|
379
329
|
...gcStats,
|
|
380
|
-
|
|
381
|
-
|
|
330
|
+
details: {
|
|
331
|
+
timestamp: currentReferenceTimestampMs,
|
|
332
|
+
sweep: this.configs.shouldRunSweep,
|
|
333
|
+
tombstone: this.configs.throwOnTombstoneLoad,
|
|
334
|
+
},
|
|
382
335
|
});
|
|
383
336
|
/** Post-GC steps */
|
|
384
337
|
// Log pending unreferenced events such as a node being used after inactive. This is done after GC runs and
|
|
@@ -420,8 +373,9 @@ export class GarbageCollector {
|
|
|
420
373
|
// It will mark nodes as referenced / unreferenced and return lists of tombstone-ready and sweep-ready nodes.
|
|
421
374
|
const { tombstoneReadyNodeIds, sweepReadyNodeIds } = this.runMarkPhase(gcResult, allReferencedNodeIds, currentReferenceTimestampMs);
|
|
422
375
|
// 4. Run the Sweep phase.
|
|
423
|
-
// It will
|
|
424
|
-
//
|
|
376
|
+
// It will initiate the deletion (sending the GC Sweep op) of any sweep-ready nodes that are
|
|
377
|
+
// allowed to be deleted per config, and tombstone the rest along with the tombstone-ready nodes.
|
|
378
|
+
// Note that no nodes will be deleted until the GC Sweep op is processed.
|
|
425
379
|
this.runSweepPhase(gcResult, tombstoneReadyNodeIds, sweepReadyNodeIds);
|
|
426
380
|
this.gcDataFromLastRun = cloneGCData(gcData);
|
|
427
381
|
// 5. Get the sweep phase stats.
|
|
@@ -501,25 +455,41 @@ export class GarbageCollector {
|
|
|
501
455
|
this.runtime.updateUnusedRoutes(gcResult.deletedNodeIds);
|
|
502
456
|
return;
|
|
503
457
|
}
|
|
504
|
-
//
|
|
458
|
+
// We'll build up the lists of nodes to be either Tombstoned or Deleted
|
|
459
|
+
// based on the configuration and the nodes' current state.
|
|
460
|
+
// We must Tombstone any sweep-ready node that Sweep won't run for.
|
|
505
461
|
// This is important because a container may never load during a node's Sweep Grace Period,
|
|
506
462
|
// so that node would directly become sweep-ready skipping over tombstone-ready state,
|
|
507
463
|
// but should be Tombstoned since Sweep is disabled.
|
|
508
|
-
const { nodesToTombstone, nodesToDelete } =
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
:
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
464
|
+
const { nodesToTombstone, nodesToDelete } = {
|
|
465
|
+
nodesToTombstone: [...tombstoneReadyNodes],
|
|
466
|
+
nodesToDelete: [],
|
|
467
|
+
};
|
|
468
|
+
switch (this.configs.shouldRunSweep) {
|
|
469
|
+
case "YES":
|
|
470
|
+
nodesToDelete.push(...sweepReadyNodes);
|
|
471
|
+
break;
|
|
472
|
+
case "ONLY_BLOBS":
|
|
473
|
+
sweepReadyNodes.forEach((nodeId) => {
|
|
474
|
+
const nodeType = this.runtime.getNodeType(nodeId);
|
|
475
|
+
if (nodeType === GCNodeType.Blob) {
|
|
476
|
+
nodesToDelete.push(nodeId);
|
|
477
|
+
}
|
|
478
|
+
else {
|
|
479
|
+
nodesToTombstone.push(nodeId);
|
|
480
|
+
}
|
|
481
|
+
});
|
|
482
|
+
break;
|
|
483
|
+
default: // case "NO":
|
|
484
|
+
nodesToTombstone.push(...sweepReadyNodes);
|
|
485
|
+
break;
|
|
486
|
+
}
|
|
517
487
|
if (this.configs.tombstoneMode) {
|
|
518
488
|
this.tombstones = nodesToTombstone;
|
|
519
489
|
// If we are running in GC tombstone mode, update tombstoned routes.
|
|
520
490
|
this.runtime.updateTombstonedRoutes(this.tombstones);
|
|
521
491
|
}
|
|
522
|
-
if (
|
|
492
|
+
if (nodesToDelete.length > 0) {
|
|
523
493
|
// Do not send DDS node ids in the GC op. This is an optimization to reduce its size. Since GC applies to
|
|
524
494
|
// to data store only, all its DDSes are deleted along with it. The DDS ids will be retrieved from the
|
|
525
495
|
// local state when processing the op.
|
|
@@ -675,6 +645,10 @@ export class GarbageCollector {
|
|
|
675
645
|
/**
|
|
676
646
|
* Delete nodes that are sweep-ready. Call the runtime to delete these nodes and clear the unreferenced state
|
|
677
647
|
* tracking for nodes that are actually deleted by the runtime.
|
|
648
|
+
*
|
|
649
|
+
* Note that this doesn't check any configuration around whether Sweep is enabled.
|
|
650
|
+
* That happens before the op is submitted, and from that point, any client should execute the delete.
|
|
651
|
+
*
|
|
678
652
|
* @param sweepReadyNodeIds - The ids of nodes that are ready to be deleted.
|
|
679
653
|
*/
|
|
680
654
|
deleteSweepReadyNodes(sweepReadyNodeIds) {
|
|
@@ -855,8 +829,9 @@ export class GarbageCollector {
|
|
|
855
829
|
}
|
|
856
830
|
/**
|
|
857
831
|
* Generates the stats of a garbage collection sweep phase run.
|
|
858
|
-
* @param deletedNodes - The nodes that have been deleted
|
|
859
|
-
* @param sweepReadyNodes - The nodes that are sweep-ready in this GC run.
|
|
832
|
+
* @param deletedNodes - The nodes that have already been deleted even before this run.
|
|
833
|
+
* @param sweepReadyNodes - The nodes that are sweep-ready in this GC run. These will be deleted but are not deleted yet,
|
|
834
|
+
* due to either sweep not being enabled or the Sweep Op needing to roundtrip before the delete is executed.
|
|
860
835
|
* @param markPhaseStats - The stats of the mark phase run.
|
|
861
836
|
* @returns the stats of the sweep phase run.
|
|
862
837
|
*/
|
|
@@ -871,9 +846,24 @@ export class GarbageCollector {
|
|
|
871
846
|
deletedDataStoreCount: 0,
|
|
872
847
|
deletedAttachmentBlobCount: 0,
|
|
873
848
|
};
|
|
849
|
+
// The runtime can't reliably identify the type of deleted nodes. So, get the type here. This should
|
|
850
|
+
// be good enough because the only types that participate in GC today are data stores, DDSes and blobs.
|
|
851
|
+
const getDeletedNodeType = (nodeId) => {
|
|
852
|
+
const pathParts = nodeId.split("/");
|
|
853
|
+
if (pathParts[1] === BlobManager.basePath) {
|
|
854
|
+
return GCNodeType.Blob;
|
|
855
|
+
}
|
|
856
|
+
if (pathParts.length === 2) {
|
|
857
|
+
return GCNodeType.DataStore;
|
|
858
|
+
}
|
|
859
|
+
if (pathParts.length === 3) {
|
|
860
|
+
return GCNodeType.SubDataStore;
|
|
861
|
+
}
|
|
862
|
+
return GCNodeType.Other;
|
|
863
|
+
};
|
|
874
864
|
for (const nodeId of deletedNodes) {
|
|
875
865
|
sweepPhaseStats.deletedNodeCount++;
|
|
876
|
-
const nodeType =
|
|
866
|
+
const nodeType = getDeletedNodeType(nodeId);
|
|
877
867
|
if (nodeType === GCNodeType.DataStore) {
|
|
878
868
|
sweepPhaseStats.deletedDataStoreCount++;
|
|
879
869
|
}
|
|
@@ -881,17 +871,17 @@ export class GarbageCollector {
|
|
|
881
871
|
sweepPhaseStats.deletedAttachmentBlobCount++;
|
|
882
872
|
}
|
|
883
873
|
}
|
|
884
|
-
//
|
|
874
|
+
// The counts from the mark phase stats do not include nodes that were
|
|
885
875
|
// deleted in previous runs. So, add the deleted node counts to life time stats.
|
|
886
876
|
sweepPhaseStats.lifetimeNodeCount += sweepPhaseStats.deletedNodeCount;
|
|
887
877
|
sweepPhaseStats.lifetimeDataStoreCount += sweepPhaseStats.deletedDataStoreCount;
|
|
888
878
|
sweepPhaseStats.lifetimeAttachmentBlobCount += sweepPhaseStats.deletedAttachmentBlobCount;
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
//
|
|
893
|
-
// is
|
|
894
|
-
//
|
|
879
|
+
// These stats are used to estimate the impact of GC in terms of how much garbage is/will be cleaned up.
|
|
880
|
+
// So we include the current sweep-ready node stats since these nodes will be deleted eventually.
|
|
881
|
+
// - If sweep is enabled, this will happen in the run after the GC op round trips back
|
|
882
|
+
// (they'll be in deletedNodes that time).
|
|
883
|
+
// - If sweep is not enabled, we still want to include these nodes since they
|
|
884
|
+
// _will be_ deleted once it is enabled.
|
|
895
885
|
for (const nodeId of sweepReadyNodes) {
|
|
896
886
|
sweepPhaseStats.deletedNodeCount++;
|
|
897
887
|
const nodeType = this.runtime.getNodeType(nodeId);
|