@fluidframework/container-runtime 0.54.1 → 0.55.1
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/blobManager.js +1 -1
- package/dist/blobManager.js.map +1 -1
- package/dist/containerRuntime.d.ts +17 -2
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +31 -21
- package/dist/containerRuntime.js.map +1 -1
- package/dist/dataStoreContext.d.ts +16 -5
- package/dist/dataStoreContext.d.ts.map +1 -1
- package/dist/dataStoreContext.js +20 -8
- package/dist/dataStoreContext.js.map +1 -1
- package/dist/dataStores.d.ts +11 -2
- package/dist/dataStores.d.ts.map +1 -1
- package/dist/dataStores.js +33 -17
- package/dist/dataStores.js.map +1 -1
- package/dist/garbageCollection.d.ts +28 -6
- package/dist/garbageCollection.d.ts.map +1 -1
- package/dist/garbageCollection.js +83 -7
- package/dist/garbageCollection.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- 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/summaryFormat.d.ts +2 -0
- package/dist/summaryFormat.d.ts.map +1 -1
- package/dist/summaryFormat.js +0 -3
- package/dist/summaryFormat.js.map +1 -1
- package/dist/summaryManager.d.ts +1 -0
- package/dist/summaryManager.d.ts.map +1 -1
- package/dist/summaryManager.js +7 -2
- package/dist/summaryManager.js.map +1 -1
- package/lib/blobManager.js +1 -1
- package/lib/blobManager.js.map +1 -1
- package/lib/containerRuntime.d.ts +17 -2
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +30 -20
- package/lib/containerRuntime.js.map +1 -1
- package/lib/dataStoreContext.d.ts +16 -5
- package/lib/dataStoreContext.d.ts.map +1 -1
- package/lib/dataStoreContext.js +20 -8
- package/lib/dataStoreContext.js.map +1 -1
- package/lib/dataStores.d.ts +11 -2
- package/lib/dataStores.d.ts.map +1 -1
- package/lib/dataStores.js +33 -17
- package/lib/dataStores.js.map +1 -1
- package/lib/garbageCollection.d.ts +28 -6
- package/lib/garbageCollection.d.ts.map +1 -1
- package/lib/garbageCollection.js +83 -7
- package/lib/garbageCollection.js.map +1 -1
- package/lib/index.d.ts +1 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -1
- 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/summaryFormat.d.ts +2 -0
- package/lib/summaryFormat.d.ts.map +1 -1
- package/lib/summaryFormat.js +0 -3
- package/lib/summaryFormat.js.map +1 -1
- package/lib/summaryManager.d.ts +1 -0
- package/lib/summaryManager.d.ts.map +1 -1
- package/lib/summaryManager.js +7 -2
- package/lib/summaryManager.js.map +1 -1
- package/package.json +21 -20
- package/src/blobManager.ts +1 -1
- package/src/containerRuntime.ts +34 -23
- package/src/dataStoreContext.ts +31 -9
- package/src/dataStores.ts +43 -20
- package/src/garbageCollection.ts +112 -10
- package/src/index.ts +1 -0
- package/src/packageVersion.ts +1 -1
- package/src/summaryFormat.ts +2 -3
- package/src/summaryManager.ts +9 -3
package/src/garbageCollection.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import { ITelemetryLogger } from "@fluidframework/common-definitions";
|
|
7
7
|
import { assert, LazyPromise, Timer } from "@fluidframework/common-utils";
|
|
8
|
+
import { ICriticalContainerError } from "@fluidframework/container-definitions";
|
|
8
9
|
import {
|
|
9
10
|
cloneGCData,
|
|
10
11
|
concatGarbageCollectionStates,
|
|
@@ -18,7 +19,7 @@ import {
|
|
|
18
19
|
gcBlobKey,
|
|
19
20
|
IGarbageCollectionData,
|
|
20
21
|
IGarbageCollectionState,
|
|
21
|
-
|
|
22
|
+
IGarbageCollectionDetailsBase,
|
|
22
23
|
ISummaryTreeWithStats,
|
|
23
24
|
} from "@fluidframework/runtime-definitions";
|
|
24
25
|
import {
|
|
@@ -77,6 +78,8 @@ export interface IGCStats {
|
|
|
77
78
|
|
|
78
79
|
/** Defines the APIs for the runtime object to be passed to the garbage collector. */
|
|
79
80
|
export interface IGarbageCollectionRuntime {
|
|
81
|
+
/** Before GC runs, called to notify the runtime to update any pending GC state. */
|
|
82
|
+
updateStateBeforeGC(): Promise<void>;
|
|
80
83
|
/** Returns the garbage collection data of the runtime. */
|
|
81
84
|
getGCData(fullGC?: boolean): Promise<IGarbageCollectionData>;
|
|
82
85
|
/** After GC has run, called to notify the runtime of routes that are used in it. */
|
|
@@ -87,6 +90,8 @@ export interface IGarbageCollectionRuntime {
|
|
|
87
90
|
export interface IGarbageCollector {
|
|
88
91
|
/** Tells whether GC should run or not. */
|
|
89
92
|
readonly shouldRunGC: boolean;
|
|
93
|
+
/** The time in ms to expire a session for a client for gc. */
|
|
94
|
+
readonly sessionExpiryTimeoutMs: number | undefined;
|
|
90
95
|
/**
|
|
91
96
|
* This tracks two things:
|
|
92
97
|
* 1. Whether GC is enabled - If this is 0, GC is disabled. If this is greater than 0, GC is enabled.
|
|
@@ -104,11 +109,12 @@ export interface IGarbageCollector {
|
|
|
104
109
|
/** Summarizes the GC data and returns it as a summary tree. */
|
|
105
110
|
summarize(): ISummaryTreeWithStats | undefined;
|
|
106
111
|
/** Returns a map of each data store id to its GC details in the base summary. */
|
|
107
|
-
getDataStoreBaseGCDetails(): Promise<Map<string,
|
|
112
|
+
getDataStoreBaseGCDetails(): Promise<Map<string, IGarbageCollectionDetailsBase>>;
|
|
108
113
|
/** Called when the latest summary of the system has been refreshed. */
|
|
109
114
|
latestSummaryStateRefreshed(result: RefreshSummaryResult, readAndParseBlob: ReadAndParseBlob): Promise<void>;
|
|
110
115
|
/** Called when a node is changed. Used to detect and log when an inactive node is changed. */
|
|
111
116
|
nodeChanged(id: string): void;
|
|
117
|
+
dispose(): void;
|
|
112
118
|
/** Called when a reference is added to a node. Used to identify nodes that were referenced between summaries. */
|
|
113
119
|
addedOutboundReference(fromNodeId: string, toNodeId: string): void;
|
|
114
120
|
}
|
|
@@ -174,6 +180,7 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
174
180
|
gcOptions: IGCRuntimeOptions,
|
|
175
181
|
deleteUnusedRoutes: (unusedRoutes: string[]) => void,
|
|
176
182
|
getCurrentTimestampMs: () => number,
|
|
183
|
+
closeFn: (error?: ICriticalContainerError) => void,
|
|
177
184
|
baseSnapshot: ISnapshotTree | undefined,
|
|
178
185
|
readAndParseBlob: ReadAndParseBlob,
|
|
179
186
|
baseLogger: ITelemetryLogger,
|
|
@@ -185,6 +192,7 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
185
192
|
gcOptions,
|
|
186
193
|
deleteUnusedRoutes,
|
|
187
194
|
getCurrentTimestampMs,
|
|
195
|
+
closeFn,
|
|
188
196
|
baseSnapshot,
|
|
189
197
|
readAndParseBlob,
|
|
190
198
|
baseLogger,
|
|
@@ -198,6 +206,11 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
198
206
|
*/
|
|
199
207
|
public readonly shouldRunGC: boolean;
|
|
200
208
|
|
|
209
|
+
/**
|
|
210
|
+
* The time in ms to expire a session for a client for gc.
|
|
211
|
+
*/
|
|
212
|
+
public readonly sessionExpiryTimeoutMs: number | undefined;
|
|
213
|
+
|
|
201
214
|
/**
|
|
202
215
|
* This tracks two things:
|
|
203
216
|
* 1. Whether GC is enabled - If this is 0, GC is disabled. If this is greater than 0, GC is enabled.
|
|
@@ -251,11 +264,13 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
251
264
|
// Promise when resolved initializes the base state of the nodes from the base summary state.
|
|
252
265
|
private readonly initializeBaseStateP: Promise<void>;
|
|
253
266
|
// The map of data store ids to their GC details in the base summary returned in getDataStoreGCDetails().
|
|
254
|
-
private readonly dataStoreGCDetailsP: Promise<Map<string,
|
|
267
|
+
private readonly dataStoreGCDetailsP: Promise<Map<string, IGarbageCollectionDetailsBase>>;
|
|
255
268
|
// The time after which an unreferenced node can be deleted. Currently, we only set the node's state to expired.
|
|
256
269
|
private readonly deleteTimeoutMs: number;
|
|
257
270
|
// Map of node ids to their unreferenced state tracker.
|
|
258
271
|
private readonly unreferencedNodesState: Map<string, UnreferencedStateTracker> = new Map();
|
|
272
|
+
// The timeout responsible for closing the container when the session has expired
|
|
273
|
+
private sessionExpiryTimer?: ReturnType<typeof setTimeout>;
|
|
259
274
|
|
|
260
275
|
protected constructor(
|
|
261
276
|
private readonly provider: IGarbageCollectionRuntime,
|
|
@@ -264,6 +279,7 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
264
279
|
private readonly deleteUnusedRoutes: (unusedRoutes: string[]) => void,
|
|
265
280
|
/** Returns the current timestamp to be assigned to nodes that become unreferenced. */
|
|
266
281
|
private readonly getCurrentTimestampMs: () => number,
|
|
282
|
+
private readonly closeFn: (error?: ICriticalContainerError) => void,
|
|
267
283
|
baseSnapshot: ISnapshotTree | undefined,
|
|
268
284
|
readAndParseBlob: ReadAndParseBlob,
|
|
269
285
|
baseLogger: ITelemetryLogger,
|
|
@@ -276,17 +292,35 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
276
292
|
this.deleteTimeoutMs = this.gcOptions.deleteTimeoutMs ?? defaultDeleteTimeoutMs;
|
|
277
293
|
|
|
278
294
|
let prevSummaryGCVersion: number | undefined;
|
|
295
|
+
|
|
279
296
|
// GC can only be enabled during creation. After that, it can never be enabled again. So, for existing
|
|
280
|
-
// documents, we get this information from the metadata blob.
|
|
297
|
+
// documents, we get this information from the metadata blob. Similarly the session timeout should be
|
|
298
|
+
// consistent across all clients, thus we grab it as well from the metadata blob, and set it once on creation.
|
|
281
299
|
if (existing) {
|
|
282
300
|
prevSummaryGCVersion = getGCVersion(metadata);
|
|
283
301
|
// Existing documents which did not have metadata blob or had GC disabled have version as 0. For all
|
|
284
302
|
// other exsiting documents, GC is enabled.
|
|
285
303
|
this.gcEnabled = prevSummaryGCVersion > 0;
|
|
304
|
+
this.sessionExpiryTimeoutMs = metadata?.sessionExpiryTimeoutMs;
|
|
286
305
|
} else {
|
|
287
306
|
// For new documents, GC has to be exlicitly enabled via the gcAllowed flag in GC options.
|
|
288
307
|
this.gcEnabled = gcOptions.gcAllowed === true;
|
|
308
|
+
this.sessionExpiryTimeoutMs = this.gcOptions.gcTestSessionTimeoutMs;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// If session expiry is enabled, we need to close the container when the timeout expires
|
|
312
|
+
if (this.sessionExpiryTimeoutMs !== undefined) {
|
|
313
|
+
// TODO: Change to ClientSessionExpiredError, issue https://github.com/microsoft/FluidFramework/issues/8605
|
|
314
|
+
// this.sessionExpiryTimer = setTimeout(() => this.closeFn(
|
|
315
|
+
// new ClientSessionExpiredError(`Client expired, it has lasted ${this.sessionExpiryTimeoutMs} ms.`,
|
|
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);
|
|
289
322
|
}
|
|
323
|
+
|
|
290
324
|
// For existing document, the latest summary is the one that we loaded from. So, use its GC version as the
|
|
291
325
|
// latest tracked GC version. For new documents, we will be writing the first summary with the current version.
|
|
292
326
|
this.latestSummaryGCVersion = prevSummaryGCVersion ?? this.currentGCVersion;
|
|
@@ -302,7 +336,8 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
302
336
|
// Whether GC sweep phase should run or not. If this is false, only GC mark phase is run. Can override with
|
|
303
337
|
// localStorage flag.
|
|
304
338
|
this.shouldRunSweep = this.shouldRunGC &&
|
|
305
|
-
(this.mc.config.getBoolean(runSweepKey) ?? gcOptions.runSweep === true)
|
|
339
|
+
(this.mc.config.getBoolean(runSweepKey) ?? gcOptions.runSweep === true)
|
|
340
|
+
&& this.sessionExpiryTimer !== undefined;
|
|
306
341
|
|
|
307
342
|
// Whether we are running in test mode. In this mode, unreferenced nodes are immediately deleted.
|
|
308
343
|
this.testMode = this.mc.config.getBoolean(gcTestModeKey) ?? gcOptions.runGCInTestMode === true;
|
|
@@ -339,7 +374,7 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
339
374
|
continue;
|
|
340
375
|
}
|
|
341
376
|
|
|
342
|
-
const gcSummaryDetails = await readAndParseBlob<
|
|
377
|
+
const gcSummaryDetails = await readAndParseBlob<IGarbageCollectionDetailsBase>(blobId);
|
|
343
378
|
// If there are no nodes for this data store, skip it.
|
|
344
379
|
if (gcSummaryDetails.gcData?.gcNodes === undefined) {
|
|
345
380
|
continue;
|
|
@@ -401,8 +436,8 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
401
436
|
});
|
|
402
437
|
|
|
403
438
|
// Get the GC details for each data store from the GC state in the base summary. This is returned in
|
|
404
|
-
// getDataStoreBaseGCDetails and is used to initialize each data store's
|
|
405
|
-
this.dataStoreGCDetailsP = new LazyPromise<Map<string,
|
|
439
|
+
// getDataStoreBaseGCDetails and is used to initialize each data store's GC state.
|
|
440
|
+
this.dataStoreGCDetailsP = new LazyPromise<Map<string, IGarbageCollectionDetailsBase>>(async () => {
|
|
406
441
|
const baseState = await baseSummaryStateP;
|
|
407
442
|
if (baseState === undefined) {
|
|
408
443
|
return new Map();
|
|
@@ -459,6 +494,9 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
459
494
|
return PerformanceEvent.timedExecAsync(logger, { eventName: "GarbageCollection" }, async (event) => {
|
|
460
495
|
await this.initializeBaseStateP;
|
|
461
496
|
|
|
497
|
+
// Let the runtime update its pending state before GC runs.
|
|
498
|
+
await this.provider.updateStateBeforeGC();
|
|
499
|
+
|
|
462
500
|
const gcStats: {
|
|
463
501
|
deletedNodes?: number,
|
|
464
502
|
totalNodes?: number,
|
|
@@ -530,9 +568,9 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
530
568
|
|
|
531
569
|
/**
|
|
532
570
|
* Returns a map of data store ids to their base GC details generated from the base summary.This is used to
|
|
533
|
-
* initialize the
|
|
571
|
+
* initialize the GC state of data stores.
|
|
534
572
|
*/
|
|
535
|
-
public async getDataStoreBaseGCDetails(): Promise<Map<string,
|
|
573
|
+
public async getDataStoreBaseGCDetails(): Promise<Map<string, IGarbageCollectionDetailsBase>> {
|
|
536
574
|
return this.dataStoreGCDetailsP;
|
|
537
575
|
}
|
|
538
576
|
|
|
@@ -574,6 +612,13 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
574
612
|
);
|
|
575
613
|
}
|
|
576
614
|
|
|
615
|
+
public dispose(): void {
|
|
616
|
+
if (this.sessionExpiryTimer !== undefined) {
|
|
617
|
+
clearTimeout(this.sessionExpiryTimer);
|
|
618
|
+
this.sessionExpiryTimer = undefined;
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
|
|
577
622
|
/**
|
|
578
623
|
* Called when an outbound reference is added to a node. This is used to identify all nodes that have been
|
|
579
624
|
* referenced between summaries so that their unreferenced timestamp can be reset.
|
|
@@ -662,6 +707,9 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
662
707
|
return;
|
|
663
708
|
}
|
|
664
709
|
|
|
710
|
+
// Validate that we have identified all references correctly.
|
|
711
|
+
this.validateReferenceCorrectness(currentGCData);
|
|
712
|
+
|
|
665
713
|
/**
|
|
666
714
|
* Generate a super set of the GC data that contains the nodes and edges from last run, plus any new node and
|
|
667
715
|
* edges that have been added since then. To do this, combine the GC data from the last run and the current
|
|
@@ -702,6 +750,60 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
702
750
|
}
|
|
703
751
|
}
|
|
704
752
|
}
|
|
753
|
+
|
|
754
|
+
/**
|
|
755
|
+
* Validates that all new references are correctly identified and processed. The basic principle for validation is
|
|
756
|
+
* that we should not have new references in the reference graph (GC data) that have not been notified to the
|
|
757
|
+
* garbage collector via `referenceAdded`.
|
|
758
|
+
* We validate that the references in the current reference graph should be a subset of the references in the last
|
|
759
|
+
* run's reference graph + references since the last run.
|
|
760
|
+
* @param currentGCData - The GC data (reference graph) from the current GC run.
|
|
761
|
+
*/
|
|
762
|
+
private validateReferenceCorrectness(currentGCData: IGarbageCollectionData) {
|
|
763
|
+
assert(this.gcDataFromLastRun !== undefined, 0x2b7
|
|
764
|
+
/* "Can't validate correctness without GC data from last run" */);
|
|
765
|
+
|
|
766
|
+
// Get a list of all the outbound routes (or references) in the current GC data.
|
|
767
|
+
const currentReferences: string[] = [];
|
|
768
|
+
for (const [nodeId, outboundRoutes] of Object.entries(currentGCData.gcNodes)) {
|
|
769
|
+
/**
|
|
770
|
+
* Remove routes from a child node to its parent which is added implicitly by the runtime. For instance,
|
|
771
|
+
* each adds its data store as an outbound route to mark it as referenced if the DDS is referenced.
|
|
772
|
+
* We won't get any explicit notification for these references so they must be removed before validation.
|
|
773
|
+
*/
|
|
774
|
+
const explicitRoutes = outboundRoutes.filter((route) => !nodeId.startsWith(route));
|
|
775
|
+
currentReferences.push(...explicitRoutes);
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
// Get a list of outbound routes (or references) from the last run's GC data plus references added since the
|
|
779
|
+
// last run that were notified via `referenceAdded`.
|
|
780
|
+
const explicitReferences: string[] = [];
|
|
781
|
+
for (const [, outboundRoutes] of Object.entries(this.gcDataFromLastRun.gcNodes)) {
|
|
782
|
+
explicitReferences.push(...outboundRoutes);
|
|
783
|
+
}
|
|
784
|
+
this.referencesSinceLastRun.forEach((outboundRoutes: string[]) => {
|
|
785
|
+
explicitReferences.push(...outboundRoutes);
|
|
786
|
+
});
|
|
787
|
+
|
|
788
|
+
// Validate that the current reference graph doesn't have references that we are not already aware of. If this
|
|
789
|
+
// happens, it might indicate data corruption since we may delete objects prematurely.
|
|
790
|
+
currentReferences.forEach((route: string) => {
|
|
791
|
+
// Validate references for data stores only whose routes are of the format "/dataStoreId". Currently, layers
|
|
792
|
+
// below data stores don't have GC implemented so there is no guarantee their references will be notified.
|
|
793
|
+
if (route.split("/").length === 2 && !explicitReferences.includes(route)) {
|
|
794
|
+
// We should ideally throw a data corruption error here. However, send an error for now until we have
|
|
795
|
+
// implemented sweep and have reasonable confidence in the sweep process.
|
|
796
|
+
|
|
797
|
+
// To be enabled when this issue is fixed - https://github.com/microsoft/FluidFramework/issues/8672
|
|
798
|
+
/**
|
|
799
|
+
this.mc.logger.sendErrorEvent({
|
|
800
|
+
eventName: "gcUnknownOutboundRoute",
|
|
801
|
+
route,
|
|
802
|
+
});
|
|
803
|
+
*/
|
|
804
|
+
}
|
|
805
|
+
});
|
|
806
|
+
}
|
|
705
807
|
}
|
|
706
808
|
|
|
707
809
|
/**
|
package/src/index.ts
CHANGED
package/src/packageVersion.ts
CHANGED
package/src/summaryFormat.ts
CHANGED
|
@@ -69,7 +69,6 @@ export function getAttributesFormatVersion(attributes: ReadFluidDataStoreAttribu
|
|
|
69
69
|
return 0;
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
-
// eslint-disable-next-line prefer-arrow/prefer-arrow-functions
|
|
73
72
|
export function hasIsolatedChannels(attributes: ReadFluidDataStoreAttributes): boolean {
|
|
74
73
|
return !!attributes.summaryFormatVersion && !attributes.disableIsolatedChannels;
|
|
75
74
|
}
|
|
@@ -85,6 +84,8 @@ export interface IContainerRuntimeMetadata extends ICreateContainerMetadata {
|
|
|
85
84
|
readonly gcFeature?: GCVersion;
|
|
86
85
|
/** Counter of the last summary happened, increments every time we summarize */
|
|
87
86
|
readonly summaryCount?: number;
|
|
87
|
+
/** If this is present, the session for this container will expire after this time and the container will close */
|
|
88
|
+
readonly sessionExpiryTimeoutMs?: number;
|
|
88
89
|
}
|
|
89
90
|
|
|
90
91
|
export interface ICreateContainerMetadata {
|
|
@@ -120,7 +121,6 @@ export const extractSummaryMetadataMessage = (
|
|
|
120
121
|
type: message.type,
|
|
121
122
|
};
|
|
122
123
|
|
|
123
|
-
// eslint-disable-next-line prefer-arrow/prefer-arrow-functions
|
|
124
124
|
export function getMetadataFormatVersion(metadata?: IContainerRuntimeMetadata): number {
|
|
125
125
|
/**
|
|
126
126
|
* Version 2+: Introduces runtime sequence number for data verification.
|
|
@@ -141,7 +141,6 @@ export const chunksBlobName = ".chunks";
|
|
|
141
141
|
export const electedSummarizerBlobName = ".electedSummarizer";
|
|
142
142
|
export const blobsTreeName = ".blobs";
|
|
143
143
|
|
|
144
|
-
// eslint-disable-next-line prefer-arrow/prefer-arrow-functions
|
|
145
144
|
export function rootHasIsolatedChannels(metadata?: IContainerRuntimeMetadata): boolean {
|
|
146
145
|
return !!metadata && !metadata.disableIsolatedChannels;
|
|
147
146
|
}
|
package/src/summaryManager.ts
CHANGED
|
@@ -141,6 +141,9 @@ export class SummaryManager extends TypedEventEmitter<ISummaryManagerEvents> imp
|
|
|
141
141
|
this.refreshSummarizer();
|
|
142
142
|
};
|
|
143
143
|
|
|
144
|
+
private static readonly isStartingOrRunning = (state: SummaryManagerState) =>
|
|
145
|
+
state === SummaryManagerState.Starting || state === SummaryManagerState.Running;
|
|
146
|
+
|
|
144
147
|
private getShouldSummarizeState(): ShouldSummarizeState {
|
|
145
148
|
if (!this.connectedState.connected) {
|
|
146
149
|
return { shouldSummarize: false, stopReason: "parentNotConnected" };
|
|
@@ -253,7 +256,9 @@ export class SummaryManager extends TypedEventEmitter<ISummaryManagerEvents> imp
|
|
|
253
256
|
// Note that summarizer may keep going (like doing last summary).
|
|
254
257
|
// Ideally we await stopping process, but this code path is due to a bug
|
|
255
258
|
// that needs to be fixed either way.
|
|
256
|
-
this.
|
|
259
|
+
if (SummaryManager.isStartingOrRunning(this.state)) {
|
|
260
|
+
this.stop("summarizerException");
|
|
261
|
+
}
|
|
257
262
|
}
|
|
258
263
|
}).finally(() => {
|
|
259
264
|
assert(this.state !== SummaryManagerState.Off, 0x264 /* "Expected: Not Off" */);
|
|
@@ -273,8 +278,9 @@ export class SummaryManager extends TypedEventEmitter<ISummaryManagerEvents> imp
|
|
|
273
278
|
}
|
|
274
279
|
|
|
275
280
|
private stop(reason: SummarizerStopReason) {
|
|
276
|
-
|
|
277
|
-
|
|
281
|
+
if (!SummaryManager.isStartingOrRunning(this.state)) {
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
278
284
|
this.state = SummaryManagerState.Stopping;
|
|
279
285
|
|
|
280
286
|
// Stopping the running summarizer client should trigger a change
|