@fluidframework/container-runtime 2.0.0-dev.1.4.6.106135 → 2.0.0-dev.2.2.0.111723
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/.eslintrc.js +1 -1
- package/dist/batchManager.d.ts +11 -6
- package/dist/batchManager.d.ts.map +1 -1
- package/dist/batchManager.js +23 -13
- package/dist/batchManager.js.map +1 -1
- package/dist/containerRuntime.d.ts +74 -20
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +190 -137
- package/dist/containerRuntime.js.map +1 -1
- package/dist/dataStore.d.ts.map +1 -1
- package/dist/dataStore.js +6 -0
- package/dist/dataStore.js.map +1 -1
- package/dist/dataStoreContext.d.ts +14 -21
- package/dist/dataStoreContext.d.ts.map +1 -1
- package/dist/dataStoreContext.js +71 -57
- package/dist/dataStoreContext.js.map +1 -1
- package/dist/dataStoreContexts.js +1 -1
- package/dist/dataStoreContexts.js.map +1 -1
- package/dist/dataStores.d.ts +11 -10
- package/dist/dataStores.d.ts.map +1 -1
- package/dist/dataStores.js +50 -20
- package/dist/dataStores.js.map +1 -1
- package/dist/garbageCollection.d.ts +36 -19
- package/dist/garbageCollection.d.ts.map +1 -1
- package/dist/garbageCollection.js +207 -121
- package/dist/garbageCollection.js.map +1 -1
- package/dist/gcSweepReadyUsageDetection.d.ts.map +1 -1
- package/dist/gcSweepReadyUsageDetection.js +3 -12
- package/dist/gcSweepReadyUsageDetection.js.map +1 -1
- package/dist/index.d.ts +4 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -5
- package/dist/index.js.map +1 -1
- package/dist/opCompressor.d.ts +18 -0
- package/dist/opCompressor.d.ts.map +1 -0
- package/dist/opCompressor.js +50 -0
- package/dist/opCompressor.js.map +1 -0
- package/dist/opDecompressor.d.ts +20 -0
- package/dist/opDecompressor.d.ts.map +1 -0
- package/dist/opDecompressor.js +72 -0
- package/dist/opDecompressor.js.map +1 -0
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/pendingStateManager.d.ts +6 -26
- package/dist/pendingStateManager.d.ts.map +1 -1
- package/dist/pendingStateManager.js +42 -62
- package/dist/pendingStateManager.js.map +1 -1
- package/dist/runningSummarizer.d.ts +3 -2
- package/dist/runningSummarizer.d.ts.map +1 -1
- package/dist/runningSummarizer.js +10 -3
- package/dist/runningSummarizer.js.map +1 -1
- package/dist/scheduleManager.js.map +1 -1
- package/dist/summarizer.js +7 -2
- package/dist/summarizer.js.map +1 -1
- package/dist/summarizerClientElection.js +1 -1
- package/dist/summarizerClientElection.js.map +1 -1
- package/dist/summarizerHeuristics.d.ts.map +1 -1
- package/dist/summarizerHeuristics.js +0 -3
- package/dist/summarizerHeuristics.js.map +1 -1
- package/dist/summarizerTypes.d.ts +19 -2
- package/dist/summarizerTypes.d.ts.map +1 -1
- package/dist/summarizerTypes.js.map +1 -1
- package/dist/summaryFormat.d.ts +4 -2
- package/dist/summaryFormat.d.ts.map +1 -1
- package/dist/summaryFormat.js.map +1 -1
- package/dist/summaryGenerator.d.ts.map +1 -1
- package/dist/summaryGenerator.js +3 -2
- package/dist/summaryGenerator.js.map +1 -1
- package/dist/summaryManager.d.ts.map +1 -1
- package/dist/summaryManager.js +10 -6
- package/dist/summaryManager.js.map +1 -1
- package/garbageCollection.md +27 -22
- package/lib/batchManager.d.ts +11 -6
- package/lib/batchManager.d.ts.map +1 -1
- package/lib/batchManager.js +23 -13
- package/lib/batchManager.js.map +1 -1
- package/lib/containerRuntime.d.ts +74 -20
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +189 -136
- package/lib/containerRuntime.js.map +1 -1
- package/lib/dataStore.d.ts.map +1 -1
- package/lib/dataStore.js +6 -0
- package/lib/dataStore.js.map +1 -1
- package/lib/dataStoreContext.d.ts +14 -21
- package/lib/dataStoreContext.d.ts.map +1 -1
- package/lib/dataStoreContext.js +75 -61
- package/lib/dataStoreContext.js.map +1 -1
- package/lib/dataStoreContexts.js +1 -1
- package/lib/dataStoreContexts.js.map +1 -1
- package/lib/dataStores.d.ts +11 -10
- package/lib/dataStores.d.ts.map +1 -1
- package/lib/dataStores.js +53 -23
- package/lib/dataStores.js.map +1 -1
- package/lib/garbageCollection.d.ts +36 -19
- package/lib/garbageCollection.d.ts.map +1 -1
- package/lib/garbageCollection.js +207 -121
- package/lib/garbageCollection.js.map +1 -1
- package/lib/gcSweepReadyUsageDetection.d.ts.map +1 -1
- package/lib/gcSweepReadyUsageDetection.js +3 -12
- package/lib/gcSweepReadyUsageDetection.js.map +1 -1
- package/lib/index.d.ts +4 -6
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +2 -4
- package/lib/index.js.map +1 -1
- package/lib/opCompressor.d.ts +18 -0
- package/lib/opCompressor.d.ts.map +1 -0
- package/lib/opCompressor.js +46 -0
- package/lib/opCompressor.js.map +1 -0
- package/lib/opDecompressor.d.ts +20 -0
- package/lib/opDecompressor.d.ts.map +1 -0
- package/lib/opDecompressor.js +68 -0
- package/lib/opDecompressor.js.map +1 -0
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/pendingStateManager.d.ts +6 -26
- package/lib/pendingStateManager.d.ts.map +1 -1
- package/lib/pendingStateManager.js +42 -62
- package/lib/pendingStateManager.js.map +1 -1
- package/lib/runningSummarizer.d.ts +3 -2
- package/lib/runningSummarizer.d.ts.map +1 -1
- package/lib/runningSummarizer.js +10 -3
- package/lib/runningSummarizer.js.map +1 -1
- package/lib/scheduleManager.js.map +1 -1
- package/lib/summarizer.js +7 -2
- package/lib/summarizer.js.map +1 -1
- package/lib/summarizerClientElection.js +1 -1
- package/lib/summarizerClientElection.js.map +1 -1
- package/lib/summarizerHeuristics.d.ts.map +1 -1
- package/lib/summarizerHeuristics.js +0 -3
- package/lib/summarizerHeuristics.js.map +1 -1
- package/lib/summarizerTypes.d.ts +19 -2
- package/lib/summarizerTypes.d.ts.map +1 -1
- package/lib/summarizerTypes.js.map +1 -1
- package/lib/summaryFormat.d.ts +4 -2
- package/lib/summaryFormat.d.ts.map +1 -1
- package/lib/summaryFormat.js.map +1 -1
- package/lib/summaryGenerator.d.ts.map +1 -1
- package/lib/summaryGenerator.js +3 -2
- package/lib/summaryGenerator.js.map +1 -1
- package/lib/summaryManager.d.ts.map +1 -1
- package/lib/summaryManager.js +10 -6
- package/lib/summaryManager.js.map +1 -1
- package/package.json +37 -63
- package/prettier.config.cjs +8 -0
- package/src/batchManager.ts +32 -15
- package/src/containerRuntime.ts +260 -156
- package/src/dataStore.ts +13 -1
- package/src/dataStoreContext.ts +100 -76
- package/src/dataStoreContexts.ts +1 -1
- package/src/dataStores.ts +61 -23
- package/src/garbageCollection.ts +255 -126
- package/src/gcSweepReadyUsageDetection.ts +2 -10
- package/src/index.ts +4 -4
- package/src/opCompressor.ts +59 -0
- package/src/opDecompressor.ts +82 -0
- package/src/packageVersion.ts +1 -1
- package/src/pendingStateManager.ts +57 -96
- package/src/runningSummarizer.ts +11 -3
- package/src/scheduleManager.ts +1 -0
- package/src/summarizer.ts +6 -6
- package/src/summarizerClientElection.ts +1 -1
- package/src/summarizerHeuristics.ts +0 -3
- package/src/summarizerTypes.ts +20 -7
- package/src/summaryFormat.ts +4 -2
- package/src/summaryGenerator.ts +3 -2
- package/src/summaryManager.ts +18 -7
package/src/dataStore.ts
CHANGED
|
@@ -7,7 +7,12 @@ import { ITelemetryLogger } from "@fluidframework/common-definitions";
|
|
|
7
7
|
import { assert, unreachableCase } from "@fluidframework/common-utils";
|
|
8
8
|
import { AttachState } from "@fluidframework/container-definitions";
|
|
9
9
|
import { UsageError } from "@fluidframework/container-utils";
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
FluidObject,
|
|
12
|
+
IFluidHandle,
|
|
13
|
+
IRequest,
|
|
14
|
+
IResponse,
|
|
15
|
+
} from "@fluidframework/core-interfaces";
|
|
11
16
|
import { AliasResult, IDataStore, IFluidDataStoreChannel } from "@fluidframework/runtime-definitions";
|
|
12
17
|
import { TelemetryDataTag } from "@fluidframework/telemetry-utils";
|
|
13
18
|
import { ContainerRuntime } from "./containerRuntime";
|
|
@@ -151,6 +156,13 @@ class DataStore implements IDataStore {
|
|
|
151
156
|
return this.fluidDataStoreChannel.request(request);
|
|
152
157
|
}
|
|
153
158
|
|
|
159
|
+
/**
|
|
160
|
+
* {@inheritDoc @fluidframework/runtime-definitions#IDataStore.entryPoint}
|
|
161
|
+
*/
|
|
162
|
+
get entryPoint(): IFluidHandle<FluidObject> | undefined {
|
|
163
|
+
return this.fluidDataStoreChannel.entryPoint;
|
|
164
|
+
}
|
|
165
|
+
|
|
154
166
|
constructor(
|
|
155
167
|
private readonly fluidDataStoreChannel: IFluidDataStoreChannel,
|
|
156
168
|
private readonly internalId: string,
|
package/src/dataStoreContext.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { IDisposable, ITelemetryLogger } from "@fluidframework/common-definitions";
|
|
6
|
+
import { IDisposable, ITelemetryLogger, ITelemetryProperties } from "@fluidframework/common-definitions";
|
|
7
7
|
import {
|
|
8
8
|
FluidObject,
|
|
9
9
|
IRequest,
|
|
@@ -42,7 +42,6 @@ import {
|
|
|
42
42
|
CreateChildSummarizerNodeFn,
|
|
43
43
|
CreateChildSummarizerNodeParam,
|
|
44
44
|
FluidDataStoreRegistryEntry,
|
|
45
|
-
gcBlobKey,
|
|
46
45
|
IAttachMessage,
|
|
47
46
|
IFluidDataStoreChannel,
|
|
48
47
|
IFluidDataStoreContext,
|
|
@@ -51,7 +50,6 @@ import {
|
|
|
51
50
|
IFluidDataStoreRegistry,
|
|
52
51
|
IGarbageCollectionData,
|
|
53
52
|
IGarbageCollectionDetailsBase,
|
|
54
|
-
IGarbageCollectionSummaryDetails,
|
|
55
53
|
IInboundSignalMessage,
|
|
56
54
|
IProvideFluidDataStoreFactory,
|
|
57
55
|
ISummarizeInternalResult,
|
|
@@ -60,14 +58,24 @@ import {
|
|
|
60
58
|
SummarizeInternalFn,
|
|
61
59
|
ITelemetryContext,
|
|
62
60
|
} from "@fluidframework/runtime-definitions";
|
|
63
|
-
import {
|
|
61
|
+
import {
|
|
62
|
+
addBlobToSummary,
|
|
63
|
+
convertSummaryTreeToITree,
|
|
64
|
+
packagePathToTelemetryProperty,
|
|
65
|
+
} from "@fluidframework/runtime-utils";
|
|
64
66
|
import {
|
|
65
67
|
ChildLogger,
|
|
68
|
+
loggerToMonitoringContext,
|
|
66
69
|
LoggingError,
|
|
70
|
+
MonitoringContext,
|
|
67
71
|
TelemetryDataTag,
|
|
68
72
|
ThresholdCounter,
|
|
69
73
|
} from "@fluidframework/telemetry-utils";
|
|
70
|
-
import {
|
|
74
|
+
import {
|
|
75
|
+
DataCorruptionError,
|
|
76
|
+
DataProcessingError,
|
|
77
|
+
extractSafePropertiesFromMessage,
|
|
78
|
+
} from "@fluidframework/container-utils";
|
|
71
79
|
|
|
72
80
|
import { ContainerRuntime } from "./containerRuntime";
|
|
73
81
|
import {
|
|
@@ -79,6 +87,8 @@ import {
|
|
|
79
87
|
getAttributesFormatVersion,
|
|
80
88
|
getFluidDataStoreAttributes,
|
|
81
89
|
} from "./summaryFormat";
|
|
90
|
+
import { throwOnTombstoneUsageKey } from "./garbageCollection";
|
|
91
|
+
import { summarizerClientType } from "./summarizerClientElection";
|
|
82
92
|
|
|
83
93
|
function createAttributes(
|
|
84
94
|
pkg: readonly string[],
|
|
@@ -117,7 +127,6 @@ export interface IFluidDataStoreContextProps {
|
|
|
117
127
|
readonly storage: IDocumentStorageService;
|
|
118
128
|
readonly scope: FluidObject;
|
|
119
129
|
readonly createSummarizerNodeFn: CreateChildSummarizerNodeFn;
|
|
120
|
-
readonly writeGCDataAtRoot: boolean;
|
|
121
130
|
readonly pkg?: Readonly<string[]>;
|
|
122
131
|
}
|
|
123
132
|
|
|
@@ -135,7 +144,7 @@ export interface ILocalFluidDataStoreContextProps extends IFluidDataStoreContext
|
|
|
135
144
|
|
|
136
145
|
/** Properties necessary for creating a remote FluidDataStoreContext */
|
|
137
146
|
export interface IRemoteFluidDataStoreContextProps extends IFluidDataStoreContextProps {
|
|
138
|
-
readonly snapshotTree: ISnapshotTree |
|
|
147
|
+
readonly snapshotTree: ISnapshotTree | undefined;
|
|
139
148
|
readonly getBaseGCDetails: () => Promise<IGarbageCollectionDetailsBase | undefined>;
|
|
140
149
|
}
|
|
141
150
|
|
|
@@ -193,6 +202,15 @@ export abstract class FluidDataStoreContext extends TypedEventEmitter<IFluidData
|
|
|
193
202
|
private _disposed = false;
|
|
194
203
|
public get disposed() { return this._disposed; }
|
|
195
204
|
|
|
205
|
+
/**
|
|
206
|
+
* Tombstone is a temporary feature that prevents a data store from sending / receiving ops, signals and from
|
|
207
|
+
* loading.
|
|
208
|
+
*/
|
|
209
|
+
private _tombstoned = false;
|
|
210
|
+
public get tombstoned() { return this._tombstoned; }
|
|
211
|
+
/** If true, throw an error when a tombstone data store is used. */
|
|
212
|
+
private readonly throwOnTombstoneUsage: boolean;
|
|
213
|
+
|
|
196
214
|
public get attachState(): AttachState {
|
|
197
215
|
return this._attachState;
|
|
198
216
|
}
|
|
@@ -235,19 +253,18 @@ export abstract class FluidDataStoreContext extends TypedEventEmitter<IFluidData
|
|
|
235
253
|
protected _attachState: AttachState;
|
|
236
254
|
private _isInMemoryRoot: boolean = false;
|
|
237
255
|
protected readonly summarizerNode: ISummarizerNodeWithGC;
|
|
238
|
-
private readonly
|
|
256
|
+
private readonly mc: MonitoringContext;
|
|
239
257
|
private readonly thresholdOpsCounter: ThresholdCounter;
|
|
240
258
|
private static readonly pendingOpsCountThreshold = 1000;
|
|
241
259
|
|
|
242
|
-
// The used
|
|
260
|
+
// The used routes of this node as per the last GC run. This is used to update the used routes of the channel
|
|
243
261
|
// if it realizes after GC is run.
|
|
244
|
-
private
|
|
262
|
+
private lastUsedRoutes: string[] | undefined;
|
|
245
263
|
|
|
246
264
|
public readonly id: string;
|
|
247
265
|
private readonly _containerRuntime: ContainerRuntime;
|
|
248
266
|
public readonly storage: IDocumentStorageService;
|
|
249
267
|
public readonly scope: FluidObject;
|
|
250
|
-
private readonly writeGCDataAtRoot: boolean;
|
|
251
268
|
protected pkg?: readonly string[];
|
|
252
269
|
|
|
253
270
|
constructor(
|
|
@@ -263,7 +280,6 @@ export abstract class FluidDataStoreContext extends TypedEventEmitter<IFluidData
|
|
|
263
280
|
this.id = props.id;
|
|
264
281
|
this.storage = props.storage;
|
|
265
282
|
this.scope = props.scope;
|
|
266
|
-
this.writeGCDataAtRoot = props.writeGCDataAtRoot;
|
|
267
283
|
this.pkg = props.pkg;
|
|
268
284
|
|
|
269
285
|
// URIs use slashes as delimiters. Handles use URIs.
|
|
@@ -291,8 +307,13 @@ export abstract class FluidDataStoreContext extends TypedEventEmitter<IFluidData
|
|
|
291
307
|
async () => this.getBaseGCDetails(),
|
|
292
308
|
);
|
|
293
309
|
|
|
294
|
-
this.
|
|
295
|
-
this.thresholdOpsCounter = new ThresholdCounter(FluidDataStoreContext.pendingOpsCountThreshold, this.
|
|
310
|
+
this.mc = loggerToMonitoringContext(ChildLogger.create(this.logger, "FluidDataStoreContext"));
|
|
311
|
+
this.thresholdOpsCounter = new ThresholdCounter(FluidDataStoreContext.pendingOpsCountThreshold, this.mc.logger);
|
|
312
|
+
|
|
313
|
+
// Tombstone should only throw when the feature flag is enabled and the client isn't a summarizer
|
|
314
|
+
this.throwOnTombstoneUsage =
|
|
315
|
+
this.mc.config.getBoolean(throwOnTombstoneUsageKey) === true &&
|
|
316
|
+
this.clientDetails.type !== summarizerClientType;
|
|
296
317
|
}
|
|
297
318
|
|
|
298
319
|
public dispose(): void {
|
|
@@ -310,6 +331,14 @@ export abstract class FluidDataStoreContext extends TypedEventEmitter<IFluidData
|
|
|
310
331
|
}
|
|
311
332
|
}
|
|
312
333
|
|
|
334
|
+
public setTombstone(tombstone: boolean) {
|
|
335
|
+
if (this.tombstoned === tombstone) {
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
this._tombstoned = tombstone;
|
|
340
|
+
}
|
|
341
|
+
|
|
313
342
|
private rejectDeferredRealize(reason: string, packageName?: string): never {
|
|
314
343
|
throw new LoggingError(reason, { packageName: { value: packageName, tag: TelemetryDataTag.CodeArtifact } });
|
|
315
344
|
}
|
|
@@ -386,7 +415,8 @@ export abstract class FluidDataStoreContext extends TypedEventEmitter<IFluidData
|
|
|
386
415
|
* its new client ID when we are connecting or connected.
|
|
387
416
|
*/
|
|
388
417
|
public setConnectionState(connected: boolean, clientId?: string) {
|
|
389
|
-
this
|
|
418
|
+
// ConnectionState should not fail in tombstone mode as this is internally run
|
|
419
|
+
this.verifyNotClosed("setConnectionState", false /* checkTombstone */);
|
|
390
420
|
|
|
391
421
|
// Connection events are ignored if the store is not yet loaded
|
|
392
422
|
if (!this.loaded) {
|
|
@@ -400,7 +430,7 @@ export abstract class FluidDataStoreContext extends TypedEventEmitter<IFluidData
|
|
|
400
430
|
}
|
|
401
431
|
|
|
402
432
|
public process(messageArg: ISequencedDocumentMessage, local: boolean, localOpMetadata: unknown): void {
|
|
403
|
-
this.verifyNotClosed();
|
|
433
|
+
this.verifyNotClosed("process", true, extractSafePropertiesFromMessage(messageArg));
|
|
404
434
|
|
|
405
435
|
const innerContents = messageArg.contents as FluidDataStoreMessage;
|
|
406
436
|
const message = {
|
|
@@ -422,7 +452,7 @@ export abstract class FluidDataStoreContext extends TypedEventEmitter<IFluidData
|
|
|
422
452
|
}
|
|
423
453
|
|
|
424
454
|
public processSignal(message: IInboundSignalMessage, local: boolean): void {
|
|
425
|
-
this.verifyNotClosed();
|
|
455
|
+
this.verifyNotClosed("processSignal");
|
|
426
456
|
|
|
427
457
|
// Signals are ignored if the store is not yet loaded
|
|
428
458
|
if (!this.loaded) {
|
|
@@ -474,11 +504,6 @@ export abstract class FluidDataStoreContext extends TypedEventEmitter<IFluidData
|
|
|
474
504
|
const attributes = createAttributes(pkg, isRoot);
|
|
475
505
|
addBlobToSummary(summarizeResult, dataStoreAttributesBlobName, JSON.stringify(attributes));
|
|
476
506
|
|
|
477
|
-
// Add GC data to the summary if it's not written at the root.
|
|
478
|
-
if (!this.writeGCDataAtRoot) {
|
|
479
|
-
addBlobToSummary(summarizeResult, gcBlobKey, JSON.stringify(this.summarizerNode.getGCSummaryDetails()));
|
|
480
|
-
}
|
|
481
|
-
|
|
482
507
|
// If we are not referenced, mark the summary tree as unreferenced. Also, update unreferenced blob
|
|
483
508
|
// size in the summary stats with the blobs size of this data store.
|
|
484
509
|
if (!this.summarizerNode.isReferenced()) {
|
|
@@ -532,21 +557,17 @@ export abstract class FluidDataStoreContext extends TypedEventEmitter<IFluidData
|
|
|
532
557
|
* 5. To update the timestamp when this data store or any children are marked as unreferenced.
|
|
533
558
|
*
|
|
534
559
|
* @param usedRoutes - The routes that are used in this data store.
|
|
535
|
-
* @param gcTimestamp - The time when GC was run that generated these used routes. If any node becomes unreferenced
|
|
536
|
-
* as part of this GC run, this should be used to update the time when it happens.
|
|
537
560
|
*/
|
|
538
|
-
public updateUsedRoutes(usedRoutes: string[]
|
|
561
|
+
public updateUsedRoutes(usedRoutes: string[]) {
|
|
539
562
|
// Update the used routes in this data store's summarizer node.
|
|
540
|
-
this.summarizerNode.updateUsedRoutes(usedRoutes
|
|
563
|
+
this.summarizerNode.updateUsedRoutes(usedRoutes);
|
|
541
564
|
|
|
542
565
|
/**
|
|
543
|
-
*
|
|
544
|
-
*
|
|
545
|
-
*
|
|
546
|
-
* child's reference state changes, the gcTimestamp has to be used to update its unreferencedTimestamp. Since
|
|
547
|
-
* it will result in a change in this data store's used routes, it will be realized to regenerate its summary.
|
|
566
|
+
* Store the used routes to update the channel if the data store is not loaded yet. If the used routes changed
|
|
567
|
+
* since the previous run, the data store will be loaded during summarize since the used state changed. So, it's
|
|
568
|
+
* safe to only store the last used routes.
|
|
548
569
|
*/
|
|
549
|
-
this.
|
|
570
|
+
this.lastUsedRoutes = usedRoutes;
|
|
550
571
|
|
|
551
572
|
// If we are loaded, call the channel so it can update the used routes of the child contexts.
|
|
552
573
|
// If we are not loaded, we will update this when we are realized.
|
|
@@ -575,16 +596,16 @@ export abstract class FluidDataStoreContext extends TypedEventEmitter<IFluidData
|
|
|
575
596
|
assert(this.loaded, 0x144 /* "Channel should be loaded when updating used routes" */);
|
|
576
597
|
assert(this.channel !== undefined, 0x145 /* "Channel should be present when data store is loaded" */);
|
|
577
598
|
|
|
578
|
-
// If there is no
|
|
579
|
-
if (this.
|
|
599
|
+
// If there is no lastUsedRoutes, GC has not run up until this point.
|
|
600
|
+
if (this.lastUsedRoutes === undefined) {
|
|
580
601
|
return;
|
|
581
602
|
}
|
|
582
603
|
|
|
583
604
|
// Remove the route to this data store, if it exists.
|
|
584
|
-
const usedChannelRoutes = this.
|
|
605
|
+
const usedChannelRoutes = this.lastUsedRoutes.filter(
|
|
585
606
|
(id: string) => { return id !== "/" && id !== ""; },
|
|
586
607
|
);
|
|
587
|
-
this.channel.updateUsedRoutes(usedChannelRoutes
|
|
608
|
+
this.channel.updateUsedRoutes(usedChannelRoutes);
|
|
588
609
|
}
|
|
589
610
|
|
|
590
611
|
/**
|
|
@@ -596,7 +617,7 @@ export abstract class FluidDataStoreContext extends TypedEventEmitter<IFluidData
|
|
|
596
617
|
}
|
|
597
618
|
|
|
598
619
|
public submitMessage(type: string, content: any, localOpMetadata: unknown): void {
|
|
599
|
-
this.verifyNotClosed();
|
|
620
|
+
this.verifyNotClosed("submitMessage");
|
|
600
621
|
assert(!!this.channel, 0x146 /* "Channel must exist when submitting message" */);
|
|
601
622
|
const fluidDataStoreContent: FluidDataStoreMessage = {
|
|
602
623
|
content,
|
|
@@ -618,7 +639,7 @@ export abstract class FluidDataStoreContext extends TypedEventEmitter<IFluidData
|
|
|
618
639
|
*
|
|
619
640
|
*/
|
|
620
641
|
public setChannelDirty(address: string): void {
|
|
621
|
-
this.verifyNotClosed();
|
|
642
|
+
this.verifyNotClosed("setChannelDirty");
|
|
622
643
|
|
|
623
644
|
// Get the latest sequence number.
|
|
624
645
|
const latestSequenceNumber = this.deltaManager.lastSequenceNumber;
|
|
@@ -633,7 +654,8 @@ export abstract class FluidDataStoreContext extends TypedEventEmitter<IFluidData
|
|
|
633
654
|
}
|
|
634
655
|
|
|
635
656
|
public submitSignal(type: string, content: any) {
|
|
636
|
-
this.verifyNotClosed();
|
|
657
|
+
this.verifyNotClosed("submitSignal");
|
|
658
|
+
|
|
637
659
|
assert(!!this.channel, 0x147 /* "Channel must exist on submitting signal" */);
|
|
638
660
|
return this._containerRuntime.submitDataStoreSignal(this.id, type, content);
|
|
639
661
|
}
|
|
@@ -718,11 +740,6 @@ export abstract class FluidDataStoreContext extends TypedEventEmitter<IFluidData
|
|
|
718
740
|
this._isInMemoryRoot = true;
|
|
719
741
|
}
|
|
720
742
|
|
|
721
|
-
/**
|
|
722
|
-
* @deprecated Renamed to `{@link FluidDataStoreContext.getBaseGCDetails}()`.
|
|
723
|
-
*/
|
|
724
|
-
public abstract getInitialGCSummaryDetails(): Promise<IGarbageCollectionSummaryDetails>;
|
|
725
|
-
|
|
726
743
|
public abstract getBaseGCDetails(): Promise<IGarbageCollectionDetailsBase>;
|
|
727
744
|
|
|
728
745
|
public reSubmit(contents: any, localOpMetadata: unknown) {
|
|
@@ -751,9 +768,30 @@ export abstract class FluidDataStoreContext extends TypedEventEmitter<IFluidData
|
|
|
751
768
|
return this.channel.applyStashedOp(innerContents.content);
|
|
752
769
|
}
|
|
753
770
|
|
|
754
|
-
private verifyNotClosed() {
|
|
771
|
+
private verifyNotClosed(callSite: string, checkTombstone = true, safeTelemetryProps: ITelemetryProperties = {}) {
|
|
755
772
|
if (this._disposed) {
|
|
756
|
-
throw new Error(
|
|
773
|
+
throw new Error(`Context is closed! Call site [${callSite}]`);
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
if (checkTombstone && this.tombstoned) {
|
|
777
|
+
const messageString = `Context is tombstoned! Call site [${callSite}]`;
|
|
778
|
+
const error = new DataCorruptionError(messageString, {
|
|
779
|
+
errorMessage: messageString,
|
|
780
|
+
...safeTelemetryProps,
|
|
781
|
+
});
|
|
782
|
+
|
|
783
|
+
// Always log an error when tombstoned data store is used. However, throw an error only if
|
|
784
|
+
// throwOnTombstoneUsage is set.
|
|
785
|
+
this.mc.logger.sendErrorEvent({
|
|
786
|
+
eventName: "GC_Tombstone_DataStore_Changed",
|
|
787
|
+
callSite,
|
|
788
|
+
pkg: packagePathToTelemetryProperty(this.pkg),
|
|
789
|
+
}, error);
|
|
790
|
+
// Always log an error when tombstoned data store is used. However, throw an error only if
|
|
791
|
+
// throwOnTombstoneUsage is set and the client is not a summarizer.
|
|
792
|
+
if (this.throwOnTombstoneUsage) {
|
|
793
|
+
throw error;
|
|
794
|
+
}
|
|
757
795
|
}
|
|
758
796
|
}
|
|
759
797
|
|
|
@@ -779,7 +817,7 @@ export abstract class FluidDataStoreContext extends TypedEventEmitter<IFluidData
|
|
|
779
817
|
}
|
|
780
818
|
|
|
781
819
|
export class RemoteFluidDataStoreContext extends FluidDataStoreContext {
|
|
782
|
-
private readonly initSnapshotValue: ISnapshotTree |
|
|
820
|
+
private readonly initSnapshotValue: ISnapshotTree | undefined;
|
|
783
821
|
private readonly baseGCDetailsP: Promise<IGarbageCollectionDetailsBase>;
|
|
784
822
|
|
|
785
823
|
constructor(props: IRemoteFluidDataStoreContextProps) {
|
|
@@ -797,28 +835,20 @@ export class RemoteFluidDataStoreContext extends FluidDataStoreContext {
|
|
|
797
835
|
this.baseGCDetailsP = new LazyPromise<IGarbageCollectionDetailsBase>(async () => {
|
|
798
836
|
return (await props.getBaseGCDetails()) ?? {};
|
|
799
837
|
});
|
|
838
|
+
|
|
839
|
+
if (props.snapshotTree !== undefined) {
|
|
840
|
+
this.summarizerNode.updateBaseSummaryState(props.snapshotTree);
|
|
841
|
+
}
|
|
800
842
|
}
|
|
801
843
|
|
|
802
844
|
private readonly initialSnapshotDetailsP = new LazyPromise<ISnapshotDetails>(async () => {
|
|
803
|
-
let tree
|
|
845
|
+
let tree = this.initSnapshotValue;
|
|
804
846
|
let isRootDataStore = true;
|
|
805
847
|
|
|
806
|
-
if (typeof this.initSnapshotValue === "string") {
|
|
807
|
-
const commit = (await this.storage.getVersions(this.initSnapshotValue, 1))[0];
|
|
808
|
-
tree = await this.storage.getSnapshotTree(commit) ?? undefined;
|
|
809
|
-
} else {
|
|
810
|
-
tree = this.initSnapshotValue;
|
|
811
|
-
}
|
|
812
|
-
|
|
813
|
-
const localReadAndParse = async <T>(id: string) => readAndParse<T>(this.storage, id);
|
|
814
|
-
if (tree) {
|
|
815
|
-
tree = await this.summarizerNode.loadBaseSummary(tree, localReadAndParse);
|
|
816
|
-
}
|
|
817
|
-
|
|
818
848
|
if (!!tree && tree.blobs[dataStoreAttributesBlobName] !== undefined) {
|
|
819
849
|
// Need to get through snapshot and use that to populate extraBlobs
|
|
820
850
|
const attributes =
|
|
821
|
-
await
|
|
851
|
+
await readAndParse<ReadFluidDataStoreAttributes>(this.storage, tree.blobs[dataStoreAttributesBlobName]);
|
|
822
852
|
|
|
823
853
|
let pkgFromSnapshot: string[];
|
|
824
854
|
// Use the snapshotFormatVersion to determine how the pkg is encoded in the snapshot.
|
|
@@ -859,13 +889,6 @@ export class RemoteFluidDataStoreContext extends FluidDataStoreContext {
|
|
|
859
889
|
return this.initialSnapshotDetailsP;
|
|
860
890
|
}
|
|
861
891
|
|
|
862
|
-
/**
|
|
863
|
-
* @deprecated Renamed to {@link RemoteFluidDataStoreContext.getBaseGCDetails}.
|
|
864
|
-
*/
|
|
865
|
-
public async getInitialGCSummaryDetails(): Promise<IGarbageCollectionSummaryDetails> {
|
|
866
|
-
return this.getBaseGCDetails();
|
|
867
|
-
}
|
|
868
|
-
|
|
869
892
|
public async getBaseGCDetails(): Promise<IGarbageCollectionDetailsBase> {
|
|
870
893
|
return this.baseGCDetailsP;
|
|
871
894
|
}
|
|
@@ -974,14 +997,6 @@ export class LocalFluidDataStoreContextBase extends FluidDataStoreContext {
|
|
|
974
997
|
};
|
|
975
998
|
}
|
|
976
999
|
|
|
977
|
-
/**
|
|
978
|
-
* @deprecated Renamed to {@link LocalFluidDataStoreContextBase.getBaseGCDetails}.
|
|
979
|
-
*/
|
|
980
|
-
public async getInitialGCSummaryDetails(): Promise<IGarbageCollectionSummaryDetails> {
|
|
981
|
-
// Local data store does not have initial summary.
|
|
982
|
-
return {};
|
|
983
|
-
}
|
|
984
|
-
|
|
985
1000
|
public async getBaseGCDetails(): Promise<IGarbageCollectionDetailsBase> {
|
|
986
1001
|
// Local data store does not have initial summary.
|
|
987
1002
|
return {};
|
|
@@ -1033,6 +1048,15 @@ export class LocalDetachedFluidDataStoreContext
|
|
|
1033
1048
|
|
|
1034
1049
|
super.bindRuntime(dataStoreChannel);
|
|
1035
1050
|
|
|
1051
|
+
// Load the handle to the data store's entryPoint to make sure that for a detached data store, the entryPoint
|
|
1052
|
+
// initialization function is called before the data store gets attached and potentially connected to the
|
|
1053
|
+
// delta stream, so it gets a chance to do things while the data store is still "purely local".
|
|
1054
|
+
// This preserves the behavior from before we introduced entryPoints, where the instantiateDataStore method
|
|
1055
|
+
// of data store factories tends to construct the data object (at least kick off an async method that returns
|
|
1056
|
+
// it); that code moved to the entryPoint initialization function, so we want to ensure it still executes
|
|
1057
|
+
// before the data store is attached.
|
|
1058
|
+
await dataStoreChannel.entryPoint?.get();
|
|
1059
|
+
|
|
1036
1060
|
if (await this.isRoot()) {
|
|
1037
1061
|
dataStoreChannel.makeVisibleAndAttachGraph();
|
|
1038
1062
|
}
|
package/src/dataStoreContexts.ts
CHANGED
package/src/dataStores.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import { ITelemetryBaseLogger, IDisposable } from "@fluidframework/common-definitions";
|
|
7
7
|
import { DataCorruptionError, extractSafePropertiesFromMessage } from "@fluidframework/container-utils";
|
|
8
8
|
import { IFluidHandle } from "@fluidframework/core-interfaces";
|
|
9
9
|
import { FluidObjectHandle } from "@fluidframework/datastore";
|
|
@@ -32,10 +32,12 @@ import {
|
|
|
32
32
|
convertSnapshotTreeToSummaryTree,
|
|
33
33
|
convertToSummaryTree,
|
|
34
34
|
create404Response,
|
|
35
|
+
createResponseError,
|
|
35
36
|
responseToException,
|
|
37
|
+
packagePathToTelemetryProperty,
|
|
36
38
|
SummaryTreeBuilder,
|
|
37
39
|
} from "@fluidframework/runtime-utils";
|
|
38
|
-
import { ChildLogger, LoggingError, TelemetryDataTag } from "@fluidframework/telemetry-utils";
|
|
40
|
+
import { ChildLogger, loggerToMonitoringContext, LoggingError, MonitoringContext, TelemetryDataTag } from "@fluidframework/telemetry-utils";
|
|
39
41
|
import { AttachState } from "@fluidframework/container-definitions";
|
|
40
42
|
import { BlobCacheStorageService, buildSnapshotTree } from "@fluidframework/driver-utils";
|
|
41
43
|
import { assert, Lazy, LazyPromise } from "@fluidframework/common-utils";
|
|
@@ -52,7 +54,8 @@ import {
|
|
|
52
54
|
} from "./dataStoreContext";
|
|
53
55
|
import { IContainerRuntimeMetadata, nonDataStorePaths, rootHasIsolatedChannels } from "./summaryFormat";
|
|
54
56
|
import { IDataStoreAliasMessage, isDataStoreAliasMessage } from "./dataStore";
|
|
55
|
-
import { GCNodeType } from "./garbageCollection";
|
|
57
|
+
import { throwOnTombstoneUsageKey, GCNodeType } from "./garbageCollection";
|
|
58
|
+
import { summarizerClientType } from "./summarizerClientElection";
|
|
56
59
|
|
|
57
60
|
type PendingAliasResolve = (success: boolean) => void;
|
|
58
61
|
|
|
@@ -66,7 +69,7 @@ export class DataStores implements IDisposable {
|
|
|
66
69
|
// 0.24 back-compat attachingBeforeSummary
|
|
67
70
|
public readonly attachOpFiredForDataStore = new Set<string>();
|
|
68
71
|
|
|
69
|
-
private readonly
|
|
72
|
+
private readonly mc: MonitoringContext;
|
|
70
73
|
|
|
71
74
|
private readonly disposeOnce = new Lazy<void>(() => this.contexts.dispose());
|
|
72
75
|
|
|
@@ -80,6 +83,8 @@ export class DataStores implements IDisposable {
|
|
|
80
83
|
// Stores the ids of new data stores between two GC runs. This is used to notify the garbage collector of new
|
|
81
84
|
// root data stores that are added.
|
|
82
85
|
private dataStoresSinceLastGC: string[] = [];
|
|
86
|
+
/** If true, throw an error when a tombstone data store is retrieved. */
|
|
87
|
+
private readonly throwOnTombstoneUsage: boolean;
|
|
83
88
|
// The handle to the container runtime. This is used mainly for GC purposes to represent outbound reference from
|
|
84
89
|
// the container runtime to other nodes.
|
|
85
90
|
private readonly containerRuntimeHandle: IFluidHandle;
|
|
@@ -97,10 +102,9 @@ export class DataStores implements IDisposable {
|
|
|
97
102
|
private readonly gcNodeUpdated: (
|
|
98
103
|
nodePath: string, timestampMs: number, packagePath?: readonly string[]) => void,
|
|
99
104
|
private readonly aliasMap: Map<string, string>,
|
|
100
|
-
private readonly writeGCDataAtRoot: boolean,
|
|
101
105
|
private readonly contexts: DataStoreContexts = new DataStoreContexts(baseLogger),
|
|
102
106
|
) {
|
|
103
|
-
this.
|
|
107
|
+
this.mc = loggerToMonitoringContext(ChildLogger.create(baseLogger));
|
|
104
108
|
this.containerRuntimeHandle = new FluidObjectHandle(this.runtime, "/", this.runtime.IFluidHandleContext);
|
|
105
109
|
|
|
106
110
|
const baseGCDetailsP = new LazyPromise(async () => {
|
|
@@ -111,6 +115,10 @@ export class DataStores implements IDisposable {
|
|
|
111
115
|
const baseGCDetails = await baseGCDetailsP;
|
|
112
116
|
return baseGCDetails.get(dataStoreId);
|
|
113
117
|
};
|
|
118
|
+
// Tombstone should only throw when the feature flag is enabled and the client isn't a summarizer
|
|
119
|
+
this.throwOnTombstoneUsage =
|
|
120
|
+
this.mc.config.getBoolean(throwOnTombstoneUsageKey) === true &&
|
|
121
|
+
this.runtime.clientDetails.type !== summarizerClientType;
|
|
114
122
|
|
|
115
123
|
// Extract stores stored inside the snapshot
|
|
116
124
|
const fluidDataStores = new Map<string, ISnapshotTree>();
|
|
@@ -142,7 +150,6 @@ export class DataStores implements IDisposable {
|
|
|
142
150
|
key,
|
|
143
151
|
{ type: CreateSummarizerNodeSource.FromSummary },
|
|
144
152
|
),
|
|
145
|
-
writeGCDataAtRoot: this.writeGCDataAtRoot,
|
|
146
153
|
});
|
|
147
154
|
} else {
|
|
148
155
|
if (typeof value !== "object") {
|
|
@@ -162,7 +169,6 @@ export class DataStores implements IDisposable {
|
|
|
162
169
|
makeLocallyVisibleFn: () => this.makeDataStoreLocallyVisible(key),
|
|
163
170
|
snapshotTree,
|
|
164
171
|
isRootDataStore: undefined,
|
|
165
|
-
writeGCDataAtRoot: this.writeGCDataAtRoot,
|
|
166
172
|
});
|
|
167
173
|
}
|
|
168
174
|
this.contexts.addBoundOrRemoted(dataStoreContext);
|
|
@@ -247,7 +253,6 @@ export class DataStores implements IDisposable {
|
|
|
247
253
|
},
|
|
248
254
|
},
|
|
249
255
|
),
|
|
250
|
-
writeGCDataAtRoot: this.writeGCDataAtRoot,
|
|
251
256
|
pkg,
|
|
252
257
|
});
|
|
253
258
|
|
|
@@ -283,7 +288,7 @@ export class DataStores implements IDisposable {
|
|
|
283
288
|
|
|
284
289
|
const context = this.contexts.get(aliasMessage.internalId);
|
|
285
290
|
if (context === undefined) {
|
|
286
|
-
this.logger.sendErrorEvent({
|
|
291
|
+
this.mc.logger.sendErrorEvent({
|
|
287
292
|
eventName: "AliasFluidDataStoreNotFound",
|
|
288
293
|
fluidDataStoreId: aliasMessage.internalId,
|
|
289
294
|
});
|
|
@@ -351,7 +356,6 @@ export class DataStores implements IDisposable {
|
|
|
351
356
|
makeLocallyVisibleFn: () => this.makeDataStoreLocallyVisible(id),
|
|
352
357
|
snapshotTree: undefined,
|
|
353
358
|
isRootDataStore: isRoot,
|
|
354
|
-
writeGCDataAtRoot: this.writeGCDataAtRoot,
|
|
355
359
|
});
|
|
356
360
|
this.contexts.addUnbound(context);
|
|
357
361
|
return context;
|
|
@@ -372,7 +376,6 @@ export class DataStores implements IDisposable {
|
|
|
372
376
|
makeLocallyVisibleFn: () => this.makeDataStoreLocallyVisible(id),
|
|
373
377
|
snapshotTree: undefined,
|
|
374
378
|
isRootDataStore: false,
|
|
375
|
-
writeGCDataAtRoot: this.writeGCDataAtRoot,
|
|
376
379
|
createProps: props,
|
|
377
380
|
});
|
|
378
381
|
this.contexts.addUnbound(context);
|
|
@@ -425,14 +428,31 @@ export class DataStores implements IDisposable {
|
|
|
425
428
|
);
|
|
426
429
|
}
|
|
427
430
|
|
|
428
|
-
public async getDataStore(id: string, wait: boolean): Promise<FluidDataStoreContext> {
|
|
431
|
+
public async getDataStore(id: string, wait: boolean, viaHandle: boolean): Promise<FluidDataStoreContext> {
|
|
429
432
|
const context = await this.contexts.getBoundOrRemoted(id, wait);
|
|
433
|
+
const request = { url: id };
|
|
430
434
|
if (context === undefined) {
|
|
431
435
|
// The requested data store does not exits. Throw a 404 response exception.
|
|
432
|
-
const request = { url: id };
|
|
433
436
|
throw responseToException(create404Response(request), request);
|
|
434
437
|
}
|
|
435
438
|
|
|
439
|
+
if (context.tombstoned) {
|
|
440
|
+
// The requested data store is removed by gc. Create a 404 gc response exception.
|
|
441
|
+
const error = responseToException(createResponseError(404, "Datastore removed by gc", request), request);
|
|
442
|
+
// Note: if a user writes a request to look like it's viaHandle, we will also send this telemetry event
|
|
443
|
+
this.mc.logger.sendErrorEvent({
|
|
444
|
+
eventName: "GC_Tombstone_DataStore_Requested",
|
|
445
|
+
url: request.url,
|
|
446
|
+
pkg: packagePathToTelemetryProperty(context.isLoaded ? context.packagePath : undefined),
|
|
447
|
+
viaHandle,
|
|
448
|
+
}, error);
|
|
449
|
+
// Always log an error when tombstoned data store is used. However, throw an error only if
|
|
450
|
+
// throwOnTombstoneUsage is set.
|
|
451
|
+
if (this.throwOnTombstoneUsage) {
|
|
452
|
+
throw error;
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
436
456
|
return context;
|
|
437
457
|
}
|
|
438
458
|
|
|
@@ -441,7 +461,7 @@ export class DataStores implements IDisposable {
|
|
|
441
461
|
if (!context) {
|
|
442
462
|
// Attach message may not have been processed yet
|
|
443
463
|
assert(!local, 0x163 /* "Missing datastore for local signal" */);
|
|
444
|
-
this.logger.sendTelemetryEvent({
|
|
464
|
+
this.mc.logger.sendTelemetryEvent({
|
|
445
465
|
eventName: "SignalFluidDataStoreNotFound",
|
|
446
466
|
fluidDataStoreId: {
|
|
447
467
|
value: address,
|
|
@@ -459,7 +479,7 @@ export class DataStores implements IDisposable {
|
|
|
459
479
|
try {
|
|
460
480
|
context.setConnectionState(connected, clientId);
|
|
461
481
|
} catch (error) {
|
|
462
|
-
this.logger.sendErrorEvent({
|
|
482
|
+
this.mc.logger.sendErrorEvent({
|
|
463
483
|
eventName: "SetConnectionStateError",
|
|
464
484
|
clientId,
|
|
465
485
|
fluidDataStore,
|
|
@@ -595,30 +615,35 @@ export class DataStores implements IDisposable {
|
|
|
595
615
|
/**
|
|
596
616
|
* After GC has run, called to notify this Container's data stores of routes that are used in it.
|
|
597
617
|
* @param usedRoutes - The routes that are used in all data stores in this Container.
|
|
598
|
-
* @param gcTimestamp - The time when GC was run that generated these used routes. If any node node becomes
|
|
599
|
-
* unreferenced as part of this GC run, this should be used to update the time when it happens.
|
|
600
618
|
*/
|
|
601
|
-
public updateUsedRoutes(usedRoutes: string[]
|
|
619
|
+
public updateUsedRoutes(usedRoutes: string[]) {
|
|
602
620
|
// Get a map of data store ids to routes used in it.
|
|
603
621
|
const usedDataStoreRoutes = unpackChildNodesUsedRoutes(usedRoutes);
|
|
604
622
|
|
|
605
623
|
// Verify that the used routes are correct.
|
|
606
624
|
for (const [id] of usedDataStoreRoutes) {
|
|
607
625
|
assert(this.contexts.has(id), 0x167 /* "Used route does not belong to any known data store" */);
|
|
626
|
+
|
|
627
|
+
// Revive datastores regardless of whether or not tombstone the tombstone flag is flipped
|
|
628
|
+
const dataStore = this.contexts.get(id);
|
|
629
|
+
assert(dataStore !== undefined, 0x46e /* No data store retrieved with specified id */);
|
|
630
|
+
dataStore.setTombstone(false /* tombstone */);
|
|
608
631
|
}
|
|
609
632
|
|
|
610
633
|
// Update the used routes in each data store. Used routes is empty for unused data stores.
|
|
611
634
|
for (const [contextId, context] of this.contexts) {
|
|
612
|
-
context.updateUsedRoutes(usedDataStoreRoutes.get(contextId) ?? []
|
|
635
|
+
context.updateUsedRoutes(usedDataStoreRoutes.get(contextId) ?? []);
|
|
613
636
|
}
|
|
614
637
|
}
|
|
615
638
|
|
|
616
639
|
/**
|
|
617
|
-
*
|
|
618
|
-
*
|
|
640
|
+
* This is called to update objects whose routes are unused. The unused objects are either deleted or marked as
|
|
641
|
+
* tombstones.
|
|
619
642
|
* @param unusedRoutes - The routes that are unused in all data stores in this Container.
|
|
643
|
+
* @param tombstone - if true, the objects corresponding to unused routes are marked tombstones. Otherwise, they
|
|
644
|
+
* are deleted.
|
|
620
645
|
*/
|
|
621
|
-
public
|
|
646
|
+
public updateUnusedRoutes(unusedRoutes: string[], tombstone: boolean) {
|
|
622
647
|
for (const route of unusedRoutes) {
|
|
623
648
|
const pathParts = route.split("/");
|
|
624
649
|
// Delete data store only if its route (/datastoreId) is in unusedRoutes. We don't want to delete a data
|
|
@@ -628,6 +653,19 @@ export class DataStores implements IDisposable {
|
|
|
628
653
|
}
|
|
629
654
|
const dataStoreId = pathParts[1];
|
|
630
655
|
assert(this.contexts.has(dataStoreId), 0x2d7 /* No data store with specified id */);
|
|
656
|
+
|
|
657
|
+
/**
|
|
658
|
+
* When running GC in tombstone mode, datastore contexts are tombstoned. Tombstoned datastore contexts
|
|
659
|
+
* enable testing scenarios with accessing deleted content without actually deleting content from
|
|
660
|
+
* summaries.
|
|
661
|
+
*/
|
|
662
|
+
if (tombstone) {
|
|
663
|
+
const dataStore = this.contexts.get(dataStoreId);
|
|
664
|
+
assert(dataStore !== undefined, 0x442 /* No data store retrieved with specified id */);
|
|
665
|
+
dataStore.setTombstone(true /* tombstone */);
|
|
666
|
+
continue;
|
|
667
|
+
}
|
|
668
|
+
|
|
631
669
|
// Delete the contexts of unused data stores.
|
|
632
670
|
this.contexts.delete(dataStoreId);
|
|
633
671
|
// Delete the summarizer node of the unused data stores.
|