@fluidframework/container-runtime 0.55.2 → 0.56.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/dist/containerRuntime.d.ts +3 -12
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +24 -38
- package/dist/containerRuntime.js.map +1 -1
- package/dist/dataStoreContext.d.ts +42 -29
- package/dist/dataStoreContext.d.ts.map +1 -1
- package/dist/dataStoreContext.js +27 -49
- package/dist/dataStoreContext.js.map +1 -1
- package/dist/dataStores.d.ts +2 -1
- package/dist/dataStores.d.ts.map +1 -1
- package/dist/dataStores.js +74 -16
- package/dist/dataStores.js.map +1 -1
- package/dist/garbageCollection.d.ts +22 -9
- package/dist/garbageCollection.d.ts.map +1 -1
- package/dist/garbageCollection.js +55 -35
- package/dist/garbageCollection.js.map +1 -1
- package/dist/index.d.ts +6 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -17
- package/dist/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/dist/runningSummarizer.d.ts.map +1 -1
- package/dist/runningSummarizer.js +2 -9
- package/dist/runningSummarizer.js.map +1 -1
- package/dist/summarizerTypes.d.ts +2 -0
- package/dist/summarizerTypes.d.ts.map +1 -1
- package/dist/summarizerTypes.js.map +1 -1
- package/dist/summaryFormat.d.ts.map +1 -1
- package/dist/summaryFormat.js +2 -1
- package/dist/summaryFormat.js.map +1 -1
- package/dist/summaryGenerator.d.ts +0 -5
- package/dist/summaryGenerator.d.ts.map +1 -1
- package/dist/summaryGenerator.js +2 -1
- package/dist/summaryGenerator.js.map +1 -1
- package/garbageCollection.md +33 -0
- package/lib/containerRuntime.d.ts +3 -12
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +25 -39
- package/lib/containerRuntime.js.map +1 -1
- package/lib/dataStoreContext.d.ts +42 -29
- package/lib/dataStoreContext.d.ts.map +1 -1
- package/lib/dataStoreContext.js +25 -47
- package/lib/dataStoreContext.js.map +1 -1
- package/lib/dataStores.d.ts +2 -1
- package/lib/dataStores.d.ts.map +1 -1
- package/lib/dataStores.js +75 -17
- package/lib/dataStores.js.map +1 -1
- package/lib/garbageCollection.d.ts +22 -9
- package/lib/garbageCollection.d.ts.map +1 -1
- package/lib/garbageCollection.js +55 -35
- package/lib/garbageCollection.js.map +1 -1
- package/lib/index.d.ts +6 -6
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +5 -6
- package/lib/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/lib/runningSummarizer.d.ts.map +1 -1
- package/lib/runningSummarizer.js +2 -9
- package/lib/runningSummarizer.js.map +1 -1
- package/lib/summarizerTypes.d.ts +2 -0
- package/lib/summarizerTypes.d.ts.map +1 -1
- package/lib/summarizerTypes.js.map +1 -1
- package/lib/summaryFormat.d.ts.map +1 -1
- package/lib/summaryFormat.js +2 -1
- package/lib/summaryFormat.js.map +1 -1
- package/lib/summaryGenerator.d.ts +0 -5
- package/lib/summaryGenerator.d.ts.map +1 -1
- package/lib/summaryGenerator.js +2 -1
- package/lib/summaryGenerator.js.map +1 -1
- package/package.json +14 -14
- package/src/containerRuntime.ts +38 -42
- package/src/dataStoreContext.ts +76 -122
- package/src/dataStores.ts +75 -48
- package/src/garbageCollection.ts +62 -39
- package/src/index.ts +51 -6
- package/src/packageVersion.ts +1 -1
- package/src/runningSummarizer.ts +2 -7
- package/src/summarizerTypes.ts +2 -0
- package/src/summaryFormat.ts +2 -1
- package/src/summaryGenerator.ts +2 -6
package/src/dataStores.ts
CHANGED
|
@@ -44,7 +44,7 @@ import { DataStoreContexts } from "./dataStoreContexts";
|
|
|
44
44
|
import { ContainerRuntime } from "./containerRuntime";
|
|
45
45
|
import {
|
|
46
46
|
FluidDataStoreContext,
|
|
47
|
-
|
|
47
|
+
RemoteFluidDataStoreContext,
|
|
48
48
|
LocalFluidDataStoreContext,
|
|
49
49
|
createAttributesBlob,
|
|
50
50
|
LocalDetachedFluidDataStoreContext,
|
|
@@ -117,6 +117,7 @@ export class DataStores implements IDisposable {
|
|
|
117
117
|
getBaseGCDetails: () => Promise<Map<string, IGarbageCollectionDetailsBase>>,
|
|
118
118
|
private readonly dataStoreChanged: (id: string) => void,
|
|
119
119
|
private readonly aliasMap: Map<string, string>,
|
|
120
|
+
private readonly writeGCDataAtRoot: boolean,
|
|
120
121
|
private readonly contexts: DataStoreContexts = new DataStoreContexts(baseLogger),
|
|
121
122
|
) {
|
|
122
123
|
this.logger = ChildLogger.create(baseLogger);
|
|
@@ -150,30 +151,41 @@ export class DataStores implements IDisposable {
|
|
|
150
151
|
}
|
|
151
152
|
// If we have a detached container, then create local data store contexts.
|
|
152
153
|
if (this.runtime.attachState !== AttachState.Detached) {
|
|
153
|
-
dataStoreContext = new
|
|
154
|
-
key,
|
|
155
|
-
value,
|
|
156
|
-
async () => dataStoreBaseGCDetails(key),
|
|
157
|
-
this.runtime,
|
|
158
|
-
this.runtime.storage,
|
|
159
|
-
this.runtime.scope,
|
|
160
|
-
this.getCreateChildSummarizerNodeFn(
|
|
154
|
+
dataStoreContext = new RemoteFluidDataStoreContext({
|
|
155
|
+
id: key,
|
|
156
|
+
snapshotTree: value,
|
|
157
|
+
getBaseGCDetails: async () => dataStoreBaseGCDetails(key),
|
|
158
|
+
runtime: this.runtime,
|
|
159
|
+
storage: this.runtime.storage,
|
|
160
|
+
scope: this.runtime.scope,
|
|
161
|
+
createSummarizerNodeFn: this.getCreateChildSummarizerNodeFn(
|
|
162
|
+
key,
|
|
163
|
+
{ type: CreateSummarizerNodeSource.FromSummary },
|
|
164
|
+
),
|
|
165
|
+
writeGCDataAtRoot: this.writeGCDataAtRoot,
|
|
166
|
+
disableIsolatedChannels: this.runtime.disableIsolatedChannels,
|
|
167
|
+
});
|
|
161
168
|
} else {
|
|
162
169
|
if (typeof value !== "object") {
|
|
163
170
|
throw new Error("Snapshot should be there to load from!!");
|
|
164
171
|
}
|
|
165
172
|
const snapshotTree = value;
|
|
166
|
-
dataStoreContext = new LocalFluidDataStoreContext(
|
|
167
|
-
key,
|
|
168
|
-
undefined,
|
|
169
|
-
this.runtime,
|
|
170
|
-
this.runtime.storage,
|
|
171
|
-
this.runtime.scope,
|
|
172
|
-
this.getCreateChildSummarizerNodeFn(
|
|
173
|
-
|
|
173
|
+
dataStoreContext = new LocalFluidDataStoreContext({
|
|
174
|
+
id: key,
|
|
175
|
+
pkg: undefined,
|
|
176
|
+
runtime: this.runtime,
|
|
177
|
+
storage: this.runtime.storage,
|
|
178
|
+
scope: this.runtime.scope,
|
|
179
|
+
createSummarizerNodeFn: this.getCreateChildSummarizerNodeFn(
|
|
180
|
+
key,
|
|
181
|
+
{ type: CreateSummarizerNodeSource.FromSummary },
|
|
182
|
+
),
|
|
183
|
+
bindChannelFn: (cr: IFluidDataStoreChannel) => this.bindFluidDataStore(cr),
|
|
174
184
|
snapshotTree,
|
|
175
|
-
undefined,
|
|
176
|
-
|
|
185
|
+
isRootDataStore: undefined,
|
|
186
|
+
writeGCDataAtRoot: this.writeGCDataAtRoot,
|
|
187
|
+
disableIsolatedChannels: this.runtime.disableIsolatedChannels,
|
|
188
|
+
});
|
|
177
189
|
}
|
|
178
190
|
this.contexts.addBoundOrRemoted(dataStoreContext);
|
|
179
191
|
}
|
|
@@ -224,17 +236,17 @@ export class DataStores implements IDisposable {
|
|
|
224
236
|
}
|
|
225
237
|
|
|
226
238
|
// Include the type of attach message which is the pkg of the store to be
|
|
227
|
-
// used by
|
|
239
|
+
// used by RemoteFluidDataStoreContext in case it is not in the snapshot.
|
|
228
240
|
const pkg = [attachMessage.type];
|
|
229
|
-
const
|
|
230
|
-
attachMessage.id,
|
|
241
|
+
const remoteFluidDataStoreContext = new RemoteFluidDataStoreContext({
|
|
242
|
+
id: attachMessage.id,
|
|
231
243
|
snapshotTree,
|
|
232
244
|
// New data stores begin with empty GC details since GC hasn't run on them yet.
|
|
233
|
-
async () => { return {}; },
|
|
234
|
-
this.runtime,
|
|
235
|
-
new BlobCacheStorageService(this.runtime.storage, flatBlobs),
|
|
236
|
-
this.runtime.scope,
|
|
237
|
-
this.getCreateChildSummarizerNodeFn(
|
|
245
|
+
getBaseGCDetails: async () => { return {}; },
|
|
246
|
+
runtime: this.runtime,
|
|
247
|
+
storage: new BlobCacheStorageService(this.runtime.storage, flatBlobs),
|
|
248
|
+
scope: this.runtime.scope,
|
|
249
|
+
createSummarizerNodeFn: this.getCreateChildSummarizerNodeFn(
|
|
238
250
|
attachMessage.id,
|
|
239
251
|
{
|
|
240
252
|
type: CreateSummarizerNodeSource.FromAttach,
|
|
@@ -246,10 +258,14 @@ export class DataStores implements IDisposable {
|
|
|
246
258
|
this.runtime.disableIsolatedChannels,
|
|
247
259
|
)],
|
|
248
260
|
},
|
|
249
|
-
}
|
|
250
|
-
|
|
261
|
+
},
|
|
262
|
+
),
|
|
263
|
+
writeGCDataAtRoot: this.writeGCDataAtRoot,
|
|
264
|
+
disableIsolatedChannels: this.runtime.disableIsolatedChannels,
|
|
265
|
+
pkg,
|
|
266
|
+
});
|
|
251
267
|
|
|
252
|
-
this.contexts.addBoundOrRemoted(
|
|
268
|
+
this.contexts.addBoundOrRemoted(remoteFluidDataStoreContext);
|
|
253
269
|
}
|
|
254
270
|
|
|
255
271
|
public processAliasMessage(
|
|
@@ -322,33 +338,44 @@ export class DataStores implements IDisposable {
|
|
|
322
338
|
isRoot: boolean,
|
|
323
339
|
id = uuid()): IFluidDataStoreContextDetached
|
|
324
340
|
{
|
|
325
|
-
const context = new LocalDetachedFluidDataStoreContext(
|
|
341
|
+
const context = new LocalDetachedFluidDataStoreContext({
|
|
326
342
|
id,
|
|
327
343
|
pkg,
|
|
328
|
-
this.runtime,
|
|
329
|
-
this.runtime.storage,
|
|
330
|
-
this.runtime.scope,
|
|
331
|
-
this.getCreateChildSummarizerNodeFn(
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
344
|
+
runtime: this.runtime,
|
|
345
|
+
storage: this.runtime.storage,
|
|
346
|
+
scope: this.runtime.scope,
|
|
347
|
+
createSummarizerNodeFn: this.getCreateChildSummarizerNodeFn(
|
|
348
|
+
id,
|
|
349
|
+
{ type: CreateSummarizerNodeSource.Local },
|
|
350
|
+
),
|
|
351
|
+
bindChannelFn: (cr: IFluidDataStoreChannel) => this.bindFluidDataStore(cr),
|
|
352
|
+
snapshotTree: undefined,
|
|
353
|
+
isRootDataStore: isRoot,
|
|
354
|
+
writeGCDataAtRoot: this.writeGCDataAtRoot,
|
|
355
|
+
disableIsolatedChannels: this.runtime.disableIsolatedChannels,
|
|
356
|
+
});
|
|
335
357
|
this.contexts.addUnbound(context);
|
|
336
358
|
return context;
|
|
337
359
|
}
|
|
338
360
|
|
|
339
361
|
public _createFluidDataStoreContext(pkg: string[], id: string, isRoot: boolean, props?: any) {
|
|
340
|
-
const context = new LocalFluidDataStoreContext(
|
|
362
|
+
const context = new LocalFluidDataStoreContext({
|
|
341
363
|
id,
|
|
342
364
|
pkg,
|
|
343
|
-
this.runtime,
|
|
344
|
-
this.runtime.storage,
|
|
345
|
-
this.runtime.scope,
|
|
346
|
-
this.getCreateChildSummarizerNodeFn(
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
365
|
+
runtime: this.runtime,
|
|
366
|
+
storage: this.runtime.storage,
|
|
367
|
+
scope: this.runtime.scope,
|
|
368
|
+
createSummarizerNodeFn: this.getCreateChildSummarizerNodeFn(
|
|
369
|
+
id,
|
|
370
|
+
{ type: CreateSummarizerNodeSource.Local },
|
|
371
|
+
),
|
|
372
|
+
bindChannelFn: (cr: IFluidDataStoreChannel) => this.bindFluidDataStore(cr),
|
|
373
|
+
snapshotTree: undefined,
|
|
374
|
+
isRootDataStore: isRoot,
|
|
375
|
+
writeGCDataAtRoot: this.writeGCDataAtRoot,
|
|
376
|
+
disableIsolatedChannels: this.runtime.disableIsolatedChannels,
|
|
377
|
+
createProps: props,
|
|
378
|
+
});
|
|
352
379
|
this.contexts.addUnbound(context);
|
|
353
380
|
return context;
|
|
354
381
|
}
|
package/src/garbageCollection.ts
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
import { ITelemetryLogger } from "@fluidframework/common-definitions";
|
|
7
7
|
import { assert, LazyPromise, Timer } from "@fluidframework/common-utils";
|
|
8
8
|
import { ICriticalContainerError } from "@fluidframework/container-definitions";
|
|
9
|
+
import { ClientSessionExpiredError } from "@fluidframework/container-utils";
|
|
9
10
|
import {
|
|
10
11
|
cloneGCData,
|
|
11
12
|
concatGarbageCollectionStates,
|
|
@@ -53,12 +54,14 @@ export const gcTreeKey = "gc";
|
|
|
53
54
|
// They prefix for GC blobs in the GC tree in summary.
|
|
54
55
|
export const gcBlobPrefix = "__gc";
|
|
55
56
|
|
|
56
|
-
//
|
|
57
|
+
// Feature gate key to turn GC on / off.
|
|
57
58
|
const runGCKey = "Fluid.GarbageCollection.RunGC";
|
|
58
|
-
//
|
|
59
|
+
// Feature gate key to turn GC test mode on / off.
|
|
59
60
|
const gcTestModeKey = "Fluid.GarbageCollection.GCTestMode";
|
|
60
|
-
//
|
|
61
|
+
// Feature gate key to turn GC sweep on / off.
|
|
61
62
|
const runSweepKey = "Fluid.GarbageCollection.RunSweep";
|
|
63
|
+
// Feature gate key to write GC data at the root of the summary tree.
|
|
64
|
+
const writeAtRootKey = "Fluid.GarbageCollection.WriteDataAtRoot";
|
|
62
65
|
|
|
63
66
|
const defaultDeleteTimeoutMs = 7 * 24 * 60 * 60 * 1000; // 7 days
|
|
64
67
|
|
|
@@ -98,8 +101,8 @@ export interface IGarbageCollector {
|
|
|
98
101
|
* 2. If GC is enabled, the version of GC used to generate the GC data written in a summary.
|
|
99
102
|
*/
|
|
100
103
|
readonly gcSummaryFeatureVersion: number;
|
|
101
|
-
/** Tells whether the GC
|
|
102
|
-
readonly
|
|
104
|
+
/** Tells whether the GC state in summary needs to be reset in the next summary. */
|
|
105
|
+
readonly summaryStateNeedsReset: boolean;
|
|
103
106
|
/** Tells whether GC data should be written to the root of the summary tree. */
|
|
104
107
|
readonly writeDataAtRoot: boolean;
|
|
105
108
|
/** Run garbage collection and update the reference / used state of the system. */
|
|
@@ -114,9 +117,9 @@ export interface IGarbageCollector {
|
|
|
114
117
|
latestSummaryStateRefreshed(result: RefreshSummaryResult, readAndParseBlob: ReadAndParseBlob): Promise<void>;
|
|
115
118
|
/** Called when a node is changed. Used to detect and log when an inactive node is changed. */
|
|
116
119
|
nodeChanged(id: string): void;
|
|
117
|
-
dispose(): void;
|
|
118
120
|
/** Called when a reference is added to a node. Used to identify nodes that were referenced between summaries. */
|
|
119
121
|
addedOutboundReference(fromNodeId: string, toNodeId: string): void;
|
|
122
|
+
dispose(): void;
|
|
120
123
|
}
|
|
121
124
|
|
|
122
125
|
/**
|
|
@@ -221,13 +224,16 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
221
224
|
}
|
|
222
225
|
|
|
223
226
|
/**
|
|
224
|
-
* Tells whether the GC
|
|
227
|
+
* Tells whether the GC state needs to be reset in the next summary. We need to do this if:
|
|
228
|
+
* 1. GC was enabled and is now disabled. The GC state needs to be removed and everything becomes referenced.
|
|
229
|
+
* 2. GC was disabled and is now enabled. The GC state needs to be regenerated and added to summary.
|
|
230
|
+
* 3. The GC version in the latest summary is different from the current GC version. This can happen if:
|
|
231
|
+
* 3.1. The summary this client loaded with has data from a different GC version.
|
|
232
|
+
* 3.2. This client's latest summary was updated from a snapshot that has a different GC version.
|
|
225
233
|
*/
|
|
226
|
-
public get
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
// 2. This client's latest summary was updated from a snapshot that has a different GC version.
|
|
230
|
-
return this.shouldRunGC && this.latestSummaryGCVersion !== this.currentGCVersion;
|
|
234
|
+
public get summaryStateNeedsReset(): boolean {
|
|
235
|
+
return this.initialStateNeedsReset ||
|
|
236
|
+
(this.shouldRunGC && this.latestSummaryGCVersion !== this.currentGCVersion);
|
|
231
237
|
}
|
|
232
238
|
|
|
233
239
|
/**
|
|
@@ -240,15 +246,24 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
240
246
|
private readonly mc: MonitoringContext;
|
|
241
247
|
|
|
242
248
|
/**
|
|
243
|
-
* Tells whether the GC data should be written to the root of the summary tree.
|
|
244
|
-
* 1. If `writeDataAtRoot` GC option is enabled.
|
|
245
|
-
* 2. If the base summary has the GC data written at the root. This is to support forward compatibility where when
|
|
246
|
-
* we start writing the GC data at root, older versions can detect that and write at root too.
|
|
249
|
+
* Tells whether the GC data should be written to the root of the summary tree.
|
|
247
250
|
*/
|
|
248
251
|
private _writeDataAtRoot: boolean = false;
|
|
249
252
|
public get writeDataAtRoot(): boolean {
|
|
250
253
|
return this._writeDataAtRoot;
|
|
251
|
-
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Tells whether the initial GC state needs to be reset. This can happen under 2 conditions:
|
|
258
|
+
* 1. The base snapshot contains GC state but GC is disabled. This will happen the first time GC is disabled after
|
|
259
|
+
* it was enabled before. GC state needs to be removed from summary and all nodes should be marked referenced.
|
|
260
|
+
* 2. The base snapshot does not have GC state but GC is enabled. This will happen the very first time GC runs on
|
|
261
|
+
* a document and the first time GC is enabled after is was disabled before.
|
|
262
|
+
*
|
|
263
|
+
* Note that the state needs reset only for the very first time summary is generated by this client. After that, the
|
|
264
|
+
* state will be up-to-date and this flag will be reset.
|
|
265
|
+
*/
|
|
266
|
+
private initialStateNeedsReset: boolean = false;
|
|
252
267
|
|
|
253
268
|
// The current GC version that this container is running.
|
|
254
269
|
private readonly currentGCVersion = GCVersion;
|
|
@@ -310,15 +325,9 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
310
325
|
|
|
311
326
|
// If session expiry is enabled, we need to close the container when the timeout expires
|
|
312
327
|
if (this.sessionExpiryTimeoutMs !== undefined) {
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
// this.sessionExpiryTimeoutMs)),
|
|
317
|
-
// this.sessionExpiryTimeoutMs);
|
|
318
|
-
this.sessionExpiryTimer = setTimeout(() => this.closeFn({
|
|
319
|
-
errorType: "clientSessionExpiredError",
|
|
320
|
-
message: `The client has reached the expiry time of ${this.sessionExpiryTimeoutMs} ms.`,
|
|
321
|
-
}), this.sessionExpiryTimeoutMs);
|
|
328
|
+
const expiryMs = this.sessionExpiryTimeoutMs;
|
|
329
|
+
this.sessionExpiryTimer = setTimeout(() => this.closeFn(
|
|
330
|
+
new ClientSessionExpiredError(`Client session expired.`, expiryMs)), expiryMs);
|
|
322
331
|
}
|
|
323
332
|
|
|
324
333
|
// For existing document, the latest summary is the one that we loaded from. So, use its GC version as the
|
|
@@ -342,9 +351,19 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
342
351
|
// Whether we are running in test mode. In this mode, unreferenced nodes are immediately deleted.
|
|
343
352
|
this.testMode = this.mc.config.getBoolean(gcTestModeKey) ?? gcOptions.runGCInTestMode === true;
|
|
344
353
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
354
|
+
/**
|
|
355
|
+
* Enable resetting initial state once the following issue is resolved:
|
|
356
|
+
* https://github.com/microsoft/FluidFramework/issues/8878.
|
|
357
|
+
* Currently, the GC tree is not written at root, so we don't know if the base snapshot contains GC tree or not.
|
|
358
|
+
*/
|
|
359
|
+
// The GC state needs to be reset if the base snapshot contains GC tree and GC is disabled or it doesn't contain
|
|
360
|
+
// GC tree and GC is enabled.
|
|
361
|
+
// const gcTreePresent = baseSnapshot?.trees[gcTreeKey] !== undefined;
|
|
362
|
+
// this.initialStateNeedsReset = gcTreePresent ? !this.shouldRunGC : this.shouldRunGC;
|
|
363
|
+
|
|
364
|
+
// If `writeDataAtRoot` setting is true, write the GC data into the root of the summary tree. We do this so that
|
|
365
|
+
// the roll out can be staged. Once its rolled out everywhere, we will start writing at root by default.
|
|
366
|
+
this._writeDataAtRoot = this.mc.config.getBoolean(writeAtRootKey) ?? this.gcOptions.writeDataAtRoot === true;
|
|
348
367
|
|
|
349
368
|
// Get the GC state from the GC blob in the base snapshot. Use LazyPromise because we only want to do
|
|
350
369
|
// this once since it involves fetching blobs from storage which is expensive.
|
|
@@ -356,7 +375,7 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
356
375
|
// For newer documents, GC data should be present in the GC tree in the root of the snapshot.
|
|
357
376
|
const gcSnapshotTree = baseSnapshot.trees[gcTreeKey];
|
|
358
377
|
if (gcSnapshotTree !== undefined) {
|
|
359
|
-
//
|
|
378
|
+
// If the GC tree is written at root, we should also do the same.
|
|
360
379
|
this._writeDataAtRoot = true;
|
|
361
380
|
return getGCStateFromSnapshot(gcSnapshotTree, readAndParseBlob);
|
|
362
381
|
}
|
|
@@ -488,7 +507,7 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
488
507
|
const {
|
|
489
508
|
logger = this.mc.logger,
|
|
490
509
|
runSweep = this.shouldRunSweep,
|
|
491
|
-
fullGC = this.gcOptions.runFullGC === true || this.
|
|
510
|
+
fullGC = this.gcOptions.runFullGC === true || this.summaryStateNeedsReset,
|
|
492
511
|
} = options;
|
|
493
512
|
|
|
494
513
|
return PerformanceEvent.timedExecAsync(logger, { eventName: "GarbageCollection" }, async (event) => {
|
|
@@ -582,6 +601,10 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
582
601
|
result: RefreshSummaryResult,
|
|
583
602
|
readAndParseBlob: ReadAndParseBlob,
|
|
584
603
|
): Promise<void> {
|
|
604
|
+
// After a summary is successfully submitted and ack'd by this client, the GC state should have been reset in
|
|
605
|
+
// the summary and doesn't need to be reset anymore.
|
|
606
|
+
this.initialStateNeedsReset = false;
|
|
607
|
+
|
|
585
608
|
if (!this.shouldRunGC || !result.latestSummaryUpdated) {
|
|
586
609
|
return;
|
|
587
610
|
}
|
|
@@ -791,16 +814,16 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
791
814
|
// Validate references for data stores only whose routes are of the format "/dataStoreId". Currently, layers
|
|
792
815
|
// below data stores don't have GC implemented so there is no guarantee their references will be notified.
|
|
793
816
|
if (route.split("/").length === 2 && !explicitReferences.includes(route)) {
|
|
817
|
+
/**
|
|
818
|
+
* The following log will be enabled once this issue is resolved:
|
|
819
|
+
* https://github.com/microsoft/FluidFramework/issues/8878.
|
|
820
|
+
*/
|
|
794
821
|
// We should ideally throw a data corruption error here. However, send an error for now until we have
|
|
795
822
|
// implemented sweep and have reasonable confidence in the sweep process.
|
|
796
|
-
|
|
797
|
-
//
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
eventName: "gcUnknownOutboundRoute",
|
|
801
|
-
route,
|
|
802
|
-
});
|
|
803
|
-
*/
|
|
823
|
+
// this.mc.logger.sendErrorEvent({
|
|
824
|
+
// eventName: "gcUnknownOutboundRoute",
|
|
825
|
+
// route,
|
|
826
|
+
// });
|
|
804
827
|
}
|
|
805
828
|
});
|
|
806
829
|
}
|
package/src/index.ts
CHANGED
|
@@ -17,8 +17,8 @@ export {
|
|
|
17
17
|
agentSchedulerId,
|
|
18
18
|
ContainerRuntime,
|
|
19
19
|
} from "./containerRuntime";
|
|
20
|
-
export
|
|
21
|
-
export
|
|
20
|
+
export { DeltaScheduler } from "./deltaScheduler";
|
|
21
|
+
export { FluidDataStoreRegistry } from "./dataStoreRegistry";
|
|
22
22
|
export {
|
|
23
23
|
gcBlobPrefix,
|
|
24
24
|
gcTreeKey,
|
|
@@ -26,8 +26,53 @@ export {
|
|
|
26
26
|
IGCStats,
|
|
27
27
|
IUsedStateStats,
|
|
28
28
|
} from "./garbageCollection";
|
|
29
|
-
export
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
29
|
+
export {
|
|
30
|
+
IPendingFlush,
|
|
31
|
+
IPendingFlushMode,
|
|
32
|
+
IPendingLocalState,
|
|
33
|
+
IPendingMessage,
|
|
34
|
+
IPendingState,
|
|
35
|
+
} from "./pendingStateManager";
|
|
36
|
+
export { Summarizer } from "./summarizer";
|
|
37
|
+
export {
|
|
38
|
+
EnqueueSummarizeResult,
|
|
39
|
+
IAckSummaryResult,
|
|
40
|
+
IBaseSummarizeResult,
|
|
41
|
+
IBroadcastSummaryResult,
|
|
42
|
+
ICancellationToken,
|
|
43
|
+
IConnectableRuntime,
|
|
44
|
+
IEnqueueSummarizeOptions,
|
|
45
|
+
IGenerateSummaryTreeResult,
|
|
46
|
+
IGeneratedSummaryStats,
|
|
47
|
+
INackSummaryResult,
|
|
48
|
+
IOnDemandSummarizeOptions,
|
|
49
|
+
IProvideSummarizer,
|
|
50
|
+
ISubmitSummaryOpResult,
|
|
51
|
+
ISubmitSummaryOptions,
|
|
52
|
+
ISummarizeOptions,
|
|
53
|
+
ISummarizeResults,
|
|
54
|
+
ISummarizer,
|
|
55
|
+
ISummarizerEvents,
|
|
56
|
+
ISummarizerInternalsProvider,
|
|
57
|
+
ISummarizerOptions,
|
|
58
|
+
ISummarizerRuntime,
|
|
59
|
+
ISummarizingWarning,
|
|
60
|
+
ISummaryCancellationToken,
|
|
61
|
+
IUploadSummaryResult,
|
|
62
|
+
SubmitSummaryResult,
|
|
63
|
+
SummarizeResultPart,
|
|
64
|
+
SummarizerStopReason,
|
|
65
|
+
} from "./summarizerTypes";
|
|
66
|
+
export {
|
|
67
|
+
IAckedSummary,
|
|
68
|
+
IClientSummaryWatcher,
|
|
69
|
+
ISummary,
|
|
70
|
+
ISummaryCollectionOpEvents,
|
|
71
|
+
ISummaryAckMessage,
|
|
72
|
+
ISummaryNackMessage,
|
|
73
|
+
ISummaryOpMessage,
|
|
74
|
+
OpActionEventListener,
|
|
75
|
+
OpActionEventName,
|
|
76
|
+
SummaryCollection,
|
|
77
|
+
} from "./summaryCollection";
|
|
33
78
|
export { ICancellableSummarizerController, neverCancelledSummaryToken } from "./runWhileConnectedCoordinator";
|
package/src/packageVersion.ts
CHANGED
package/src/runningSummarizer.ts
CHANGED
|
@@ -201,13 +201,8 @@ export class RunningSummarizer implements IDisposable {
|
|
|
201
201
|
}
|
|
202
202
|
this.heuristicData.lastOpSequenceNumber = sequenceNumber;
|
|
203
203
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
} else if (type === MessageType.Save) {
|
|
207
|
-
// Check for ops requesting summary
|
|
208
|
-
// Note: as const is only required until TypeScript version 4.3
|
|
209
|
-
this.trySummarize(`save;${clientId}: ${contents}` as const);
|
|
210
|
-
} else {
|
|
204
|
+
// Check for enqueued on-demand summaries; Intentionally do nothing otherwise
|
|
205
|
+
if (!this.tryRunEnqueuedSummary()) {
|
|
211
206
|
this.heuristicRunner?.run();
|
|
212
207
|
}
|
|
213
208
|
}
|
package/src/summarizerTypes.ts
CHANGED
|
@@ -166,6 +166,8 @@ export interface IGenerateSummaryTreeResult extends Omit<IBaseSummarizeResult, "
|
|
|
166
166
|
readonly summaryStats: IGeneratedSummaryStats;
|
|
167
167
|
/** Time it took to generate the summary tree and stats. */
|
|
168
168
|
readonly generateDuration: number;
|
|
169
|
+
/** True if the full tree regeneration with no handle reuse optimizations was forced. */
|
|
170
|
+
readonly forcedFullTree: boolean;
|
|
169
171
|
}
|
|
170
172
|
|
|
171
173
|
/** Results of submitSummary after uploading the tree to storage. */
|
package/src/summaryFormat.ts
CHANGED
|
@@ -8,6 +8,7 @@ import { IDocumentStorageService } from "@fluidframework/driver-definitions";
|
|
|
8
8
|
import { readAndParse } from "@fluidframework/driver-utils";
|
|
9
9
|
import { ISequencedDocumentMessage, ISnapshotTree, SummaryType } from "@fluidframework/protocol-definitions";
|
|
10
10
|
import { channelsTreeName, ISummaryTreeWithStats } from "@fluidframework/runtime-definitions";
|
|
11
|
+
import { gcTreeKey } from "./garbageCollection";
|
|
11
12
|
|
|
12
13
|
type OmitAttributesVersions<T> = Omit<T, "snapshotFormatVersion" | "summaryFormatVersion">;
|
|
13
14
|
interface IFluidDataStoreAttributes0 {
|
|
@@ -161,7 +162,7 @@ export const protocolTreeName = ".protocol";
|
|
|
161
162
|
* isolated data stores namespace. Without the namespace, this must
|
|
162
163
|
* be used to prevent name collisions with data store IDs.
|
|
163
164
|
*/
|
|
164
|
-
export const nonDataStorePaths = [protocolTreeName, ".logTail", ".serviceProtocol", blobsTreeName];
|
|
165
|
+
export const nonDataStorePaths = [protocolTreeName, ".logTail", ".serviceProtocol", blobsTreeName, gcTreeKey];
|
|
165
166
|
|
|
166
167
|
export const dataStoreAttributesBlobName = ".component";
|
|
167
168
|
|
package/src/summaryGenerator.ts
CHANGED
|
@@ -76,11 +76,6 @@ export type SummarizeReason =
|
|
|
76
76
|
* ack op.
|
|
77
77
|
*/
|
|
78
78
|
| "maxOps"
|
|
79
|
-
/**
|
|
80
|
-
* Special case to generate a summary in response to a Save op.
|
|
81
|
-
* @deprecated - do not use save ops
|
|
82
|
-
*/
|
|
83
|
-
| `save;${string}: ${string}`
|
|
84
79
|
/**
|
|
85
80
|
* Special case to attempt to summarize one last time before the
|
|
86
81
|
* summarizer client closes itself. This is to prevent cases where
|
|
@@ -249,10 +244,11 @@ export class SummaryGenerator {
|
|
|
249
244
|
|
|
250
245
|
// Cumulatively add telemetry properties based on how far generateSummary went.
|
|
251
246
|
const { referenceSequenceNumber: refSequenceNumber } = summaryData;
|
|
247
|
+
const opsSinceLastSummary = refSequenceNumber - this.heuristicData.lastSuccessfulSummary.refSequenceNumber;
|
|
252
248
|
generateTelemetryProps = {
|
|
253
249
|
referenceSequenceNumber: refSequenceNumber,
|
|
254
250
|
opsSinceLastAttempt: refSequenceNumber - this.heuristicData.lastAttempt.refSequenceNumber,
|
|
255
|
-
opsSinceLastSummary
|
|
251
|
+
opsSinceLastSummary,
|
|
256
252
|
};
|
|
257
253
|
if (summaryData.stage !== "base") {
|
|
258
254
|
generateTelemetryProps = {
|