@fluidframework/container-runtime 2.0.0-internal.1.0.0.83139 → 2.0.0-internal.1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/batchTracker.js +1 -1
- package/dist/batchTracker.js.map +1 -1
- package/dist/blobManager.d.ts.map +1 -1
- package/dist/blobManager.js +16 -16
- package/dist/blobManager.js.map +1 -1
- package/dist/containerRuntime.d.ts +1 -38
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +60 -109
- package/dist/containerRuntime.js.map +1 -1
- package/dist/dataStore.d.ts +1 -1
- package/dist/dataStore.d.ts.map +1 -1
- package/dist/dataStore.js +2 -3
- package/dist/dataStore.js.map +1 -1
- package/dist/dataStores.d.ts +1 -1
- package/dist/dataStores.d.ts.map +1 -1
- package/dist/dataStores.js +2 -2
- package/dist/dataStores.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/runningSummarizer.js +1 -1
- package/dist/runningSummarizer.js.map +1 -1
- package/dist/summarizer.d.ts.map +1 -1
- package/dist/summarizer.js +20 -1
- package/dist/summarizer.js.map +1 -1
- package/dist/summaryGenerator.js +1 -1
- package/dist/summaryGenerator.js.map +1 -1
- package/lib/batchTracker.js +1 -1
- package/lib/batchTracker.js.map +1 -1
- package/lib/blobManager.d.ts.map +1 -1
- package/lib/blobManager.js +16 -16
- package/lib/blobManager.js.map +1 -1
- package/lib/containerRuntime.d.ts +1 -38
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +60 -109
- package/lib/containerRuntime.js.map +1 -1
- package/lib/dataStore.d.ts +1 -1
- package/lib/dataStore.d.ts.map +1 -1
- package/lib/dataStore.js +2 -3
- package/lib/dataStore.js.map +1 -1
- package/lib/dataStores.d.ts +1 -1
- package/lib/dataStores.d.ts.map +1 -1
- package/lib/dataStores.js +2 -2
- package/lib/dataStores.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/runningSummarizer.js +1 -1
- package/lib/runningSummarizer.js.map +1 -1
- package/lib/summarizer.d.ts.map +1 -1
- package/lib/summarizer.js +22 -3
- package/lib/summarizer.js.map +1 -1
- package/lib/summaryGenerator.js +1 -1
- package/lib/summaryGenerator.js.map +1 -1
- package/package.json +27 -18
- package/src/batchTracker.ts +1 -1
- package/src/blobManager.ts +20 -16
- package/src/containerRuntime.ts +84 -164
- package/src/dataStore.ts +1 -4
- package/src/dataStores.ts +13 -13
- package/src/packageVersion.ts +1 -1
- package/src/runningSummarizer.ts +1 -1
- package/src/summarizer.ts +28 -3
- package/src/summaryGenerator.ts +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fluidframework/container-runtime",
|
|
3
|
-
"version": "2.0.0-internal.1.0.
|
|
3
|
+
"version": "2.0.0-internal.1.0.1",
|
|
4
4
|
"description": "Fluid container runtime",
|
|
5
5
|
"homepage": "https://fluidframework.com",
|
|
6
6
|
"repository": {
|
|
@@ -62,30 +62,30 @@
|
|
|
62
62
|
},
|
|
63
63
|
"dependencies": {
|
|
64
64
|
"@fluidframework/common-definitions": "^0.20.1",
|
|
65
|
-
"@fluidframework/common-utils": "^0.
|
|
66
|
-
"@fluidframework/container-definitions": "2.0.0-internal.1.0.
|
|
67
|
-
"@fluidframework/container-runtime-definitions": "2.0.0-internal.1.0.
|
|
68
|
-
"@fluidframework/container-utils": "2.0.0-internal.1.0.
|
|
69
|
-
"@fluidframework/core-interfaces": "2.0.0-internal.1.0.
|
|
70
|
-
"@fluidframework/datastore": "2.0.0-internal.1.0.
|
|
71
|
-
"@fluidframework/driver-definitions": "2.0.0-internal.1.0.
|
|
72
|
-
"@fluidframework/driver-utils": "2.0.0-internal.1.0.
|
|
73
|
-
"@fluidframework/garbage-collector": "2.0.0-internal.1.0.
|
|
74
|
-
"@fluidframework/protocol-base": "^0.1037.1000
|
|
75
|
-
"@fluidframework/protocol-definitions": "^0.
|
|
76
|
-
"@fluidframework/runtime-definitions": "2.0.0-internal.1.0.
|
|
77
|
-
"@fluidframework/runtime-utils": "2.0.0-internal.1.0.
|
|
78
|
-
"@fluidframework/telemetry-utils": "2.0.0-internal.1.0.
|
|
65
|
+
"@fluidframework/common-utils": "^1.0.0",
|
|
66
|
+
"@fluidframework/container-definitions": "^2.0.0-internal.1.0.1",
|
|
67
|
+
"@fluidframework/container-runtime-definitions": "^2.0.0-internal.1.0.1",
|
|
68
|
+
"@fluidframework/container-utils": "^2.0.0-internal.1.0.1",
|
|
69
|
+
"@fluidframework/core-interfaces": "^2.0.0-internal.1.0.1",
|
|
70
|
+
"@fluidframework/datastore": "^2.0.0-internal.1.0.1",
|
|
71
|
+
"@fluidframework/driver-definitions": "^2.0.0-internal.1.0.1",
|
|
72
|
+
"@fluidframework/driver-utils": "^2.0.0-internal.1.0.1",
|
|
73
|
+
"@fluidframework/garbage-collector": "^2.0.0-internal.1.0.1",
|
|
74
|
+
"@fluidframework/protocol-base": "^0.1037.1000",
|
|
75
|
+
"@fluidframework/protocol-definitions": "^1.0.0",
|
|
76
|
+
"@fluidframework/runtime-definitions": "^2.0.0-internal.1.0.1",
|
|
77
|
+
"@fluidframework/runtime-utils": "^2.0.0-internal.1.0.1",
|
|
78
|
+
"@fluidframework/telemetry-utils": "^2.0.0-internal.1.0.1",
|
|
79
79
|
"double-ended-queue": "^2.1.0-0",
|
|
80
80
|
"uuid": "^8.3.1"
|
|
81
81
|
},
|
|
82
82
|
"devDependencies": {
|
|
83
83
|
"@fluidframework/build-common": "^0.24.0",
|
|
84
|
-
"@fluidframework/build-tools": "^0.3.
|
|
84
|
+
"@fluidframework/build-tools": "^0.3.1000",
|
|
85
85
|
"@fluidframework/container-runtime-previous": "npm:@fluidframework/container-runtime@^1.0.0",
|
|
86
86
|
"@fluidframework/eslint-config-fluid": "^0.28.2000",
|
|
87
|
-
"@fluidframework/mocha-test-setup": "2.0.0-internal.1.0.
|
|
88
|
-
"@fluidframework/test-runtime-utils": "2.0.0-internal.1.0.
|
|
87
|
+
"@fluidframework/mocha-test-setup": "^2.0.0-internal.1.0.1",
|
|
88
|
+
"@fluidframework/test-runtime-utils": "^2.0.0-internal.1.0.1",
|
|
89
89
|
"@microsoft/api-extractor": "^7.22.2",
|
|
90
90
|
"@rushstack/eslint-config": "^2.5.1",
|
|
91
91
|
"@types/double-ended-queue": "^2.1.0",
|
|
@@ -130,6 +130,15 @@
|
|
|
130
130
|
},
|
|
131
131
|
"TypeAliasDeclaration_SubmitSummaryResult": {
|
|
132
132
|
"backCompat": false
|
|
133
|
+
},
|
|
134
|
+
"ClassDeclaration_ContainerRuntime": {
|
|
135
|
+
"backCompat": false
|
|
136
|
+
},
|
|
137
|
+
"InterfaceDeclaration_IConnectableRuntime": {
|
|
138
|
+
"backCompat": false
|
|
139
|
+
},
|
|
140
|
+
"InterfaceDeclaration_ISummarizerRuntime": {
|
|
141
|
+
"backCompat": false
|
|
133
142
|
}
|
|
134
143
|
}
|
|
135
144
|
}
|
package/src/batchTracker.ts
CHANGED
|
@@ -37,7 +37,7 @@ export class BatchTracker {
|
|
|
37
37
|
|
|
38
38
|
const length = message.sequenceNumber - this.startBatchSequenceNumber + 1;
|
|
39
39
|
if (length >= batchLengthThreshold) {
|
|
40
|
-
this.logger.
|
|
40
|
+
this.logger.sendPerformanceEvent({
|
|
41
41
|
eventName: "LengthTooBig",
|
|
42
42
|
length,
|
|
43
43
|
threshold: batchLengthThreshold,
|
package/src/blobManager.ts
CHANGED
|
@@ -219,7 +219,7 @@ export class BlobManager {
|
|
|
219
219
|
// For a detached container, entries are inserted into the redirect table with an undefined storage ID.
|
|
220
220
|
// For an attached container, entries are inserted w/storage ID after the BlobAttach op round-trips.
|
|
221
221
|
assert(!undefinedValueInTable || this.runtime.attachState === AttachState.Detached && ids.size === 0,
|
|
222
|
-
|
|
222
|
+
0x382 /* 'redirectTable' must contain only undefined while detached / defined values while attached */);
|
|
223
223
|
|
|
224
224
|
return ids as Set<string>;
|
|
225
225
|
}
|
|
@@ -231,7 +231,7 @@ export class BlobManager {
|
|
|
231
231
|
}
|
|
232
232
|
let storageId;
|
|
233
233
|
if (this.runtime.attachState === AttachState.Detached) {
|
|
234
|
-
assert(this.redirectTable.has(blobId),
|
|
234
|
+
assert(this.redirectTable.has(blobId), 0x383 /* requesting unknown blobs */);
|
|
235
235
|
|
|
236
236
|
// Blobs created while the container is detached are stored in IDetachedBlobStorage.
|
|
237
237
|
// The 'IDocumentStorageService.readBlob()' call below will retrieve these via localId.
|
|
@@ -256,7 +256,7 @@ export class BlobManager {
|
|
|
256
256
|
|
|
257
257
|
private getBlobHandle(id: string): IFluidHandle<ArrayBufferLike> {
|
|
258
258
|
assert(this.redirectTable.has(id) || this.pendingBlobs.has(id),
|
|
259
|
-
|
|
259
|
+
0x384 /* requesting handle for unknown blob */);
|
|
260
260
|
return new BlobHandle(
|
|
261
261
|
`${BlobManager.basePath}/${id}`,
|
|
262
262
|
this.routeContext,
|
|
@@ -282,7 +282,7 @@ export class BlobManager {
|
|
|
282
282
|
await new Promise<void>((resolve) => this.runtime.once("attached", resolve));
|
|
283
283
|
}
|
|
284
284
|
assert(this.runtime.attachState === AttachState.Attached,
|
|
285
|
-
|
|
285
|
+
0x385 /* For clarity and paranoid defense against adding future attachment states */);
|
|
286
286
|
|
|
287
287
|
// Create a local ID for each blob. This is used to support blobs if/when the client goes
|
|
288
288
|
// offline since we don't have the ID from storage yet. If online flow succeeds this won't be used.
|
|
@@ -314,7 +314,7 @@ export class BlobManager {
|
|
|
314
314
|
const entry = this.pendingBlobs.get(localId);
|
|
315
315
|
assert(entry?.status === PendingBlobStatus.OnlinePendingUpload ||
|
|
316
316
|
entry?.status === PendingBlobStatus.OfflinePendingUpload,
|
|
317
|
-
|
|
317
|
+
0x386 /* Must have pending blob entry for uploaded blob */);
|
|
318
318
|
entry.storageId = response.id;
|
|
319
319
|
if (this.runtime.connected) {
|
|
320
320
|
if (entry.status === PendingBlobStatus.OnlinePendingUpload) {
|
|
@@ -348,7 +348,7 @@ export class BlobManager {
|
|
|
348
348
|
|
|
349
349
|
private async onUploadReject(localId: string, error) {
|
|
350
350
|
const entry = this.pendingBlobs.get(localId);
|
|
351
|
-
assert(!!entry,
|
|
351
|
+
assert(!!entry, 0x387 /* Must have pending blob entry for blob which failed to upload */);
|
|
352
352
|
if (!this.runtime.connected) {
|
|
353
353
|
if (entry.status === PendingBlobStatus.OnlinePendingUpload) {
|
|
354
354
|
this.transitionToOffline(localId);
|
|
@@ -363,11 +363,11 @@ export class BlobManager {
|
|
|
363
363
|
}
|
|
364
364
|
|
|
365
365
|
private transitionToOffline(localId: string) {
|
|
366
|
-
assert(!this.runtime.connected,
|
|
366
|
+
assert(!this.runtime.connected, 0x388 /* Must only transition to offline flow while runtime is disconnected */);
|
|
367
367
|
const entry = this.pendingBlobs.get(localId);
|
|
368
|
-
assert(!!entry,
|
|
368
|
+
assert(!!entry, 0x389 /* No pending blob entry */);
|
|
369
369
|
assert([PendingBlobStatus.OnlinePendingUpload, PendingBlobStatus.OnlinePendingOp].includes(entry.status),
|
|
370
|
-
|
|
370
|
+
0x38a /* Blob must be in online flow to transition to offline flow */);
|
|
371
371
|
|
|
372
372
|
entry.status = entry.status === PendingBlobStatus.OnlinePendingUpload
|
|
373
373
|
? PendingBlobStatus.OfflinePendingUpload
|
|
@@ -388,14 +388,14 @@ export class BlobManager {
|
|
|
388
388
|
* @param metadata - op metadata containing storage and/or local IDs
|
|
389
389
|
*/
|
|
390
390
|
public reSubmit(metadata: Record<string, unknown> | undefined) {
|
|
391
|
-
assert(!!metadata,
|
|
391
|
+
assert(!!metadata, 0x38b /* Resubmitted ops must have metadata */);
|
|
392
392
|
const { blobId, localId }: { blobId?: string; localId?: string; } = metadata;
|
|
393
393
|
if (!blobId) {
|
|
394
|
-
assert(!!localId,
|
|
394
|
+
assert(!!localId, 0x38c /* Submitted BlobAttach ops must have a blobId or localId */);
|
|
395
395
|
// We submitted this op while offline. The blob should have been uploaded by now.
|
|
396
396
|
const pendingEntry = this.pendingBlobs.get(localId);
|
|
397
397
|
assert(pendingEntry?.status === PendingBlobStatus.OfflinePendingOp &&
|
|
398
|
-
!!pendingEntry?.storageId,
|
|
398
|
+
!!pendingEntry?.storageId, 0x38d /* blob must be uploaded before resubmitting BlobAttach op */);
|
|
399
399
|
return this.sendBlobAttachOp(pendingEntry.storageId, localId);
|
|
400
400
|
}
|
|
401
401
|
return this.sendBlobAttachOp(blobId, localId);
|
|
@@ -413,10 +413,13 @@ export class BlobManager {
|
|
|
413
413
|
if (message.metadata.localId === undefined) {
|
|
414
414
|
// Since there is no local ID, we know this op was submitted while online.
|
|
415
415
|
const waitingBlobs = this.opsInFlight.get(message.metadata.blobId);
|
|
416
|
-
assert(!!waitingBlobs,
|
|
416
|
+
assert(!!waitingBlobs, 0x38e /* local online BlobAttach op with no pending blob */);
|
|
417
417
|
waitingBlobs.forEach((localId) => {
|
|
418
418
|
const pendingBlobEntry = this.pendingBlobs.get(localId);
|
|
419
|
-
assert(
|
|
419
|
+
assert(
|
|
420
|
+
pendingBlobEntry !== undefined,
|
|
421
|
+
0x38f, /* local online BlobAttach op with no pending blob entry */
|
|
422
|
+
);
|
|
420
423
|
|
|
421
424
|
// It's possible we transitioned to offline flow while waiting for this op.
|
|
422
425
|
if (pendingBlobEntry.status === PendingBlobStatus.OnlinePendingOp) {
|
|
@@ -497,7 +500,7 @@ export class BlobManager {
|
|
|
497
500
|
// Note that because of de-duping, there can be multiple localIds that all redirect to the same storageId or
|
|
498
501
|
// a blob may be referenced via its storageId handle.
|
|
499
502
|
for (const [localId, storageId] of this.redirectTable) {
|
|
500
|
-
assert(!!storageId,
|
|
503
|
+
assert(!!storageId, 0x390 /* Must be attached to get GC data */);
|
|
501
504
|
// Add node for the localId and add a route to the storageId node. The storageId node will have been
|
|
502
505
|
// added above when adding nodes for this.blobIds.
|
|
503
506
|
gcData.gcNodes[this.getBlobGCNodePath(localId)] = [this.getBlobGCNodePath(storageId)];
|
|
@@ -554,7 +557,8 @@ export class BlobManager {
|
|
|
554
557
|
public setRedirectTable(table: Map<string, string>) {
|
|
555
558
|
assert(this.runtime.attachState === AttachState.Detached,
|
|
556
559
|
0x252 /* "redirect table can only be set in detached container" */);
|
|
557
|
-
assert(this.redirectTable.size === table.size,
|
|
560
|
+
assert(this.redirectTable.size === table.size,
|
|
561
|
+
0x391 /* Redirect table size must match BlobManager's local ID count */);
|
|
558
562
|
for (const [localId, storageId] of table) {
|
|
559
563
|
assert(this.redirectTable.has(localId), 0x254 /* "unrecognized id in redirect table" */);
|
|
560
564
|
this.redirectTable.set(localId, storageId);
|
package/src/containerRuntime.ts
CHANGED
|
@@ -425,12 +425,6 @@ export interface IContainerRuntimeOptions {
|
|
|
425
425
|
* 3. "bypass" will skip the check entirely. This is not recommended.
|
|
426
426
|
*/
|
|
427
427
|
readonly loadSequenceNumberVerification?: "close" | "log" | "bypass";
|
|
428
|
-
/**
|
|
429
|
-
* Should the runtime use data store aliasing for creating root datastores.
|
|
430
|
-
* In case of aliasing conflicts, the runtime will raise an exception which does
|
|
431
|
-
* not effect the status of the container.
|
|
432
|
-
*/
|
|
433
|
-
readonly useDataStoreAliasing?: boolean;
|
|
434
428
|
/**
|
|
435
429
|
* Sets the flush mode for the runtime. In Immediate flush mode the runtime will immediately
|
|
436
430
|
* send all operations to the driver layer, while in TurnBased the operations will be buffered
|
|
@@ -509,7 +503,6 @@ export interface IPendingRuntimeState {
|
|
|
509
503
|
savedOps: ISequencedDocumentMessage[];
|
|
510
504
|
}
|
|
511
505
|
|
|
512
|
-
const useDataStoreAliasingKey = "Fluid.ContainerRuntime.UseDataStoreAliasing";
|
|
513
506
|
const maxConsecutiveReconnectsKey = "Fluid.ContainerRuntime.MaxConsecutiveReconnects";
|
|
514
507
|
|
|
515
508
|
// Feature gate for the max op size. If the value is negative, chunking is enabled
|
|
@@ -903,7 +896,6 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
903
896
|
summaryOptions = {},
|
|
904
897
|
gcOptions = {},
|
|
905
898
|
loadSequenceNumberVerification = "close",
|
|
906
|
-
useDataStoreAliasing = false,
|
|
907
899
|
flushMode = defaultFlushMode,
|
|
908
900
|
enableOfflineLoad = false,
|
|
909
901
|
} = runtimeOptions;
|
|
@@ -979,7 +971,6 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
979
971
|
summaryOptions,
|
|
980
972
|
gcOptions,
|
|
981
973
|
loadSequenceNumberVerification,
|
|
982
|
-
useDataStoreAliasing,
|
|
983
974
|
flushMode,
|
|
984
975
|
enableOfflineLoad,
|
|
985
976
|
},
|
|
@@ -1069,7 +1060,6 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1069
1060
|
private readonly summaryCollection: SummaryCollection;
|
|
1070
1061
|
|
|
1071
1062
|
private readonly summarizerNode: IRootSummarizerNodeWithGC;
|
|
1072
|
-
private readonly _aliasingEnabled: boolean;
|
|
1073
1063
|
private readonly _maxOpSizeInBytes: number;
|
|
1074
1064
|
|
|
1075
1065
|
private readonly maxConsecutiveReconnects: number;
|
|
@@ -1263,10 +1253,6 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1263
1253
|
this.maxOpsSinceLastSummary = this.getMaxOpsSinceLastSummary();
|
|
1264
1254
|
this.initialSummarizerDelayMs = this.getInitialSummarizerDelayMs();
|
|
1265
1255
|
|
|
1266
|
-
this._aliasingEnabled =
|
|
1267
|
-
(this.mc.config.getBoolean(useDataStoreAliasingKey) ?? false) ||
|
|
1268
|
-
(runtimeOptions.useDataStoreAliasing ?? false);
|
|
1269
|
-
|
|
1270
1256
|
this._maxOpSizeInBytes = (this.mc.config.getNumber(maxOpSizeInBytesKey) ?? defaultMaxOpSizeInBytes);
|
|
1271
1257
|
this.maxConsecutiveReconnects =
|
|
1272
1258
|
this.mc.config.getNumber(maxConsecutiveReconnectsKey) ?? this.defaultMaxConsecutiveReconnects;
|
|
@@ -1829,8 +1815,9 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1829
1815
|
// ensure we don't submit ops referencing a blob that has not been uploaded
|
|
1830
1816
|
const connecting = connected && !this._connected && !this.deltaManager.readOnlyInfo.readonly;
|
|
1831
1817
|
if (connecting && this.blobManager.hasPendingOfflineUploads) {
|
|
1832
|
-
assert(!this.delayConnectClientId,
|
|
1833
|
-
|
|
1818
|
+
assert(!this.delayConnectClientId,
|
|
1819
|
+
0x392 /* Connect event delay must be canceled before subsequent connect event */);
|
|
1820
|
+
assert(!!clientId, 0x393 /* Must have clientId when connecting */);
|
|
1834
1821
|
this.delayConnectClientId = clientId;
|
|
1835
1822
|
this.blobManager.onConnected().then(() => {
|
|
1836
1823
|
// make sure we didn't reconnect before the promise resolved
|
|
@@ -1846,7 +1833,8 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1846
1833
|
}
|
|
1847
1834
|
|
|
1848
1835
|
private setConnectionStateCore(connected: boolean, clientId?: string) {
|
|
1849
|
-
assert(!this.delayConnectClientId,
|
|
1836
|
+
assert(!this.delayConnectClientId,
|
|
1837
|
+
0x394 /* connect event delay must be cleared before propagating connect event */);
|
|
1850
1838
|
this.verifyNotClosed();
|
|
1851
1839
|
|
|
1852
1840
|
// There might be no change of state due to Container calling this API after loading runtime.
|
|
@@ -1870,11 +1858,11 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1870
1858
|
"Runtime detected too many reconnects with no progress syncing local ops",
|
|
1871
1859
|
"setConnectionState",
|
|
1872
1860
|
undefined,
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1861
|
+
{
|
|
1862
|
+
dataLoss: 1,
|
|
1863
|
+
attempts: this.consecutiveReconnects,
|
|
1864
|
+
pendingMessages: this.pendingStateManager.pendingMessagesCount,
|
|
1865
|
+
}));
|
|
1878
1866
|
return;
|
|
1879
1867
|
}
|
|
1880
1868
|
}
|
|
@@ -2137,83 +2125,13 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2137
2125
|
public async createDataStore(pkg: string | string[]): Promise<IDataStore> {
|
|
2138
2126
|
const internalId = uuid();
|
|
2139
2127
|
return channelToDataStore(
|
|
2140
|
-
await this._createDataStore(pkg,
|
|
2128
|
+
await this._createDataStore(pkg, internalId),
|
|
2141
2129
|
internalId,
|
|
2142
2130
|
this,
|
|
2143
2131
|
this.dataStores,
|
|
2144
2132
|
this.mc.logger);
|
|
2145
2133
|
}
|
|
2146
2134
|
|
|
2147
|
-
/**
|
|
2148
|
-
* Creates a root datastore directly with a user generated id and attaches it to storage.
|
|
2149
|
-
* It is vulnerable to name collisions and should not be used.
|
|
2150
|
-
*
|
|
2151
|
-
* This method will be removed. See #6465.
|
|
2152
|
-
*/
|
|
2153
|
-
private async createRootDataStoreLegacy(pkg: string | string[], rootDataStoreId: string): Promise<IFluidRouter> {
|
|
2154
|
-
const fluidDataStore = await this._createDataStore(pkg, true /* isRoot */, rootDataStoreId);
|
|
2155
|
-
fluidDataStore.makeVisibleAndAttachGraph();
|
|
2156
|
-
return fluidDataStore;
|
|
2157
|
-
}
|
|
2158
|
-
|
|
2159
|
-
/**
|
|
2160
|
-
* @deprecated - will be removed in an upcoming release. See #9660.
|
|
2161
|
-
*/
|
|
2162
|
-
public async createRootDataStore(pkg: string | string[], rootDataStoreId: string): Promise<IFluidRouter> {
|
|
2163
|
-
if (rootDataStoreId.includes("/")) {
|
|
2164
|
-
throw new UsageError(`Id cannot contain slashes: '${rootDataStoreId}'`);
|
|
2165
|
-
}
|
|
2166
|
-
return this._aliasingEnabled === true ?
|
|
2167
|
-
this.createAndAliasDataStore(pkg, rootDataStoreId) :
|
|
2168
|
-
this.createRootDataStoreLegacy(pkg, rootDataStoreId);
|
|
2169
|
-
}
|
|
2170
|
-
|
|
2171
|
-
/**
|
|
2172
|
-
* Creates a data store then attempts to alias it.
|
|
2173
|
-
* If aliasing fails, it will raise an exception.
|
|
2174
|
-
*
|
|
2175
|
-
* This method will be removed. See #6465.
|
|
2176
|
-
*
|
|
2177
|
-
* @param pkg - Package name of the data store
|
|
2178
|
-
* @param alias - Alias to be assigned to the data store
|
|
2179
|
-
* @param props - Properties for the data store
|
|
2180
|
-
* @returns - An aliased data store which can can be found / loaded by alias.
|
|
2181
|
-
*/
|
|
2182
|
-
private async createAndAliasDataStore(pkg: string | string[], alias: string, props?: any): Promise<IDataStore> {
|
|
2183
|
-
const internalId = uuid();
|
|
2184
|
-
|
|
2185
|
-
try {
|
|
2186
|
-
// A similar call may have been initiated by the same client, so we should try to get
|
|
2187
|
-
// a possible existing aliased datastore first.
|
|
2188
|
-
const existingDataStore = await this.getRootDataStoreChannel(alias, /* wait */ false);
|
|
2189
|
-
return channelToDataStore(
|
|
2190
|
-
existingDataStore,
|
|
2191
|
-
internalId,
|
|
2192
|
-
this,
|
|
2193
|
-
this.dataStores,
|
|
2194
|
-
this.mc.logger,
|
|
2195
|
-
true, // AlreadyAliased. This will block further alias attempts for the datastore
|
|
2196
|
-
);
|
|
2197
|
-
} catch (err) {
|
|
2198
|
-
const newChannel = await this._createDataStore(pkg, false /* isRoot */, internalId, props);
|
|
2199
|
-
const newDataStore = channelToDataStore(newChannel, internalId, this, this.dataStores, this.mc.logger);
|
|
2200
|
-
const aliasResult = await newDataStore.trySetAlias(alias);
|
|
2201
|
-
if (aliasResult === "Success") {
|
|
2202
|
-
return newDataStore;
|
|
2203
|
-
}
|
|
2204
|
-
|
|
2205
|
-
const existingDataStore = await this.getRootDataStoreChannel(alias, /* wait */ false);
|
|
2206
|
-
return channelToDataStore(
|
|
2207
|
-
existingDataStore,
|
|
2208
|
-
internalId,
|
|
2209
|
-
this,
|
|
2210
|
-
this.dataStores,
|
|
2211
|
-
this.mc.logger,
|
|
2212
|
-
true, // AlreadyAliased. This will block further alias attempts for the datastore
|
|
2213
|
-
);
|
|
2214
|
-
}
|
|
2215
|
-
}
|
|
2216
|
-
|
|
2217
2135
|
public createDetachedRootDataStore(
|
|
2218
2136
|
pkg: Readonly<string[]>,
|
|
2219
2137
|
rootDataStoreId: string): IFluidDataStoreContextDetached {
|
|
@@ -2227,49 +2145,23 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2227
2145
|
return this.dataStores.createDetachedDataStoreCore(pkg, false);
|
|
2228
2146
|
}
|
|
2229
2147
|
|
|
2230
|
-
|
|
2231
|
-
* Creates a possibly root datastore directly with a possibly user generated id and attaches it to storage.
|
|
2232
|
-
* It is vulnerable to name collisions if both aforementioned conditions are true, and should not be used.
|
|
2233
|
-
*
|
|
2234
|
-
* This method will be removed. See #6465.
|
|
2235
|
-
*/
|
|
2236
|
-
private async _createDataStoreWithPropsLegacy(
|
|
2148
|
+
public async _createDataStoreWithProps(
|
|
2237
2149
|
pkg: string | string[],
|
|
2238
2150
|
props?: any,
|
|
2239
2151
|
id = uuid(),
|
|
2240
|
-
isRoot = false,
|
|
2241
2152
|
): Promise<IDataStore> {
|
|
2242
2153
|
const fluidDataStore = await this.dataStores._createFluidDataStoreContext(
|
|
2243
|
-
Array.isArray(pkg) ? pkg : [pkg], id,
|
|
2244
|
-
if (isRoot) {
|
|
2245
|
-
fluidDataStore.makeVisibleAndAttachGraph();
|
|
2246
|
-
this.logger.sendTelemetryEvent({
|
|
2247
|
-
eventName: "Root datastore with props",
|
|
2248
|
-
hasProps: props !== undefined,
|
|
2249
|
-
});
|
|
2250
|
-
}
|
|
2154
|
+
Array.isArray(pkg) ? pkg : [pkg], id, props).realize();
|
|
2251
2155
|
return channelToDataStore(fluidDataStore, id, this, this.dataStores, this.mc.logger);
|
|
2252
2156
|
}
|
|
2253
2157
|
|
|
2254
|
-
public async _createDataStoreWithProps(
|
|
2255
|
-
pkg: string | string[],
|
|
2256
|
-
props?: any,
|
|
2257
|
-
id = uuid(),
|
|
2258
|
-
isRoot = false,
|
|
2259
|
-
): Promise<IDataStore> {
|
|
2260
|
-
return this._aliasingEnabled === true && isRoot ?
|
|
2261
|
-
this.createAndAliasDataStore(pkg, id, props) :
|
|
2262
|
-
this._createDataStoreWithPropsLegacy(pkg, props, id, isRoot);
|
|
2263
|
-
}
|
|
2264
|
-
|
|
2265
2158
|
private async _createDataStore(
|
|
2266
2159
|
pkg: string | string[],
|
|
2267
|
-
isRoot: boolean,
|
|
2268
2160
|
id = uuid(),
|
|
2269
2161
|
props?: any,
|
|
2270
2162
|
): Promise<IFluidDataStoreChannel> {
|
|
2271
2163
|
return this.dataStores
|
|
2272
|
-
._createFluidDataStoreContext(Array.isArray(pkg) ? pkg : [pkg], id,
|
|
2164
|
+
._createFluidDataStoreContext(Array.isArray(pkg) ? pkg : [pkg], id, props)
|
|
2273
2165
|
.realize();
|
|
2274
2166
|
}
|
|
2275
2167
|
|
|
@@ -2626,21 +2518,24 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2626
2518
|
},
|
|
2627
2519
|
);
|
|
2628
2520
|
|
|
2521
|
+
let latestSnapshotVersionId: string | undefined;
|
|
2629
2522
|
if (refreshLatestAck) {
|
|
2630
|
-
const
|
|
2523
|
+
const latestSnapshotInfo = await this.refreshLatestSummaryAckFromServer(
|
|
2631
2524
|
ChildLogger.create(summaryNumberLogger, undefined, { all: { safeSummary: true } }));
|
|
2525
|
+
const latestSnapshotRefSeq = latestSnapshotInfo.latestSnapshotRefSeq;
|
|
2526
|
+
latestSnapshotVersionId = latestSnapshotInfo.latestSnapshotVersionId;
|
|
2632
2527
|
|
|
2633
|
-
if (
|
|
2528
|
+
if (latestSnapshotRefSeq > this.deltaManager.lastSequenceNumber) {
|
|
2634
2529
|
// We need to catch up to the latest summary's reference sequence number before pausing.
|
|
2635
2530
|
await PerformanceEvent.timedExecAsync(
|
|
2636
2531
|
summaryNumberLogger,
|
|
2637
2532
|
{
|
|
2638
2533
|
eventName: "WaitingForSeq",
|
|
2639
2534
|
lastSequenceNumber: this.deltaManager.lastSequenceNumber,
|
|
2640
|
-
targetSequenceNumber:
|
|
2535
|
+
targetSequenceNumber: latestSnapshotRefSeq,
|
|
2641
2536
|
lastKnownSeqNumber: this.deltaManager.lastKnownSeqNumber,
|
|
2642
2537
|
},
|
|
2643
|
-
async () => waitForSeq(this.deltaManager,
|
|
2538
|
+
async () => waitForSeq(this.deltaManager, latestSnapshotRefSeq),
|
|
2644
2539
|
{ start: true, end: true, cancel: "error" }, // definitely want start event
|
|
2645
2540
|
);
|
|
2646
2541
|
}
|
|
@@ -2683,7 +2578,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2683
2578
|
};
|
|
2684
2579
|
}
|
|
2685
2580
|
assert(summaryRefSeqNum === this.deltaManager.lastMessage?.sequenceNumber,
|
|
2686
|
-
|
|
2581
|
+
0x395 /* it's one and the same thing */);
|
|
2687
2582
|
|
|
2688
2583
|
if (lastAck !== this.summaryCollection.latestAck) {
|
|
2689
2584
|
return {
|
|
@@ -2765,18 +2660,32 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2765
2660
|
return { stage: "generate", ...generateSummaryData, error: continueResult.error };
|
|
2766
2661
|
}
|
|
2767
2662
|
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2663
|
+
// It may happen that the lastAck it not correct due to missing summaryAck in case of single commit
|
|
2664
|
+
// summary. So if the previous summarizer closes just after submitting the summary and before
|
|
2665
|
+
// submitting the summaryOp then we can't rely on summaryAck. So in case we have
|
|
2666
|
+
// latestSnapshotVersionId from storage and it does not match with the lastAck ackHandle, then use
|
|
2667
|
+
// the one fetched from storage as parent as that is the latest.
|
|
2668
|
+
let summaryContext: ISummaryContext;
|
|
2669
|
+
if (lastAck?.summaryAck.contents.handle !== latestSnapshotVersionId
|
|
2670
|
+
&& latestSnapshotVersionId !== undefined) {
|
|
2671
|
+
summaryContext = {
|
|
2672
|
+
proposalHandle: undefined,
|
|
2673
|
+
ackHandle: latestSnapshotVersionId,
|
|
2674
|
+
referenceSequenceNumber: summaryRefSeqNum,
|
|
2675
|
+
};
|
|
2676
|
+
} else if (lastAck === undefined) {
|
|
2677
|
+
summaryContext = {
|
|
2678
|
+
proposalHandle: undefined,
|
|
2679
|
+
ackHandle: this.context.getLoadedFromVersion()?.id,
|
|
2680
|
+
referenceSequenceNumber: summaryRefSeqNum,
|
|
2681
|
+
};
|
|
2682
|
+
} else {
|
|
2683
|
+
summaryContext = {
|
|
2684
|
+
proposalHandle: lastAck.summaryOp.contents.handle,
|
|
2685
|
+
ackHandle: lastAck.summaryAck.contents.handle,
|
|
2686
|
+
referenceSequenceNumber: summaryRefSeqNum,
|
|
2687
|
+
};
|
|
2688
|
+
}
|
|
2780
2689
|
|
|
2781
2690
|
let handle: string;
|
|
2782
2691
|
try {
|
|
@@ -3123,20 +3032,29 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
3123
3032
|
summaryLogger: ITelemetryLogger,
|
|
3124
3033
|
) {
|
|
3125
3034
|
const readAndParseBlob = async <T>(id: string) => readAndParse<T>(this.storage, id);
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3035
|
+
// The call to fetch the snapshot is very expensive and not always needed.
|
|
3036
|
+
// It should only be done by the summarizerNode, if required.
|
|
3037
|
+
const snapshotTreeFetcher = async () => {
|
|
3038
|
+
const fetchResult = await this.fetchSnapshotFromStorage(
|
|
3039
|
+
ackHandle,
|
|
3040
|
+
summaryLogger,
|
|
3041
|
+
{
|
|
3042
|
+
eventName: "RefreshLatestSummaryGetSnapshot",
|
|
3043
|
+
ackHandle,
|
|
3044
|
+
summaryRefSeq,
|
|
3045
|
+
fetchLatest: false,
|
|
3046
|
+
});
|
|
3047
|
+
return fetchResult.snapshotTree;
|
|
3048
|
+
};
|
|
3049
|
+
const result = await this.summarizerNode.refreshLatestSummary(
|
|
3050
|
+
proposalHandle,
|
|
3051
|
+
summaryRefSeq,
|
|
3052
|
+
snapshotTreeFetcher,
|
|
3053
|
+
readAndParseBlob,
|
|
3054
|
+
summaryLogger,
|
|
3055
|
+
);
|
|
3056
|
+
|
|
3057
|
+
// Notify the garbage collector so it can update its latest summary state.
|
|
3140
3058
|
await this.garbageCollector.latestSummaryStateRefreshed(result, readAndParseBlob);
|
|
3141
3059
|
}
|
|
3142
3060
|
|
|
@@ -3146,21 +3064,23 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
3146
3064
|
* @param summaryLogger - logger to use when fetching snapshot from storage
|
|
3147
3065
|
* @returns downloaded snapshot's reference sequence number
|
|
3148
3066
|
*/
|
|
3149
|
-
private async refreshLatestSummaryAckFromServer(
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
3067
|
+
private async refreshLatestSummaryAckFromServer(
|
|
3068
|
+
summaryLogger: ITelemetryLogger,
|
|
3069
|
+
): Promise<{ latestSnapshotRefSeq: number; latestSnapshotVersionId: string | undefined; }> {
|
|
3070
|
+
const { snapshotTree, versionId } = await this.fetchSnapshotFromStorage(null, summaryLogger, {
|
|
3071
|
+
eventName: "RefreshLatestSummaryGetSnapshot",
|
|
3072
|
+
fetchLatest: true,
|
|
3073
|
+
},
|
|
3154
3074
|
FetchSource.noCache,
|
|
3155
3075
|
);
|
|
3156
3076
|
|
|
3157
3077
|
const readAndParseBlob = async <T>(id: string) => readAndParse<T>(this.storage, id);
|
|
3158
|
-
const
|
|
3078
|
+
const latestSnapshotRefSeq = await seqFromTree(snapshotTree, readAndParseBlob);
|
|
3159
3079
|
|
|
3160
3080
|
const result = await this.summarizerNode.refreshLatestSummary(
|
|
3161
3081
|
undefined,
|
|
3162
|
-
|
|
3163
|
-
async () =>
|
|
3082
|
+
latestSnapshotRefSeq,
|
|
3083
|
+
async () => snapshotTree,
|
|
3164
3084
|
readAndParseBlob,
|
|
3165
3085
|
summaryLogger,
|
|
3166
3086
|
);
|
|
@@ -3168,7 +3088,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
3168
3088
|
// Notify the garbage collector so it can update its latest summary state.
|
|
3169
3089
|
await this.garbageCollector.latestSummaryStateRefreshed(result, readAndParseBlob);
|
|
3170
3090
|
|
|
3171
|
-
return
|
|
3091
|
+
return { latestSnapshotRefSeq, latestSnapshotVersionId: versionId };
|
|
3172
3092
|
}
|
|
3173
3093
|
|
|
3174
3094
|
private async fetchSnapshotFromStorage(
|
|
@@ -3176,7 +3096,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
3176
3096
|
logger: ITelemetryLogger,
|
|
3177
3097
|
event: ITelemetryGenericEvent,
|
|
3178
3098
|
fetchSource?: FetchSource,
|
|
3179
|
-
) {
|
|
3099
|
+
): Promise<{ snapshotTree: ISnapshotTree; versionId: string; }> {
|
|
3180
3100
|
return PerformanceEvent.timedExecAsync(
|
|
3181
3101
|
logger, event, async (perfEvent: {
|
|
3182
3102
|
end: (arg0: {
|
|
@@ -3197,7 +3117,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
3197
3117
|
stats.getSnapshotDuration = trace.trace().duration;
|
|
3198
3118
|
|
|
3199
3119
|
perfEvent.end(stats);
|
|
3200
|
-
return maybeSnapshot;
|
|
3120
|
+
return { snapshotTree: maybeSnapshot, versionId: versions[0].id };
|
|
3201
3121
|
});
|
|
3202
3122
|
}
|
|
3203
3123
|
|
package/src/dataStore.ts
CHANGED
|
@@ -43,8 +43,7 @@ export const channelToDataStore = (
|
|
|
43
43
|
runtime: ContainerRuntime,
|
|
44
44
|
datastores: DataStores,
|
|
45
45
|
logger: ITelemetryLogger,
|
|
46
|
-
|
|
47
|
-
): IDataStore => new DataStore(fluidDataStoreChannel, internalId, runtime, datastores, logger, alreadyAliased);
|
|
46
|
+
): IDataStore => new DataStore(fluidDataStoreChannel, internalId, runtime, datastores, logger);
|
|
48
47
|
|
|
49
48
|
enum AliasState {
|
|
50
49
|
Aliased = "Aliased",
|
|
@@ -158,10 +157,8 @@ class DataStore implements IDataStore {
|
|
|
158
157
|
private readonly runtime: ContainerRuntime,
|
|
159
158
|
private readonly datastores: DataStores,
|
|
160
159
|
private readonly logger: ITelemetryLogger,
|
|
161
|
-
alreadyAliased: boolean,
|
|
162
160
|
) {
|
|
163
161
|
this.pendingAliases = datastores.pendingAliases;
|
|
164
|
-
this.aliasState = alreadyAliased ? AliasState.Aliased : AliasState.None;
|
|
165
162
|
}
|
|
166
163
|
|
|
167
164
|
public get IFluidRouter() { return this.fluidDataStoreChannel; }
|