@fluidframework/container-runtime 2.0.0-internal.7.1.0 → 2.0.0-internal.7.1.2
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-report/container-runtime.api.md +2 -1
- package/dist/blobManager.d.ts +3 -6
- package/dist/blobManager.d.ts.map +1 -1
- package/dist/blobManager.js +17 -42
- package/dist/blobManager.js.map +1 -1
- package/dist/container-runtime-alpha.d.ts +4 -4
- package/dist/container-runtime-beta.d.ts +4 -4
- package/dist/container-runtime-public.d.ts +4 -4
- package/dist/container-runtime.d.ts +4 -4
- package/dist/containerRuntime.d.ts +5 -4
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +33 -8
- package/dist/containerRuntime.js.map +1 -1
- package/dist/dataStoreContext.d.ts +1 -0
- package/dist/dataStoreContext.d.ts.map +1 -1
- package/dist/dataStoreContext.js +39 -34
- package/dist/dataStoreContext.js.map +1 -1
- package/dist/dataStores.d.ts +0 -16
- package/dist/dataStores.d.ts.map +1 -1
- package/dist/dataStores.js +0 -48
- package/dist/dataStores.js.map +1 -1
- package/dist/gc/garbageCollection.d.ts +12 -3
- package/dist/gc/garbageCollection.d.ts.map +1 -1
- package/dist/gc/garbageCollection.js +41 -18
- package/dist/gc/garbageCollection.js.map +1 -1
- package/dist/gc/gcConfigs.d.ts +1 -0
- package/dist/gc/gcConfigs.d.ts.map +1 -1
- package/dist/gc/gcConfigs.js +15 -2
- package/dist/gc/gcConfigs.js.map +1 -1
- package/dist/gc/gcDefinitions.d.ts +28 -9
- package/dist/gc/gcDefinitions.d.ts.map +1 -1
- package/dist/gc/gcDefinitions.js +8 -3
- package/dist/gc/gcDefinitions.js.map +1 -1
- package/dist/gc/gcTelemetry.d.ts +12 -6
- package/dist/gc/gcTelemetry.d.ts.map +1 -1
- package/dist/gc/gcTelemetry.js +91 -47
- 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 +3 -5
- package/dist/gc/index.js.map +1 -1
- package/dist/opLifecycle/opGroupingManager.d.ts +10 -2
- package/dist/opLifecycle/opGroupingManager.d.ts.map +1 -1
- package/dist/opLifecycle/opGroupingManager.js +23 -3
- package/dist/opLifecycle/opGroupingManager.js.map +1 -1
- package/dist/opLifecycle/outbox.d.ts +0 -1
- package/dist/opLifecycle/outbox.d.ts.map +1 -1
- package/dist/opLifecycle/outbox.js +2 -1
- package/dist/opLifecycle/outbox.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 +3 -6
- package/lib/blobManager.d.ts.map +1 -1
- package/lib/blobManager.js +18 -43
- package/lib/blobManager.js.map +1 -1
- package/lib/containerRuntime.d.ts +5 -4
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +34 -9
- package/lib/containerRuntime.js.map +1 -1
- package/lib/dataStoreContext.d.ts +1 -0
- package/lib/dataStoreContext.d.ts.map +1 -1
- package/lib/dataStoreContext.js +40 -35
- package/lib/dataStoreContext.js.map +1 -1
- package/lib/dataStores.d.ts +0 -16
- package/lib/dataStores.d.ts.map +1 -1
- package/lib/dataStores.js +2 -50
- package/lib/dataStores.js.map +1 -1
- package/lib/gc/garbageCollection.d.ts +12 -3
- package/lib/gc/garbageCollection.d.ts.map +1 -1
- package/lib/gc/garbageCollection.js +42 -19
- package/lib/gc/garbageCollection.js.map +1 -1
- package/lib/gc/gcConfigs.d.ts +1 -0
- package/lib/gc/gcConfigs.d.ts.map +1 -1
- package/lib/gc/gcConfigs.js +17 -4
- package/lib/gc/gcConfigs.js.map +1 -1
- package/lib/gc/gcDefinitions.d.ts +28 -9
- package/lib/gc/gcDefinitions.d.ts.map +1 -1
- package/lib/gc/gcDefinitions.js +7 -2
- package/lib/gc/gcDefinitions.js.map +1 -1
- package/lib/gc/gcTelemetry.d.ts +12 -6
- package/lib/gc/gcTelemetry.d.ts.map +1 -1
- package/lib/gc/gcTelemetry.js +92 -48
- 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/opLifecycle/opGroupingManager.d.ts +10 -2
- package/lib/opLifecycle/opGroupingManager.d.ts.map +1 -1
- package/lib/opLifecycle/opGroupingManager.js +23 -3
- package/lib/opLifecycle/opGroupingManager.js.map +1 -1
- package/lib/opLifecycle/outbox.d.ts +0 -1
- package/lib/opLifecycle/outbox.d.ts.map +1 -1
- package/lib/opLifecycle/outbox.js +2 -1
- package/lib/opLifecycle/outbox.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 +21 -17
- package/src/blobManager.ts +18 -58
- package/src/containerRuntime.ts +50 -18
- package/src/dataStoreContext.ts +17 -8
- package/src/dataStores.ts +2 -80
- package/src/gc/garbageCollection.ts +53 -24
- package/src/gc/gcConfigs.ts +28 -4
- package/src/gc/gcDefinitions.ts +32 -9
- package/src/gc/gcTelemetry.ts +123 -65
- package/src/gc/index.ts +2 -4
- package/src/opLifecycle/opGroupingManager.ts +37 -2
- package/src/opLifecycle/outbox.ts +4 -2
- package/src/packageVersion.ts +1 -1
package/src/dataStoreContext.ts
CHANGED
|
@@ -78,7 +78,7 @@ import {
|
|
|
78
78
|
summarizerClientType,
|
|
79
79
|
} from "./summary";
|
|
80
80
|
import { ContainerRuntime } from "./containerRuntime";
|
|
81
|
-
import { sendGCUnexpectedUsageEvent
|
|
81
|
+
import { sendGCUnexpectedUsageEvent } from "./gc";
|
|
82
82
|
|
|
83
83
|
function createAttributes(
|
|
84
84
|
pkg: readonly string[],
|
|
@@ -325,11 +325,7 @@ export abstract class FluidDataStoreContext
|
|
|
325
325
|
this.mc.logger,
|
|
326
326
|
);
|
|
327
327
|
|
|
328
|
-
|
|
329
|
-
this.throwOnTombstoneUsage =
|
|
330
|
-
this.mc.config.getBoolean(throwOnTombstoneUsageKey) === true &&
|
|
331
|
-
this._containerRuntime.gcTombstoneEnforcementAllowed &&
|
|
332
|
-
this.clientDetails.type !== summarizerClientType;
|
|
328
|
+
this.throwOnTombstoneUsage = this._containerRuntime.gcThrowOnTombstoneUsage;
|
|
333
329
|
|
|
334
330
|
// By default, a data store can log maximum 10 local changes telemetry in summarizer.
|
|
335
331
|
this.localChangesTelemetryCount =
|
|
@@ -486,7 +482,16 @@ export abstract class FluidDataStoreContext
|
|
|
486
482
|
local: boolean,
|
|
487
483
|
localOpMetadata: unknown,
|
|
488
484
|
): void {
|
|
489
|
-
|
|
485
|
+
const safeTelemetryProps = extractSafePropertiesFromMessage(messageArg);
|
|
486
|
+
// On op process, tombstone error is logged in garbage collector. So, set "checkTombstone" to false when calling
|
|
487
|
+
// "verifyNotClosed" which logs tombstone errors. Throw error if tombstoned and throwing on load is configured.
|
|
488
|
+
this.verifyNotClosed("process", false /* checkTombstone */, safeTelemetryProps);
|
|
489
|
+
if (this.tombstoned && this.throwOnTombstoneUsage) {
|
|
490
|
+
throw new DataCorruptionError(
|
|
491
|
+
"Context is tombstoned! Call site [process]",
|
|
492
|
+
safeTelemetryProps,
|
|
493
|
+
);
|
|
494
|
+
}
|
|
490
495
|
|
|
491
496
|
const innerContents = messageArg.contents as FluidDataStoreMessage;
|
|
492
497
|
const message = {
|
|
@@ -1089,7 +1094,7 @@ export class LocalFluidDataStoreContextBase extends FluidDataStoreContext {
|
|
|
1089
1094
|
return message;
|
|
1090
1095
|
}
|
|
1091
1096
|
|
|
1092
|
-
|
|
1097
|
+
private readonly initialSnapshotDetailsP = new LazyPromise<ISnapshotDetails>(async () => {
|
|
1093
1098
|
let snapshot = this.snapshotTree;
|
|
1094
1099
|
let attributes: ReadFluidDataStoreAttributes;
|
|
1095
1100
|
let isRootDataStore = false;
|
|
@@ -1122,6 +1127,10 @@ export class LocalFluidDataStoreContextBase extends FluidDataStoreContext {
|
|
|
1122
1127
|
isRootDataStore,
|
|
1123
1128
|
snapshot,
|
|
1124
1129
|
};
|
|
1130
|
+
});
|
|
1131
|
+
|
|
1132
|
+
public async getInitialSnapshotDetails(): Promise<ISnapshotDetails> {
|
|
1133
|
+
return this.initialSnapshotDetailsP;
|
|
1125
1134
|
}
|
|
1126
1135
|
|
|
1127
1136
|
/**
|
package/src/dataStores.ts
CHANGED
|
@@ -50,12 +50,7 @@ import { buildSnapshotTree } from "@fluidframework/driver-utils";
|
|
|
50
50
|
import { assert, Lazy } from "@fluidframework/core-utils";
|
|
51
51
|
import { v4 as uuid } from "uuid";
|
|
52
52
|
import { DataStoreContexts } from "./dataStoreContexts";
|
|
53
|
-
import {
|
|
54
|
-
ContainerRuntime,
|
|
55
|
-
defaultRuntimeHeaderData,
|
|
56
|
-
RuntimeHeaderData,
|
|
57
|
-
TombstoneResponseHeaderKey,
|
|
58
|
-
} from "./containerRuntime";
|
|
53
|
+
import { ContainerRuntime, defaultRuntimeHeaderData, RuntimeHeaderData } from "./containerRuntime";
|
|
59
54
|
import {
|
|
60
55
|
FluidDataStoreContext,
|
|
61
56
|
RemoteFluidDataStoreContext,
|
|
@@ -65,12 +60,7 @@ import {
|
|
|
65
60
|
} from "./dataStoreContext";
|
|
66
61
|
import { StorageServiceWithAttachBlobs } from "./storageServiceWithAttachBlobs";
|
|
67
62
|
import { IDataStoreAliasMessage, isDataStoreAliasMessage } from "./dataStore";
|
|
68
|
-
import {
|
|
69
|
-
GCNodeType,
|
|
70
|
-
disableDatastoreSweepKey,
|
|
71
|
-
throwOnTombstoneLoadKey,
|
|
72
|
-
sendGCUnexpectedUsageEvent,
|
|
73
|
-
} from "./gc";
|
|
63
|
+
import { GCNodeType, disableDatastoreSweepKey, sendGCUnexpectedUsageEvent } from "./gc";
|
|
74
64
|
import {
|
|
75
65
|
summarizerClientType,
|
|
76
66
|
IContainerRuntimeMetadata,
|
|
@@ -104,8 +94,6 @@ export class DataStores implements IDisposable {
|
|
|
104
94
|
// Stores the ids of new data stores between two GC runs. This is used to notify the garbage collector of new
|
|
105
95
|
// root data stores that are added.
|
|
106
96
|
private dataStoresSinceLastGC: string[] = [];
|
|
107
|
-
/** If true, throw an error when a tombstone data store is retrieved. */
|
|
108
|
-
private readonly throwOnTombstoneLoad: boolean;
|
|
109
97
|
// The handle to the container runtime. This is used mainly for GC purposes to represent outbound reference from
|
|
110
98
|
// the container runtime to other nodes.
|
|
111
99
|
private readonly containerRuntimeHandle: IFluidHandle;
|
|
@@ -140,12 +128,6 @@ export class DataStores implements IDisposable {
|
|
|
140
128
|
this.runtime.IFluidHandleContext,
|
|
141
129
|
);
|
|
142
130
|
|
|
143
|
-
// Tombstone should only throw when the feature flag is enabled and the client isn't a summarizer
|
|
144
|
-
this.throwOnTombstoneLoad =
|
|
145
|
-
this.mc.config.getBoolean(throwOnTombstoneLoadKey) === true &&
|
|
146
|
-
this.runtime.gcTombstoneEnforcementAllowed &&
|
|
147
|
-
this.runtime.clientDetails.type !== summarizerClientType;
|
|
148
|
-
|
|
149
131
|
// Extract stores stored inside the snapshot
|
|
150
132
|
const fluidDataStores = new Map<string, ISnapshotTree>();
|
|
151
133
|
if (baseSnapshot) {
|
|
@@ -455,9 +437,6 @@ export class DataStores implements IDisposable {
|
|
|
455
437
|
const request: IRequest = { url: id };
|
|
456
438
|
throw responseToException(create404Response(request), request);
|
|
457
439
|
}
|
|
458
|
-
|
|
459
|
-
this.validateNotTombstoned(context, requestHeaderData);
|
|
460
|
-
|
|
461
440
|
return context;
|
|
462
441
|
}
|
|
463
442
|
|
|
@@ -477,8 +456,6 @@ export class DataStores implements IDisposable {
|
|
|
477
456
|
if (context === undefined) {
|
|
478
457
|
return undefined;
|
|
479
458
|
}
|
|
480
|
-
// Check if the data store is tombstoned. If so, we want to log a telemetry event.
|
|
481
|
-
this.checkIfTombstoned(context, requestHeaderData);
|
|
482
459
|
return context;
|
|
483
460
|
}
|
|
484
461
|
|
|
@@ -529,61 +506,6 @@ export class DataStores implements IDisposable {
|
|
|
529
506
|
}
|
|
530
507
|
}
|
|
531
508
|
|
|
532
|
-
/**
|
|
533
|
-
* Checks if the data store has not been marked as tombstone by GC or not.
|
|
534
|
-
* @param context - the data store context in question
|
|
535
|
-
* @param requestHeaderData - the request header information to log if the validation detects the data store has been tombstoned
|
|
536
|
-
* @returns true if the data store is tombstoned. Otherwise, returns false.
|
|
537
|
-
*/
|
|
538
|
-
private checkIfTombstoned(
|
|
539
|
-
context: FluidDataStoreContext,
|
|
540
|
-
requestHeaderData: RuntimeHeaderData,
|
|
541
|
-
) {
|
|
542
|
-
if (!context.tombstoned) {
|
|
543
|
-
return false;
|
|
544
|
-
}
|
|
545
|
-
const logErrorEvent = this.throwOnTombstoneLoad && !requestHeaderData.allowTombstone;
|
|
546
|
-
sendGCUnexpectedUsageEvent(
|
|
547
|
-
this.mc,
|
|
548
|
-
{
|
|
549
|
-
eventName: "GC_Tombstone_DataStore_Requested",
|
|
550
|
-
category: logErrorEvent ? "error" : "generic",
|
|
551
|
-
isSummarizerClient: this.runtime.clientDetails.type === summarizerClientType,
|
|
552
|
-
id: context.id,
|
|
553
|
-
headers: JSON.stringify(requestHeaderData),
|
|
554
|
-
gcTombstoneEnforcementAllowed: this.runtime.gcTombstoneEnforcementAllowed,
|
|
555
|
-
},
|
|
556
|
-
context.isLoaded ? context.packagePath : undefined,
|
|
557
|
-
);
|
|
558
|
-
return true;
|
|
559
|
-
}
|
|
560
|
-
|
|
561
|
-
/**
|
|
562
|
-
* Validates that the data store context requested has not been marked as tombstone by GC.
|
|
563
|
-
* @param context - the data store context in question
|
|
564
|
-
* @param request - the request information to log if the validation detects the data store has been tombstoned
|
|
565
|
-
* @param requestHeaderData - the request header information to log if the validation detects the data store has been tombstoned
|
|
566
|
-
*/
|
|
567
|
-
private validateNotTombstoned(
|
|
568
|
-
context: FluidDataStoreContext,
|
|
569
|
-
requestHeaderData: RuntimeHeaderData,
|
|
570
|
-
) {
|
|
571
|
-
if (this.checkIfTombstoned(context, requestHeaderData)) {
|
|
572
|
-
// The requested data store is removed by gc. Create a 404 gc response exception.
|
|
573
|
-
const request: IRequest = { url: context.id };
|
|
574
|
-
const error = responseToException(
|
|
575
|
-
createResponseError(404, "DataStore was deleted", request, {
|
|
576
|
-
[TombstoneResponseHeaderKey]: true,
|
|
577
|
-
}),
|
|
578
|
-
request,
|
|
579
|
-
);
|
|
580
|
-
// Throw an error if configured via options and via request headers.
|
|
581
|
-
if (this.throwOnTombstoneLoad && !requestHeaderData.allowTombstone) {
|
|
582
|
-
throw error;
|
|
583
|
-
}
|
|
584
|
-
}
|
|
585
|
-
}
|
|
586
|
-
|
|
587
509
|
public processSignal(fluidDataStoreId: string, message: IInboundSignalMessage, local: boolean) {
|
|
588
510
|
this.validateNotDeleted(fluidDataStoreId);
|
|
589
511
|
const context = this.contexts.get(fluidDataStoreId);
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { LazyPromise, Timer } from "@fluidframework/core-utils";
|
|
7
|
-
import { IRequest
|
|
7
|
+
import { IRequest } from "@fluidframework/core-interfaces";
|
|
8
8
|
import {
|
|
9
9
|
gcTreeKey,
|
|
10
10
|
IGarbageCollectionData,
|
|
@@ -23,9 +23,9 @@ import {
|
|
|
23
23
|
} from "@fluidframework/telemetry-utils";
|
|
24
24
|
|
|
25
25
|
import {
|
|
26
|
-
AllowInactiveRequestHeaderKey,
|
|
27
26
|
InactiveResponseHeaderKey,
|
|
28
|
-
|
|
27
|
+
RuntimeHeaderData,
|
|
28
|
+
TombstoneResponseHeaderKey,
|
|
29
29
|
} from "../containerRuntime";
|
|
30
30
|
import { ClientSessionExpiredError } from "../error";
|
|
31
31
|
import { IRefreshSummaryResult } from "../summary";
|
|
@@ -113,6 +113,19 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
113
113
|
private readonly summaryStateTracker: GCSummaryStateTracker;
|
|
114
114
|
private readonly telemetryTracker: GCTelemetryTracker;
|
|
115
115
|
|
|
116
|
+
/** If false, loading or using a Tombstoned object should merely log, not fail */
|
|
117
|
+
public get tombstoneEnforcementAllowed(): boolean {
|
|
118
|
+
return this.configs.tombstoneEnforcementAllowed;
|
|
119
|
+
}
|
|
120
|
+
/** If true, throw an error when a tombstone data store is retrieved */
|
|
121
|
+
public get throwOnTombstoneLoad(): boolean {
|
|
122
|
+
return this.configs.throwOnTombstoneLoad;
|
|
123
|
+
}
|
|
124
|
+
/** If true, throw an error when a tombstone data store is used */
|
|
125
|
+
public get throwOnTombstoneUsage(): boolean {
|
|
126
|
+
return this.configs.throwOnTombstoneUsage;
|
|
127
|
+
}
|
|
128
|
+
|
|
116
129
|
/** For a given node path, returns the node's package path. */
|
|
117
130
|
private readonly getNodePackagePath: (
|
|
118
131
|
nodePath: string,
|
|
@@ -176,7 +189,6 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
176
189
|
this.mc,
|
|
177
190
|
this.configs,
|
|
178
191
|
this.isSummarizerClient,
|
|
179
|
-
this.runtime.gcTombstoneEnforcementAllowed,
|
|
180
192
|
createParams.createContainerMetadata,
|
|
181
193
|
(nodeId: string) => this.runtime.getNodeType(nodeId),
|
|
182
194
|
(nodeId: string) => this.unreferencedNodesState.get(nodeId),
|
|
@@ -850,11 +862,13 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
850
862
|
}
|
|
851
863
|
|
|
852
864
|
/**
|
|
853
|
-
* Called when a node with the given id is updated. If the node is inactive, log an error
|
|
865
|
+
* Called when a node with the given id is updated. If the node is inactive or tombstoned, this will log an error
|
|
866
|
+
* or throw an error if failing on incorrect usage is configured.
|
|
854
867
|
* @param nodePath - The path of the node that changed.
|
|
855
868
|
* @param reason - Whether the node was loaded or changed.
|
|
856
869
|
* @param timestampMs - The timestamp when the node changed.
|
|
857
870
|
* @param packagePath - The package path of the node. This may not be available if the node hasn't been loaded yet.
|
|
871
|
+
* @param request - The original request for loads to preserve it in telemetry.
|
|
858
872
|
* @param requestHeaders - If the node was loaded via request path, the headers in the request.
|
|
859
873
|
*/
|
|
860
874
|
public nodeUpdated(
|
|
@@ -862,12 +876,15 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
862
876
|
reason: "Loaded" | "Changed",
|
|
863
877
|
timestampMs?: number,
|
|
864
878
|
packagePath?: readonly string[],
|
|
865
|
-
|
|
879
|
+
request?: IRequest,
|
|
880
|
+
headerData?: RuntimeHeaderData,
|
|
866
881
|
) {
|
|
867
882
|
if (!this.configs.shouldRunGC) {
|
|
868
883
|
return;
|
|
869
884
|
}
|
|
870
885
|
|
|
886
|
+
const isTombstoned = this.tombstones.includes(nodePath);
|
|
887
|
+
|
|
871
888
|
// This will log if appropriate
|
|
872
889
|
this.telemetryTracker.nodeUsed({
|
|
873
890
|
id: nodePath,
|
|
@@ -876,32 +893,44 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
876
893
|
timestampMs ?? this.runtime.getCurrentReferenceTimestampMs(),
|
|
877
894
|
packagePath,
|
|
878
895
|
completedGCRuns: this.completedRuns,
|
|
879
|
-
isTombstoned
|
|
896
|
+
isTombstoned,
|
|
880
897
|
lastSummaryTime: this.getLastSummaryTimestampMs(),
|
|
881
|
-
|
|
898
|
+
headers: headerData,
|
|
882
899
|
});
|
|
883
900
|
|
|
884
|
-
|
|
885
|
-
|
|
901
|
+
const nodeType = this.runtime.getNodeType(nodePath);
|
|
902
|
+
|
|
903
|
+
// Unless this is a Loaded event for a Blob or DataStore, we're done after telemetry tracking
|
|
904
|
+
if (reason !== "Loaded" || ![GCNodeType.Blob, GCNodeType.DataStore].includes(nodeType)) {
|
|
886
905
|
return;
|
|
887
906
|
}
|
|
888
907
|
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
if (shouldThrowOnInactiveLoad && state === "Inactive") {
|
|
897
|
-
const request: IRequest = { url: nodePath };
|
|
898
|
-
const error = responseToException(
|
|
899
|
-
createResponseError(404, "Object is inactive", request, {
|
|
900
|
-
[InactiveResponseHeaderKey]: true,
|
|
908
|
+
const errorRequest: IRequest = request ?? { url: nodePath };
|
|
909
|
+
// If the object is tombstoned and tombstone enforcement is configured, throw an error.
|
|
910
|
+
if (isTombstoned && this.throwOnTombstoneLoad && headerData?.allowTombstone !== true) {
|
|
911
|
+
// The requested data store is removed by gc. Create a 404 gc response exception.
|
|
912
|
+
throw responseToException(
|
|
913
|
+
createResponseError(404, `${nodeType} was tombstoned`, errorRequest, {
|
|
914
|
+
[TombstoneResponseHeaderKey]: true,
|
|
901
915
|
}),
|
|
902
|
-
|
|
916
|
+
errorRequest,
|
|
903
917
|
);
|
|
904
|
-
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
// If the object is inactive and inactive enforcement is configured, throw an error.
|
|
921
|
+
if (this.unreferencedNodesState.get(nodePath)?.state === "Inactive") {
|
|
922
|
+
const shouldThrowOnInactiveLoad =
|
|
923
|
+
!this.isSummarizerClient &&
|
|
924
|
+
this.configs.throwOnInactiveLoad === true &&
|
|
925
|
+
headerData?.allowInactive !== true;
|
|
926
|
+
if (shouldThrowOnInactiveLoad) {
|
|
927
|
+
throw responseToException(
|
|
928
|
+
createResponseError(404, `${nodeType} is inactive`, errorRequest, {
|
|
929
|
+
[InactiveResponseHeaderKey]: true,
|
|
930
|
+
}),
|
|
931
|
+
errorRequest,
|
|
932
|
+
);
|
|
933
|
+
}
|
|
905
934
|
}
|
|
906
935
|
}
|
|
907
936
|
|
package/src/gc/gcConfigs.ts
CHANGED
|
@@ -24,8 +24,11 @@ import {
|
|
|
24
24
|
runSessionExpiryKey,
|
|
25
25
|
runSweepKey,
|
|
26
26
|
stableGCVersion,
|
|
27
|
+
throwOnTombstoneLoadOverrideKey,
|
|
28
|
+
throwOnTombstoneUsageKey,
|
|
29
|
+
gcThrowOnTombstoneLoadOptionName,
|
|
27
30
|
} from "./gcDefinitions";
|
|
28
|
-
import { getGCVersion, shouldAllowGcSweep } from "./gcHelpers";
|
|
31
|
+
import { getGCVersion, shouldAllowGcSweep, shouldAllowGcTombstoneEnforcement } from "./gcHelpers";
|
|
29
32
|
|
|
30
33
|
/**
|
|
31
34
|
* Generates configurations for the Garbage Collector that it uses to determine what to run and how.
|
|
@@ -42,6 +45,7 @@ export function generateGCConfigs(
|
|
|
42
45
|
gcOptions: IGCRuntimeOptions;
|
|
43
46
|
metadata: IContainerRuntimeMetadata | undefined;
|
|
44
47
|
existing: boolean;
|
|
48
|
+
isSummarizerClient: boolean;
|
|
45
49
|
},
|
|
46
50
|
): IGarbageCollectorConfigs {
|
|
47
51
|
let gcEnabled: boolean;
|
|
@@ -152,8 +156,6 @@ export function generateGCConfigs(
|
|
|
152
156
|
throw new UsageError("inactive timeout should not be greater than the sweep timeout");
|
|
153
157
|
}
|
|
154
158
|
|
|
155
|
-
const throwOnInactiveLoad: boolean | undefined = createParams.gcOptions.throwOnInactiveLoad;
|
|
156
|
-
|
|
157
159
|
// Whether we are running in test mode. In this mode, unreferenced nodes are immediately deleted.
|
|
158
160
|
const testMode =
|
|
159
161
|
mc.config.getBoolean(gcTestModeKey) ?? createParams.gcOptions.runGCInTestMode === true;
|
|
@@ -162,6 +164,25 @@ export function generateGCConfigs(
|
|
|
162
164
|
const tombstoneMode = !shouldRunSweep && mc.config.getBoolean(disableTombstoneKey) !== true;
|
|
163
165
|
const runFullGC = createParams.gcOptions.runFullGC;
|
|
164
166
|
|
|
167
|
+
const throwOnInactiveLoad: boolean | undefined = createParams.gcOptions.throwOnInactiveLoad;
|
|
168
|
+
const tombstoneEnforcementAllowed = shouldAllowGcTombstoneEnforcement(
|
|
169
|
+
createParams.metadata?.gcFeatureMatrix?.tombstoneGeneration /* persisted */,
|
|
170
|
+
createParams.gcOptions[gcTombstoneGenerationOptionName] /* current */,
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
const throwOnTombstoneLoadConfig =
|
|
174
|
+
mc.config.getBoolean(throwOnTombstoneLoadOverrideKey) ??
|
|
175
|
+
createParams.gcOptions[gcThrowOnTombstoneLoadOptionName] ??
|
|
176
|
+
false;
|
|
177
|
+
const throwOnTombstoneLoad =
|
|
178
|
+
throwOnTombstoneLoadConfig &&
|
|
179
|
+
tombstoneEnforcementAllowed &&
|
|
180
|
+
!createParams.isSummarizerClient;
|
|
181
|
+
const throwOnTombstoneUsage =
|
|
182
|
+
mc.config.getBoolean(throwOnTombstoneUsageKey) === true &&
|
|
183
|
+
tombstoneEnforcementAllowed &&
|
|
184
|
+
!createParams.isSummarizerClient;
|
|
185
|
+
|
|
165
186
|
return {
|
|
166
187
|
gcEnabled,
|
|
167
188
|
sweepEnabled,
|
|
@@ -173,10 +194,13 @@ export function generateGCConfigs(
|
|
|
173
194
|
sessionExpiryTimeoutMs,
|
|
174
195
|
sweepTimeoutMs,
|
|
175
196
|
inactiveTimeoutMs,
|
|
176
|
-
throwOnInactiveLoad,
|
|
177
197
|
persistedGcFeatureMatrix,
|
|
178
198
|
gcVersionInBaseSnapshot,
|
|
179
199
|
gcVersionInEffect,
|
|
200
|
+
throwOnInactiveLoad,
|
|
201
|
+
tombstoneEnforcementAllowed,
|
|
202
|
+
throwOnTombstoneLoad,
|
|
203
|
+
throwOnTombstoneUsage,
|
|
180
204
|
};
|
|
181
205
|
}
|
|
182
206
|
|
package/src/gc/gcDefinitions.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { ICriticalContainerError } from "@fluidframework/container-definitions";
|
|
7
|
-
import {
|
|
7
|
+
import { IRequest } from "@fluidframework/core-interfaces";
|
|
8
8
|
import { ISnapshotTree } from "@fluidframework/protocol-definitions";
|
|
9
9
|
import {
|
|
10
10
|
IGarbageCollectionData,
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
ICreateContainerMetadata,
|
|
20
20
|
IRefreshSummaryResult,
|
|
21
21
|
} from "../summary";
|
|
22
|
+
import { RuntimeHeaderData } from "../containerRuntime";
|
|
22
23
|
|
|
23
24
|
export type GCVersion = number;
|
|
24
25
|
|
|
@@ -34,6 +35,13 @@ export const nextGCVersion: GCVersion = 4;
|
|
|
34
35
|
* Otherwise, only enforce GC Tombstone if the passed in value matches the persisted value
|
|
35
36
|
*/
|
|
36
37
|
export const gcTombstoneGenerationOptionName = "gcTombstoneGeneration";
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* This undocumented GC Option (on ContainerRuntime Options) allows an app to enable throwing an error when tombstone
|
|
41
|
+
* object is loaded (requested).
|
|
42
|
+
*/
|
|
43
|
+
export const gcThrowOnTombstoneLoadOptionName = "gcThrowOnTombstoneLoad";
|
|
44
|
+
|
|
37
45
|
/**
|
|
38
46
|
* This GC Option (on ContainerRuntime Options) allows an app to disable GC Sweep on old documents by incrementing this value.
|
|
39
47
|
*
|
|
@@ -55,8 +63,9 @@ export const runSessionExpiryKey = "Fluid.GarbageCollection.RunSessionExpiry";
|
|
|
55
63
|
export const disableSweepLogKey = "Fluid.GarbageCollection.DisableSweepLog";
|
|
56
64
|
/** Config key to disable the tombstone feature, i.e., tombstone information is not read / written into summary. */
|
|
57
65
|
export const disableTombstoneKey = "Fluid.GarbageCollection.DisableTombstone";
|
|
58
|
-
/** Config key to
|
|
59
|
-
export const
|
|
66
|
+
/** Config key to override throwing an error when tombstone object is loaded (requested). */
|
|
67
|
+
export const throwOnTombstoneLoadOverrideKey =
|
|
68
|
+
"Fluid.GarbageCollection.ThrowOnTombstoneLoadOverride";
|
|
60
69
|
/** Config key to enable throwing an error when tombstone object is used (e.g. outgoing or incoming ops). */
|
|
61
70
|
export const throwOnTombstoneUsageKey = "Fluid.GarbageCollection.ThrowOnTombstoneUsage";
|
|
62
71
|
/** Config key to enable GC version upgrade. */
|
|
@@ -193,8 +202,6 @@ export interface IGarbageCollectionRuntime {
|
|
|
193
202
|
getNodeType(nodePath: string): GCNodeType;
|
|
194
203
|
/** Called when the runtime should close because of an error. */
|
|
195
204
|
closeFn: (error?: ICriticalContainerError) => void;
|
|
196
|
-
/** If false, loading or using a Tombstoned object should merely log, not fail */
|
|
197
|
-
gcTombstoneEnforcementAllowed: boolean;
|
|
198
205
|
}
|
|
199
206
|
|
|
200
207
|
/** Defines the contract for the garbage collector. */
|
|
@@ -205,6 +212,12 @@ export interface IGarbageCollector {
|
|
|
205
212
|
readonly summaryStateNeedsReset: boolean;
|
|
206
213
|
/** The count of data stores whose GC state updated since the last summary. */
|
|
207
214
|
readonly updatedDSCountSinceLastSummary: number;
|
|
215
|
+
/** Tells whether tombstone feature is enabled and enforced. */
|
|
216
|
+
readonly tombstoneEnforcementAllowed: boolean;
|
|
217
|
+
/** Tells whether loading a tombstone object should fail or merely log. */
|
|
218
|
+
readonly throwOnTombstoneLoad: boolean;
|
|
219
|
+
/** Tells whether using a tombstone object should fail or merely log. */
|
|
220
|
+
readonly throwOnTombstoneUsage: boolean;
|
|
208
221
|
/** Initialize the state from the base snapshot after its creation. */
|
|
209
222
|
initializeBaseState(): Promise<void>;
|
|
210
223
|
/** Run garbage collection and update the reference / used state of the system. */
|
|
@@ -228,13 +241,17 @@ export interface IGarbageCollector {
|
|
|
228
241
|
getBaseGCDetails(): Promise<IGarbageCollectionDetailsBase>;
|
|
229
242
|
/** Called when the latest summary of the system has been refreshed. */
|
|
230
243
|
refreshLatestSummary(result: IRefreshSummaryResult): Promise<void>;
|
|
231
|
-
/**
|
|
244
|
+
/**
|
|
245
|
+
* Called when a node with the given path is updated. If the node is inactive or tombstoned, this will log an error
|
|
246
|
+
* or throw an error if failing on incorrect usage is configured.
|
|
247
|
+
*/
|
|
232
248
|
nodeUpdated(
|
|
233
249
|
nodePath: string,
|
|
234
250
|
reason: "Loaded" | "Changed",
|
|
235
251
|
timestampMs?: number,
|
|
236
252
|
packagePath?: readonly string[],
|
|
237
|
-
|
|
253
|
+
request?: IRequest,
|
|
254
|
+
headerData?: RuntimeHeaderData,
|
|
238
255
|
): void;
|
|
239
256
|
/** Called when a reference is added to a node. Used to identify nodes that were referenced between summaries. */
|
|
240
257
|
addedOutboundReference(fromNodePath: string, toNodePath: string): void;
|
|
@@ -332,8 +349,6 @@ export interface IGarbageCollectorConfigs {
|
|
|
332
349
|
readonly sweepTimeoutMs: number | undefined;
|
|
333
350
|
/** The time after which an unreferenced node is inactive. */
|
|
334
351
|
readonly inactiveTimeoutMs: number;
|
|
335
|
-
/** It is easier for users to diagnose InactiveObject usage if we throw on load, which this option enables */
|
|
336
|
-
readonly throwOnInactiveLoad: boolean | undefined;
|
|
337
352
|
/** Tracks whether GC should run in test mode. In this mode, unreferenced objects are deleted immediately. */
|
|
338
353
|
readonly testMode: boolean;
|
|
339
354
|
/**
|
|
@@ -349,6 +364,14 @@ export interface IGarbageCollectorConfigs {
|
|
|
349
364
|
readonly gcVersionInBaseSnapshot: GCVersion | undefined;
|
|
350
365
|
/** The current version of GC data in the running code */
|
|
351
366
|
readonly gcVersionInEffect: GCVersion;
|
|
367
|
+
/** It is easier for users to diagnose InactiveObject usage if we throw on load, which this option enables */
|
|
368
|
+
readonly throwOnInactiveLoad: boolean | undefined;
|
|
369
|
+
/** If false, loading or using a Tombstoned object should merely log, not fail */
|
|
370
|
+
readonly tombstoneEnforcementAllowed: boolean;
|
|
371
|
+
/** If true, throw an error when a tombstone data store is retrieved */
|
|
372
|
+
readonly throwOnTombstoneLoad: boolean;
|
|
373
|
+
/** If true, throw an error when a tombstone data store is used. */
|
|
374
|
+
readonly throwOnTombstoneUsage: boolean;
|
|
352
375
|
}
|
|
353
376
|
|
|
354
377
|
/** The state of node that is unreferenced. */
|