@fluidframework/container-loader 2.0.0-internal.5.0.1 → 2.0.0-internal.5.1.0
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/CHANGELOG.md +4 -0
- package/README.md +21 -0
- package/dist/connectionManager.d.ts.map +1 -1
- package/dist/connectionManager.js +32 -13
- package/dist/connectionManager.js.map +1 -1
- package/dist/connectionStateHandler.d.ts +11 -0
- package/dist/connectionStateHandler.d.ts.map +1 -1
- package/dist/connectionStateHandler.js +24 -1
- package/dist/connectionStateHandler.js.map +1 -1
- package/dist/container.d.ts +15 -11
- package/dist/container.d.ts.map +1 -1
- package/dist/container.js +44 -55
- package/dist/container.js.map +1 -1
- package/dist/containerStorageAdapter.d.ts.map +1 -1
- package/dist/containerStorageAdapter.js +6 -15
- package/dist/containerStorageAdapter.js.map +1 -1
- package/dist/contracts.d.ts +8 -0
- package/dist/contracts.d.ts.map +1 -1
- package/dist/contracts.js.map +1 -1
- package/dist/deltaManager.d.ts +18 -7
- package/dist/deltaManager.d.ts.map +1 -1
- package/dist/deltaManager.js +42 -30
- package/dist/deltaManager.js.map +1 -1
- package/dist/deltaQueue.d.ts +2 -3
- package/dist/deltaQueue.d.ts.map +1 -1
- package/dist/deltaQueue.js +2 -3
- package/dist/deltaQueue.js.map +1 -1
- package/dist/loader.d.ts +0 -2
- package/dist/loader.d.ts.map +1 -1
- package/dist/loader.js +24 -32
- package/dist/loader.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/tsdoc-metadata.json +11 -0
- package/dist/utils.d.ts +2 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +8 -1
- package/dist/utils.js.map +1 -1
- package/lib/connectionManager.d.ts.map +1 -1
- package/lib/connectionManager.js +32 -13
- package/lib/connectionManager.js.map +1 -1
- package/lib/connectionStateHandler.d.ts +11 -0
- package/lib/connectionStateHandler.d.ts.map +1 -1
- package/lib/connectionStateHandler.js +24 -1
- package/lib/connectionStateHandler.js.map +1 -1
- package/lib/container.d.ts +15 -11
- package/lib/container.d.ts.map +1 -1
- package/lib/container.js +44 -55
- package/lib/container.js.map +1 -1
- package/lib/containerStorageAdapter.d.ts.map +1 -1
- package/lib/containerStorageAdapter.js +6 -15
- package/lib/containerStorageAdapter.js.map +1 -1
- package/lib/contracts.d.ts +8 -0
- package/lib/contracts.d.ts.map +1 -1
- package/lib/contracts.js.map +1 -1
- package/lib/deltaManager.d.ts +18 -7
- package/lib/deltaManager.d.ts.map +1 -1
- package/lib/deltaManager.js +44 -32
- package/lib/deltaManager.js.map +1 -1
- package/lib/deltaQueue.d.ts +2 -3
- package/lib/deltaQueue.d.ts.map +1 -1
- package/lib/deltaQueue.js +2 -3
- package/lib/deltaQueue.js.map +1 -1
- package/lib/loader.d.ts +0 -2
- package/lib/loader.d.ts.map +1 -1
- package/lib/loader.js +24 -32
- package/lib/loader.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/utils.d.ts +2 -0
- package/lib/utils.d.ts.map +1 -1
- package/lib/utils.js +7 -1
- package/lib/utils.js.map +1 -1
- package/package.json +17 -36
- package/src/connectionManager.ts +33 -15
- package/src/connectionStateHandler.ts +45 -1
- package/src/container.ts +87 -75
- package/src/containerStorageAdapter.ts +4 -16
- package/src/contracts.ts +10 -0
- package/src/deltaManager.ts +52 -33
- package/src/deltaQueue.ts +2 -3
- package/src/loader.ts +33 -40
- package/src/packageVersion.ts +1 -1
- package/src/utils.ts +15 -1
package/src/container.ts
CHANGED
|
@@ -342,17 +342,6 @@ export class Container
|
|
|
342
342
|
{
|
|
343
343
|
public static version = "^0.1.0";
|
|
344
344
|
|
|
345
|
-
public static async clone(
|
|
346
|
-
container: Container,
|
|
347
|
-
loadProps: IContainerLoadProps,
|
|
348
|
-
createParamOverrides: Partial<IContainerCreateProps>,
|
|
349
|
-
) {
|
|
350
|
-
return this.load(loadProps, {
|
|
351
|
-
...container.createProps,
|
|
352
|
-
...createParamOverrides,
|
|
353
|
-
});
|
|
354
|
-
}
|
|
355
|
-
|
|
356
345
|
/**
|
|
357
346
|
* Load an existing container.
|
|
358
347
|
* @internal
|
|
@@ -451,14 +440,30 @@ export class Container
|
|
|
451
440
|
);
|
|
452
441
|
}
|
|
453
442
|
|
|
454
|
-
public subLogger: TelemetryLogger;
|
|
455
|
-
|
|
456
443
|
// Tells if container can reconnect on losing fist connection
|
|
457
444
|
// If false, container gets closed on loss of connection.
|
|
458
|
-
private readonly _canReconnect: boolean
|
|
445
|
+
private readonly _canReconnect: boolean;
|
|
446
|
+
private readonly clientDetailsOverride: IClientDetails | undefined;
|
|
447
|
+
private readonly urlResolver: IUrlResolver;
|
|
448
|
+
private readonly serviceFactory: IDocumentServiceFactory;
|
|
449
|
+
private readonly codeLoader: ICodeDetailsLoader;
|
|
450
|
+
public readonly options: ILoaderOptions;
|
|
451
|
+
private readonly scope: FluidObject;
|
|
452
|
+
public subLogger: TelemetryLogger;
|
|
453
|
+
private readonly detachedBlobStorage: IDetachedBlobStorage | undefined;
|
|
454
|
+
private readonly protocolHandlerBuilder: ProtocolHandlerBuilder;
|
|
459
455
|
|
|
460
456
|
private readonly mc: MonitoringContext;
|
|
461
457
|
|
|
458
|
+
/**
|
|
459
|
+
* Used by the RelativeLoader to spawn a new Container for the same document. Used to create the summarizing client.
|
|
460
|
+
* @internal
|
|
461
|
+
*/
|
|
462
|
+
public readonly clone: (
|
|
463
|
+
loadProps: IContainerLoadProps,
|
|
464
|
+
createParamOverrides: Partial<IContainerCreateProps>,
|
|
465
|
+
) => Promise<Container>;
|
|
466
|
+
|
|
462
467
|
/**
|
|
463
468
|
* Lifecycle state of the container, used mainly to prevent re-entrancy and telemetry
|
|
464
469
|
*
|
|
@@ -508,7 +513,6 @@ export class Container
|
|
|
508
513
|
return this.storageAdapter;
|
|
509
514
|
}
|
|
510
515
|
|
|
511
|
-
private readonly clientDetailsOverride: IClientDetails | undefined;
|
|
512
516
|
private readonly _deltaManager: DeltaManager<ConnectionManager>;
|
|
513
517
|
private service: IDocumentService | undefined;
|
|
514
518
|
|
|
@@ -672,20 +676,6 @@ export class Container
|
|
|
672
676
|
return this._dirtyContainer;
|
|
673
677
|
}
|
|
674
678
|
|
|
675
|
-
private get serviceFactory() {
|
|
676
|
-
return this.createProps.documentServiceFactory;
|
|
677
|
-
}
|
|
678
|
-
private get urlResolver() {
|
|
679
|
-
return this.createProps.urlResolver;
|
|
680
|
-
}
|
|
681
|
-
public readonly options: ILoaderOptions;
|
|
682
|
-
private get scope() {
|
|
683
|
-
return this.createProps.scope;
|
|
684
|
-
}
|
|
685
|
-
private get codeLoader() {
|
|
686
|
-
return this.createProps.codeLoader;
|
|
687
|
-
}
|
|
688
|
-
|
|
689
679
|
/**
|
|
690
680
|
* {@inheritDoc @fluidframework/container-definitions#IContainer.entryPoint}
|
|
691
681
|
*/
|
|
@@ -725,8 +715,8 @@ export class Container
|
|
|
725
715
|
* @internal
|
|
726
716
|
*/
|
|
727
717
|
constructor(
|
|
728
|
-
|
|
729
|
-
loadProps?: IContainerLoadProps,
|
|
718
|
+
createProps: IContainerCreateProps,
|
|
719
|
+
loadProps?: Pick<IContainerLoadProps, "pendingLocalState">,
|
|
730
720
|
) {
|
|
731
721
|
super((name, error) => {
|
|
732
722
|
this.mc.logger.sendErrorEvent(
|
|
@@ -738,10 +728,46 @@ export class Container
|
|
|
738
728
|
);
|
|
739
729
|
});
|
|
740
730
|
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
731
|
+
const {
|
|
732
|
+
canReconnect,
|
|
733
|
+
clientDetailsOverride,
|
|
734
|
+
urlResolver,
|
|
735
|
+
documentServiceFactory,
|
|
736
|
+
codeLoader,
|
|
737
|
+
options,
|
|
738
|
+
scope,
|
|
739
|
+
subLogger,
|
|
740
|
+
detachedBlobStorage,
|
|
741
|
+
protocolHandlerBuilder,
|
|
742
|
+
} = createProps;
|
|
743
|
+
|
|
744
|
+
this.connectionTransitionTimes[ConnectionState.Disconnected] = performance.now();
|
|
745
|
+
const pendingLocalState = loadProps?.pendingLocalState;
|
|
746
|
+
|
|
747
|
+
this._canReconnect = canReconnect ?? true;
|
|
748
|
+
this.clientDetailsOverride = clientDetailsOverride;
|
|
749
|
+
this.urlResolver = urlResolver;
|
|
750
|
+
this.serviceFactory = documentServiceFactory;
|
|
751
|
+
this.codeLoader = codeLoader;
|
|
752
|
+
// Warning: this is only a shallow clone. Mutation of any individual loader option will mutate it for
|
|
753
|
+
// all clients that were loaded from the same loader (including summarizer clients).
|
|
754
|
+
// Tracking alternative ways to handle this in AB#4129.
|
|
755
|
+
this.options = { ...options };
|
|
756
|
+
this.scope = scope;
|
|
757
|
+
this.detachedBlobStorage = detachedBlobStorage;
|
|
758
|
+
this.protocolHandlerBuilder =
|
|
759
|
+
protocolHandlerBuilder ?? ((...args) => new ProtocolHandler(...args, new Audience()));
|
|
760
|
+
|
|
761
|
+
// Note that we capture the createProps here so we can replicate the creation call when we want to clone.
|
|
762
|
+
this.clone = async (
|
|
763
|
+
_loadProps: IContainerLoadProps,
|
|
764
|
+
createParamOverrides: Partial<IContainerCreateProps>,
|
|
765
|
+
) => {
|
|
766
|
+
return Container.load(_loadProps, {
|
|
767
|
+
...createProps,
|
|
768
|
+
...createParamOverrides,
|
|
769
|
+
});
|
|
770
|
+
};
|
|
745
771
|
|
|
746
772
|
// Create logger for data stores to use
|
|
747
773
|
const type = this.client.details.type;
|
|
@@ -751,7 +777,7 @@ export class Container
|
|
|
751
777
|
}`;
|
|
752
778
|
// Need to use the property getter for docId because for detached flow we don't have the docId initially.
|
|
753
779
|
// We assign the id later so property getter is used.
|
|
754
|
-
this.subLogger = ChildLogger.create(
|
|
780
|
+
this.subLogger = ChildLogger.create(subLogger, undefined, {
|
|
755
781
|
all: {
|
|
756
782
|
clientType, // Differentiating summarizer container from main container
|
|
757
783
|
containerId: uuid(),
|
|
@@ -759,7 +785,7 @@ export class Container
|
|
|
759
785
|
containerAttachState: () => this._attachState,
|
|
760
786
|
containerLifecycleState: () => this._lifecycleState,
|
|
761
787
|
containerConnectionState: () => ConnectionState[this.connectionState],
|
|
762
|
-
serializedContainer:
|
|
788
|
+
serializedContainer: pendingLocalState !== undefined,
|
|
763
789
|
},
|
|
764
790
|
// we need to be judicious with our logging here to avoid generating too much data
|
|
765
791
|
// all data logged here should be broadly applicable, and not specific to a
|
|
@@ -785,13 +811,6 @@ export class Container
|
|
|
785
811
|
// Prefix all events in this file with container-loader
|
|
786
812
|
this.mc = loggerToMonitoringContext(ChildLogger.create(this.subLogger, "Container"));
|
|
787
813
|
|
|
788
|
-
// Warning: this is only a shallow clone. Mutation of any individual loader option will mutate it for
|
|
789
|
-
// all clients that were loaded from the same loader (including summarizer clients).
|
|
790
|
-
// Tracking alternative ways to handle this in AB#4129.
|
|
791
|
-
this.options = {
|
|
792
|
-
...this.createProps.options,
|
|
793
|
-
};
|
|
794
|
-
|
|
795
814
|
this._deltaManager = this.createDeltaManager();
|
|
796
815
|
|
|
797
816
|
this.connectionStateHandler = createConnectionStateHandler(
|
|
@@ -812,7 +831,7 @@ export class Container
|
|
|
812
831
|
}
|
|
813
832
|
},
|
|
814
833
|
shouldClientJoinWrite: () => this._deltaManager.connectionManager.shouldJoinWrite(),
|
|
815
|
-
maxClientLeaveWaitTime:
|
|
834
|
+
maxClientLeaveWaitTime: options.maxClientLeaveWaitTime,
|
|
816
835
|
logConnectionIssue: (
|
|
817
836
|
eventName: string,
|
|
818
837
|
category: TelemetryEventCategory,
|
|
@@ -849,7 +868,7 @@ export class Container
|
|
|
849
868
|
},
|
|
850
869
|
},
|
|
851
870
|
this.deltaManager,
|
|
852
|
-
|
|
871
|
+
pendingLocalState?.clientId,
|
|
853
872
|
);
|
|
854
873
|
|
|
855
874
|
this.on(savedContainerEvent, () => {
|
|
@@ -868,12 +887,12 @@ export class Container
|
|
|
868
887
|
// Even if not forced on via this flag, combined summaries may still be enabled by service policy.
|
|
869
888
|
const forceEnableSummarizeProtocolTree =
|
|
870
889
|
this.mc.config.getBoolean("Fluid.Container.summarizeProtocolTree2") ??
|
|
871
|
-
|
|
890
|
+
options.summarizeProtocolTree;
|
|
872
891
|
|
|
873
892
|
this.storageAdapter = new ContainerStorageAdapter(
|
|
874
|
-
|
|
893
|
+
detachedBlobStorage,
|
|
875
894
|
this.mc.logger,
|
|
876
|
-
|
|
895
|
+
pendingLocalState?.snapshotBlobs,
|
|
877
896
|
addProtocolSummaryIfMissing,
|
|
878
897
|
forceEnableSummarizeProtocolTree,
|
|
879
898
|
);
|
|
@@ -908,7 +927,7 @@ export class Container
|
|
|
908
927
|
}
|
|
909
928
|
|
|
910
929
|
public dispose(error?: ICriticalContainerError) {
|
|
911
|
-
this._deltaManager.
|
|
930
|
+
this._deltaManager.dispose(error);
|
|
912
931
|
this.verifyClosed();
|
|
913
932
|
}
|
|
914
933
|
|
|
@@ -1078,7 +1097,7 @@ export class Container
|
|
|
1078
1097
|
const protocolSummary = this.captureProtocolSummary();
|
|
1079
1098
|
const combinedSummary = combineAppAndProtocolSummary(appSummary, protocolSummary);
|
|
1080
1099
|
|
|
1081
|
-
if (this.
|
|
1100
|
+
if (this.detachedBlobStorage && this.detachedBlobStorage.size > 0) {
|
|
1082
1101
|
combinedSummary.tree[".hasAttachmentBlobs"] = {
|
|
1083
1102
|
type: SummaryType.Blob,
|
|
1084
1103
|
content: "true",
|
|
@@ -1108,8 +1127,7 @@ export class Container
|
|
|
1108
1127
|
|
|
1109
1128
|
// If attachment blobs were uploaded in detached state we will go through a different attach flow
|
|
1110
1129
|
const hasAttachmentBlobs =
|
|
1111
|
-
this.
|
|
1112
|
-
this.createProps.detachedBlobStorage.size > 0;
|
|
1130
|
+
this.detachedBlobStorage !== undefined && this.detachedBlobStorage.size > 0;
|
|
1113
1131
|
|
|
1114
1132
|
try {
|
|
1115
1133
|
assert(
|
|
@@ -1167,7 +1185,7 @@ export class Container
|
|
|
1167
1185
|
if (hasAttachmentBlobs) {
|
|
1168
1186
|
// upload blobs to storage
|
|
1169
1187
|
assert(
|
|
1170
|
-
!!this.
|
|
1188
|
+
!!this.detachedBlobStorage,
|
|
1171
1189
|
0x24e /* "assertion for type narrowing" */,
|
|
1172
1190
|
);
|
|
1173
1191
|
|
|
@@ -1175,14 +1193,12 @@ export class Container
|
|
|
1175
1193
|
// support blob handles that only know about the local IDs
|
|
1176
1194
|
const redirectTable = new Map<string, string>();
|
|
1177
1195
|
// if new blobs are added while uploading, upload them too
|
|
1178
|
-
while (redirectTable.size < this.
|
|
1179
|
-
const newIds = this.
|
|
1196
|
+
while (redirectTable.size < this.detachedBlobStorage.size) {
|
|
1197
|
+
const newIds = this.detachedBlobStorage
|
|
1180
1198
|
.getBlobIds()
|
|
1181
1199
|
.filter((id) => !redirectTable.has(id));
|
|
1182
1200
|
for (const id of newIds) {
|
|
1183
|
-
const blob = await this.
|
|
1184
|
-
id,
|
|
1185
|
-
);
|
|
1201
|
+
const blob = await this.detachedBlobStorage.readBlob(id);
|
|
1186
1202
|
const response = await this.storageAdapter.createBlob(blob);
|
|
1187
1203
|
redirectTable.set(id, response.id);
|
|
1188
1204
|
}
|
|
@@ -1376,15 +1392,7 @@ export class Container
|
|
|
1376
1392
|
return versions[0];
|
|
1377
1393
|
}
|
|
1378
1394
|
|
|
1379
|
-
private recordConnectStartTime() {
|
|
1380
|
-
if (this.connectionTransitionTimes[ConnectionState.Disconnected] === undefined) {
|
|
1381
|
-
this.connectionTransitionTimes[ConnectionState.Disconnected] = performance.now();
|
|
1382
|
-
}
|
|
1383
|
-
}
|
|
1384
|
-
|
|
1385
1395
|
private connectToDeltaStream(args: IConnectionArgs) {
|
|
1386
|
-
this.recordConnectStartTime();
|
|
1387
|
-
|
|
1388
1396
|
// All agents need "write" access, including summarizer.
|
|
1389
1397
|
if (!this._canReconnect || !this.client.details.capabilities.interactive) {
|
|
1390
1398
|
args.mode = "write";
|
|
@@ -1619,8 +1627,7 @@ export class Container
|
|
|
1619
1627
|
private async rehydrateDetachedFromSnapshot(detachedContainerSnapshot: ISummaryTree) {
|
|
1620
1628
|
if (detachedContainerSnapshot.tree[".hasAttachmentBlobs"] !== undefined) {
|
|
1621
1629
|
assert(
|
|
1622
|
-
!!this.
|
|
1623
|
-
this.createProps.detachedBlobStorage.size > 0,
|
|
1630
|
+
!!this.detachedBlobStorage && this.detachedBlobStorage.size > 0,
|
|
1624
1631
|
0x250 /* "serialized container with attachment blobs must be rehydrated with detached blob storage" */,
|
|
1625
1632
|
);
|
|
1626
1633
|
delete detachedContainerSnapshot.tree[".hasAttachmentBlobs"];
|
|
@@ -1717,10 +1724,7 @@ export class Container
|
|
|
1717
1724
|
attributes: IDocumentAttributes,
|
|
1718
1725
|
quorumSnapshot: IQuorumSnapshot,
|
|
1719
1726
|
): void {
|
|
1720
|
-
const protocolHandlerBuilder
|
|
1721
|
-
this.createProps.protocolHandlerBuilder ??
|
|
1722
|
-
((...args) => new ProtocolHandler(...args, new Audience()));
|
|
1723
|
-
const protocol = protocolHandlerBuilder(attributes, quorumSnapshot, (key, value) =>
|
|
1727
|
+
const protocol = this.protocolHandlerBuilder(attributes, quorumSnapshot, (key, value) =>
|
|
1724
1728
|
this.submitMessage(MessageType.Propose, JSON.stringify({ key, value })),
|
|
1725
1729
|
);
|
|
1726
1730
|
|
|
@@ -1859,6 +1863,14 @@ export class Container
|
|
|
1859
1863
|
this.connectionStateHandler.receivedConnectEvent(details);
|
|
1860
1864
|
});
|
|
1861
1865
|
|
|
1866
|
+
deltaManager.on("establishingConnection", (reason: string) => {
|
|
1867
|
+
this.connectionStateHandler.establishingConnection(reason);
|
|
1868
|
+
});
|
|
1869
|
+
|
|
1870
|
+
deltaManager.on("cancelEstablishingConnection", (reason: string) => {
|
|
1871
|
+
this.connectionStateHandler.cancelEstablishingConnection(reason);
|
|
1872
|
+
});
|
|
1873
|
+
|
|
1862
1874
|
deltaManager.on("disconnect", (reason: string, error?: IAnyDriverError) => {
|
|
1863
1875
|
this.collabWindowTracker?.stopSequenceNumberUpdate();
|
|
1864
1876
|
if (!this.closed) {
|
|
@@ -1935,8 +1947,8 @@ export class Container
|
|
|
1935
1947
|
durationFromDisconnected =
|
|
1936
1948
|
time - this.connectionTransitionTimes[ConnectionState.Disconnected];
|
|
1937
1949
|
durationFromDisconnected = TelemetryLogger.formatTick(durationFromDisconnected);
|
|
1938
|
-
} else {
|
|
1939
|
-
// This info is of most
|
|
1950
|
+
} else if (value === ConnectionState.CatchingUp) {
|
|
1951
|
+
// This info is of most interesting while Catching Up.
|
|
1940
1952
|
checkpointSequenceNumber = this.deltaManager.lastKnownSeqNumber;
|
|
1941
1953
|
if (this.deltaManager.hasCheckpointSequenceNumber) {
|
|
1942
1954
|
opsBehind = checkpointSequenceNumber - this.deltaManager.lastSequenceNumber;
|
|
@@ -229,12 +229,6 @@ class BlobOnlyStorage implements IDocumentStorageService {
|
|
|
229
229
|
}
|
|
230
230
|
}
|
|
231
231
|
|
|
232
|
-
// runtime will write a tree to the summary containing only "attachment" type entries
|
|
233
|
-
// which reference attachment blobs by ID. However, some drivers do not support this type
|
|
234
|
-
// and will convert them to "blob" type entries. We want to avoid saving these to reduce
|
|
235
|
-
// the size of stashed change blobs.
|
|
236
|
-
const blobsTreeName = ".blobs";
|
|
237
|
-
|
|
238
232
|
/**
|
|
239
233
|
* Get blob contents of a snapshot tree from storage (or, ideally, cache)
|
|
240
234
|
*/
|
|
@@ -251,13 +245,10 @@ async function getBlobContentsFromTreeCore(
|
|
|
251
245
|
tree: ISnapshotTree,
|
|
252
246
|
blobs: ISerializableBlobContents,
|
|
253
247
|
storage: IDocumentStorageService,
|
|
254
|
-
root = true,
|
|
255
248
|
) {
|
|
256
249
|
const treePs: Promise<any>[] = [];
|
|
257
|
-
for (const
|
|
258
|
-
|
|
259
|
-
treePs.push(getBlobContentsFromTreeCore(subTree, blobs, storage, false));
|
|
260
|
-
}
|
|
250
|
+
for (const subTree of Object.values(tree.trees)) {
|
|
251
|
+
treePs.push(getBlobContentsFromTreeCore(subTree, blobs, storage));
|
|
261
252
|
}
|
|
262
253
|
for (const id of Object.values(tree.blobs)) {
|
|
263
254
|
const blob = await storage.readBlob(id);
|
|
@@ -281,12 +272,9 @@ export function getBlobContentsFromTreeWithBlobContents(
|
|
|
281
272
|
function getBlobContentsFromTreeWithBlobContentsCore(
|
|
282
273
|
tree: ISnapshotTreeWithBlobContents,
|
|
283
274
|
blobs: ISerializableBlobContents,
|
|
284
|
-
root = true,
|
|
285
275
|
) {
|
|
286
|
-
for (const
|
|
287
|
-
|
|
288
|
-
getBlobContentsFromTreeWithBlobContentsCore(subTree, blobs, false);
|
|
289
|
-
}
|
|
276
|
+
for (const subTree of Object.values(tree.trees)) {
|
|
277
|
+
getBlobContentsFromTreeWithBlobContentsCore(subTree, blobs);
|
|
290
278
|
}
|
|
291
279
|
for (const id of Object.values(tree.blobs)) {
|
|
292
280
|
const blob = tree.blobsContents[id];
|
package/src/contracts.ts
CHANGED
|
@@ -167,6 +167,16 @@ export interface IConnectionManagerFactoryArgs {
|
|
|
167
167
|
* `undefined` indicates that user permissions are not yet known.
|
|
168
168
|
*/
|
|
169
169
|
readonly readonlyChangeHandler: (readonly?: boolean) => void;
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Called whenever we try to start establishing a new connection.
|
|
173
|
+
*/
|
|
174
|
+
readonly establishConnectionHandler: (reason: string) => void;
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Called whenever we cancel the connection in progress.
|
|
178
|
+
*/
|
|
179
|
+
readonly cancelConnectionHandler: (reason: string) => void;
|
|
170
180
|
}
|
|
171
181
|
|
|
172
182
|
/**
|
package/src/deltaManager.ts
CHANGED
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
normalizeError,
|
|
25
25
|
logIfFalse,
|
|
26
26
|
safeRaiseEvent,
|
|
27
|
+
isFluidError,
|
|
27
28
|
ITelemetryLoggerExt,
|
|
28
29
|
} from "@fluidframework/telemetry-utils";
|
|
29
30
|
import {
|
|
@@ -45,6 +46,7 @@ import {
|
|
|
45
46
|
DataCorruptionError,
|
|
46
47
|
extractSafePropertiesFromMessage,
|
|
47
48
|
DataProcessingError,
|
|
49
|
+
UsageError,
|
|
48
50
|
} from "@fluidframework/container-utils";
|
|
49
51
|
import { IConnectionManagerFactoryArgs, IConnectionManager } from "./contracts";
|
|
50
52
|
import { DeltaQueue } from "./deltaQueue";
|
|
@@ -64,6 +66,8 @@ export interface IDeltaManagerInternalEvents extends IDeltaManagerEvents {
|
|
|
64
66
|
(event: "throttled", listener: (error: IThrottlingWarning) => void);
|
|
65
67
|
(event: "closed" | "disposed", listener: (error?: ICriticalContainerError) => void);
|
|
66
68
|
(event: "connect", listener: (details: IConnectionDetailsInternal, opsBehind?: number) => void);
|
|
69
|
+
(event: "establishingConnection", listener: (reason: string) => void);
|
|
70
|
+
(event: "cancelEstablishingConnection", listener: (reason: string) => void);
|
|
67
71
|
}
|
|
68
72
|
|
|
69
73
|
/**
|
|
@@ -369,6 +373,8 @@ export class DeltaManager<TConnectionManager extends IConnectionManager>
|
|
|
369
373
|
pongHandler: (latency: number) => this.emit("pong", latency),
|
|
370
374
|
readonlyChangeHandler: (readonly?: boolean) =>
|
|
371
375
|
safeRaiseEvent(this, this.logger, "readonly", readonly),
|
|
376
|
+
establishConnectionHandler: (reason: string) => this.establishingConnection(reason),
|
|
377
|
+
cancelConnectionHandler: (reason: string) => this.cancelEstablishingConnection(reason),
|
|
372
378
|
};
|
|
373
379
|
|
|
374
380
|
this.connectionManager = createConnectionManager(props);
|
|
@@ -408,6 +414,14 @@ export class DeltaManager<TConnectionManager extends IConnectionManager>
|
|
|
408
414
|
// - inbound & inboundSignal are resumed in attachOpHandler() when we have handler setup
|
|
409
415
|
}
|
|
410
416
|
|
|
417
|
+
private cancelEstablishingConnection(reason: string) {
|
|
418
|
+
this.emit("cancelEstablishingConnection", reason);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
private establishingConnection(reason: string) {
|
|
422
|
+
this.emit("establishingConnection", reason);
|
|
423
|
+
}
|
|
424
|
+
|
|
411
425
|
private connectHandler(connection: IConnectionDetailsInternal) {
|
|
412
426
|
this.refreshDelayInfo(this.deltaStreamDelayId);
|
|
413
427
|
|
|
@@ -457,10 +471,6 @@ export class DeltaManager<TConnectionManager extends IConnectionManager>
|
|
|
457
471
|
}
|
|
458
472
|
}
|
|
459
473
|
|
|
460
|
-
public dispose() {
|
|
461
|
-
throw new Error("Not implemented.");
|
|
462
|
-
}
|
|
463
|
-
|
|
464
474
|
/**
|
|
465
475
|
* Sets the sequence number from which inbound messages should be returned
|
|
466
476
|
*/
|
|
@@ -648,23 +658,51 @@ export class DeltaManager<TConnectionManager extends IConnectionManager>
|
|
|
648
658
|
/**
|
|
649
659
|
* Closes the connection and clears inbound & outbound queues.
|
|
650
660
|
*
|
|
651
|
-
*
|
|
652
|
-
*
|
|
653
|
-
* -
|
|
654
|
-
* -
|
|
655
|
-
* - dispose can be called after closure, but not vis versa
|
|
661
|
+
* Differences from dispose:
|
|
662
|
+
* - close will trigger readonly notification
|
|
663
|
+
* - close emits "closed"
|
|
664
|
+
* - close cannot be called after dispose
|
|
656
665
|
*/
|
|
657
|
-
public close(error?: ICriticalContainerError
|
|
666
|
+
public close(error?: ICriticalContainerError): void {
|
|
658
667
|
if (this._closed) {
|
|
659
|
-
if (doDispose === true) {
|
|
660
|
-
this.disposeInternal(error);
|
|
661
|
-
}
|
|
662
668
|
return;
|
|
663
669
|
}
|
|
664
670
|
this._closed = true;
|
|
665
671
|
|
|
666
|
-
this.connectionManager.dispose(error,
|
|
672
|
+
this.connectionManager.dispose(error, true /* switchToReadonly */);
|
|
673
|
+
this.clearQueues();
|
|
674
|
+
this.emit("closed", error);
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
/**
|
|
678
|
+
* Disposes the connection and clears the inbound & outbound queues.
|
|
679
|
+
*
|
|
680
|
+
* Differences from close:
|
|
681
|
+
* - dispose will emit "disposed"
|
|
682
|
+
* - dispose will remove all listeners
|
|
683
|
+
* - dispose can be called after closure
|
|
684
|
+
*/
|
|
685
|
+
public dispose(error?: Error | ICriticalContainerError): void {
|
|
686
|
+
if (this._disposed) {
|
|
687
|
+
return;
|
|
688
|
+
}
|
|
689
|
+
if (error !== undefined && !isFluidError(error)) {
|
|
690
|
+
throw new UsageError("Error must be a Fluid error");
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
this._disposed = true;
|
|
694
|
+
this._closed = true; // We consider "disposed" as a further state than "closed"
|
|
667
695
|
|
|
696
|
+
this.connectionManager.dispose(error, false /* switchToReadonly */);
|
|
697
|
+
this.clearQueues();
|
|
698
|
+
|
|
699
|
+
// This needs to be the last thing we do (before removing listeners), as it causes
|
|
700
|
+
// Container to dispose context and break ability of data stores / runtime to "hear" from delta manager.
|
|
701
|
+
this.emit("disposed", error);
|
|
702
|
+
this.removeAllListeners();
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
private clearQueues() {
|
|
668
706
|
this.closeAbortController.abort();
|
|
669
707
|
|
|
670
708
|
this._inbound.clear();
|
|
@@ -677,25 +715,6 @@ export class DeltaManager<TConnectionManager extends IConnectionManager>
|
|
|
677
715
|
|
|
678
716
|
// Drop pending messages - this will ensure catchUp() does not go into infinite loop
|
|
679
717
|
this.pending = [];
|
|
680
|
-
|
|
681
|
-
if (doDispose === true) {
|
|
682
|
-
this.disposeInternal(error);
|
|
683
|
-
} else {
|
|
684
|
-
this.emit("closed", error);
|
|
685
|
-
}
|
|
686
|
-
}
|
|
687
|
-
|
|
688
|
-
private disposeInternal(error?: ICriticalContainerError): void {
|
|
689
|
-
if (this._disposed) {
|
|
690
|
-
return;
|
|
691
|
-
}
|
|
692
|
-
this._disposed = true;
|
|
693
|
-
|
|
694
|
-
// This needs to be the last thing we do (before removing listeners), as it causes
|
|
695
|
-
// Container to dispose context and break ability of data stores / runtime to "hear"
|
|
696
|
-
// from delta manager, including notification (above) about readonly state.
|
|
697
|
-
this.emit("disposed", error);
|
|
698
|
-
this.removeAllListeners();
|
|
699
718
|
}
|
|
700
719
|
|
|
701
720
|
public refreshDelayInfo(id: string) {
|
package/src/deltaQueue.ts
CHANGED
|
@@ -20,8 +20,8 @@ export class DeltaQueue<T>
|
|
|
20
20
|
private readonly q = new Deque<T>();
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
|
-
* Tracks the number of pause requests for the queue
|
|
24
|
-
* The DeltaQueue is
|
|
23
|
+
* Tracks the number of pause requests for the queue.
|
|
24
|
+
* The DeltaQueue is created initially paused.
|
|
25
25
|
*/
|
|
26
26
|
private pauseCount = 1;
|
|
27
27
|
|
|
@@ -58,7 +58,6 @@ export class DeltaQueue<T>
|
|
|
58
58
|
|
|
59
59
|
/**
|
|
60
60
|
* @param worker - A callback to process a delta.
|
|
61
|
-
* @param logger - For logging telemetry.
|
|
62
61
|
*/
|
|
63
62
|
constructor(private readonly worker: (delta: T) => void) {
|
|
64
63
|
super();
|
package/src/loader.ts
CHANGED
|
@@ -79,8 +79,7 @@ export class RelativeLoader implements ILoader {
|
|
|
79
79
|
return this.container;
|
|
80
80
|
} else {
|
|
81
81
|
ensureResolvedUrlDefined(this.container.resolvedUrl);
|
|
82
|
-
const container = await
|
|
83
|
-
this.container,
|
|
82
|
+
const container = await this.container.clone(
|
|
84
83
|
{
|
|
85
84
|
resolvedUrl: { ...this.container.resolvedUrl },
|
|
86
85
|
version: request.headers?.[LoaderHeader.version] ?? undefined,
|
|
@@ -308,28 +307,41 @@ export class Loader implements IHostLoader {
|
|
|
308
307
|
private readonly mc: MonitoringContext;
|
|
309
308
|
|
|
310
309
|
constructor(loaderProps: ILoaderProps) {
|
|
311
|
-
const
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
310
|
+
const {
|
|
311
|
+
urlResolver,
|
|
312
|
+
documentServiceFactory,
|
|
313
|
+
codeLoader,
|
|
314
|
+
options,
|
|
315
|
+
scope,
|
|
316
|
+
logger,
|
|
317
|
+
detachedBlobStorage,
|
|
318
|
+
configProvider,
|
|
319
|
+
protocolHandlerBuilder,
|
|
320
|
+
} = loaderProps;
|
|
321
|
+
|
|
315
322
|
const telemetryProps = {
|
|
316
323
|
loaderId: uuid(),
|
|
317
324
|
loaderVersion: pkgVersion,
|
|
318
325
|
};
|
|
319
326
|
|
|
320
327
|
const subMc = mixinMonitoringContext(
|
|
321
|
-
DebugLogger.mixinDebugLogger("fluid:telemetry",
|
|
328
|
+
DebugLogger.mixinDebugLogger("fluid:telemetry", logger, {
|
|
322
329
|
all: telemetryProps,
|
|
323
330
|
}),
|
|
324
331
|
sessionStorageConfigProvider.value,
|
|
325
|
-
|
|
332
|
+
configProvider,
|
|
326
333
|
);
|
|
327
334
|
|
|
328
335
|
this.services = {
|
|
329
|
-
|
|
330
|
-
|
|
336
|
+
urlResolver,
|
|
337
|
+
documentServiceFactory,
|
|
338
|
+
codeLoader,
|
|
339
|
+
options: options ?? {},
|
|
340
|
+
scope:
|
|
341
|
+
options?.provideScopeLoader !== false ? { ...scope, ILoader: this } : { ...scope },
|
|
342
|
+
detachedBlobStorage,
|
|
343
|
+
protocolHandlerBuilder,
|
|
331
344
|
subLogger: subMc.logger,
|
|
332
|
-
options: loaderProps.options ?? {},
|
|
333
345
|
};
|
|
334
346
|
this.mc = loggerToMonitoringContext(ChildLogger.create(this.services.subLogger, "Loader"));
|
|
335
347
|
}
|
|
@@ -431,11 +443,18 @@ export class Loader implements IHostLoader {
|
|
|
431
443
|
}
|
|
432
444
|
}
|
|
433
445
|
|
|
434
|
-
|
|
435
|
-
|
|
446
|
+
request.headers ??= {};
|
|
447
|
+
// If set in both query string and headers, use query string. Also write the value from the query string into the header either way.
|
|
448
|
+
request.headers[LoaderHeader.version] =
|
|
449
|
+
parsed.version ?? request.headers[LoaderHeader.version];
|
|
450
|
+
const canCache =
|
|
451
|
+
this.cachingEnabled &&
|
|
452
|
+
request.headers[LoaderHeader.cache] !== false &&
|
|
453
|
+
pendingLocalState === undefined;
|
|
454
|
+
const fromSequenceNumber = request.headers[LoaderHeader.sequenceNumber] ?? -1;
|
|
436
455
|
|
|
437
456
|
let container: Container;
|
|
438
|
-
if (
|
|
457
|
+
if (canCache) {
|
|
439
458
|
const key = this.getKeyForContainerCache(request, parsed);
|
|
440
459
|
const maybeContainer = await this.containers.get(key);
|
|
441
460
|
if (maybeContainer !== undefined) {
|
|
@@ -469,32 +488,6 @@ export class Loader implements IHostLoader {
|
|
|
469
488
|
return this.services.options.cache !== false;
|
|
470
489
|
}
|
|
471
490
|
|
|
472
|
-
private canCacheForRequest(headers: IRequestHeader): boolean {
|
|
473
|
-
return this.cachingEnabled && headers[LoaderHeader.cache] !== false;
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
private parseHeader(parsed: IParsedUrl, request: IRequest) {
|
|
477
|
-
let fromSequenceNumber = -1;
|
|
478
|
-
|
|
479
|
-
request.headers = request.headers ?? {};
|
|
480
|
-
|
|
481
|
-
const headerSeqNum = request.headers[LoaderHeader.sequenceNumber];
|
|
482
|
-
if (headerSeqNum !== undefined) {
|
|
483
|
-
fromSequenceNumber = headerSeqNum;
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
// If set in both query string and headers, use query string
|
|
487
|
-
request.headers[LoaderHeader.version] =
|
|
488
|
-
parsed.version ?? request.headers[LoaderHeader.version];
|
|
489
|
-
|
|
490
|
-
const canCache = this.canCacheForRequest(request.headers);
|
|
491
|
-
|
|
492
|
-
return {
|
|
493
|
-
canCache,
|
|
494
|
-
fromSequenceNumber,
|
|
495
|
-
};
|
|
496
|
-
}
|
|
497
|
-
|
|
498
491
|
private async loadContainer(
|
|
499
492
|
request: IRequest,
|
|
500
493
|
resolvedUrl: IResolvedUrl,
|
package/src/packageVersion.ts
CHANGED