@fluidframework/container-runtime 2.0.0-internal.2.3.1 → 2.0.0-internal.3.0.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/dist/blobManager.d.ts +3 -1
- package/dist/blobManager.d.ts.map +1 -1
- package/dist/blobManager.js +35 -2
- package/dist/blobManager.js.map +1 -1
- package/dist/containerRuntime.d.ts +45 -42
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +89 -40
- package/dist/containerRuntime.js.map +1 -1
- package/dist/dataStoreContext.d.ts +1 -0
- package/dist/dataStoreContext.d.ts.map +1 -1
- package/dist/dataStoreContext.js +7 -2
- package/dist/dataStoreContext.js.map +1 -1
- package/dist/garbageCollection.d.ts +15 -7
- package/dist/garbageCollection.d.ts.map +1 -1
- package/dist/garbageCollection.js +96 -36
- package/dist/garbageCollection.js.map +1 -1
- package/dist/opLifecycle/outbox.d.ts.map +1 -1
- package/dist/opLifecycle/outbox.js +0 -1
- package/dist/opLifecycle/outbox.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/pendingStateManager.d.ts +4 -13
- package/dist/pendingStateManager.d.ts.map +1 -1
- package/dist/pendingStateManager.js +130 -160
- package/dist/pendingStateManager.js.map +1 -1
- package/dist/summarizer.js.map +1 -1
- package/dist/summarizerClientElection.d.ts +1 -2
- package/dist/summarizerClientElection.d.ts.map +1 -1
- package/dist/summarizerClientElection.js +3 -30
- package/dist/summarizerClientElection.js.map +1 -1
- package/dist/summarizerTypes.d.ts +0 -4
- package/dist/summarizerTypes.d.ts.map +1 -1
- package/dist/summarizerTypes.js.map +1 -1
- package/lib/blobManager.d.ts +3 -1
- package/lib/blobManager.d.ts.map +1 -1
- package/lib/blobManager.js +35 -2
- package/lib/blobManager.js.map +1 -1
- package/lib/containerRuntime.d.ts +45 -42
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +89 -40
- package/lib/containerRuntime.js.map +1 -1
- package/lib/dataStoreContext.d.ts +1 -0
- package/lib/dataStoreContext.d.ts.map +1 -1
- package/lib/dataStoreContext.js +7 -2
- package/lib/dataStoreContext.js.map +1 -1
- package/lib/garbageCollection.d.ts +15 -7
- package/lib/garbageCollection.d.ts.map +1 -1
- package/lib/garbageCollection.js +97 -37
- package/lib/garbageCollection.js.map +1 -1
- package/lib/opLifecycle/outbox.d.ts.map +1 -1
- package/lib/opLifecycle/outbox.js +0 -1
- package/lib/opLifecycle/outbox.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/pendingStateManager.d.ts +4 -13
- package/lib/pendingStateManager.d.ts.map +1 -1
- package/lib/pendingStateManager.js +130 -160
- package/lib/pendingStateManager.js.map +1 -1
- package/lib/summarizer.js.map +1 -1
- package/lib/summarizerClientElection.d.ts +1 -2
- package/lib/summarizerClientElection.d.ts.map +1 -1
- package/lib/summarizerClientElection.js +3 -30
- package/lib/summarizerClientElection.js.map +1 -1
- package/lib/summarizerTypes.d.ts +0 -4
- package/lib/summarizerTypes.d.ts.map +1 -1
- package/lib/summarizerTypes.js.map +1 -1
- package/package.json +55 -20
- package/src/blobManager.ts +41 -2
- package/src/containerRuntime.ts +118 -85
- package/src/dataStoreContext.ts +12 -6
- package/src/garbageCollection.ts +103 -34
- package/src/opLifecycle/outbox.ts +0 -2
- package/src/packageVersion.ts +1 -1
- package/src/pendingStateManager.ts +146 -187
- package/src/summarizer.ts +1 -1
- package/src/summarizerClientElection.ts +1 -30
- package/src/summarizerTypes.ts +0 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fluidframework/container-runtime",
|
|
3
|
-
"version": "2.0.0-internal.
|
|
3
|
+
"version": "2.0.0-internal.3.0.0",
|
|
4
4
|
"description": "Fluid container runtime",
|
|
5
5
|
"homepage": "https://fluidframework.com",
|
|
6
6
|
"repository": {
|
|
@@ -65,32 +65,32 @@
|
|
|
65
65
|
"dependencies": {
|
|
66
66
|
"@fluidframework/common-definitions": "^0.20.1",
|
|
67
67
|
"@fluidframework/common-utils": "^1.0.0",
|
|
68
|
-
"@fluidframework/container-definitions": ">=2.0.0-internal.
|
|
69
|
-
"@fluidframework/container-runtime-definitions": ">=2.0.0-internal.
|
|
70
|
-
"@fluidframework/container-utils": ">=2.0.0-internal.
|
|
71
|
-
"@fluidframework/core-interfaces": ">=2.0.0-internal.
|
|
72
|
-
"@fluidframework/datastore": ">=2.0.0-internal.
|
|
73
|
-
"@fluidframework/driver-definitions": ">=2.0.0-internal.
|
|
74
|
-
"@fluidframework/driver-utils": ">=2.0.0-internal.
|
|
75
|
-
"@fluidframework/garbage-collector": ">=2.0.0-internal.
|
|
68
|
+
"@fluidframework/container-definitions": ">=2.0.0-internal.3.0.0 <2.0.0-internal.4.0.0",
|
|
69
|
+
"@fluidframework/container-runtime-definitions": ">=2.0.0-internal.3.0.0 <2.0.0-internal.4.0.0",
|
|
70
|
+
"@fluidframework/container-utils": ">=2.0.0-internal.3.0.0 <2.0.0-internal.4.0.0",
|
|
71
|
+
"@fluidframework/core-interfaces": ">=2.0.0-internal.3.0.0 <2.0.0-internal.4.0.0",
|
|
72
|
+
"@fluidframework/datastore": ">=2.0.0-internal.3.0.0 <2.0.0-internal.4.0.0",
|
|
73
|
+
"@fluidframework/driver-definitions": ">=2.0.0-internal.3.0.0 <2.0.0-internal.4.0.0",
|
|
74
|
+
"@fluidframework/driver-utils": ">=2.0.0-internal.3.0.0 <2.0.0-internal.4.0.0",
|
|
75
|
+
"@fluidframework/garbage-collector": ">=2.0.0-internal.3.0.0 <2.0.0-internal.4.0.0",
|
|
76
76
|
"@fluidframework/protocol-base": "^0.1038.2000",
|
|
77
77
|
"@fluidframework/protocol-definitions": "^1.1.0",
|
|
78
|
-
"@fluidframework/runtime-definitions": ">=2.0.0-internal.
|
|
79
|
-
"@fluidframework/runtime-utils": ">=2.0.0-internal.
|
|
80
|
-
"@fluidframework/telemetry-utils": ">=2.0.0-internal.
|
|
78
|
+
"@fluidframework/runtime-definitions": ">=2.0.0-internal.3.0.0 <2.0.0-internal.4.0.0",
|
|
79
|
+
"@fluidframework/runtime-utils": ">=2.0.0-internal.3.0.0 <2.0.0-internal.4.0.0",
|
|
80
|
+
"@fluidframework/telemetry-utils": ">=2.0.0-internal.3.0.0 <2.0.0-internal.4.0.0",
|
|
81
81
|
"double-ended-queue": "^2.1.0-0",
|
|
82
82
|
"events": "^3.1.0",
|
|
83
83
|
"lz4js": "^0.2.0",
|
|
84
84
|
"uuid": "^8.3.1"
|
|
85
85
|
},
|
|
86
86
|
"devDependencies": {
|
|
87
|
-
"@fluid-tools/build-cli": "^0.
|
|
87
|
+
"@fluid-tools/build-cli": "^0.8.0",
|
|
88
88
|
"@fluidframework/build-common": "^1.1.0",
|
|
89
|
-
"@fluidframework/build-tools": "^0.
|
|
90
|
-
"@fluidframework/container-runtime-previous": "npm:@fluidframework/container-runtime@2.0.0-internal.2.
|
|
89
|
+
"@fluidframework/build-tools": "^0.8.0",
|
|
90
|
+
"@fluidframework/container-runtime-previous": "npm:@fluidframework/container-runtime@2.0.0-internal.2.2.0",
|
|
91
91
|
"@fluidframework/eslint-config-fluid": "^2.0.0",
|
|
92
|
-
"@fluidframework/mocha-test-setup": ">=2.0.0-internal.
|
|
93
|
-
"@fluidframework/test-runtime-utils": ">=2.0.0-internal.
|
|
92
|
+
"@fluidframework/mocha-test-setup": ">=2.0.0-internal.3.0.0 <2.0.0-internal.4.0.0",
|
|
93
|
+
"@fluidframework/test-runtime-utils": ">=2.0.0-internal.3.0.0 <2.0.0-internal.4.0.0",
|
|
94
94
|
"@microsoft/api-extractor": "^7.22.2",
|
|
95
95
|
"@rushstack/eslint-config": "^2.5.1",
|
|
96
96
|
"@types/double-ended-queue": "^2.1.0",
|
|
@@ -109,8 +109,43 @@
|
|
|
109
109
|
"typescript": "~4.5.5"
|
|
110
110
|
},
|
|
111
111
|
"typeValidation": {
|
|
112
|
-
"version": "2.0.0-internal.
|
|
113
|
-
"baselineRange": "2.0.0-internal.2.3.0",
|
|
114
|
-
"
|
|
112
|
+
"version": "2.0.0-internal.3.0.0",
|
|
113
|
+
"baselineRange": ">=2.0.0-internal.2.0.0 <2.0.0-internal.3.0.0",
|
|
114
|
+
"baselineVersion": "2.0.0-internal.2.2.0",
|
|
115
|
+
"broken": {
|
|
116
|
+
"RemovedVariableDeclaration_gcBlobPrefix": {
|
|
117
|
+
"forwardCompat": false,
|
|
118
|
+
"backCompat": false
|
|
119
|
+
},
|
|
120
|
+
"RemovedVariableDeclaration_gcTombstoneBlobKey": {
|
|
121
|
+
"forwardCompat": false,
|
|
122
|
+
"backCompat": false
|
|
123
|
+
},
|
|
124
|
+
"RemovedVariableDeclaration_gcTreeKey": {
|
|
125
|
+
"forwardCompat": false,
|
|
126
|
+
"backCompat": false
|
|
127
|
+
},
|
|
128
|
+
"VariableDeclaration_DefaultSummaryConfiguration": {
|
|
129
|
+
"backCompat": false
|
|
130
|
+
},
|
|
131
|
+
"InterfaceDeclaration_ISummaryBaseConfiguration": {
|
|
132
|
+
"backCompat": false
|
|
133
|
+
},
|
|
134
|
+
"TypeAliasDeclaration_ISummaryConfiguration": {
|
|
135
|
+
"backCompat": false
|
|
136
|
+
},
|
|
137
|
+
"InterfaceDeclaration_ISummaryConfigurationDisableHeuristics": {
|
|
138
|
+
"backCompat": false
|
|
139
|
+
},
|
|
140
|
+
"InterfaceDeclaration_ISummaryConfigurationHeuristics": {
|
|
141
|
+
"backCompat": false
|
|
142
|
+
},
|
|
143
|
+
"ClassDeclaration_ContainerRuntime": {
|
|
144
|
+
"forwardCompat": false
|
|
145
|
+
},
|
|
146
|
+
"InterfaceDeclaration_ISummarizerRuntime": {
|
|
147
|
+
"backCompat": false
|
|
148
|
+
}
|
|
149
|
+
}
|
|
115
150
|
}
|
|
116
151
|
}
|
package/src/blobManager.ts
CHANGED
|
@@ -101,12 +101,17 @@ enum PendingBlobStatus {
|
|
|
101
101
|
OfflinePendingOp,
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
+
type ICreateBlobResponseWithTTL = ICreateBlobResponse & Partial<Record<"minTTLInSeconds", number>>;
|
|
105
|
+
|
|
104
106
|
interface PendingBlob {
|
|
105
107
|
blob: ArrayBufferLike;
|
|
106
108
|
status: PendingBlobStatus;
|
|
107
109
|
storageId?: string;
|
|
108
110
|
handleP: Deferred<IFluidHandle<ArrayBufferLike>>;
|
|
109
111
|
uploadP: Promise<ICreateBlobResponse>;
|
|
112
|
+
localUploadTime?: number;
|
|
113
|
+
serverUploadTime?: number;
|
|
114
|
+
minTTLInSeconds?: number;
|
|
110
115
|
}
|
|
111
116
|
|
|
112
117
|
export interface IPendingBlobs { [id: string]: { blob: string; }; }
|
|
@@ -179,6 +184,7 @@ export class BlobManager extends TypedEventEmitter<IBlobManagerEvents> {
|
|
|
179
184
|
private readonly addedBlobReference: (fromNodePath: string, toNodePath: string) => void,
|
|
180
185
|
private readonly runtime: IBlobManagerRuntime,
|
|
181
186
|
stashedBlobs: IPendingBlobs = {},
|
|
187
|
+
private readonly getCurrentReferenceTimestampMs: () => number | undefined,
|
|
182
188
|
) {
|
|
183
189
|
super();
|
|
184
190
|
this.mc = loggerToMonitoringContext(ChildLogger.create(this.runtime.logger, "BlobManager"));
|
|
@@ -398,12 +404,15 @@ export class BlobManager extends TypedEventEmitter<IBlobManagerEvents> {
|
|
|
398
404
|
}
|
|
399
405
|
}
|
|
400
406
|
|
|
401
|
-
private onUploadResolve(localId: string, response:
|
|
407
|
+
private onUploadResolve(localId: string, response: ICreateBlobResponseWithTTL) {
|
|
402
408
|
const entry = this.pendingBlobs.get(localId);
|
|
403
409
|
assert(entry?.status === PendingBlobStatus.OnlinePendingUpload ||
|
|
404
410
|
entry?.status === PendingBlobStatus.OfflinePendingUpload,
|
|
405
411
|
0x386 /* Must have pending blob entry for uploaded blob */);
|
|
406
412
|
entry.storageId = response.id;
|
|
413
|
+
entry.localUploadTime = Date.now();
|
|
414
|
+
entry.minTTLInSeconds = response.minTTLInSeconds;
|
|
415
|
+
entry.serverUploadTime = this.getCurrentReferenceTimestampMs();
|
|
407
416
|
if (this.runtime.connected) {
|
|
408
417
|
if (entry.status === PendingBlobStatus.OnlinePendingUpload) {
|
|
409
418
|
// Send a blob attach op. This serves two purposes:
|
|
@@ -411,6 +420,7 @@ export class BlobManager extends TypedEventEmitter<IBlobManagerEvents> {
|
|
|
411
420
|
// until its storage ID is added to the next summary.
|
|
412
421
|
// 2. It will create a local ID to storage ID mapping in all clients which is needed to retrieve the
|
|
413
422
|
// blob from the server via the storage ID.
|
|
423
|
+
this.logTimeInfo(entry, "sendBlobAttachResolveTTL");
|
|
414
424
|
this.sendBlobAttachOp(localId, response.id);
|
|
415
425
|
if (this.storageIds.has(response.id)) {
|
|
416
426
|
// The blob is de-duped. Set up a local ID to storage ID mapping and return the blob. Since this is
|
|
@@ -472,6 +482,7 @@ export class BlobManager extends TypedEventEmitter<IBlobManagerEvents> {
|
|
|
472
482
|
* is called on reconnection.
|
|
473
483
|
*/
|
|
474
484
|
if (entry.status !== PendingBlobStatus.OnlinePendingOp) {
|
|
485
|
+
this.logTimeInfo(entry, "sendBlobAttachTransitionOfflineTTL");
|
|
475
486
|
this.sendBlobAttachOp(localId, entry.storageId);
|
|
476
487
|
}
|
|
477
488
|
|
|
@@ -491,9 +502,12 @@ export class BlobManager extends TypedEventEmitter<IBlobManagerEvents> {
|
|
|
491
502
|
assert(!!metadata, 0x38b /* Resubmitted ops must have metadata */);
|
|
492
503
|
const { localId, blobId }: { localId?: string; blobId?: string } = metadata;
|
|
493
504
|
assert(localId !== undefined, 0x50d /* local ID not available on reSubmit */);
|
|
505
|
+
const pendingEntry = this.pendingBlobs.get(localId);
|
|
506
|
+
if (pendingEntry) {
|
|
507
|
+
this.logTimeInfo(pendingEntry, "sendBlobAttachResubmitTTL");
|
|
508
|
+
}
|
|
494
509
|
if (!blobId) {
|
|
495
510
|
// We submitted this op while offline. The blob should have been uploaded by now.
|
|
496
|
-
const pendingEntry = this.pendingBlobs.get(localId);
|
|
497
511
|
assert(pendingEntry?.status === PendingBlobStatus.OfflinePendingOp &&
|
|
498
512
|
!!pendingEntry?.storageId, 0x38d /* blob must be uploaded before resubmitting BlobAttach op */);
|
|
499
513
|
return this.sendBlobAttachOp(localId, pendingEntry.storageId);
|
|
@@ -501,6 +515,31 @@ export class BlobManager extends TypedEventEmitter<IBlobManagerEvents> {
|
|
|
501
515
|
return this.sendBlobAttachOp(localId, blobId);
|
|
502
516
|
}
|
|
503
517
|
|
|
518
|
+
private logTimeInfo(pendingEntry: PendingBlob, eventName: string) {
|
|
519
|
+
let timeLapseSinceLocalUpload: number = 0;
|
|
520
|
+
let timeLapseSinceServerUpload: number = 0;
|
|
521
|
+
let expiredUsingLocalTime;
|
|
522
|
+
let expiredUsingServerTime;
|
|
523
|
+
if(pendingEntry.localUploadTime){
|
|
524
|
+
timeLapseSinceLocalUpload = (Date.now() - pendingEntry.localUploadTime) / 1000;
|
|
525
|
+
expiredUsingLocalTime = (pendingEntry.minTTLInSeconds?? 0) - timeLapseSinceLocalUpload < 0 ? true : false;
|
|
526
|
+
}
|
|
527
|
+
if(pendingEntry.serverUploadTime){
|
|
528
|
+
timeLapseSinceServerUpload = (Date.now() - pendingEntry.serverUploadTime) / 1000;
|
|
529
|
+
expiredUsingServerTime = (pendingEntry.minTTLInSeconds?? 0) - timeLapseSinceServerUpload < 0 ? true : false;
|
|
530
|
+
}
|
|
531
|
+
this.mc.logger.sendTelemetryEvent({
|
|
532
|
+
eventName,
|
|
533
|
+
entryStatus: pendingEntry.status,
|
|
534
|
+
timeLapseSinceLocalUpload,
|
|
535
|
+
timeLapseSinceServerUpload,
|
|
536
|
+
minTTLInSeconds: pendingEntry.minTTLInSeconds,
|
|
537
|
+
expiredUsingLocalTime,
|
|
538
|
+
expiredUsingServerTime,
|
|
539
|
+
});
|
|
540
|
+
|
|
541
|
+
}
|
|
542
|
+
|
|
504
543
|
public processBlobAttachOp(message: ISequencedDocumentMessage, local: boolean) {
|
|
505
544
|
const localId = message.metadata?.localId;
|
|
506
545
|
const blobId = message.metadata?.blobId;
|
package/src/containerRuntime.ts
CHANGED
|
@@ -143,7 +143,6 @@ import {
|
|
|
143
143
|
ISubmitSummaryOptions,
|
|
144
144
|
ISummarizer,
|
|
145
145
|
ISummarizerInternalsProvider,
|
|
146
|
-
ISummarizerOptions,
|
|
147
146
|
ISummarizerRuntime,
|
|
148
147
|
IRefreshSummaryAckOptions,
|
|
149
148
|
} from "./summarizerTypes";
|
|
@@ -205,13 +204,6 @@ export interface ISummaryBaseConfiguration {
|
|
|
205
204
|
*/
|
|
206
205
|
initialSummarizerDelayMs: number;
|
|
207
206
|
|
|
208
|
-
/**
|
|
209
|
-
* @deprecated
|
|
210
|
-
* Flag that will enable changing elected summarizer client after maxOpsSinceLastSummary.
|
|
211
|
-
* This defaults to false (disabled) and must be explicitly set to true to enable.
|
|
212
|
-
*/
|
|
213
|
-
summarizerClientElection: boolean;
|
|
214
|
-
|
|
215
207
|
/**
|
|
216
208
|
* Defines the maximum allowed time to wait for a pending summary ack.
|
|
217
209
|
* The maximum amount of time client will wait for a summarize is the minimum of
|
|
@@ -317,8 +309,6 @@ export const DefaultSummaryConfiguration: ISummaryConfiguration = {
|
|
|
317
309
|
|
|
318
310
|
initialSummarizerDelayMs: 5 * 1000, // 5 secs.
|
|
319
311
|
|
|
320
|
-
summarizerClientElection: false,
|
|
321
|
-
|
|
322
312
|
nonRuntimeOpWeight: 0.1,
|
|
323
313
|
|
|
324
314
|
runtimeOpWeight: 1.0,
|
|
@@ -386,40 +376,6 @@ export interface ISummaryRuntimeOptions {
|
|
|
386
376
|
* {@link ISummaryBaseConfiguration.initialSummarizerDelayMs} instead.
|
|
387
377
|
*/
|
|
388
378
|
initialSummarizerDelayMs?: number;
|
|
389
|
-
|
|
390
|
-
/**
|
|
391
|
-
* Flag that disables summaries if it is set to true.
|
|
392
|
-
*
|
|
393
|
-
* @deprecated Use {@link ISummaryRuntimeOptions.summaryConfigOverrides}'s
|
|
394
|
-
* {@link ISummaryConfigurationDisableSummarizer.state} instead.
|
|
395
|
-
*/
|
|
396
|
-
disableSummaries?: boolean;
|
|
397
|
-
|
|
398
|
-
/**
|
|
399
|
-
* @defaultValue 7000 operations (ops)
|
|
400
|
-
*
|
|
401
|
-
* @deprecated Use {@link ISummaryRuntimeOptions.summaryConfigOverrides}'s
|
|
402
|
-
* {@link ISummaryBaseConfiguration.maxOpsSinceLastSummary} instead.
|
|
403
|
-
*/
|
|
404
|
-
maxOpsSinceLastSummary?: number;
|
|
405
|
-
|
|
406
|
-
/**
|
|
407
|
-
* Flag that will enable changing elected summarizer client after maxOpsSinceLastSummary.
|
|
408
|
-
*
|
|
409
|
-
* @defaultValue `false` (disabled) and must be explicitly set to true to enable.
|
|
410
|
-
*
|
|
411
|
-
* @deprecated Use {@link ISummaryRuntimeOptions.summaryConfigOverrides}'s
|
|
412
|
-
* {@link ISummaryBaseConfiguration.summarizerClientElection} instead.
|
|
413
|
-
*/
|
|
414
|
-
summarizerClientElection?: boolean;
|
|
415
|
-
|
|
416
|
-
/**
|
|
417
|
-
* Options that control the running summarizer behavior.
|
|
418
|
-
*
|
|
419
|
-
* @deprecated Use {@link ISummaryRuntimeOptions.summaryConfigOverrides}'s
|
|
420
|
-
* `{@link ISummaryConfiguration.state} = "DisableHeuristics"` instead.
|
|
421
|
-
* */
|
|
422
|
-
summarizerOptions?: Readonly<Partial<ISummarizerOptions>>;
|
|
423
379
|
}
|
|
424
380
|
|
|
425
381
|
/**
|
|
@@ -491,9 +447,10 @@ export interface IContainerRuntimeOptions {
|
|
|
491
447
|
*/
|
|
492
448
|
readonly chunkSizeInBytes?: number;
|
|
493
449
|
/**
|
|
494
|
-
* If enabled, the runtime will block all attempts to send an op
|
|
495
|
-
*
|
|
496
|
-
*
|
|
450
|
+
* If enabled, the runtime will block all attempts to send an op inside the
|
|
451
|
+
* {@link ContainerRuntime#ensureNoDataModelChanges} callback. The callback is used by
|
|
452
|
+
* {@link @fluidframework/shared-object-base#SharedObjectCore} for event handlers so enabling this
|
|
453
|
+
* will disallow modifying DDSes while handling DDS events.
|
|
497
454
|
*
|
|
498
455
|
* By default, the feature is disabled. If enabled from options, the `Fluid.ContainerRuntime.DisableOpReentryCheck`
|
|
499
456
|
* can be used to disable it at runtime.
|
|
@@ -663,6 +620,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
663
620
|
public get IFluidRouter() { return this; }
|
|
664
621
|
|
|
665
622
|
/**
|
|
623
|
+
* @deprecated - use loadRuntime instead.
|
|
666
624
|
* Load the stores from a snapshot and returns the runtime.
|
|
667
625
|
* @param context - Context of the container.
|
|
668
626
|
* @param registryEntries - Mapping to the stores.
|
|
@@ -681,6 +639,54 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
681
639
|
existing?: boolean,
|
|
682
640
|
containerRuntimeCtor: typeof ContainerRuntime = ContainerRuntime
|
|
683
641
|
): Promise<ContainerRuntime> {
|
|
642
|
+
let existingFlag = true;
|
|
643
|
+
if (!existing) {
|
|
644
|
+
existingFlag = false;
|
|
645
|
+
}
|
|
646
|
+
return this.loadRuntime({
|
|
647
|
+
context,
|
|
648
|
+
registryEntries,
|
|
649
|
+
existing: existingFlag,
|
|
650
|
+
requestHandler,
|
|
651
|
+
runtimeOptions,
|
|
652
|
+
containerScope,
|
|
653
|
+
containerRuntimeCtor,
|
|
654
|
+
});
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
/**
|
|
658
|
+
* Load the stores from a snapshot and returns the runtime.
|
|
659
|
+
* @param params - An object housing the runtime properties:
|
|
660
|
+
* - context - Context of the container.
|
|
661
|
+
* - registryEntries - Mapping to the stores.
|
|
662
|
+
* - existing - When loading from an existing snapshot
|
|
663
|
+
* - requestHandler - Request handlers for the container runtime
|
|
664
|
+
* - runtimeOptions - Additional options to be passed to the runtime
|
|
665
|
+
* - containerScope - runtime services provided with context
|
|
666
|
+
* - containerRuntimeCtor - Constructor to use to create the ContainerRuntime instance.
|
|
667
|
+
* This allows mixin classes to leverage this method to define their own async initializer.
|
|
668
|
+
*/
|
|
669
|
+
public static async loadRuntime(
|
|
670
|
+
params: {
|
|
671
|
+
context: IContainerContext;
|
|
672
|
+
registryEntries: NamedFluidDataStoreRegistryEntries;
|
|
673
|
+
existing: boolean;
|
|
674
|
+
requestHandler?: (request: IRequest, runtime: IContainerRuntime) => Promise<IResponse>;
|
|
675
|
+
runtimeOptions?: IContainerRuntimeOptions;
|
|
676
|
+
containerScope?: FluidObject;
|
|
677
|
+
containerRuntimeCtor?: typeof ContainerRuntime;
|
|
678
|
+
},
|
|
679
|
+
): Promise<ContainerRuntime> {
|
|
680
|
+
const {
|
|
681
|
+
context,
|
|
682
|
+
registryEntries,
|
|
683
|
+
existing,
|
|
684
|
+
requestHandler,
|
|
685
|
+
runtimeOptions = {},
|
|
686
|
+
containerScope = {},
|
|
687
|
+
containerRuntimeCtor = ContainerRuntime
|
|
688
|
+
} = params;
|
|
689
|
+
|
|
684
690
|
// If taggedLogger exists, use it. Otherwise, wrap the vanilla logger:
|
|
685
691
|
// back-compat: Remove the TaggedLoggerAdapter fallback once all the host are using loader > 0.45
|
|
686
692
|
const backCompatContext: IContainerContext | OldContainerContextWithLogger = context;
|
|
@@ -762,7 +768,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
762
768
|
if (loadSequenceNumberVerification === "log") {
|
|
763
769
|
logger.sendErrorEvent({ eventName: "SequenceNumberMismatch" }, error);
|
|
764
770
|
} else {
|
|
765
|
-
// Call both close and dispose as
|
|
771
|
+
// Call both close and dispose as closeFn implementation will no longer dispose runtime in future
|
|
766
772
|
context.closeFn(error);
|
|
767
773
|
context.disposeFn?.(error);
|
|
768
774
|
}
|
|
@@ -905,6 +911,33 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
905
911
|
*/
|
|
906
912
|
private delayConnectClientId?: string;
|
|
907
913
|
|
|
914
|
+
private ensureNoDataModelChangesCalls = 0;
|
|
915
|
+
|
|
916
|
+
/**
|
|
917
|
+
* Tracks the number of detected reentrant ops to report,
|
|
918
|
+
* in order to self-throttle the telemetry events.
|
|
919
|
+
*
|
|
920
|
+
* This should be removed as part of ADO:2322
|
|
921
|
+
*/
|
|
922
|
+
private opReentryCallsToReport = 5;
|
|
923
|
+
|
|
924
|
+
/**
|
|
925
|
+
* Invokes the given callback and expects that no ops are submitted
|
|
926
|
+
* until execution finishes. If an op is submitted, an error will be raised.
|
|
927
|
+
*
|
|
928
|
+
* Can be disabled by feature gate `Fluid.ContainerRuntime.DisableOpReentryCheck`
|
|
929
|
+
*
|
|
930
|
+
* @param callback - the callback to be invoked
|
|
931
|
+
*/
|
|
932
|
+
public ensureNoDataModelChanges<T>(callback: () => T): T {
|
|
933
|
+
this.ensureNoDataModelChangesCalls++;
|
|
934
|
+
try {
|
|
935
|
+
return callback();
|
|
936
|
+
} finally {
|
|
937
|
+
this.ensureNoDataModelChangesCalls--;
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
|
|
908
941
|
public get connected(): boolean {
|
|
909
942
|
return this._connected;
|
|
910
943
|
}
|
|
@@ -955,45 +988,16 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
955
988
|
|
|
956
989
|
private readonly summariesDisabled: boolean;
|
|
957
990
|
private isSummariesDisabled(): boolean {
|
|
958
|
-
// back-compat: disableSummaries was moved from ISummaryRuntimeOptions
|
|
959
|
-
// to ISummaryConfiguration in 0.60.
|
|
960
|
-
if (this.runtimeOptions.summaryOptions.disableSummaries === true) {
|
|
961
|
-
return true;
|
|
962
|
-
}
|
|
963
991
|
return this.summaryConfiguration.state === "disabled";
|
|
964
992
|
}
|
|
965
993
|
|
|
966
994
|
private readonly heuristicsDisabled: boolean;
|
|
967
995
|
private isHeuristicsDisabled(): boolean {
|
|
968
|
-
// back-compat: disableHeuristics was moved from ISummarizerOptions
|
|
969
|
-
// to ISummaryConfiguration in 0.60.
|
|
970
|
-
if (this.runtimeOptions.summaryOptions.summarizerOptions?.disableHeuristics === true) {
|
|
971
|
-
return true;
|
|
972
|
-
}
|
|
973
996
|
return this.summaryConfiguration.state === "disableHeuristics";
|
|
974
997
|
}
|
|
975
998
|
|
|
976
|
-
private readonly summarizerClientElectionEnabled: boolean;
|
|
977
|
-
private isSummarizerClientElectionEnabled(): boolean {
|
|
978
|
-
if (this.mc.config.getBoolean("Fluid.ContainerRuntime.summarizerClientElection")) {
|
|
979
|
-
return this.mc.config.getBoolean("Fluid.ContainerRuntime.summarizerClientElection") ?? true;
|
|
980
|
-
}
|
|
981
|
-
// back-compat: summarizerClientElection was moved from ISummaryRuntimeOptions
|
|
982
|
-
// to ISummaryConfiguration in 0.60.
|
|
983
|
-
if (this.runtimeOptions.summaryOptions.summarizerClientElection === true) {
|
|
984
|
-
return true;
|
|
985
|
-
}
|
|
986
|
-
return this.summaryConfiguration.state !== "disabled"
|
|
987
|
-
? this.summaryConfiguration.summarizerClientElection === true
|
|
988
|
-
: false;
|
|
989
|
-
}
|
|
990
999
|
private readonly maxOpsSinceLastSummary: number;
|
|
991
1000
|
private getMaxOpsSinceLastSummary(): number {
|
|
992
|
-
// back-compat: maxOpsSinceLastSummary was moved from ISummaryRuntimeOptions
|
|
993
|
-
// to ISummaryConfiguration in 0.60.
|
|
994
|
-
if (this.runtimeOptions.summaryOptions.maxOpsSinceLastSummary !== undefined) {
|
|
995
|
-
return this.runtimeOptions.summaryOptions.maxOpsSinceLastSummary;
|
|
996
|
-
}
|
|
997
1001
|
return this.summaryConfiguration.state !== "disabled"
|
|
998
1002
|
? this.summaryConfiguration.maxOpsSinceLastSummary
|
|
999
1003
|
: 0;
|
|
@@ -1091,7 +1095,6 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1091
1095
|
|
|
1092
1096
|
this.summariesDisabled = this.isSummariesDisabled();
|
|
1093
1097
|
this.heuristicsDisabled = this.isHeuristicsDisabled();
|
|
1094
|
-
this.summarizerClientElectionEnabled = this.isSummarizerClientElectionEnabled();
|
|
1095
1098
|
this.maxOpsSinceLastSummary = this.getMaxOpsSinceLastSummary();
|
|
1096
1099
|
this.initialSummarizerDelayMs = this.getInitialSummarizerDelayMs();
|
|
1097
1100
|
|
|
@@ -1198,6 +1201,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1198
1201
|
(fromPath: string, toPath: string) => this.garbageCollector.addedOutboundReference(fromPath, toPath),
|
|
1199
1202
|
this,
|
|
1200
1203
|
pendingRuntimeState?.pendingAttachmentBlobs,
|
|
1204
|
+
() => this.getCurrentReferenceTimestampMs(),
|
|
1201
1205
|
);
|
|
1202
1206
|
|
|
1203
1207
|
this.scheduleManager = new ScheduleManager(
|
|
@@ -1213,7 +1217,6 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1213
1217
|
clientId: () => this.clientId,
|
|
1214
1218
|
close: this.closeFn,
|
|
1215
1219
|
connected: () => this.connected,
|
|
1216
|
-
flush: this.flush.bind(this),
|
|
1217
1220
|
reSubmit: this.reSubmit.bind(this),
|
|
1218
1221
|
rollback: this.rollback.bind(this),
|
|
1219
1222
|
orderSequentially: this.orderSequentially.bind(this),
|
|
@@ -1272,7 +1275,6 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1272
1275
|
this.summaryCollection,
|
|
1273
1276
|
orderedClientElectionForSummarizer,
|
|
1274
1277
|
this.maxOpsSinceLastSummary,
|
|
1275
|
-
this.summarizerClientElectionEnabled,
|
|
1276
1278
|
);
|
|
1277
1279
|
|
|
1278
1280
|
if (this.context.clientDetails.type === summarizerClientType) {
|
|
@@ -1945,7 +1947,8 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1945
1947
|
this._orderSequentiallyCalls--;
|
|
1946
1948
|
}
|
|
1947
1949
|
|
|
1948
|
-
|
|
1950
|
+
// We don't flush on TurnBased since we expect all messages in the same JS turn to be part of the same batch
|
|
1951
|
+
if (this.flushMode !== FlushMode.TurnBased && this._orderSequentiallyCalls === 0) {
|
|
1949
1952
|
this.flush();
|
|
1950
1953
|
}
|
|
1951
1954
|
return result;
|
|
@@ -2630,6 +2633,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2630
2633
|
metadata: Record<string, unknown> | undefined = undefined,
|
|
2631
2634
|
): void {
|
|
2632
2635
|
this.verifyNotClosed();
|
|
2636
|
+
this.verifyCanSubmitOps();
|
|
2633
2637
|
|
|
2634
2638
|
// There should be no ops in detached container state!
|
|
2635
2639
|
assert(this.attachState !== AttachState.Detached, 0x132 /* "sending ops in detached container" */);
|
|
@@ -2723,6 +2727,36 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2723
2727
|
}
|
|
2724
2728
|
}
|
|
2725
2729
|
|
|
2730
|
+
private verifyCanSubmitOps() {
|
|
2731
|
+
if (this.ensureNoDataModelChangesCalls > 0) {
|
|
2732
|
+
const errorMessage = "Op was submitted from within a `ensureNoDataModelChanges` callback";
|
|
2733
|
+
if (this.opReentryCallsToReport > 0) {
|
|
2734
|
+
this.mc.logger.sendTelemetryEvent(
|
|
2735
|
+
{ eventName: "OpReentry" },
|
|
2736
|
+
// We need to capture the call stack in order to inspect the source of this usage pattern
|
|
2737
|
+
new UsageError(errorMessage),
|
|
2738
|
+
);
|
|
2739
|
+
this.opReentryCallsToReport--;
|
|
2740
|
+
}
|
|
2741
|
+
|
|
2742
|
+
// Creating ops while processing ops can lead
|
|
2743
|
+
// to undefined behavior and events observed in the wrong order.
|
|
2744
|
+
// For example, we have two callbacks registered for a DDS, A and B.
|
|
2745
|
+
// Then if on change #1 callback A creates change #2, the invocation flow will be:
|
|
2746
|
+
//
|
|
2747
|
+
// A because of #1
|
|
2748
|
+
// A because of #2
|
|
2749
|
+
// B because of #2
|
|
2750
|
+
// B because of #1
|
|
2751
|
+
//
|
|
2752
|
+
// The runtime must enforce op coherence by not allowing ops to be submitted
|
|
2753
|
+
// while ops are being processed.
|
|
2754
|
+
if (this.enableOpReentryCheck) {
|
|
2755
|
+
throw new UsageError(errorMessage);
|
|
2756
|
+
}
|
|
2757
|
+
}
|
|
2758
|
+
}
|
|
2759
|
+
|
|
2726
2760
|
/**
|
|
2727
2761
|
* Finds the right store and asks it to resubmit the message. This typically happens when we
|
|
2728
2762
|
* reconnect and there are pending messages.
|
|
@@ -2958,15 +2992,14 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2958
2992
|
throw new UsageError("can't get state when offline load disabled");
|
|
2959
2993
|
}
|
|
2960
2994
|
|
|
2995
|
+
if (this._orderSequentiallyCalls !== 0) {
|
|
2996
|
+
throw new UsageError("can't get state during orderSequentially");
|
|
2997
|
+
}
|
|
2961
2998
|
// Flush pending batch.
|
|
2962
2999
|
// getPendingLocalState() is only exposed through Container.closeAndGetPendingLocalState(), so it's safe
|
|
2963
3000
|
// to close current batch.
|
|
2964
3001
|
this.flush();
|
|
2965
3002
|
|
|
2966
|
-
if (this._orderSequentiallyCalls !== 0) {
|
|
2967
|
-
throw new UsageError("can't get state during orderSequentially");
|
|
2968
|
-
}
|
|
2969
|
-
|
|
2970
3003
|
const previousPendingState = this.context.pendingLocalState as IPendingRuntimeState | undefined;
|
|
2971
3004
|
if (previousPendingState) {
|
|
2972
3005
|
return {
|
package/src/dataStoreContext.ts
CHANGED
|
@@ -191,6 +191,10 @@ export abstract class FluidDataStoreContext extends TypedEventEmitter<IFluidData
|
|
|
191
191
|
return this._containerRuntime;
|
|
192
192
|
}
|
|
193
193
|
|
|
194
|
+
public ensureNoDataModelChanges<T>(callback: () => T): T {
|
|
195
|
+
return this._containerRuntime.ensureNoDataModelChanges(callback);
|
|
196
|
+
}
|
|
197
|
+
|
|
194
198
|
public get isLoaded(): boolean {
|
|
195
199
|
return this.loaded;
|
|
196
200
|
}
|
|
@@ -299,7 +303,7 @@ export abstract class FluidDataStoreContext extends TypedEventEmitter<IFluidData
|
|
|
299
303
|
|
|
300
304
|
const thisSummarizeInternal =
|
|
301
305
|
async (fullTree: boolean, trackState: boolean, telemetryContext?: ITelemetryContext) =>
|
|
302
|
-
|
|
306
|
+
this.summarizeInternal(fullTree, trackState, telemetryContext);
|
|
303
307
|
|
|
304
308
|
this.summarizerNode = props.createSummarizerNodeFn(
|
|
305
309
|
thisSummarizeInternal,
|
|
@@ -327,7 +331,7 @@ export abstract class FluidDataStoreContext extends TypedEventEmitter<IFluidData
|
|
|
327
331
|
if (this.channelDeferred) {
|
|
328
332
|
this.channelDeferred.promise.then((runtime) => {
|
|
329
333
|
runtime.dispose();
|
|
330
|
-
}).catch((error) => {});
|
|
334
|
+
}).catch((error) => { });
|
|
331
335
|
}
|
|
332
336
|
}
|
|
333
337
|
|
|
@@ -712,10 +716,12 @@ export abstract class FluidDataStoreContext extends TypedEventEmitter<IFluidData
|
|
|
712
716
|
} catch (error) {
|
|
713
717
|
this.channelDeferred?.reject(error);
|
|
714
718
|
this.logger.sendErrorEvent(
|
|
715
|
-
{
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
+
{
|
|
720
|
+
eventName: "BindRuntimeError", fluidDataStoreId: {
|
|
721
|
+
value: this.id,
|
|
722
|
+
tag: TelemetryDataTag.CodeArtifact,
|
|
723
|
+
},
|
|
724
|
+
},
|
|
719
725
|
error);
|
|
720
726
|
}
|
|
721
727
|
}
|