@fluidframework/container-runtime 2.0.0-internal.1.0.0.83139 → 2.0.0-internal.1.1.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/.mocharc.js +12 -0
- package/dist/batchTracker.js +1 -1
- package/dist/batchTracker.js.map +1 -1
- package/dist/blobManager.d.ts +7 -1
- package/dist/blobManager.d.ts.map +1 -1
- package/dist/blobManager.js +34 -17
- package/dist/blobManager.js.map +1 -1
- package/dist/containerRuntime.d.ts +3 -104
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +83 -395
- package/dist/containerRuntime.js.map +1 -1
- package/dist/dataStore.d.ts +1 -1
- package/dist/dataStore.d.ts.map +1 -1
- package/dist/dataStore.js +2 -3
- package/dist/dataStore.js.map +1 -1
- package/dist/dataStoreContext.d.ts +3 -5
- package/dist/dataStoreContext.d.ts.map +1 -1
- package/dist/dataStoreContext.js +13 -23
- package/dist/dataStoreContext.js.map +1 -1
- package/dist/dataStores.d.ts +1 -1
- package/dist/dataStores.d.ts.map +1 -1
- package/dist/dataStores.js +3 -8
- package/dist/dataStores.js.map +1 -1
- package/dist/garbageCollection.d.ts +37 -6
- package/dist/garbageCollection.d.ts.map +1 -1
- package/dist/garbageCollection.js +61 -65
- package/dist/garbageCollection.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.d.ts.map +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/pendingStateManager.d.ts.map +1 -1
- package/dist/pendingStateManager.js +15 -2
- package/dist/pendingStateManager.js.map +1 -1
- package/dist/runningSummarizer.js +1 -1
- package/dist/runningSummarizer.js.map +1 -1
- package/dist/scheduleManager.d.ts +28 -0
- package/dist/scheduleManager.d.ts.map +1 -0
- package/dist/scheduleManager.js +235 -0
- package/dist/scheduleManager.js.map +1 -0
- package/dist/summarizer.d.ts.map +1 -1
- package/dist/summarizer.js +20 -1
- package/dist/summarizer.js.map +1 -1
- package/dist/summaryCollection.js +1 -1
- package/dist/summaryCollection.js.map +1 -1
- package/dist/summaryGenerator.js +1 -1
- package/dist/summaryGenerator.js.map +1 -1
- package/dist/summaryManager.d.ts.map +1 -1
- package/dist/summaryManager.js +20 -5
- package/dist/summaryManager.js.map +1 -1
- package/lib/batchTracker.js +1 -1
- package/lib/batchTracker.js.map +1 -1
- package/lib/blobManager.d.ts +7 -1
- package/lib/blobManager.d.ts.map +1 -1
- package/lib/blobManager.js +35 -18
- package/lib/blobManager.js.map +1 -1
- package/lib/containerRuntime.d.ts +3 -104
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +84 -395
- package/lib/containerRuntime.js.map +1 -1
- package/lib/dataStore.d.ts +1 -1
- package/lib/dataStore.d.ts.map +1 -1
- package/lib/dataStore.js +2 -3
- package/lib/dataStore.js.map +1 -1
- package/lib/dataStoreContext.d.ts +3 -5
- package/lib/dataStoreContext.d.ts.map +1 -1
- package/lib/dataStoreContext.js +13 -23
- package/lib/dataStoreContext.js.map +1 -1
- package/lib/dataStores.d.ts +1 -1
- package/lib/dataStores.d.ts.map +1 -1
- package/lib/dataStores.js +3 -8
- package/lib/dataStores.js.map +1 -1
- package/lib/garbageCollection.d.ts +37 -6
- package/lib/garbageCollection.d.ts.map +1 -1
- package/lib/garbageCollection.js +47 -52
- package/lib/garbageCollection.js.map +1 -1
- package/lib/index.d.ts +2 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +2 -1
- package/lib/index.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.d.ts.map +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/pendingStateManager.d.ts.map +1 -1
- package/lib/pendingStateManager.js +15 -2
- package/lib/pendingStateManager.js.map +1 -1
- package/lib/runningSummarizer.js +1 -1
- package/lib/runningSummarizer.js.map +1 -1
- package/lib/scheduleManager.d.ts +28 -0
- package/lib/scheduleManager.d.ts.map +1 -0
- package/lib/scheduleManager.js +231 -0
- package/lib/scheduleManager.js.map +1 -0
- package/lib/summarizer.d.ts.map +1 -1
- package/lib/summarizer.js +22 -3
- package/lib/summarizer.js.map +1 -1
- package/lib/summaryCollection.js +1 -1
- package/lib/summaryCollection.js.map +1 -1
- package/lib/summaryGenerator.js +1 -1
- package/lib/summaryGenerator.js.map +1 -1
- package/lib/summaryManager.d.ts.map +1 -1
- package/lib/summaryManager.js +20 -5
- package/lib/summaryManager.js.map +1 -1
- package/package.json +32 -19
- package/src/batchTracker.ts +1 -1
- package/src/blobManager.ts +43 -17
- package/src/containerRuntime.ts +113 -547
- package/src/dataStore.ts +1 -4
- package/src/dataStoreContext.ts +10 -25
- package/src/dataStores.ts +13 -19
- package/src/garbageCollection.ts +64 -69
- package/src/index.ts +1 -2
- package/src/packageVersion.ts +1 -1
- package/src/pendingStateManager.ts +18 -2
- package/src/runningSummarizer.ts +1 -1
- package/src/scheduleManager.ts +294 -0
- package/src/summarizer.ts +28 -3
- package/src/summaryCollection.ts +1 -1
- package/src/summaryGenerator.ts +1 -1
- package/src/summaryManager.ts +20 -5
package/src/dataStore.ts
CHANGED
|
@@ -43,8 +43,7 @@ export const channelToDataStore = (
|
|
|
43
43
|
runtime: ContainerRuntime,
|
|
44
44
|
datastores: DataStores,
|
|
45
45
|
logger: ITelemetryLogger,
|
|
46
|
-
|
|
47
|
-
): IDataStore => new DataStore(fluidDataStoreChannel, internalId, runtime, datastores, logger, alreadyAliased);
|
|
46
|
+
): IDataStore => new DataStore(fluidDataStoreChannel, internalId, runtime, datastores, logger);
|
|
48
47
|
|
|
49
48
|
enum AliasState {
|
|
50
49
|
Aliased = "Aliased",
|
|
@@ -158,10 +157,8 @@ class DataStore implements IDataStore {
|
|
|
158
157
|
private readonly runtime: ContainerRuntime,
|
|
159
158
|
private readonly datastores: DataStores,
|
|
160
159
|
private readonly logger: ITelemetryLogger,
|
|
161
|
-
alreadyAliased: boolean,
|
|
162
160
|
) {
|
|
163
161
|
this.pendingAliases = datastores.pendingAliases;
|
|
164
|
-
this.aliasState = alreadyAliased ? AliasState.Aliased : AliasState.None;
|
|
165
162
|
}
|
|
166
163
|
|
|
167
164
|
public get IFluidRouter() { return this.fluidDataStoreChannel; }
|
package/src/dataStoreContext.ts
CHANGED
|
@@ -82,14 +82,9 @@ import {
|
|
|
82
82
|
function createAttributes(
|
|
83
83
|
pkg: readonly string[],
|
|
84
84
|
isRootDataStore: boolean,
|
|
85
|
-
disableIsolatedChannels: boolean,
|
|
86
85
|
): WriteFluidDataStoreAttributes {
|
|
87
86
|
const stringifiedPkg = JSON.stringify(pkg);
|
|
88
|
-
return
|
|
89
|
-
pkg: stringifiedPkg,
|
|
90
|
-
snapshotFormatVersion: "0.1",
|
|
91
|
-
isRootDataStore,
|
|
92
|
-
} : {
|
|
87
|
+
return {
|
|
93
88
|
pkg: stringifiedPkg,
|
|
94
89
|
summaryFormatVersion: 2,
|
|
95
90
|
isRootDataStore,
|
|
@@ -98,9 +93,8 @@ function createAttributes(
|
|
|
98
93
|
export function createAttributesBlob(
|
|
99
94
|
pkg: readonly string[],
|
|
100
95
|
isRootDataStore: boolean,
|
|
101
|
-
disableIsolatedChannels: boolean,
|
|
102
96
|
): ITreeEntry {
|
|
103
|
-
const attributes = createAttributes(pkg, isRootDataStore
|
|
97
|
+
const attributes = createAttributes(pkg, isRootDataStore);
|
|
104
98
|
return new BlobTreeEntry(dataStoreAttributesBlobName, JSON.stringify(attributes));
|
|
105
99
|
}
|
|
106
100
|
|
|
@@ -123,7 +117,6 @@ export interface IFluidDataStoreContextProps {
|
|
|
123
117
|
readonly scope: FluidObject;
|
|
124
118
|
readonly createSummarizerNodeFn: CreateChildSummarizerNodeFn;
|
|
125
119
|
readonly writeGCDataAtRoot: boolean;
|
|
126
|
-
readonly disableIsolatedChannels: boolean;
|
|
127
120
|
readonly pkg?: Readonly<string[]>;
|
|
128
121
|
}
|
|
129
122
|
|
|
@@ -255,7 +248,6 @@ export abstract class FluidDataStoreContext extends TypedEventEmitter<IFluidData
|
|
|
255
248
|
public readonly storage: IDocumentStorageService;
|
|
256
249
|
public readonly scope: FluidObject;
|
|
257
250
|
private readonly writeGCDataAtRoot: boolean;
|
|
258
|
-
protected readonly disableIsolatedChannels: boolean;
|
|
259
251
|
protected pkg?: readonly string[];
|
|
260
252
|
|
|
261
253
|
constructor(
|
|
@@ -271,7 +263,6 @@ export abstract class FluidDataStoreContext extends TypedEventEmitter<IFluidData
|
|
|
271
263
|
this.storage = props.storage;
|
|
272
264
|
this.scope = props.scope;
|
|
273
265
|
this.writeGCDataAtRoot = props.writeGCDataAtRoot;
|
|
274
|
-
this.disableIsolatedChannels = props.disableIsolatedChannels;
|
|
275
266
|
this.pkg = props.pkg;
|
|
276
267
|
|
|
277
268
|
// URIs use slashes as delimiters. Handles use URIs.
|
|
@@ -387,8 +378,8 @@ export abstract class FluidDataStoreContext extends TypedEventEmitter<IFluidData
|
|
|
387
378
|
/**
|
|
388
379
|
* Notifies this object about changes in the connection state.
|
|
389
380
|
* @param value - New connection state.
|
|
390
|
-
* @param clientId - ID of the client.
|
|
391
|
-
*
|
|
381
|
+
* @param clientId - ID of the client. Its old ID when in disconnected state and
|
|
382
|
+
* its new client ID when we are connecting or connected.
|
|
392
383
|
*/
|
|
393
384
|
public setConnectionState(connected: boolean, clientId?: string) {
|
|
394
385
|
this.verifyNotClosed();
|
|
@@ -468,18 +459,15 @@ export abstract class FluidDataStoreContext extends TypedEventEmitter<IFluidData
|
|
|
468
459
|
|
|
469
460
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
470
461
|
const summarizeResult = await this.channel!.summarize(fullTree, trackState, telemetryContext);
|
|
471
|
-
let pathPartsForChildren: string[] | undefined;
|
|
472
462
|
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
pathPartsForChildren = [channelsTreeName];
|
|
477
|
-
}
|
|
463
|
+
// Wrap dds summaries in .channels subtree.
|
|
464
|
+
wrapSummaryInChannelsTree(summarizeResult);
|
|
465
|
+
const pathPartsForChildren = [channelsTreeName];
|
|
478
466
|
|
|
479
467
|
// Add data store's attributes to the summary.
|
|
480
468
|
const { pkg } = await this.getInitialSnapshotDetails();
|
|
481
469
|
const isRoot = await this.isRoot();
|
|
482
|
-
const attributes = createAttributes(pkg, isRoot
|
|
470
|
+
const attributes = createAttributes(pkg, isRoot);
|
|
483
471
|
addBlobToSummary(summarizeResult, dataStoreAttributesBlobName, JSON.stringify(attributes));
|
|
484
472
|
|
|
485
473
|
// Add GC data to the summary if it's not written at the root.
|
|
@@ -921,16 +909,13 @@ export class LocalFluidDataStoreContextBase extends FluidDataStoreContext {
|
|
|
921
909
|
|
|
922
910
|
const summarizeResult = this.channel.getAttachSummary();
|
|
923
911
|
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
wrapSummaryInChannelsTree(summarizeResult);
|
|
927
|
-
}
|
|
912
|
+
// Wrap dds summaries in .channels subtree.
|
|
913
|
+
wrapSummaryInChannelsTree(summarizeResult);
|
|
928
914
|
|
|
929
915
|
// Add data store's attributes to the summary.
|
|
930
916
|
const attributes = createAttributes(
|
|
931
917
|
this.pkg,
|
|
932
918
|
this.isInMemoryRoot(),
|
|
933
|
-
this.disableIsolatedChannels,
|
|
934
919
|
);
|
|
935
920
|
addBlobToSummary(summarizeResult, dataStoreAttributesBlobName, JSON.stringify(attributes));
|
|
936
921
|
|
package/src/dataStores.ts
CHANGED
|
@@ -29,11 +29,11 @@ import {
|
|
|
29
29
|
ITelemetryContext,
|
|
30
30
|
} from "@fluidframework/runtime-definitions";
|
|
31
31
|
import {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
32
|
+
convertSnapshotTreeToSummaryTree,
|
|
33
|
+
convertToSummaryTree,
|
|
34
|
+
create404Response,
|
|
35
|
+
responseToException,
|
|
36
|
+
SummaryTreeBuilder,
|
|
37
37
|
} from "@fluidframework/runtime-utils";
|
|
38
38
|
import { ChildLogger, LoggingError, TelemetryDataTag } from "@fluidframework/telemetry-utils";
|
|
39
39
|
import { AttachState } from "@fluidframework/container-definitions";
|
|
@@ -56,10 +56,10 @@ import { GCNodeType } from "./garbageCollection";
|
|
|
56
56
|
|
|
57
57
|
type PendingAliasResolve = (success: boolean) => void;
|
|
58
58
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
59
|
+
/**
|
|
60
|
+
* This class encapsulates data store handling. Currently it is only used by the container runtime,
|
|
61
|
+
* but eventually could be hosted on any channel once we formalize the channel api boundary.
|
|
62
|
+
*/
|
|
63
63
|
export class DataStores implements IDisposable {
|
|
64
64
|
// Stores tracked by the Domain
|
|
65
65
|
private readonly pendingAttach = new Map<string, IAttachMessage>();
|
|
@@ -143,7 +143,6 @@ export class DataStores implements IDisposable {
|
|
|
143
143
|
{ type: CreateSummarizerNodeSource.FromSummary },
|
|
144
144
|
),
|
|
145
145
|
writeGCDataAtRoot: this.writeGCDataAtRoot,
|
|
146
|
-
disableIsolatedChannels: this.runtime.disableIsolatedChannels,
|
|
147
146
|
});
|
|
148
147
|
} else {
|
|
149
148
|
if (typeof value !== "object") {
|
|
@@ -164,7 +163,6 @@ export class DataStores implements IDisposable {
|
|
|
164
163
|
snapshotTree,
|
|
165
164
|
isRootDataStore: undefined,
|
|
166
165
|
writeGCDataAtRoot: this.writeGCDataAtRoot,
|
|
167
|
-
disableIsolatedChannels: this.runtime.disableIsolatedChannels,
|
|
168
166
|
});
|
|
169
167
|
}
|
|
170
168
|
this.contexts.addBoundOrRemoted(dataStoreContext);
|
|
@@ -202,7 +200,7 @@ export class DataStores implements IDisposable {
|
|
|
202
200
|
return;
|
|
203
201
|
}
|
|
204
202
|
|
|
205
|
-
|
|
203
|
+
// If a non-local operation then go and create the object, otherwise mark it as officially attached.
|
|
206
204
|
if (this.alreadyProcessed(attachMessage.id)) {
|
|
207
205
|
// TODO: dataStoreId may require a different tag from PackageData #7488
|
|
208
206
|
const error = new DataCorruptionError(
|
|
@@ -245,13 +243,11 @@ export class DataStores implements IDisposable {
|
|
|
245
243
|
entries: [createAttributesBlob(
|
|
246
244
|
pkg,
|
|
247
245
|
true /* isRootDataStore */,
|
|
248
|
-
this.runtime.disableIsolatedChannels,
|
|
249
246
|
)],
|
|
250
247
|
},
|
|
251
248
|
},
|
|
252
249
|
),
|
|
253
250
|
writeGCDataAtRoot: this.writeGCDataAtRoot,
|
|
254
|
-
disableIsolatedChannels: this.runtime.disableIsolatedChannels,
|
|
255
251
|
pkg,
|
|
256
252
|
});
|
|
257
253
|
|
|
@@ -356,13 +352,12 @@ export class DataStores implements IDisposable {
|
|
|
356
352
|
snapshotTree: undefined,
|
|
357
353
|
isRootDataStore: isRoot,
|
|
358
354
|
writeGCDataAtRoot: this.writeGCDataAtRoot,
|
|
359
|
-
disableIsolatedChannels: this.runtime.disableIsolatedChannels,
|
|
360
355
|
});
|
|
361
356
|
this.contexts.addUnbound(context);
|
|
362
357
|
return context;
|
|
363
358
|
}
|
|
364
359
|
|
|
365
|
-
public _createFluidDataStoreContext(pkg: string[], id: string,
|
|
360
|
+
public _createFluidDataStoreContext(pkg: string[], id: string, props?: any) {
|
|
366
361
|
assert(!id.includes("/"), 0x30d /* Id cannot contain slashes */);
|
|
367
362
|
const context = new LocalFluidDataStoreContext({
|
|
368
363
|
id,
|
|
@@ -376,9 +371,8 @@ export class DataStores implements IDisposable {
|
|
|
376
371
|
),
|
|
377
372
|
makeLocallyVisibleFn: () => this.makeDataStoreLocallyVisible(id),
|
|
378
373
|
snapshotTree: undefined,
|
|
379
|
-
isRootDataStore:
|
|
374
|
+
isRootDataStore: false,
|
|
380
375
|
writeGCDataAtRoot: this.writeGCDataAtRoot,
|
|
381
|
-
disableIsolatedChannels: this.runtime.disableIsolatedChannels,
|
|
382
376
|
createProps: props,
|
|
383
377
|
});
|
|
384
378
|
this.contexts.addUnbound(context);
|
|
@@ -481,7 +475,7 @@ export class DataStores implements IDisposable {
|
|
|
481
475
|
} else {
|
|
482
476
|
eventName = "attached";
|
|
483
477
|
}
|
|
484
|
-
for (const [,context] of this.contexts) {
|
|
478
|
+
for (const [, context] of this.contexts) {
|
|
485
479
|
// Fire only for bounded stores.
|
|
486
480
|
if (!this.contexts.isNotBound(context.id)) {
|
|
487
481
|
context.emit(eventName);
|
package/src/garbageCollection.ts
CHANGED
|
@@ -61,11 +61,11 @@ export const gcTreeKey = "gc";
|
|
|
61
61
|
export const gcBlobPrefix = "__gc";
|
|
62
62
|
|
|
63
63
|
// Feature gate key to turn GC on / off.
|
|
64
|
-
const runGCKey = "Fluid.GarbageCollection.RunGC";
|
|
64
|
+
export const runGCKey = "Fluid.GarbageCollection.RunGC";
|
|
65
65
|
// Feature gate key to turn GC sweep on / off.
|
|
66
|
-
|
|
66
|
+
export const runSweepKey = "Fluid.GarbageCollection.RunSweep";
|
|
67
67
|
// Feature gate key to turn GC test mode on / off.
|
|
68
|
-
const gcTestModeKey = "Fluid.GarbageCollection.GCTestMode";
|
|
68
|
+
export const gcTestModeKey = "Fluid.GarbageCollection.GCTestMode";
|
|
69
69
|
// Feature gate key to write GC data at the root of the summary tree.
|
|
70
70
|
const writeAtRootKey = "Fluid.GarbageCollection.WriteDataAtRoot";
|
|
71
71
|
// Feature gate key to expire a session after a set period of time.
|
|
@@ -80,7 +80,7 @@ const disableSweepLogKey = "Fluid.GarbageCollection.DisableSweepLog";
|
|
|
80
80
|
// One day in milliseconds.
|
|
81
81
|
export const oneDayMs = 1 * 24 * 60 * 60 * 1000;
|
|
82
82
|
|
|
83
|
-
const defaultInactiveTimeoutMs = 7 * oneDayMs; // 7 days
|
|
83
|
+
export const defaultInactiveTimeoutMs = 7 * oneDayMs; // 7 days
|
|
84
84
|
export const defaultSessionExpiryDurationMs = 30 * oneDayMs; // 30 days
|
|
85
85
|
|
|
86
86
|
/** The statistics of the system state after a garbage collection run. */
|
|
@@ -190,14 +190,14 @@ export interface IGarbageCollectorCreateParams {
|
|
|
190
190
|
}
|
|
191
191
|
|
|
192
192
|
/** The state of node that is unreferenced. */
|
|
193
|
-
const UnreferencedState = {
|
|
193
|
+
export const UnreferencedState = {
|
|
194
194
|
/** The node is active, i.e., it can become referenced again. */
|
|
195
195
|
Active: "Active",
|
|
196
196
|
/** The node is inactive, i.e., it should not become referenced. */
|
|
197
197
|
Inactive: "Inactive",
|
|
198
198
|
/** The node is ready to be deleted by the sweep phase. */
|
|
199
199
|
SweepReady: "SweepReady",
|
|
200
|
-
};
|
|
200
|
+
} as const;
|
|
201
201
|
export type UnreferencedState = typeof UnreferencedState[keyof typeof UnreferencedState];
|
|
202
202
|
|
|
203
203
|
/** The event that is logged when unreferenced node is used after a certain time. */
|
|
@@ -220,14 +220,16 @@ interface IUnreferencedEventProps {
|
|
|
220
220
|
* Helper class that tracks the state of an unreferenced node such as the time it was unreferenced and if it can
|
|
221
221
|
* be deleted by the sweep phase.
|
|
222
222
|
*/
|
|
223
|
-
class UnreferencedStateTracker {
|
|
223
|
+
export class UnreferencedStateTracker {
|
|
224
224
|
private _state: UnreferencedState = UnreferencedState.Active;
|
|
225
225
|
public get state(): UnreferencedState {
|
|
226
226
|
return this._state;
|
|
227
227
|
}
|
|
228
228
|
|
|
229
|
-
|
|
230
|
-
private
|
|
229
|
+
/** Timer to indicate when an unreferenced object is considered Inactive */
|
|
230
|
+
private readonly inactiveTimer: TimerWithNoDefaultTimeout;
|
|
231
|
+
/** Timer to indicate when an unreferenced object is Sweep-Ready */
|
|
232
|
+
private readonly sweepTimer: TimerWithNoDefaultTimeout;
|
|
231
233
|
|
|
232
234
|
constructor(
|
|
233
235
|
public readonly unreferencedTimestampMs: number,
|
|
@@ -238,8 +240,29 @@ class UnreferencedStateTracker {
|
|
|
238
240
|
/** The current reference timestamp; undefined if no ops have ever been processed which can happen in tests. */
|
|
239
241
|
currentReferenceTimestampMs?: number,
|
|
240
242
|
) {
|
|
241
|
-
|
|
242
|
-
|
|
243
|
+
if (this.sweepTimeoutMs !== undefined) {
|
|
244
|
+
assert(this.inactiveTimeoutMs <= this.sweepTimeoutMs,
|
|
245
|
+
0x3b0 /* inactive timeout must not be greater than the sweep timeout */);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
this.sweepTimer = new TimerWithNoDefaultTimeout(
|
|
249
|
+
() => {
|
|
250
|
+
this._state = UnreferencedState.SweepReady;
|
|
251
|
+
assert(!this.inactiveTimer.hasTimer, 0x3b1 /* inactiveTimer still running after sweepTimer fired! */);
|
|
252
|
+
},
|
|
253
|
+
);
|
|
254
|
+
|
|
255
|
+
this.inactiveTimer = new TimerWithNoDefaultTimeout(() => {
|
|
256
|
+
this._state = UnreferencedState.Inactive;
|
|
257
|
+
|
|
258
|
+
// After the node becomes inactive, start the sweep timer after which the node will be ready for sweep.
|
|
259
|
+
if (this.sweepTimeoutMs !== undefined) {
|
|
260
|
+
this.sweepTimer.restart(this.sweepTimeoutMs - this.inactiveTimeoutMs);
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
// If there is no current reference timestamp, can't track the node's unreferenced state at this time.
|
|
265
|
+
// This will happen later when updateTracking is called with a reference timestamp.
|
|
243
266
|
if (currentReferenceTimestampMs !== undefined) {
|
|
244
267
|
this.updateTracking(currentReferenceTimestampMs);
|
|
245
268
|
}
|
|
@@ -260,42 +283,21 @@ class UnreferencedStateTracker {
|
|
|
260
283
|
// Also, start a timer for the sweep timeout.
|
|
261
284
|
if (unreferencedDurationMs >= this.inactiveTimeoutMs) {
|
|
262
285
|
this._state = UnreferencedState.Inactive;
|
|
263
|
-
this.
|
|
286
|
+
this.inactiveTimer.clear();
|
|
264
287
|
|
|
265
288
|
if (this.sweepTimeoutMs !== undefined) {
|
|
266
|
-
|
|
267
|
-
this.sweepTimeoutMs - unreferencedDurationMs,
|
|
268
|
-
() => { this._state = UnreferencedState.SweepReady; },
|
|
269
|
-
(timer) => { this.sweepTimer = timer; },
|
|
270
|
-
);
|
|
289
|
+
this.sweepTimer.restart(this.sweepTimeoutMs - unreferencedDurationMs);
|
|
271
290
|
}
|
|
272
291
|
return;
|
|
273
292
|
}
|
|
274
293
|
|
|
275
|
-
// The node is still active.
|
|
276
|
-
|
|
277
|
-
if (this.inactiveTimer === undefined) {
|
|
278
|
-
const inactiveTimeoutHandler = () => {
|
|
279
|
-
this._state = UnreferencedState.Inactive;
|
|
280
|
-
// After the node becomes inactive, start the sweep timer after which the node will be ready for sweep.
|
|
281
|
-
if (this.sweepTimeoutMs !== undefined) {
|
|
282
|
-
setLongTimeout(
|
|
283
|
-
this.sweepTimeoutMs - this.inactiveTimeoutMs,
|
|
284
|
-
() => { this._state = UnreferencedState.SweepReady; },
|
|
285
|
-
(timer) => { this.sweepTimer = timer; },
|
|
286
|
-
);
|
|
287
|
-
}
|
|
288
|
-
};
|
|
289
|
-
this.inactiveTimer = new Timer(remainingDurationMs, () => inactiveTimeoutHandler());
|
|
290
|
-
}
|
|
291
|
-
this.inactiveTimer.restart(remainingDurationMs);
|
|
294
|
+
// The node is still active. Ensure the inactive timer is running with the proper remaining duration.
|
|
295
|
+
this.inactiveTimer.restart(this.inactiveTimeoutMs - unreferencedDurationMs);
|
|
292
296
|
}
|
|
293
297
|
|
|
294
298
|
private clearTimers() {
|
|
295
|
-
this.inactiveTimer
|
|
296
|
-
|
|
297
|
-
clearTimeout(this.sweepTimer);
|
|
298
|
-
}
|
|
299
|
+
this.inactiveTimer.clear();
|
|
300
|
+
this.sweepTimer.clear();
|
|
299
301
|
}
|
|
300
302
|
|
|
301
303
|
/** Stop tracking this node. Reset the unreferenced timers and state, if any. */
|
|
@@ -412,8 +414,8 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
412
414
|
private readonly baseGCDetailsP: Promise<Map<string, IGarbageCollectionDetailsBase>>;
|
|
413
415
|
// Map of node ids to their unreferenced state tracker.
|
|
414
416
|
private readonly unreferencedNodesState: Map<string, UnreferencedStateTracker> = new Map();
|
|
415
|
-
// The
|
|
416
|
-
private sessionExpiryTimer
|
|
417
|
+
// The Timer responsible for closing the container when the session has expired
|
|
418
|
+
private sessionExpiryTimer: Timer | undefined;
|
|
417
419
|
|
|
418
420
|
// Keeps track of unreferenced events that are logged for a node. This is used to limit the log generation to one
|
|
419
421
|
// per event per node.
|
|
@@ -484,7 +486,7 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
484
486
|
// The sweep phase has to be explicitly enabled by setting the sweepAllowed flag in GC options to true.
|
|
485
487
|
this.sweepEnabled = this.gcOptions.sweepAllowed === true;
|
|
486
488
|
|
|
487
|
-
// Set the Session Expiry only if the flag is enabled
|
|
489
|
+
// Set the Session Expiry only if the flag is enabled and GC is enabled.
|
|
488
490
|
if (this.mc.config.getBoolean(runSessionExpiryKey) && this.gcEnabled) {
|
|
489
491
|
this.sessionExpiryTimeoutMs = this.gcOptions.sessionExpiryTimeoutMs ?? defaultSessionExpiryDurationMs;
|
|
490
492
|
}
|
|
@@ -497,11 +499,11 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
497
499
|
this.mc.config.getNumber("Fluid.GarbageCollection.TestOverride.SessionExpiryMs");
|
|
498
500
|
const timeoutMs = overrideSessionExpiryTimeoutMs ?? this.sessionExpiryTimeoutMs;
|
|
499
501
|
|
|
500
|
-
|
|
502
|
+
this.sessionExpiryTimer = new Timer(
|
|
501
503
|
timeoutMs,
|
|
502
504
|
() => { this.runtime.closeFn(new ClientSessionExpiredError(`Client session expired.`, timeoutMs)); },
|
|
503
|
-
(timer) => { this.sessionExpiryTimer = timer; },
|
|
504
505
|
);
|
|
506
|
+
this.sessionExpiryTimer.start();
|
|
505
507
|
|
|
506
508
|
// TEMPORARY: Hardcode a default of 2 days which is the value used in the ODSP driver.
|
|
507
509
|
// This unblocks the Sweep Log (see logSweepEvents function).
|
|
@@ -557,7 +559,7 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
557
559
|
|
|
558
560
|
// Inactive timeout must be greater than sweep timeout since a node goes from active -> inactive -> sweep ready.
|
|
559
561
|
if (this.sweepTimeoutMs !== undefined && this.inactiveTimeoutMs > this.sweepTimeoutMs) {
|
|
560
|
-
throw new UsageError("inactive timeout should not be
|
|
562
|
+
throw new UsageError("inactive timeout should not be greater than the sweep timeout");
|
|
561
563
|
}
|
|
562
564
|
|
|
563
565
|
// Whether we are running in test mode. In this mode, unreferenced nodes are immediately deleted.
|
|
@@ -688,7 +690,7 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
688
690
|
const usedRoutes = runGarbageCollection(gcNodes, ["/"]).referencedNodeIds;
|
|
689
691
|
|
|
690
692
|
const baseGCDetailsMap = unpackChildNodesGCDetails({ gcData: { gcNodes }, usedRoutes });
|
|
691
|
-
// Currently, the nodes may write the GC data. So, we need to update
|
|
693
|
+
// Currently, the nodes may write the GC data. So, we need to update its base GC details with the
|
|
692
694
|
// unreferenced timestamp. Once we start writing the GC data here, we won't need to do this anymore.
|
|
693
695
|
for (const [nodeId, nodeData] of Object.entries(baseState.gcNodes)) {
|
|
694
696
|
if (nodeData.unreferencedTimestampMs !== undefined) {
|
|
@@ -986,10 +988,8 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
986
988
|
}
|
|
987
989
|
|
|
988
990
|
public dispose(): void {
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
this.sessionExpiryTimer = undefined;
|
|
992
|
-
}
|
|
991
|
+
this.sessionExpiryTimer?.clear();
|
|
992
|
+
this.sessionExpiryTimer = undefined;
|
|
993
993
|
}
|
|
994
994
|
|
|
995
995
|
/**
|
|
@@ -1416,25 +1416,20 @@ function generateSortedGCState(gcState: IGarbageCollectionState): IGarbageCollec
|
|
|
1416
1416
|
return sortedGCState;
|
|
1417
1417
|
}
|
|
1418
1418
|
|
|
1419
|
-
/**
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
)
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
const newTimeoutMs = timeoutMs - maxTimeout;
|
|
1435
|
-
timer = setTimeout(() => setLongTimeout(newTimeoutMs, timeoutFn, setTimerFn), maxTimeout);
|
|
1436
|
-
} else {
|
|
1437
|
-
timer = setTimeout(() => timeoutFn(), timeoutMs);
|
|
1419
|
+
/** A wrapper around common-utils Timer that requires the timeout when calling start/restart */
|
|
1420
|
+
class TimerWithNoDefaultTimeout extends Timer {
|
|
1421
|
+
constructor(
|
|
1422
|
+
private readonly callback: () => void,
|
|
1423
|
+
) {
|
|
1424
|
+
// The default timeout/handlers will never be used since start/restart pass overrides below
|
|
1425
|
+
super(0, () => { throw new Error("DefaultHandler should not be used"); });
|
|
1426
|
+
}
|
|
1427
|
+
|
|
1428
|
+
start(timeoutMs: number) {
|
|
1429
|
+
super.start(timeoutMs, this.callback);
|
|
1430
|
+
}
|
|
1431
|
+
|
|
1432
|
+
restart(timeoutMs: number): void {
|
|
1433
|
+
super.restart(timeoutMs, this.callback);
|
|
1438
1434
|
}
|
|
1439
|
-
setTimerFn(timer);
|
|
1440
1435
|
}
|
package/src/index.ts
CHANGED
|
@@ -14,12 +14,10 @@ export {
|
|
|
14
14
|
ISummaryConfigurationDisableSummarizer,
|
|
15
15
|
ISummaryConfigurationDisableHeuristics,
|
|
16
16
|
IContainerRuntimeOptions,
|
|
17
|
-
IPendingRuntimeState,
|
|
18
17
|
IRootSummaryTreeWithStats,
|
|
19
18
|
isRuntimeMessage,
|
|
20
19
|
RuntimeMessage,
|
|
21
20
|
unpackRuntimeMessage,
|
|
22
|
-
ScheduleManager,
|
|
23
21
|
agentSchedulerId,
|
|
24
22
|
ContainerRuntime,
|
|
25
23
|
RuntimeHeaders,
|
|
@@ -41,6 +39,7 @@ export {
|
|
|
41
39
|
IPendingMessage,
|
|
42
40
|
IPendingState,
|
|
43
41
|
} from "./pendingStateManager";
|
|
42
|
+
export { ScheduleManager } from "./scheduleManager";
|
|
44
43
|
export { Summarizer } from "./summarizer";
|
|
45
44
|
export {
|
|
46
45
|
EnqueueSummarizeResult,
|
package/src/packageVersion.ts
CHANGED
|
@@ -14,6 +14,7 @@ import { FlushMode } from "@fluidframework/runtime-definitions";
|
|
|
14
14
|
import { wrapError } from "@fluidframework/telemetry-utils";
|
|
15
15
|
import Deque from "double-ended-queue";
|
|
16
16
|
import { ContainerMessageType } from "./containerRuntime";
|
|
17
|
+
import { pkgVersion } from "./packageVersion";
|
|
17
18
|
|
|
18
19
|
/**
|
|
19
20
|
* This represents a message that has been submitted and is added to the pending queue when `submit` is called on the
|
|
@@ -368,8 +369,23 @@ export class PendingStateManager implements IDisposable {
|
|
|
368
369
|
} else {
|
|
369
370
|
// Get the batch metadata from the last message in the batch.
|
|
370
371
|
const batchEndMetadata = message.metadata?.batch;
|
|
371
|
-
|
|
372
|
-
|
|
372
|
+
if (batchBeginMetadata !== true || batchEndMetadata !== false) {
|
|
373
|
+
this.stateHandler.close(DataProcessingError.create(
|
|
374
|
+
"Pending batch inconsistency", // Formerly known as asserts 0x16f and 0x170
|
|
375
|
+
"processPendingLocalMessage",
|
|
376
|
+
message,
|
|
377
|
+
{
|
|
378
|
+
runtimeVersion: pkgVersion,
|
|
379
|
+
batchClientId: this.pendingBatchBeginMessage.clientId,
|
|
380
|
+
clientId: this.stateHandler.clientId(),
|
|
381
|
+
hasBatchStart: batchBeginMetadata === true,
|
|
382
|
+
hasBatchEnd: batchEndMetadata === false,
|
|
383
|
+
messageType: message.type,
|
|
384
|
+
batchStartSequenceNumber: this.pendingBatchBeginMessage.clientSequenceNumber,
|
|
385
|
+
pendingMessagesCount: this.pendingMessagesCount,
|
|
386
|
+
nextPendingState: nextPendingState.type,
|
|
387
|
+
}));
|
|
388
|
+
}
|
|
373
389
|
}
|
|
374
390
|
|
|
375
391
|
// Clear the pending batch state now that we have processed the entire batch.
|
package/src/runningSummarizer.ts
CHANGED
|
@@ -343,7 +343,7 @@ export class RunningSummarizer implements IDisposable {
|
|
|
343
343
|
*/
|
|
344
344
|
public async lockedRefreshSummaryAckAction<T>(action: () => Promise<T>) {
|
|
345
345
|
assert(this.refreshSummaryAckLock === undefined,
|
|
346
|
-
|
|
346
|
+
0x396 /* Refresh Summary Ack - Caller is responsible for checking lock */);
|
|
347
347
|
|
|
348
348
|
const refreshSummaryAckLock = new Deferred<void>();
|
|
349
349
|
this.refreshSummaryAckLock = refreshSummaryAckLock.promise;
|