@fluidframework/container-runtime 0.52.0 → 0.54.0-47413
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/containerHandleContext.d.ts +0 -1
- package/dist/containerHandleContext.d.ts.map +1 -1
- package/dist/containerHandleContext.js +0 -1
- package/dist/containerHandleContext.js.map +1 -1
- package/dist/containerRuntime.d.ts +43 -19
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +201 -111
- package/dist/containerRuntime.js.map +1 -1
- package/dist/dataStoreContext.d.ts +33 -4
- package/dist/dataStoreContext.d.ts.map +1 -1
- package/dist/dataStoreContext.js +45 -17
- package/dist/dataStoreContext.js.map +1 -1
- package/dist/dataStores.d.ts +14 -10
- package/dist/dataStores.d.ts.map +1 -1
- package/dist/dataStores.js +73 -41
- package/dist/dataStores.js.map +1 -1
- package/dist/garbageCollection.d.ts +82 -15
- package/dist/garbageCollection.d.ts.map +1 -1
- package/dist/garbageCollection.js +359 -26
- package/dist/garbageCollection.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -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 +0 -1
- package/dist/pendingStateManager.d.ts.map +1 -1
- package/dist/pendingStateManager.js +0 -36
- 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 +6 -6
- package/dist/runningSummarizer.js.map +1 -1
- package/dist/summarizer.d.ts +23 -3
- package/dist/summarizer.d.ts.map +1 -1
- package/dist/summarizer.js +135 -45
- package/dist/summarizer.js.map +1 -1
- package/dist/summarizerTypes.d.ts +3 -10
- package/dist/summarizerTypes.d.ts.map +1 -1
- package/dist/summarizerTypes.js.map +1 -1
- package/dist/summaryFormat.d.ts +10 -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.map +1 -1
- package/dist/summaryGenerator.js +1 -3
- package/dist/summaryGenerator.js.map +1 -1
- package/dist/summaryManager.d.ts +0 -15
- package/dist/summaryManager.d.ts.map +1 -1
- package/dist/summaryManager.js +1 -35
- package/dist/summaryManager.js.map +1 -1
- package/lib/containerHandleContext.d.ts +0 -1
- package/lib/containerHandleContext.d.ts.map +1 -1
- package/lib/containerHandleContext.js +0 -1
- package/lib/containerHandleContext.js.map +1 -1
- package/lib/containerRuntime.d.ts +43 -19
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +206 -117
- package/lib/containerRuntime.js.map +1 -1
- package/lib/dataStoreContext.d.ts +33 -4
- package/lib/dataStoreContext.d.ts.map +1 -1
- package/lib/dataStoreContext.js +45 -17
- package/lib/dataStoreContext.js.map +1 -1
- package/lib/dataStores.d.ts +14 -10
- package/lib/dataStores.d.ts.map +1 -1
- package/lib/dataStores.js +76 -44
- package/lib/dataStores.js.map +1 -1
- package/lib/garbageCollection.d.ts +82 -15
- package/lib/garbageCollection.d.ts.map +1 -1
- package/lib/garbageCollection.js +361 -28
- package/lib/garbageCollection.js.map +1 -1
- package/lib/index.d.ts +2 -2
- 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 +0 -1
- package/lib/pendingStateManager.d.ts.map +1 -1
- package/lib/pendingStateManager.js +0 -36
- 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 +6 -6
- package/lib/runningSummarizer.js.map +1 -1
- package/lib/summarizer.d.ts +23 -3
- package/lib/summarizer.d.ts.map +1 -1
- package/lib/summarizer.js +135 -45
- package/lib/summarizer.js.map +1 -1
- package/lib/summarizerTypes.d.ts +3 -10
- package/lib/summarizerTypes.d.ts.map +1 -1
- package/lib/summarizerTypes.js.map +1 -1
- package/lib/summaryFormat.d.ts +10 -1
- package/lib/summaryFormat.d.ts.map +1 -1
- package/lib/summaryFormat.js +1 -0
- package/lib/summaryFormat.js.map +1 -1
- package/lib/summaryGenerator.d.ts.map +1 -1
- package/lib/summaryGenerator.js +1 -3
- package/lib/summaryGenerator.js.map +1 -1
- package/lib/summaryManager.d.ts +0 -15
- package/lib/summaryManager.d.ts.map +1 -1
- package/lib/summaryManager.js +1 -34
- package/lib/summaryManager.js.map +1 -1
- package/package.json +14 -14
- package/src/containerHandleContext.ts +0 -1
- package/src/containerRuntime.ts +280 -140
- package/src/dataStoreContext.ts +59 -20
- package/src/dataStores.ts +116 -54
- package/src/garbageCollection.ts +492 -29
- package/src/index.ts +20 -2
- package/src/packageVersion.ts +1 -1
- package/src/pendingStateManager.ts +0 -43
- package/src/runningSummarizer.ts +12 -10
- package/src/summarizer.ts +154 -53
- package/src/summarizerTypes.ts +3 -11
- package/src/summaryFormat.ts +11 -1
- package/src/summaryGenerator.ts +2 -3
- package/src/summaryManager.ts +2 -49
- package/dist/localStorageFeatureGates.d.ts +0 -13
- package/dist/localStorageFeatureGates.d.ts.map +0 -1
- package/dist/localStorageFeatureGates.js +0 -31
- package/dist/localStorageFeatureGates.js.map +0 -1
- package/lib/localStorageFeatureGates.d.ts +0 -13
- package/lib/localStorageFeatureGates.d.ts.map +0 -1
- package/lib/localStorageFeatureGates.js +0 -27
- package/lib/localStorageFeatureGates.js.map +0 -1
- package/src/localStorageFeatureGates.ts +0 -27
package/src/containerRuntime.ts
CHANGED
|
@@ -6,15 +6,14 @@
|
|
|
6
6
|
import { EventEmitter } from "events";
|
|
7
7
|
import { ITelemetryGenericEvent, ITelemetryLogger } from "@fluidframework/common-definitions";
|
|
8
8
|
import {
|
|
9
|
+
FluidObject,
|
|
10
|
+
IFluidConfiguration,
|
|
11
|
+
IFluidHandle,
|
|
12
|
+
IFluidHandleContext,
|
|
9
13
|
IFluidObject,
|
|
10
14
|
IFluidRouter,
|
|
11
|
-
IFluidHandleContext,
|
|
12
|
-
IFluidSerializer,
|
|
13
15
|
IRequest,
|
|
14
16
|
IResponse,
|
|
15
|
-
IFluidHandle,
|
|
16
|
-
IFluidConfiguration,
|
|
17
|
-
FluidObject,
|
|
18
17
|
} from "@fluidframework/core-interfaces";
|
|
19
18
|
import {
|
|
20
19
|
IAudience,
|
|
@@ -27,6 +26,7 @@ import {
|
|
|
27
26
|
ICriticalContainerError,
|
|
28
27
|
AttachState,
|
|
29
28
|
ILoaderOptions,
|
|
29
|
+
LoaderHeader,
|
|
30
30
|
} from "@fluidframework/container-definitions";
|
|
31
31
|
import {
|
|
32
32
|
IContainerRuntime,
|
|
@@ -45,18 +45,22 @@ import {
|
|
|
45
45
|
PerformanceEvent,
|
|
46
46
|
normalizeError,
|
|
47
47
|
TaggedLoggerAdapter,
|
|
48
|
+
MonitoringContext,
|
|
49
|
+
loggerToMonitoringContext,
|
|
48
50
|
} from "@fluidframework/telemetry-utils";
|
|
49
|
-
import { IDocumentStorageService, ISummaryContext } from "@fluidframework/driver-definitions";
|
|
51
|
+
import { DriverHeader, IDocumentStorageService, ISummaryContext } from "@fluidframework/driver-definitions";
|
|
50
52
|
import { readAndParse, BlobAggregationStorage } from "@fluidframework/driver-utils";
|
|
51
|
-
import { DataCorruptionError, GenericError, extractSafePropertiesFromMessage } from "@fluidframework/container-utils";
|
|
52
53
|
import {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
54
|
+
CreateProcessingError,
|
|
55
|
+
DataCorruptionError,
|
|
56
|
+
GenericError,
|
|
57
|
+
UsageError,
|
|
58
|
+
extractSafePropertiesFromMessage,
|
|
59
|
+
} from "@fluidframework/container-utils";
|
|
56
60
|
import {
|
|
57
61
|
IClientDetails,
|
|
58
62
|
IDocumentMessage,
|
|
59
|
-
|
|
63
|
+
IQuorumClients,
|
|
60
64
|
ISequencedDocumentMessage,
|
|
61
65
|
ISignalMessage,
|
|
62
66
|
ISummaryConfiguration,
|
|
@@ -90,19 +94,20 @@ import {
|
|
|
90
94
|
addTreeToSummary,
|
|
91
95
|
convertToSummaryTree,
|
|
92
96
|
createRootSummarizerNodeWithGC,
|
|
93
|
-
FluidSerializer,
|
|
94
97
|
IRootSummarizerNodeWithGC,
|
|
95
98
|
RequestParser,
|
|
96
99
|
create404Response,
|
|
97
100
|
exceptionToResponse,
|
|
101
|
+
requestFluidObject,
|
|
98
102
|
responseToException,
|
|
99
103
|
seqFromTree,
|
|
104
|
+
convertSummaryTreeToITree,
|
|
100
105
|
} from "@fluidframework/runtime-utils";
|
|
101
106
|
import { v4 as uuid } from "uuid";
|
|
102
107
|
import { ContainerFluidHandleContext } from "./containerHandleContext";
|
|
103
108
|
import { FluidDataStoreRegistry } from "./dataStoreRegistry";
|
|
104
109
|
import { Summarizer } from "./summarizer";
|
|
105
|
-
import {
|
|
110
|
+
import { SummaryManager } from "./summaryManager";
|
|
106
111
|
import { DeltaScheduler } from "./deltaScheduler";
|
|
107
112
|
import { ReportOpPerfTelemetry, latencyThreshold } from "./connectionTelemetry";
|
|
108
113
|
import { IPendingLocalState, PendingStateManager } from "./pendingStateManager";
|
|
@@ -110,17 +115,18 @@ import { pkgVersion } from "./packageVersion";
|
|
|
110
115
|
import { BlobManager, IBlobManagerLoadInfo } from "./blobManager";
|
|
111
116
|
import { DataStores, getSummaryForDatastores } from "./dataStores";
|
|
112
117
|
import {
|
|
118
|
+
aliasBlobName,
|
|
113
119
|
blobsTreeName,
|
|
114
120
|
chunksBlobName,
|
|
115
121
|
electedSummarizerBlobName,
|
|
116
122
|
extractSummaryMetadataMessage,
|
|
117
123
|
IContainerRuntimeMetadata,
|
|
124
|
+
ICreateContainerMetadata,
|
|
118
125
|
ISummaryMetadataMessage,
|
|
119
126
|
metadataBlobName,
|
|
120
127
|
wrapSummaryInChannelsTree,
|
|
121
128
|
} from "./summaryFormat";
|
|
122
129
|
import { SummaryCollection } from "./summaryCollection";
|
|
123
|
-
import { getLocalStorageFeatureGate } from "./localStorageFeatureGates";
|
|
124
130
|
import { ISerializedElection, OrderedClientCollection, OrderedClientElection } from "./orderedClientElection";
|
|
125
131
|
import { SummarizerClientElection, summarizerClientType } from "./summarizerClientElection";
|
|
126
132
|
import {
|
|
@@ -137,6 +143,7 @@ import { formExponentialFn, Throttler } from "./throttler";
|
|
|
137
143
|
import { RunWhileConnectedCoordinator } from "./runWhileConnectedCoordinator";
|
|
138
144
|
import {
|
|
139
145
|
GarbageCollector,
|
|
146
|
+
gcTreeKey,
|
|
140
147
|
IGarbageCollectionRuntime,
|
|
141
148
|
IGarbageCollector,
|
|
142
149
|
IGCStats,
|
|
@@ -158,6 +165,9 @@ export enum ContainerMessageType {
|
|
|
158
165
|
|
|
159
166
|
// Ties our new clientId to our old one on reconnect
|
|
160
167
|
Rejoin = "rejoin",
|
|
168
|
+
|
|
169
|
+
// Sets the alias of a root data store
|
|
170
|
+
Alias = "alias",
|
|
161
171
|
}
|
|
162
172
|
|
|
163
173
|
export interface IChunkedOp {
|
|
@@ -282,13 +292,14 @@ type IRuntimeMessageMetadata = undefined | {
|
|
|
282
292
|
};
|
|
283
293
|
|
|
284
294
|
// Local storage key to set the default flush mode to TurnBased
|
|
285
|
-
const turnBasedFlushModeKey = "
|
|
295
|
+
const turnBasedFlushModeKey = "Fluid.ContainerRuntime.FlushModeTurnBased";
|
|
286
296
|
|
|
287
297
|
export function isRuntimeMessage(message: ISequencedDocumentMessage): boolean {
|
|
288
298
|
switch (message.type) {
|
|
289
299
|
case ContainerMessageType.FluidDataStoreOp:
|
|
290
300
|
case ContainerMessageType.ChunkedOp:
|
|
291
301
|
case ContainerMessageType.Attach:
|
|
302
|
+
case ContainerMessageType.Alias:
|
|
292
303
|
case ContainerMessageType.BlobAttach:
|
|
293
304
|
case ContainerMessageType.Rejoin:
|
|
294
305
|
case MessageType.Operation:
|
|
@@ -584,6 +595,19 @@ export class ScheduleManager {
|
|
|
584
595
|
*/
|
|
585
596
|
export const agentSchedulerId = "_scheduler";
|
|
586
597
|
|
|
598
|
+
// safely check navigator and get the hardware spec value
|
|
599
|
+
export function getDeviceSpec() {
|
|
600
|
+
try {
|
|
601
|
+
if (typeof navigator === "object" && navigator !== null) {
|
|
602
|
+
return {
|
|
603
|
+
deviceMemory: (navigator as any).deviceMemory,
|
|
604
|
+
hardwareConcurrency: navigator.hardwareConcurrency,
|
|
605
|
+
};
|
|
606
|
+
}
|
|
607
|
+
} catch {
|
|
608
|
+
}
|
|
609
|
+
return {};
|
|
610
|
+
}
|
|
587
611
|
/**
|
|
588
612
|
* Represents the runtime of the container. Contains helper functions/state of the container.
|
|
589
613
|
* It will define the store level mappings.
|
|
@@ -599,13 +623,6 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
599
623
|
public get IContainerRuntime() { return this; }
|
|
600
624
|
public get IFluidRouter() { return this; }
|
|
601
625
|
|
|
602
|
-
// back-compat: Used by loader in <= 0.35
|
|
603
|
-
/**
|
|
604
|
-
* @internal
|
|
605
|
-
* @deprecated Back-compat only. Used by the loader in versions earlier than 0.35.
|
|
606
|
-
*/
|
|
607
|
-
public readonly runtimeVersion: string = pkgVersion;
|
|
608
|
-
|
|
609
626
|
/**
|
|
610
627
|
* Load the stores from a snapshot and returns the runtime.
|
|
611
628
|
* @param context - Context of the container.
|
|
@@ -675,9 +692,14 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
675
692
|
return readAndParse<T>(storage, blobId);
|
|
676
693
|
}
|
|
677
694
|
};
|
|
678
|
-
|
|
679
|
-
const metadata = await
|
|
680
|
-
|
|
695
|
+
|
|
696
|
+
const [chunks, metadata, electedSummarizerData, aliases] = await Promise.all([
|
|
697
|
+
tryFetchBlob<[string, string[]][]>(chunksBlobName),
|
|
698
|
+
tryFetchBlob<IContainerRuntimeMetadata>(metadataBlobName),
|
|
699
|
+
tryFetchBlob<ISerializedElection>(electedSummarizerBlobName),
|
|
700
|
+
tryFetchBlob<[string, string][]>(aliasBlobName),
|
|
701
|
+
]);
|
|
702
|
+
|
|
681
703
|
const loadExisting = existing === true || context.existing === true;
|
|
682
704
|
|
|
683
705
|
// read snapshot blobs needed for BlobManager to load
|
|
@@ -716,7 +738,8 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
716
738
|
registry,
|
|
717
739
|
metadata,
|
|
718
740
|
electedSummarizerData,
|
|
719
|
-
chunks,
|
|
741
|
+
chunks ?? [],
|
|
742
|
+
aliases ?? [],
|
|
720
743
|
{
|
|
721
744
|
summaryOptions,
|
|
722
745
|
gcOptions,
|
|
@@ -733,6 +756,9 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
733
756
|
return runtime;
|
|
734
757
|
}
|
|
735
758
|
|
|
759
|
+
/**
|
|
760
|
+
* @deprecated This will be removed in a later release. Deprecated in 0.53
|
|
761
|
+
*/
|
|
736
762
|
public get id(): string {
|
|
737
763
|
return this.context.id;
|
|
738
764
|
}
|
|
@@ -805,13 +831,13 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
805
831
|
return this.context.attachState;
|
|
806
832
|
}
|
|
807
833
|
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
834
|
+
public get IFluidHandleContext(): IFluidHandleContext {
|
|
835
|
+
return this.handleContext;
|
|
836
|
+
}
|
|
837
|
+
private readonly handleContext: ContainerFluidHandleContext;
|
|
812
838
|
|
|
813
839
|
// internal logger for ContainerRuntime. Use this.logger for stores, summaries, etc.
|
|
814
|
-
private readonly
|
|
840
|
+
private readonly mc: MonitoringContext;
|
|
815
841
|
private readonly summarizerClientElection?: SummarizerClientElection;
|
|
816
842
|
/**
|
|
817
843
|
* summaryManager will only be created if this client is permitted to spawn a summarizing client
|
|
@@ -824,7 +850,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
824
850
|
private readonly summarizerNode: IRootSummarizerNodeWithGC;
|
|
825
851
|
|
|
826
852
|
private _orderSequentiallyCalls: number = 0;
|
|
827
|
-
private _flushMode
|
|
853
|
+
private _flushMode: FlushMode;
|
|
828
854
|
private needsFlush = false;
|
|
829
855
|
private flushTrigger = false;
|
|
830
856
|
|
|
@@ -874,6 +900,14 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
874
900
|
|
|
875
901
|
private readonly dataStores: DataStores;
|
|
876
902
|
|
|
903
|
+
/**
|
|
904
|
+
* True, if GC data should be written at root of the summary tree.
|
|
905
|
+
* False, if data stores should write GC blobs in their summary tree.
|
|
906
|
+
*/
|
|
907
|
+
public get writeGCDataAtRoot(): boolean {
|
|
908
|
+
return this.garbageCollector.writeDataAtRoot;
|
|
909
|
+
}
|
|
910
|
+
|
|
877
911
|
/**
|
|
878
912
|
* True if generating summaries with isolated channels is
|
|
879
913
|
* explicitly disabled. This only affects how summaries are written,
|
|
@@ -883,21 +917,26 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
883
917
|
/** The message in the metadata of the base summary this container is loaded from. */
|
|
884
918
|
private readonly baseSummaryMessage: ISummaryMetadataMessage | undefined;
|
|
885
919
|
|
|
886
|
-
private static get defaultFlushMode(): FlushMode {
|
|
887
|
-
return getLocalStorageFeatureGate(turnBasedFlushModeKey) ? FlushMode.TurnBased : FlushMode.Immediate;
|
|
888
|
-
}
|
|
889
|
-
|
|
890
920
|
private get summarizer(): Summarizer {
|
|
891
921
|
assert(this._summarizer !== undefined, 0x257 /* "This is not summarizing container" */);
|
|
892
922
|
return this._summarizer;
|
|
893
923
|
}
|
|
894
924
|
|
|
925
|
+
private get summariesDisabled(): boolean {
|
|
926
|
+
return this.runtimeOptions.summaryOptions.disableSummaries === true ||
|
|
927
|
+
this.runtimeOptions.summaryOptions.summaryConfigOverrides?.disableSummaries === true;
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
private readonly createContainerMetadata: ICreateContainerMetadata;
|
|
931
|
+
private summaryCount: number | undefined;
|
|
932
|
+
|
|
895
933
|
private constructor(
|
|
896
934
|
private readonly context: IContainerContext,
|
|
897
935
|
private readonly registry: IFluidDataStoreRegistry,
|
|
898
936
|
metadata: IContainerRuntimeMetadata | undefined,
|
|
899
937
|
electedSummarizerData: ISerializedElection | undefined,
|
|
900
938
|
chunks: [string, string[]][],
|
|
939
|
+
dataStoreAliasMap: [string, string][],
|
|
901
940
|
private readonly runtimeOptions: Readonly<Required<IContainerRuntimeOptions>>,
|
|
902
941
|
private readonly containerScope: FluidObject,
|
|
903
942
|
public readonly logger: ITelemetryLogger,
|
|
@@ -907,25 +946,58 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
907
946
|
private _storage?: IDocumentStorageService,
|
|
908
947
|
) {
|
|
909
948
|
super();
|
|
910
|
-
|
|
911
949
|
this.baseSummaryMessage = metadata?.message;
|
|
912
950
|
|
|
951
|
+
// If this is an existing container, we get values from metadata.
|
|
952
|
+
// otherwise, we initialize them.
|
|
953
|
+
if (existing) {
|
|
954
|
+
this.createContainerMetadata = {
|
|
955
|
+
createContainerRuntimeVersion: metadata?.createContainerRuntimeVersion,
|
|
956
|
+
createContainerTimestamp: metadata?.createContainerTimestamp,
|
|
957
|
+
};
|
|
958
|
+
this.summaryCount = metadata?.summaryCount;
|
|
959
|
+
} else {
|
|
960
|
+
this.createContainerMetadata = {
|
|
961
|
+
createContainerRuntimeVersion: pkgVersion,
|
|
962
|
+
createContainerTimestamp: Date.now(),
|
|
963
|
+
};
|
|
964
|
+
}
|
|
965
|
+
|
|
913
966
|
// Default to false (enabled).
|
|
914
967
|
this.disableIsolatedChannels = this.runtimeOptions.summaryOptions.disableIsolatedChannels ?? false;
|
|
915
968
|
|
|
916
969
|
this._connected = this.context.connected;
|
|
917
970
|
this.chunkMap = new Map<string, string[]>(chunks);
|
|
918
971
|
|
|
919
|
-
this.
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
972
|
+
this.handleContext = new ContainerFluidHandleContext("", this);
|
|
973
|
+
|
|
974
|
+
this.mc = loggerToMonitoringContext(
|
|
975
|
+
ChildLogger.create(this.logger, "ContainerRuntime"));
|
|
976
|
+
|
|
977
|
+
this._flushMode =
|
|
978
|
+
this.mc.config.getBoolean(turnBasedFlushModeKey) ?? false
|
|
979
|
+
? FlushMode.TurnBased : FlushMode.Immediate;
|
|
980
|
+
|
|
981
|
+
/**
|
|
982
|
+
* Function that return the current server timestamp. This is used by the garbage collector to set the
|
|
983
|
+
* time when a node becomes unreferenced.
|
|
984
|
+
* We use the timestamp of the last op for current timestamp. However, there can be cases where
|
|
985
|
+
* we don't have an op (on demand summaries for instance). In those cases, we will use the timestamp
|
|
986
|
+
* of this client's connection.
|
|
987
|
+
*/
|
|
988
|
+
const getCurrentTimestamp = () => {
|
|
989
|
+
const client = this.clientId !== undefined ? this.getAudience().getMember(this.clientId) : undefined;
|
|
990
|
+
const timestamp = client?.timestamp;
|
|
991
|
+
return this.deltaManager.lastMessage?.timestamp ?? timestamp ?? Date.now();
|
|
992
|
+
};
|
|
924
993
|
this.garbageCollector = GarbageCollector.create(
|
|
925
994
|
this,
|
|
926
995
|
this.runtimeOptions.gcOptions,
|
|
927
996
|
(unusedRoutes: string[]) => this.dataStores.deleteUnusedRoutes(unusedRoutes),
|
|
928
|
-
|
|
997
|
+
getCurrentTimestamp,
|
|
998
|
+
context.baseSnapshot,
|
|
999
|
+
async <T>(id: string) => readAndParse<T>(this.storage, id),
|
|
1000
|
+
this.mc.logger,
|
|
929
1001
|
existing,
|
|
930
1002
|
metadata,
|
|
931
1003
|
);
|
|
@@ -948,8 +1020,6 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
948
1020
|
throwOnFailure: true,
|
|
949
1021
|
// If GC should not run, let the summarizer node know so that it does not track GC state.
|
|
950
1022
|
gcDisabled: !this.garbageCollector.shouldRunGC,
|
|
951
|
-
// The max duration for which objects can be unreferenced before they are eligible for deletion.
|
|
952
|
-
maxUnreferencedDurationMs: this.runtimeOptions.gcOptions.maxUnreferencedDurationMs,
|
|
953
1023
|
},
|
|
954
1024
|
);
|
|
955
1025
|
|
|
@@ -974,10 +1044,14 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
974
1044
|
getInitialGCSummaryDetailsFn,
|
|
975
1045
|
),
|
|
976
1046
|
(id: string) => this.summarizerNode.deleteChild(id),
|
|
977
|
-
this.
|
|
1047
|
+
this.mc.logger,
|
|
1048
|
+
async () => this.garbageCollector.getDataStoreBaseGCDetails(),
|
|
1049
|
+
(id: string) => this.garbageCollector.nodeChanged(id),
|
|
1050
|
+
new Map<string, string>(dataStoreAliasMap),
|
|
1051
|
+
);
|
|
978
1052
|
|
|
979
1053
|
this.blobManager = new BlobManager(
|
|
980
|
-
this.
|
|
1054
|
+
this.handleContext,
|
|
981
1055
|
blobManagerSnapshot,
|
|
982
1056
|
() => this.storage,
|
|
983
1057
|
(blobId) => this.submit(ContainerMessageType.BlobAttach, undefined, undefined, { blobId }),
|
|
@@ -1002,54 +1076,34 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1002
1076
|
this.clearPartialChunks(clientId);
|
|
1003
1077
|
});
|
|
1004
1078
|
|
|
1005
|
-
this.context.quorum.on("addProposal", (proposal) => {
|
|
1006
|
-
if (proposal.key === "code" || proposal.key === "code2") {
|
|
1007
|
-
this.emit("codeDetailsProposed", proposal.value, proposal);
|
|
1008
|
-
}
|
|
1009
|
-
});
|
|
1010
|
-
|
|
1011
1079
|
this.summaryCollection = new SummaryCollection(this.deltaManager, this.logger);
|
|
1012
1080
|
|
|
1013
|
-
// Only create a SummaryManager if summaries are enabled and we are not the summarizer client
|
|
1014
1081
|
// Map the deprecated generateSummaries flag to disableSummaries.
|
|
1015
1082
|
if (this.runtimeOptions.summaryOptions.generateSummaries === false) {
|
|
1016
1083
|
this.runtimeOptions.summaryOptions.disableSummaries = true;
|
|
1017
1084
|
}
|
|
1018
|
-
if (this.summariesDisabled()) {
|
|
1019
|
-
this._logger.sendTelemetryEvent({ eventName: "SummariesDisabled" });
|
|
1020
|
-
} else {
|
|
1021
|
-
const maxOpsSinceLastSummary = this.runtimeOptions.summaryOptions.maxOpsSinceLastSummary ?? 7000;
|
|
1022
|
-
const defaultAction = () => {
|
|
1023
|
-
if (this.summaryCollection.opsSinceLastAck > maxOpsSinceLastSummary) {
|
|
1024
|
-
this.logger.sendErrorEvent({eventName: "SummaryStatus:Behind"});
|
|
1025
|
-
// unregister default to no log on every op after falling behind
|
|
1026
|
-
// and register summary ack handler to re-register this handler
|
|
1027
|
-
// after successful summary
|
|
1028
|
-
this.summaryCollection.once(MessageType.SummaryAck, () => {
|
|
1029
|
-
this.logger.sendTelemetryEvent({eventName: "SummaryStatus:CaughtUp"});
|
|
1030
|
-
// we've caught up, so re-register the default action to monitor for
|
|
1031
|
-
// falling behind, and unregister ourself
|
|
1032
|
-
this.summaryCollection.on("default", defaultAction);
|
|
1033
|
-
});
|
|
1034
|
-
this.summaryCollection.off("default", defaultAction);
|
|
1035
|
-
}
|
|
1036
|
-
};
|
|
1037
1085
|
|
|
1038
|
-
|
|
1039
|
-
|
|
1086
|
+
if (this.summariesDisabled) {
|
|
1087
|
+
this.mc.logger.sendTelemetryEvent({ eventName: "SummariesDisabled" });
|
|
1088
|
+
}
|
|
1089
|
+
else {
|
|
1090
|
+
const orderedClientLogger = ChildLogger.create(this.logger, "OrderedClientElection");
|
|
1040
1091
|
const orderedClientCollection = new OrderedClientCollection(
|
|
1041
1092
|
orderedClientLogger,
|
|
1042
1093
|
this.context.deltaManager,
|
|
1043
1094
|
this.context.quorum,
|
|
1044
1095
|
);
|
|
1045
1096
|
const orderedClientElectionForSummarizer = new OrderedClientElection(
|
|
1097
|
+
|
|
1046
1098
|
orderedClientLogger,
|
|
1047
1099
|
orderedClientCollection,
|
|
1048
1100
|
electedSummarizerData ?? this.context.deltaManager.lastSequenceNumber,
|
|
1049
1101
|
SummarizerClientElection.isClientEligible,
|
|
1050
1102
|
);
|
|
1051
|
-
const summarizerClientElectionEnabled =
|
|
1103
|
+
const summarizerClientElectionEnabled =
|
|
1104
|
+
this.mc.config.getBoolean("Fluid.ContainerRuntime.summarizerClientElection") ??
|
|
1052
1105
|
this.runtimeOptions.summaryOptions?.summarizerClientElection === true;
|
|
1106
|
+
const maxOpsSinceLastSummary = this.runtimeOptions.summaryOptions.maxOpsSinceLastSummary ?? 7000;
|
|
1053
1107
|
this.summarizerClientElection = new SummarizerClientElection(
|
|
1054
1108
|
orderedClientLogger,
|
|
1055
1109
|
this.summaryCollection,
|
|
@@ -1064,27 +1118,39 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1064
1118
|
this /* ISummarizerRuntime */,
|
|
1065
1119
|
() => this.summaryConfiguration,
|
|
1066
1120
|
this /* ISummarizerInternalsProvider */,
|
|
1067
|
-
this.
|
|
1121
|
+
this.handleContext,
|
|
1068
1122
|
this.summaryCollection,
|
|
1069
1123
|
async (runtime: IConnectableRuntime) => RunWhileConnectedCoordinator.create(runtime),
|
|
1070
1124
|
);
|
|
1071
|
-
}
|
|
1125
|
+
}
|
|
1126
|
+
else if (SummarizerClientElection.clientDetailsPermitElection(this.context.clientDetails)) {
|
|
1127
|
+
// Only create a SummaryManager and SummarizerClientElection
|
|
1128
|
+
// if summaries are enabled and we are not the summarizer client.
|
|
1129
|
+
const defaultAction = () => {
|
|
1130
|
+
if (this.summaryCollection.opsSinceLastAck > maxOpsSinceLastSummary) {
|
|
1131
|
+
this.logger.sendErrorEvent({eventName: "SummaryStatus:Behind"});
|
|
1132
|
+
// unregister default to no log on every op after falling behind
|
|
1133
|
+
// and register summary ack handler to re-register this handler
|
|
1134
|
+
// after successful summary
|
|
1135
|
+
this.summaryCollection.once(MessageType.SummaryAck, () => {
|
|
1136
|
+
this.logger.sendTelemetryEvent({eventName: "SummaryStatus:CaughtUp"});
|
|
1137
|
+
// we've caught up, so re-register the default action to monitor for
|
|
1138
|
+
// falling behind, and unregister ourself
|
|
1139
|
+
this.summaryCollection.on("default", defaultAction);
|
|
1140
|
+
});
|
|
1141
|
+
this.summaryCollection.off("default", defaultAction);
|
|
1142
|
+
}
|
|
1143
|
+
};
|
|
1144
|
+
|
|
1145
|
+
this.summaryCollection.on("default", defaultAction);
|
|
1146
|
+
|
|
1072
1147
|
// Create the SummaryManager and mark the initial state
|
|
1073
|
-
const requestOptions: ISummarizerRequestOptions =
|
|
1074
|
-
{
|
|
1075
|
-
cache: false,
|
|
1076
|
-
reconnect: false,
|
|
1077
|
-
summarizingClient: true,
|
|
1078
|
-
};
|
|
1079
1148
|
this.summaryManager = new SummaryManager(
|
|
1080
1149
|
this.summarizerClientElection,
|
|
1081
1150
|
this, // IConnectedState
|
|
1082
1151
|
this.summaryCollection,
|
|
1083
1152
|
this.logger,
|
|
1084
|
-
formRequestSummarizerFn(
|
|
1085
|
-
this.context.loader,
|
|
1086
|
-
this.context.deltaManager.lastSequenceNumber,
|
|
1087
|
-
requestOptions),
|
|
1153
|
+
this.formRequestSummarizerFn(this.context.loader),
|
|
1088
1154
|
new Throttler(
|
|
1089
1155
|
60 * 1000, // 60 sec delay window
|
|
1090
1156
|
30 * 1000, // 30 sec max delay
|
|
@@ -1104,7 +1170,8 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1104
1170
|
this.deltaManager.on("readonly", (readonly: boolean) => {
|
|
1105
1171
|
// we accumulate ops while being in read-only state.
|
|
1106
1172
|
// once user gets write permissions and we have active connection, flush all pending ops.
|
|
1107
|
-
assert(readonly === this.deltaManager.readonly,
|
|
1173
|
+
assert(readonly === this.deltaManager.readOnlyInfo.readonly,
|
|
1174
|
+
0x124 /* "inconsistent readonly property/event state" */);
|
|
1108
1175
|
|
|
1109
1176
|
// We need to be very careful with when we (re)send pending ops, to ensure that we only send ops
|
|
1110
1177
|
// when we either never send an op, or attempted to send it but we know for sure it was not
|
|
@@ -1127,6 +1194,23 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1127
1194
|
this.deltaManager.on("op", this.onOp);
|
|
1128
1195
|
}
|
|
1129
1196
|
|
|
1197
|
+
// logging hardware telemetry
|
|
1198
|
+
logger.sendTelemetryEvent({
|
|
1199
|
+
eventName:"DeviceSpec",
|
|
1200
|
+
...getDeviceSpec(),
|
|
1201
|
+
});
|
|
1202
|
+
|
|
1203
|
+
// logging container load stats
|
|
1204
|
+
this.logger.sendTelemetryEvent({
|
|
1205
|
+
eventName: "ContainerLoadStats",
|
|
1206
|
+
...this.createContainerMetadata,
|
|
1207
|
+
...this.dataStores.containerLoadStats,
|
|
1208
|
+
summaryCount: this.summaryCount,
|
|
1209
|
+
summaryFormatVersion: metadata?.summaryFormatVersion,
|
|
1210
|
+
disableIsolatedChannels: metadata?.disableIsolatedChannels,
|
|
1211
|
+
gcVersion: metadata?.gcFeature,
|
|
1212
|
+
});
|
|
1213
|
+
|
|
1130
1214
|
ReportOpPerfTelemetry(this.context.clientId, this.deltaManager, this.logger);
|
|
1131
1215
|
}
|
|
1132
1216
|
|
|
@@ -1251,6 +1335,8 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1251
1335
|
|
|
1252
1336
|
private formMetadata(): IContainerRuntimeMetadata {
|
|
1253
1337
|
return {
|
|
1338
|
+
...this.createContainerMetadata,
|
|
1339
|
+
summaryCount: this.summaryCount,
|
|
1254
1340
|
summaryFormatVersion: 1,
|
|
1255
1341
|
disableIsolatedChannels: this.disableIsolatedChannels || undefined,
|
|
1256
1342
|
gcFeature: this.garbageCollector.gcSummaryFeatureVersion,
|
|
@@ -1287,26 +1373,14 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1287
1373
|
* @deprecated - Use summarize to get summary of the container runtime.
|
|
1288
1374
|
*/
|
|
1289
1375
|
public async snapshot(): Promise<ITree> {
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
root.entries = root.entries.concat(entries);
|
|
1299
|
-
} else {
|
|
1300
|
-
root.entries.push(new TreeTreeEntry(channelsTreeName, { entries }));
|
|
1301
|
-
}
|
|
1302
|
-
|
|
1303
|
-
root.entries.push(new BlobTreeEntry(metadataBlobName, JSON.stringify(this.formMetadata())));
|
|
1304
|
-
|
|
1305
|
-
if (this.chunkMap.size > 0) {
|
|
1306
|
-
root.entries.push(new BlobTreeEntry(chunksBlobName, JSON.stringify([...this.chunkMap])));
|
|
1307
|
-
}
|
|
1308
|
-
|
|
1309
|
-
return root;
|
|
1376
|
+
const summaryResult = await this.summarize({
|
|
1377
|
+
summaryLogger: this.logger,
|
|
1378
|
+
fullTree: true,
|
|
1379
|
+
trackState: false,
|
|
1380
|
+
runGC: this.garbageCollector.shouldRunGC,
|
|
1381
|
+
fullGC: true,
|
|
1382
|
+
});
|
|
1383
|
+
return convertSummaryTreeToITree(summaryResult.summary);
|
|
1310
1384
|
}
|
|
1311
1385
|
|
|
1312
1386
|
private addContainerBlobsToSummary(summaryTree: ISummaryTreeWithStats) {
|
|
@@ -1316,6 +1390,11 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1316
1390
|
addBlobToSummary(summaryTree, chunksBlobName, content);
|
|
1317
1391
|
}
|
|
1318
1392
|
|
|
1393
|
+
const dataStoreAliases = this.dataStores.aliases();
|
|
1394
|
+
if (dataStoreAliases.size > 0) {
|
|
1395
|
+
addBlobToSummary(summaryTree, aliasBlobName, JSON.stringify([...dataStoreAliases]));
|
|
1396
|
+
}
|
|
1397
|
+
|
|
1319
1398
|
if (this.summarizerClientElection) {
|
|
1320
1399
|
const electedSummarizerContent = JSON.stringify(this.summarizerClientElection?.serialize());
|
|
1321
1400
|
addBlobToSummary(summaryTree, electedSummarizerBlobName, electedSummarizerContent);
|
|
@@ -1328,6 +1407,13 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1328
1407
|
const blobsTree = convertToSummaryTree(snapshot, false);
|
|
1329
1408
|
addTreeToSummary(summaryTree, blobsTreeName, blobsTree);
|
|
1330
1409
|
}
|
|
1410
|
+
|
|
1411
|
+
if (this.writeGCDataAtRoot) {
|
|
1412
|
+
const gcSummary = this.garbageCollector.summarize();
|
|
1413
|
+
if (gcSummary !== undefined) {
|
|
1414
|
+
addTreeToSummary(summaryTree, gcTreeKey, gcSummary);
|
|
1415
|
+
}
|
|
1416
|
+
}
|
|
1331
1417
|
}
|
|
1332
1418
|
|
|
1333
1419
|
private replayPendingStates() {
|
|
@@ -1388,6 +1474,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1388
1474
|
return this.dataStores.applyStashedOp(op);
|
|
1389
1475
|
case ContainerMessageType.Attach:
|
|
1390
1476
|
return this.dataStores.applyStashedAttachOp(op as unknown as IAttachMessage);
|
|
1477
|
+
case ContainerMessageType.Alias:
|
|
1391
1478
|
case ContainerMessageType.BlobAttach:
|
|
1392
1479
|
return;
|
|
1393
1480
|
case ContainerMessageType.ChunkedOp:
|
|
@@ -1414,7 +1501,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1414
1501
|
|
|
1415
1502
|
this.dataStores.setConnectionState(connected, clientId);
|
|
1416
1503
|
|
|
1417
|
-
raiseConnectedEvent(this.
|
|
1504
|
+
raiseConnectedEvent(this.mc.logger, this, connected, clientId);
|
|
1418
1505
|
}
|
|
1419
1506
|
|
|
1420
1507
|
public process(messageArg: ISequencedDocumentMessage, local: boolean) {
|
|
@@ -1456,6 +1543,9 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1456
1543
|
case ContainerMessageType.Attach:
|
|
1457
1544
|
this.dataStores.processAttachMessage(message, local || localAck);
|
|
1458
1545
|
break;
|
|
1546
|
+
case ContainerMessageType.Alias:
|
|
1547
|
+
this.processAliasMessage(message, localOpMetadata, local);
|
|
1548
|
+
break;
|
|
1459
1549
|
case ContainerMessageType.FluidDataStoreOp:
|
|
1460
1550
|
// if localAck === true, treat this as a local op because it's one we sent on a previous container
|
|
1461
1551
|
this.dataStores.processFluidDataStoreOp(message, local || localAck, localOpMetadata);
|
|
@@ -1475,6 +1565,14 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1475
1565
|
}
|
|
1476
1566
|
}
|
|
1477
1567
|
|
|
1568
|
+
private processAliasMessage(
|
|
1569
|
+
message: ISequencedDocumentMessage,
|
|
1570
|
+
localOpMetadata: unknown,
|
|
1571
|
+
local: boolean,
|
|
1572
|
+
) {
|
|
1573
|
+
this.dataStores.processAliasMessage(message, localOpMetadata, local);
|
|
1574
|
+
}
|
|
1575
|
+
|
|
1478
1576
|
public processSignal(message: ISignalMessage, local: boolean) {
|
|
1479
1577
|
const envelope = message.content as ISignalEnvelope;
|
|
1480
1578
|
const transformed: IInboundSignalMessage = {
|
|
@@ -1538,6 +1636,13 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1538
1636
|
}
|
|
1539
1637
|
|
|
1540
1638
|
this.needsFlush = false;
|
|
1639
|
+
|
|
1640
|
+
// Did we disconnect in the middle of turn-based batch?
|
|
1641
|
+
// If so, do nothing, as pending state manager will resubmit it correctly on reconnect.
|
|
1642
|
+
if (!this.canSendOps()) {
|
|
1643
|
+
return;
|
|
1644
|
+
}
|
|
1645
|
+
|
|
1541
1646
|
return this.deltaSender.flush();
|
|
1542
1647
|
}
|
|
1543
1648
|
|
|
@@ -1557,9 +1662,10 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1557
1662
|
|
|
1558
1663
|
try {
|
|
1559
1664
|
this.trackOrderSequentiallyCalls(callback);
|
|
1560
|
-
} finally {
|
|
1561
1665
|
this.flush();
|
|
1562
1666
|
this.setFlushMode(savedFlushMode);
|
|
1667
|
+
} catch(error) {
|
|
1668
|
+
this.closeFn(CreateProcessingError(error, "orderSequentially"));
|
|
1563
1669
|
}
|
|
1564
1670
|
}
|
|
1565
1671
|
|
|
@@ -1619,7 +1725,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1619
1725
|
return this.connected && !this.deltaManager.readonly;
|
|
1620
1726
|
}
|
|
1621
1727
|
|
|
1622
|
-
public getQuorum():
|
|
1728
|
+
public getQuorum(): IQuorumClients {
|
|
1623
1729
|
return this.context.quorum;
|
|
1624
1730
|
}
|
|
1625
1731
|
|
|
@@ -1777,25 +1883,21 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1777
1883
|
* Implementation of IGarbageCollectionRuntime::updateUsedRoutes.
|
|
1778
1884
|
* After GC has run, called to notify this container's nodes of routes that are used in it.
|
|
1779
1885
|
* @param usedRoutes - The routes that are used in all nodes in this Container.
|
|
1886
|
+
* @param gcTimestamp - The time when GC was run that generated these used routes. If any node node becomes
|
|
1887
|
+
* unreferenced as part of this GC run, this should be used to update the time when it happens.
|
|
1780
1888
|
* @returns the statistics of the used state of the data stores.
|
|
1781
1889
|
*/
|
|
1782
|
-
public updateUsedRoutes(usedRoutes: string[]): IUsedStateStats {
|
|
1890
|
+
public updateUsedRoutes(usedRoutes: string[], gcTimestamp?: number): IUsedStateStats {
|
|
1783
1891
|
// Update our summarizer node's used routes. Updating used routes in summarizer node before
|
|
1784
1892
|
// summarizing is required and asserted by the the summarizer node. We are the root and are
|
|
1785
1893
|
// always referenced, so the used routes is only self-route (empty string).
|
|
1786
1894
|
this.summarizerNode.updateUsedRoutes([""]);
|
|
1787
1895
|
|
|
1788
|
-
return this.dataStores.updateUsedRoutes(
|
|
1789
|
-
usedRoutes,
|
|
1790
|
-
// For now, we use the timestamp of the last op for gcTimestamp. However, there can be cases where
|
|
1791
|
-
// we don't have an op (on demand summaries for instance). In those cases, we will use the timestamp
|
|
1792
|
-
// of this client's connection - https://github.com/microsoft/FluidFramework/issues/7152.
|
|
1793
|
-
this.deltaManager.lastMessage?.timestamp,
|
|
1794
|
-
);
|
|
1896
|
+
return this.dataStores.updateUsedRoutes(usedRoutes, gcTimestamp);
|
|
1795
1897
|
}
|
|
1796
1898
|
|
|
1797
1899
|
/**
|
|
1798
|
-
* Runs garbage collection and
|
|
1900
|
+
* Runs garbage collection and updates the reference / used state of the nodes in the container.
|
|
1799
1901
|
* @returns the statistics of the garbage collection run.
|
|
1800
1902
|
*/
|
|
1801
1903
|
public async collectGarbage(
|
|
@@ -1811,6 +1913,16 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1811
1913
|
return this.garbageCollector.collectGarbage(options);
|
|
1812
1914
|
}
|
|
1813
1915
|
|
|
1916
|
+
/**
|
|
1917
|
+
* Called when a new outbound reference is added to another node. This is used by garbage collection to identify
|
|
1918
|
+
* all references added in the system.
|
|
1919
|
+
* @param srcHandle - The handle of the node that added the reference.
|
|
1920
|
+
* @param outboundHandle - The handle of the outbound node that is referenced.
|
|
1921
|
+
*/
|
|
1922
|
+
public addedGCOutboundReference(srcHandle: IFluidHandle, outboundHandle: IFluidHandle) {
|
|
1923
|
+
this.garbageCollector.addedOutboundReference(srcHandle.absolutePath, outboundHandle.absolutePath);
|
|
1924
|
+
}
|
|
1925
|
+
|
|
1814
1926
|
/**
|
|
1815
1927
|
* Generates the summary tree, uploads it to storage, and then submits the summarize op.
|
|
1816
1928
|
* This is intended to be called by the summarizer, since it is the implementation of
|
|
@@ -1883,6 +1995,13 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1883
1995
|
return { stage: "base", referenceSequenceNumber: summaryRefSeqNum, error: continueResult.error };
|
|
1884
1996
|
}
|
|
1885
1997
|
|
|
1998
|
+
// increment summary count
|
|
1999
|
+
if (this.summaryCount !== undefined) {
|
|
2000
|
+
this.summaryCount++;
|
|
2001
|
+
} else {
|
|
2002
|
+
this.summaryCount = 1;
|
|
2003
|
+
}
|
|
2004
|
+
|
|
1886
2005
|
const trace = Trace.start();
|
|
1887
2006
|
let summarizeResult: ISummaryTreeWithStats;
|
|
1888
2007
|
try {
|
|
@@ -2036,10 +2155,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2036
2155
|
this.dirtyContainer = dirty;
|
|
2037
2156
|
if (this.emitDirtyDocumentEvent) {
|
|
2038
2157
|
this.emit(dirty ? "dirty" : "saved");
|
|
2039
|
-
|
|
2040
|
-
if (this.context.updateDirtyContainerState !== undefined) {
|
|
2041
|
-
this.context.updateDirtyContainerState(dirty);
|
|
2042
|
-
}
|
|
2158
|
+
this.context.updateDirtyContainerState(dirty);
|
|
2043
2159
|
}
|
|
2044
2160
|
}
|
|
2045
2161
|
|
|
@@ -2158,7 +2274,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2158
2274
|
// That might be not what caller hopes to get, but we can look deeper if telemetry tells us it's a problem.
|
|
2159
2275
|
const middleOfBatch = this.flushMode === FlushMode.TurnBased && this.needsFlush;
|
|
2160
2276
|
if (middleOfBatch) {
|
|
2161
|
-
this.
|
|
2277
|
+
this.mc.logger.sendErrorEvent({ eventName: "submitSystemMessageError", type });
|
|
2162
2278
|
}
|
|
2163
2279
|
|
|
2164
2280
|
return this.context.submitFn(
|
|
@@ -2212,6 +2328,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2212
2328
|
this.dataStores.resubmitDataStoreOp(content, localOpMetadata);
|
|
2213
2329
|
break;
|
|
2214
2330
|
case ContainerMessageType.Attach:
|
|
2331
|
+
case ContainerMessageType.Alias:
|
|
2215
2332
|
this.submit(type, content, localOpMetadata);
|
|
2216
2333
|
break;
|
|
2217
2334
|
case ContainerMessageType.ChunkedOp:
|
|
@@ -2257,7 +2374,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2257
2374
|
* @returns downloaded snapshot's reference sequence number
|
|
2258
2375
|
*/
|
|
2259
2376
|
private async refreshLatestSummaryAckFromServer(summaryLogger: ITelemetryLogger): Promise<number> {
|
|
2260
|
-
const snapshot = await this.fetchSnapshotFromStorage(
|
|
2377
|
+
const snapshot = await this.fetchSnapshotFromStorage(null, summaryLogger, {
|
|
2261
2378
|
eventName: "RefreshLatestSummaryGetSnapshot",
|
|
2262
2379
|
fetchLatest: true,
|
|
2263
2380
|
});
|
|
@@ -2279,7 +2396,8 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2279
2396
|
return snapshotRefSeq;
|
|
2280
2397
|
}
|
|
2281
2398
|
|
|
2282
|
-
private async fetchSnapshotFromStorage(
|
|
2399
|
+
private async fetchSnapshotFromStorage(
|
|
2400
|
+
versionId: string | null, logger: ITelemetryLogger, event: ITelemetryGenericEvent) {
|
|
2283
2401
|
return PerformanceEvent.timedExecAsync(logger, event, async (perfEvent) => {
|
|
2284
2402
|
const stats: { getVersionDuration?: number; getSnapshotDuration?: number } = {};
|
|
2285
2403
|
const trace = Trace.start();
|
|
@@ -2301,14 +2419,6 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2301
2419
|
return this.pendingStateManager.getLocalState();
|
|
2302
2420
|
}
|
|
2303
2421
|
|
|
2304
|
-
/**
|
|
2305
|
-
* @returns true if summaries are explicitly disabled for this ContainerRuntime, false otherwise
|
|
2306
|
-
*/
|
|
2307
|
-
public summariesDisabled(): boolean {
|
|
2308
|
-
return this.runtimeOptions.summaryOptions.disableSummaries === true ||
|
|
2309
|
-
this.runtimeOptions.summaryOptions.summaryConfigOverrides?.disableSummaries === true;
|
|
2310
|
-
}
|
|
2311
|
-
|
|
2312
2422
|
public readonly summarizeOnDemand: ISummarizer["summarizeOnDemand"] = (...args) => {
|
|
2313
2423
|
if (this.clientDetails.type === summarizerClientType) {
|
|
2314
2424
|
return this.summarizer.summarizeOnDemand(...args);
|
|
@@ -2318,8 +2428,8 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2318
2428
|
// If we're not the summarizer, and we don't have a summaryManager, we expect that
|
|
2319
2429
|
// disableSummaries is turned on. We are throwing instead of returning a failure here,
|
|
2320
2430
|
// because it is a misuse of the API rather than an expected failure.
|
|
2321
|
-
throw new
|
|
2322
|
-
`Can't summarize, disableSummaries: ${this.summariesDisabled
|
|
2431
|
+
throw new UsageError(
|
|
2432
|
+
`Can't summarize, disableSummaries: ${this.summariesDisabled}`,
|
|
2323
2433
|
);
|
|
2324
2434
|
}
|
|
2325
2435
|
};
|
|
@@ -2333,11 +2443,41 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2333
2443
|
// If we're not the summarizer, and we don't have a summaryManager, we expect that
|
|
2334
2444
|
// generateSummaries is turned off. We are throwing instead of returning a failure here,
|
|
2335
2445
|
// because it is a misuse of the API rather than an expected failure.
|
|
2336
|
-
throw new
|
|
2337
|
-
`Can't summarize, disableSummaries: ${this.summariesDisabled
|
|
2446
|
+
throw new UsageError(
|
|
2447
|
+
`Can't summarize, disableSummaries: ${this.summariesDisabled}`,
|
|
2338
2448
|
);
|
|
2339
2449
|
}
|
|
2340
2450
|
};
|
|
2451
|
+
|
|
2452
|
+
/**
|
|
2453
|
+
* * Forms a function that will request a Summarizer.
|
|
2454
|
+
* @param loaderRouter - the loader acting as an IFluidRouter
|
|
2455
|
+
* */
|
|
2456
|
+
private formRequestSummarizerFn(loaderRouter: IFluidRouter) {
|
|
2457
|
+
return async () => {
|
|
2458
|
+
const request: IRequest = {
|
|
2459
|
+
headers: {
|
|
2460
|
+
[LoaderHeader.cache]: false,
|
|
2461
|
+
[LoaderHeader.clientDetails]: {
|
|
2462
|
+
capabilities: { interactive: false },
|
|
2463
|
+
type: summarizerClientType,
|
|
2464
|
+
},
|
|
2465
|
+
[DriverHeader.summarizingClient]: true,
|
|
2466
|
+
[LoaderHeader.reconnect]: false,
|
|
2467
|
+
},
|
|
2468
|
+
url: "/_summarizer",
|
|
2469
|
+
};
|
|
2470
|
+
|
|
2471
|
+
const fluidObject = await requestFluidObject<FluidObject<ISummarizer>>(loaderRouter, request);
|
|
2472
|
+
const summarizer = fluidObject.ISummarizer;
|
|
2473
|
+
|
|
2474
|
+
if (!summarizer) {
|
|
2475
|
+
throw new UsageError("Fluid object does not implement ISummarizer");
|
|
2476
|
+
}
|
|
2477
|
+
|
|
2478
|
+
return summarizer;
|
|
2479
|
+
};
|
|
2480
|
+
}
|
|
2341
2481
|
}
|
|
2342
2482
|
|
|
2343
2483
|
/**
|