@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/dataStoreContext.ts
CHANGED
|
@@ -30,7 +30,7 @@ import { BlobTreeEntry } from "@fluidframework/protocol-base";
|
|
|
30
30
|
import {
|
|
31
31
|
IClientDetails,
|
|
32
32
|
IDocumentMessage,
|
|
33
|
-
|
|
33
|
+
IQuorumClients,
|
|
34
34
|
ISequencedDocumentMessage,
|
|
35
35
|
ISnapshotTree,
|
|
36
36
|
ITreeEntry,
|
|
@@ -67,6 +67,7 @@ import {
|
|
|
67
67
|
ThresholdCounter,
|
|
68
68
|
} from "@fluidframework/telemetry-utils";
|
|
69
69
|
import { CreateProcessingError } from "@fluidframework/container-utils";
|
|
70
|
+
|
|
70
71
|
import { ContainerRuntime } from "./containerRuntime";
|
|
71
72
|
import {
|
|
72
73
|
dataStoreAttributesBlobName,
|
|
@@ -188,6 +189,11 @@ export abstract class FluidDataStoreContext extends TypedEventEmitter<IFluidData
|
|
|
188
189
|
return this._containerRuntime.disableIsolatedChannels;
|
|
189
190
|
}
|
|
190
191
|
|
|
192
|
+
/** Tells whether GC data will be written at the root of the summary tree. If so, data store should not write it. */
|
|
193
|
+
protected get writeGCDataAtRoot(): boolean {
|
|
194
|
+
return this._containerRuntime.writeGCDataAtRoot;
|
|
195
|
+
}
|
|
196
|
+
|
|
191
197
|
protected registry: IFluidDataStoreRegistry | undefined;
|
|
192
198
|
|
|
193
199
|
protected detachedRuntimeCreation = false;
|
|
@@ -381,7 +387,7 @@ export abstract class FluidDataStoreContext extends TypedEventEmitter<IFluidData
|
|
|
381
387
|
this.channel?.processSignal(message, local);
|
|
382
388
|
}
|
|
383
389
|
|
|
384
|
-
public getQuorum():
|
|
390
|
+
public getQuorum(): IQuorumClients {
|
|
385
391
|
return this._containerRuntime.getQuorum();
|
|
386
392
|
}
|
|
387
393
|
|
|
@@ -416,8 +422,10 @@ export abstract class FluidDataStoreContext extends TypedEventEmitter<IFluidData
|
|
|
416
422
|
const attributes = createAttributes(pkg, isRootDataStore, this.disableIsolatedChannels);
|
|
417
423
|
addBlobToSummary(summarizeResult, dataStoreAttributesBlobName, JSON.stringify(attributes));
|
|
418
424
|
|
|
419
|
-
// Add GC
|
|
420
|
-
|
|
425
|
+
// Add GC data to the summary if it's not written at the root.
|
|
426
|
+
if (!this.writeGCDataAtRoot) {
|
|
427
|
+
addBlobToSummary(summarizeResult, gcBlobKey, JSON.stringify(this.summarizerNode.getGCSummaryDetails()));
|
|
428
|
+
}
|
|
421
429
|
|
|
422
430
|
// If we are not referenced, mark the summary tree as unreferenced. Also, update unreferenced blob
|
|
423
431
|
// size in the summary stats with the blobs size of this data store.
|
|
@@ -489,9 +497,19 @@ export abstract class FluidDataStoreContext extends TypedEventEmitter<IFluidData
|
|
|
489
497
|
}
|
|
490
498
|
}
|
|
491
499
|
|
|
500
|
+
/**
|
|
501
|
+
* Called when a new outbound reference is added to another node. This is used by garbage collection to identify
|
|
502
|
+
* all references added in the system.
|
|
503
|
+
* @param srcHandle - The handle of the node that added the reference.
|
|
504
|
+
* @param outboundHandle - The handle of the outbound node that is referenced.
|
|
505
|
+
*/
|
|
506
|
+
public addedGCOutboundReference(srcHandle: IFluidHandle, outboundHandle: IFluidHandle) {
|
|
507
|
+
this._containerRuntime.addedGCOutboundReference(srcHandle, outboundHandle);
|
|
508
|
+
}
|
|
509
|
+
|
|
492
510
|
/**
|
|
493
511
|
* Updates the used routes of the channel and its child contexts. The channel must be loaded before calling this.
|
|
494
|
-
* It is called in these two
|
|
512
|
+
* It is called in these two scenarios:
|
|
495
513
|
* 1. When the used routes of the data store is updated and the data store is loaded.
|
|
496
514
|
* 2. When the data store is realized. This updates the channel's used routes as per last GC run.
|
|
497
515
|
*/
|
|
@@ -575,7 +593,7 @@ export abstract class FluidDataStoreContext extends TypedEventEmitter<IFluidData
|
|
|
575
593
|
try
|
|
576
594
|
{
|
|
577
595
|
assert(!this.detachedRuntimeCreation, 0x148 /* "Detached runtime creation on runtime bind" */);
|
|
578
|
-
assert(this.channelDeferred !== undefined, 0x149 /* "Undefined channel
|
|
596
|
+
assert(this.channelDeferred !== undefined, 0x149 /* "Undefined channel deferral" */);
|
|
579
597
|
assert(this.pkg !== undefined, 0x14a /* "Undefined package path" */);
|
|
580
598
|
|
|
581
599
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
@@ -627,6 +645,13 @@ export abstract class FluidDataStoreContext extends TypedEventEmitter<IFluidData
|
|
|
627
645
|
|
|
628
646
|
protected abstract getInitialSnapshotDetails(): Promise<ISnapshotDetails>;
|
|
629
647
|
|
|
648
|
+
/**
|
|
649
|
+
* @deprecated - Sets the datastore as root, for aliasing purposes: #7948
|
|
650
|
+
* This method should not be used outside of the aliasing context.
|
|
651
|
+
* It will be removed, as the source of truth for this flag will be the aliasing blob.
|
|
652
|
+
*/
|
|
653
|
+
public abstract setRoot(): void;
|
|
654
|
+
|
|
630
655
|
public abstract getInitialGCSummaryDetails(): Promise<IGarbageCollectionSummaryDetails>;
|
|
631
656
|
|
|
632
657
|
public reSubmit(contents: any, localOpMetadata: unknown) {
|
|
@@ -672,9 +697,12 @@ export abstract class FluidDataStoreContext extends TypedEventEmitter<IFluidData
|
|
|
672
697
|
}
|
|
673
698
|
|
|
674
699
|
export class RemotedFluidDataStoreContext extends FluidDataStoreContext {
|
|
700
|
+
private isRootDataStore: boolean | undefined;
|
|
701
|
+
|
|
675
702
|
constructor(
|
|
676
703
|
id: string,
|
|
677
704
|
private readonly initSnapshotValue: ISnapshotTree | string | undefined,
|
|
705
|
+
private readonly getBaseSummaryGCDetails: () => Promise<IGarbageCollectionSummaryDetails | undefined>,
|
|
678
706
|
runtime: ContainerRuntime,
|
|
679
707
|
storage: IDocumentStorageService,
|
|
680
708
|
scope: FluidObject,
|
|
@@ -742,7 +770,7 @@ export class RemotedFluidDataStoreContext extends FluidDataStoreContext {
|
|
|
742
770
|
* data stores in older documents are not garbage collected incorrectly. This may lead to additional
|
|
743
771
|
* roots in the document but they won't break.
|
|
744
772
|
*/
|
|
745
|
-
isRootDataStore = attributes.isRootDataStore ?? true;
|
|
773
|
+
isRootDataStore = this.isRootDataStore === true || (attributes.isRootDataStore ?? true);
|
|
746
774
|
|
|
747
775
|
if (hasIsolatedChannels(attributes)) {
|
|
748
776
|
tree = tree.trees[channelsTreeName];
|
|
@@ -760,16 +788,7 @@ export class RemotedFluidDataStoreContext extends FluidDataStoreContext {
|
|
|
760
788
|
});
|
|
761
789
|
|
|
762
790
|
private readonly gcDetailsInInitialSummaryP = new LazyPromise<IGarbageCollectionSummaryDetails>(async () => {
|
|
763
|
-
|
|
764
|
-
if (!(!this.initSnapshotValue || typeof this.initSnapshotValue === "string")
|
|
765
|
-
&& this.initSnapshotValue.blobs[gcBlobKey] !== undefined) {
|
|
766
|
-
return readAndParse<IGarbageCollectionSummaryDetails>(
|
|
767
|
-
this.storage,
|
|
768
|
-
this.initSnapshotValue.blobs[gcBlobKey],
|
|
769
|
-
);
|
|
770
|
-
} else {
|
|
771
|
-
return {};
|
|
772
|
-
}
|
|
791
|
+
return (await this.getBaseSummaryGCDetails()) ?? {};
|
|
773
792
|
});
|
|
774
793
|
|
|
775
794
|
protected async getInitialSnapshotDetails(): Promise<ISnapshotDetails> {
|
|
@@ -783,6 +802,15 @@ export class RemotedFluidDataStoreContext extends FluidDataStoreContext {
|
|
|
783
802
|
public generateAttachMessage(): IAttachMessage {
|
|
784
803
|
throw new Error("Cannot attach remote store");
|
|
785
804
|
}
|
|
805
|
+
|
|
806
|
+
/**
|
|
807
|
+
* @deprecated - Sets the datastore as root, for aliasing purposes: #7948
|
|
808
|
+
* This method should not be used outside of the aliasing context.
|
|
809
|
+
* It will be removed, as the source of truth for this flag will be the aliasing blob.
|
|
810
|
+
*/
|
|
811
|
+
public setRoot(): void {
|
|
812
|
+
this.isRootDataStore = true;
|
|
813
|
+
}
|
|
786
814
|
}
|
|
787
815
|
|
|
788
816
|
/**
|
|
@@ -850,8 +878,10 @@ export class LocalFluidDataStoreContextBase extends FluidDataStoreContext {
|
|
|
850
878
|
);
|
|
851
879
|
addBlobToSummary(summarizeResult, dataStoreAttributesBlobName, JSON.stringify(attributes));
|
|
852
880
|
|
|
853
|
-
// Add GC
|
|
854
|
-
|
|
881
|
+
// Add GC data to the summary if it's not written at the root.
|
|
882
|
+
if (!this.writeGCDataAtRoot) {
|
|
883
|
+
addBlobToSummary(summarizeResult, gcBlobKey, JSON.stringify(this.summarizerNode.getGCSummaryDetails()));
|
|
884
|
+
}
|
|
855
885
|
|
|
856
886
|
// Attach message needs the summary in ITree format. Convert the ISummaryTree into an ITree.
|
|
857
887
|
const snapshot = convertSummaryTreeToITree(summarizeResult.summary);
|
|
@@ -882,7 +912,7 @@ export class LocalFluidDataStoreContextBase extends FluidDataStoreContext {
|
|
|
882
912
|
// If there is no isRootDataStore in the attributes blob, set it to true. This ensures that data
|
|
883
913
|
// stores in older documents are not garbage collected incorrectly. This may lead to additional
|
|
884
914
|
// roots in the document but they won't break.
|
|
885
|
-
this.isRootDataStore = attributes.isRootDataStore ?? true;
|
|
915
|
+
this.isRootDataStore = this.isRootDataStore || (attributes.isRootDataStore ?? true);
|
|
886
916
|
}
|
|
887
917
|
}
|
|
888
918
|
assert(this.pkg !== undefined, 0x152 /* "pkg should be available in local data store" */);
|
|
@@ -900,6 +930,15 @@ export class LocalFluidDataStoreContextBase extends FluidDataStoreContext {
|
|
|
900
930
|
// Local data store does not have initial summary.
|
|
901
931
|
return {};
|
|
902
932
|
}
|
|
933
|
+
|
|
934
|
+
/**
|
|
935
|
+
* @deprecated - Sets the datastore as root, for aliasing purposes: #7948
|
|
936
|
+
* This method should not be used outside of the aliasing context.
|
|
937
|
+
* It will be removed, as the source of truth for this flag will be the aliasing blob.
|
|
938
|
+
*/
|
|
939
|
+
public setRoot(): void {
|
|
940
|
+
this.isRootDataStore = true;
|
|
941
|
+
}
|
|
903
942
|
}
|
|
904
943
|
|
|
905
944
|
/**
|
package/src/dataStores.ts
CHANGED
|
@@ -8,8 +8,6 @@ import { DataCorruptionError, extractSafePropertiesFromMessage } from "@fluidfra
|
|
|
8
8
|
import {
|
|
9
9
|
ISequencedDocumentMessage,
|
|
10
10
|
ISnapshotTree,
|
|
11
|
-
ITreeEntry,
|
|
12
|
-
SummaryType,
|
|
13
11
|
} from "@fluidframework/protocol-definitions";
|
|
14
12
|
import {
|
|
15
13
|
channelsTreeName,
|
|
@@ -21,6 +19,7 @@ import {
|
|
|
21
19
|
IFluidDataStoreChannel,
|
|
22
20
|
IFluidDataStoreContextDetached,
|
|
23
21
|
IGarbageCollectionData,
|
|
22
|
+
IGarbageCollectionSummaryDetails,
|
|
24
23
|
IInboundSignalMessage,
|
|
25
24
|
InboundAttachMessage,
|
|
26
25
|
ISummarizeResult,
|
|
@@ -28,7 +27,6 @@ import {
|
|
|
28
27
|
} from "@fluidframework/runtime-definitions";
|
|
29
28
|
import {
|
|
30
29
|
convertSnapshotTreeToSummaryTree,
|
|
31
|
-
convertSummaryTreeToITree,
|
|
32
30
|
convertToSummaryTree,
|
|
33
31
|
create404Response,
|
|
34
32
|
responseToException,
|
|
@@ -37,10 +35,9 @@ import {
|
|
|
37
35
|
import { ChildLogger, TelemetryDataTag } from "@fluidframework/telemetry-utils";
|
|
38
36
|
import { AttachState } from "@fluidframework/container-definitions";
|
|
39
37
|
import { BlobCacheStorageService, buildSnapshotTree } from "@fluidframework/driver-utils";
|
|
40
|
-
import { assert, Lazy } from "@fluidframework/common-utils";
|
|
38
|
+
import { assert, Lazy, LazyPromise } from "@fluidframework/common-utils";
|
|
41
39
|
import { v4 as uuid } from "uuid";
|
|
42
|
-
import {
|
|
43
|
-
import { GCDataBuilder, getChildNodesUsedRoutes } from "@fluidframework/garbage-collector";
|
|
40
|
+
import { GCDataBuilder, unpackChildNodesUsedRoutes } from "@fluidframework/garbage-collector";
|
|
44
41
|
import { DataStoreContexts } from "./dataStoreContexts";
|
|
45
42
|
import { ContainerRuntime } from "./containerRuntime";
|
|
46
43
|
import {
|
|
@@ -53,6 +50,32 @@ import {
|
|
|
53
50
|
import { IContainerRuntimeMetadata, nonDataStorePaths, rootHasIsolatedChannels } from "./summaryFormat";
|
|
54
51
|
import { IUsedStateStats } from "./garbageCollection";
|
|
55
52
|
|
|
53
|
+
type PendingAliasResolve = (success: boolean) => void;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Interface for an op to be used for assigning an
|
|
57
|
+
* alias to a datastore
|
|
58
|
+
*/
|
|
59
|
+
interface IDataStoreAliasMessage {
|
|
60
|
+
/** The internal id of the datastore */
|
|
61
|
+
readonly internalId: string;
|
|
62
|
+
/** The alias name to be assigned to the datastore */
|
|
63
|
+
readonly alias: string;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Type guard that returns true if the given alias message is actually an instance of
|
|
68
|
+
* a class which implements @see IDataStoreAliasMessage
|
|
69
|
+
* @param maybeDataStoreAliasMessage - message object to be validated
|
|
70
|
+
* @returns True if the @see IDataStoreAliasMessage is fully implemented, false otherwise
|
|
71
|
+
*/
|
|
72
|
+
const isDataStoreAliasMessage = (
|
|
73
|
+
maybeDataStoreAliasMessage: any,
|
|
74
|
+
): maybeDataStoreAliasMessage is IDataStoreAliasMessage => {
|
|
75
|
+
return typeof maybeDataStoreAliasMessage?.internalId === "string"
|
|
76
|
+
&& typeof maybeDataStoreAliasMessage?.alias === "string";
|
|
77
|
+
};
|
|
78
|
+
|
|
56
79
|
/**
|
|
57
80
|
* This class encapsulates data store handling. Currently it is only used by the container runtime,
|
|
58
81
|
* but eventually could be hosted on any channel once we formalize the channel api boundary.
|
|
@@ -67,6 +90,13 @@ export class DataStores implements IDisposable {
|
|
|
67
90
|
|
|
68
91
|
private readonly disposeOnce = new Lazy<void>(() => this.contexts.dispose());
|
|
69
92
|
|
|
93
|
+
public readonly containerLoadStats: {
|
|
94
|
+
// number of dataStores during loadContainer
|
|
95
|
+
readonly containerLoadDataStoreCount: number;
|
|
96
|
+
// number of unreferenced dataStores during loadContainer
|
|
97
|
+
readonly referencedDataStoreCount: number;
|
|
98
|
+
};
|
|
99
|
+
|
|
70
100
|
constructor(
|
|
71
101
|
private readonly baseSnapshot: ISnapshotTree | undefined,
|
|
72
102
|
private readonly runtime: ContainerRuntime,
|
|
@@ -75,12 +105,24 @@ export class DataStores implements IDisposable {
|
|
|
75
105
|
(id: string, createParam: CreateChildSummarizerNodeParam) => CreateChildSummarizerNodeFn,
|
|
76
106
|
private readonly deleteChildSummarizerNodeFn: (id: string) => void,
|
|
77
107
|
baseLogger: ITelemetryBaseLogger,
|
|
108
|
+
getDataStoreBaseGCDetails: () => Promise<Map<string, IGarbageCollectionSummaryDetails>>,
|
|
109
|
+
private readonly dataStoreChanged: (id: string) => void,
|
|
110
|
+
private readonly aliasMap: Map<string, string>,
|
|
78
111
|
private readonly contexts: DataStoreContexts = new DataStoreContexts(baseLogger),
|
|
79
112
|
) {
|
|
80
113
|
this.logger = ChildLogger.create(baseLogger);
|
|
114
|
+
|
|
115
|
+
const baseDataStoresGCDetailsP = new LazyPromise(async () => {
|
|
116
|
+
return getDataStoreBaseGCDetails();
|
|
117
|
+
});
|
|
118
|
+
// Returns the base summary GC details for the data store with the given id.
|
|
119
|
+
const dataStoreBaseGCDetails = async (dataStoreId: string) => {
|
|
120
|
+
const baseGCDetails = await baseDataStoresGCDetailsP;
|
|
121
|
+
return baseGCDetails.get(dataStoreId);
|
|
122
|
+
};
|
|
123
|
+
|
|
81
124
|
// Extract stores stored inside the snapshot
|
|
82
125
|
const fluidDataStores = new Map<string, ISnapshotTree>();
|
|
83
|
-
|
|
84
126
|
if (baseSnapshot) {
|
|
85
127
|
for (const [key, value] of Object.entries(baseSnapshot.trees)) {
|
|
86
128
|
fluidDataStores.set(key, value);
|
|
@@ -101,6 +143,7 @@ export class DataStores implements IDisposable {
|
|
|
101
143
|
dataStoreContext = new RemotedFluidDataStoreContext(
|
|
102
144
|
key,
|
|
103
145
|
value,
|
|
146
|
+
async () => dataStoreBaseGCDetails(key),
|
|
104
147
|
this.runtime,
|
|
105
148
|
this.runtime.storage,
|
|
106
149
|
this.runtime.scope,
|
|
@@ -124,11 +167,14 @@ export class DataStores implements IDisposable {
|
|
|
124
167
|
}
|
|
125
168
|
this.contexts.addBoundOrRemoted(dataStoreContext);
|
|
126
169
|
}
|
|
127
|
-
this.
|
|
128
|
-
|
|
129
|
-
dataStoreCount: fluidDataStores.size,
|
|
170
|
+
this.containerLoadStats = {
|
|
171
|
+
containerLoadDataStoreCount: fluidDataStores.size,
|
|
130
172
|
referencedDataStoreCount: fluidDataStores.size - unreferencedDataStoreCount,
|
|
131
|
-
}
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
public aliases(): ReadonlyMap<string, string> {
|
|
177
|
+
return this.aliasMap;
|
|
132
178
|
}
|
|
133
179
|
|
|
134
180
|
public processAttachMessage(message: ISequencedDocumentMessage, local: boolean) {
|
|
@@ -170,6 +216,8 @@ export class DataStores implements IDisposable {
|
|
|
170
216
|
const remotedFluidDataStoreContext = new RemotedFluidDataStoreContext(
|
|
171
217
|
attachMessage.id,
|
|
172
218
|
snapshotTree,
|
|
219
|
+
// New data stores begin with empty GC details since GC hasn't run on them yet.
|
|
220
|
+
async () => { return {}; },
|
|
173
221
|
this.runtime,
|
|
174
222
|
new BlobCacheStorageService(this.runtime.storage, flatBlobs),
|
|
175
223
|
this.runtime.scope,
|
|
@@ -188,7 +236,6 @@ export class DataStores implements IDisposable {
|
|
|
188
236
|
}),
|
|
189
237
|
pkg);
|
|
190
238
|
|
|
191
|
-
// Resolve pending gets and store off any new ones
|
|
192
239
|
this.contexts.addBoundOrRemoted(remotedFluidDataStoreContext);
|
|
193
240
|
|
|
194
241
|
// Equivalent of nextTick() - Prefetch once all current ops have completed
|
|
@@ -196,6 +243,55 @@ export class DataStores implements IDisposable {
|
|
|
196
243
|
Promise.resolve().then(async () => remotedFluidDataStoreContext.realize());
|
|
197
244
|
}
|
|
198
245
|
|
|
246
|
+
public processAliasMessage(
|
|
247
|
+
message: ISequencedDocumentMessage,
|
|
248
|
+
localOpMetadata: unknown,
|
|
249
|
+
local: boolean,
|
|
250
|
+
): void {
|
|
251
|
+
const aliasMessage = message.contents as IDataStoreAliasMessage;
|
|
252
|
+
if (!isDataStoreAliasMessage(aliasMessage)) {
|
|
253
|
+
throw new DataCorruptionError(
|
|
254
|
+
"malformedDataStoreAliasMessage",
|
|
255
|
+
{
|
|
256
|
+
...extractSafePropertiesFromMessage(message),
|
|
257
|
+
},
|
|
258
|
+
);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
const resolve = localOpMetadata as PendingAliasResolve;
|
|
262
|
+
const aliasResult = this.processAliasMessageCore(aliasMessage);
|
|
263
|
+
if (local) {
|
|
264
|
+
resolve(aliasResult);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
private processAliasMessageCore(aliasMessage: IDataStoreAliasMessage): boolean {
|
|
269
|
+
const existingMapping = this.aliasMap.get(aliasMessage.alias);
|
|
270
|
+
if (existingMapping !== undefined) {
|
|
271
|
+
return false;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Unlikely scenario, but we may receive an alias OP with the alias value
|
|
275
|
+
// equal to one of the ids supplied to `createRootDataStore` in the past
|
|
276
|
+
const maybeContextWithAliasAsId = this.contexts.get(aliasMessage.alias);
|
|
277
|
+
if (maybeContextWithAliasAsId !== undefined) {
|
|
278
|
+
return false;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
const currentContext = this.contexts.get(aliasMessage.internalId);
|
|
282
|
+
if (currentContext === undefined) {
|
|
283
|
+
this.logger.sendErrorEvent({
|
|
284
|
+
eventName: "AliasFluidDataStoreNotFound",
|
|
285
|
+
fluidDataStoreId: aliasMessage.internalId,
|
|
286
|
+
});
|
|
287
|
+
return false;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
this.aliasMap.set(aliasMessage.alias, currentContext.id);
|
|
291
|
+
currentContext.setRoot();
|
|
292
|
+
return true;
|
|
293
|
+
}
|
|
294
|
+
|
|
199
295
|
public bindFluidDataStore(fluidDataStoreRuntime: IFluidDataStoreChannel): void {
|
|
200
296
|
const id = fluidDataStoreRuntime.id;
|
|
201
297
|
const localContext = this.contexts.getUnbound(id);
|
|
@@ -281,11 +377,15 @@ export class DataStores implements IDisposable {
|
|
|
281
377
|
const context = this.contexts.get(envelope.address);
|
|
282
378
|
assert(!!context, 0x162 /* "There should be a store context for the op" */);
|
|
283
379
|
context.process(transformed, local, localMessageMetadata);
|
|
380
|
+
|
|
381
|
+
// Notify that a data store changed. This is used to detect if a deleted data store is being used.
|
|
382
|
+
this.dataStoreChanged(envelope.address);
|
|
284
383
|
}
|
|
285
384
|
|
|
286
385
|
public async getDataStore(id: string, wait: boolean): Promise<FluidDataStoreContext> {
|
|
287
|
-
const
|
|
386
|
+
const internalId = this.aliasMap.get(id) ?? id;
|
|
288
387
|
|
|
388
|
+
const context = await this.contexts.getBoundOrRemoted(internalId, wait);
|
|
289
389
|
if (context === undefined) {
|
|
290
390
|
// The requested data store does not exits. Throw a 404 response exception.
|
|
291
391
|
const request = { url: id };
|
|
@@ -339,44 +439,6 @@ export class DataStores implements IDisposable {
|
|
|
339
439
|
}
|
|
340
440
|
}
|
|
341
441
|
|
|
342
|
-
/**
|
|
343
|
-
* Notifies this object to take the snapshot of the container.
|
|
344
|
-
* @deprecated - Use summarize to get summary of the container runtime.
|
|
345
|
-
*/
|
|
346
|
-
public async snapshot(): Promise<ITreeEntry[]> {
|
|
347
|
-
// Iterate over each store and ask it to snapshot
|
|
348
|
-
const fluidDataStoreSnapshotsP = Array.from(this.contexts).map(async ([fluidDataStoreId, value]) => {
|
|
349
|
-
const summaryTree = await value.summarize(true /* fullTree */, false /* trackState */);
|
|
350
|
-
assert(
|
|
351
|
-
summaryTree.summary.type === SummaryType.Tree,
|
|
352
|
-
0x164 /* "summarize should always return a tree when fullTree is true" */);
|
|
353
|
-
// back-compat summary - Remove this once snapshot is removed.
|
|
354
|
-
const snapshot = convertSummaryTreeToITree(summaryTree.summary);
|
|
355
|
-
|
|
356
|
-
// If ID exists then previous commit is still valid
|
|
357
|
-
return {
|
|
358
|
-
fluidDataStoreId,
|
|
359
|
-
snapshot,
|
|
360
|
-
};
|
|
361
|
-
});
|
|
362
|
-
|
|
363
|
-
const entries: ITreeEntry[] = [];
|
|
364
|
-
|
|
365
|
-
// Add in module references to the store snapshots
|
|
366
|
-
const fluidDataStoreSnapshots = await Promise.all(fluidDataStoreSnapshotsP);
|
|
367
|
-
|
|
368
|
-
// Sort for better diffing of snapshots (in replay tool, used to find bugs in snapshotting logic)
|
|
369
|
-
fluidDataStoreSnapshots.sort((a, b) => a?.fluidDataStoreId.localeCompare(b.fluidDataStoreId));
|
|
370
|
-
|
|
371
|
-
for (const fluidDataStoreSnapshot of fluidDataStoreSnapshots) {
|
|
372
|
-
entries.push(new TreeTreeEntry(
|
|
373
|
-
fluidDataStoreSnapshot.fluidDataStoreId,
|
|
374
|
-
fluidDataStoreSnapshot.snapshot,
|
|
375
|
-
));
|
|
376
|
-
}
|
|
377
|
-
return entries;
|
|
378
|
-
}
|
|
379
|
-
|
|
380
442
|
public get size(): number {
|
|
381
443
|
return this.contexts.size;
|
|
382
444
|
}
|
|
@@ -439,8 +501,8 @@ export class DataStores implements IDisposable {
|
|
|
439
501
|
/**
|
|
440
502
|
* Generates data used for garbage collection. It does the following:
|
|
441
503
|
* 1. Calls into each child data store context to get its GC data.
|
|
442
|
-
* 2.
|
|
443
|
-
*
|
|
504
|
+
* 2. Prefixes the child context's id to the GC nodes in the child's GC data. This makes sure that the node can be
|
|
505
|
+
* identified as belonging to the child.
|
|
444
506
|
* 3. Adds a GC node for this channel to the nodes received from the children. All these nodes together represent
|
|
445
507
|
* the GC data of this channel.
|
|
446
508
|
* @param fullGC - true to bypass optimizations and force full generation of GC data.
|
|
@@ -474,7 +536,7 @@ export class DataStores implements IDisposable {
|
|
|
474
536
|
*/
|
|
475
537
|
public updateUsedRoutes(usedRoutes: string[], gcTimestamp?: number): IUsedStateStats {
|
|
476
538
|
// Get a map of data store ids to routes used in it.
|
|
477
|
-
const usedDataStoreRoutes =
|
|
539
|
+
const usedDataStoreRoutes = unpackChildNodesUsedRoutes(usedRoutes);
|
|
478
540
|
|
|
479
541
|
// Verify that the used routes are correct.
|
|
480
542
|
for (const [id] of usedDataStoreRoutes) {
|