@fluidframework/container-runtime 1.2.2 → 2.0.0-internal.1.0.0.82159
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 +81 -25
- package/dist/blobManager.d.ts.map +1 -1
- package/dist/blobManager.js +301 -100
- package/dist/blobManager.js.map +1 -1
- package/dist/containerRuntime.d.ts +65 -11
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +101 -82
- 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 +32 -26
- package/dist/dataStore.js.map +1 -1
- package/dist/dataStoreContext.d.ts +3 -4
- package/dist/dataStoreContext.d.ts.map +1 -1
- package/dist/dataStoreContext.js +16 -23
- package/dist/dataStoreContext.js.map +1 -1
- package/dist/dataStores.d.ts +5 -2
- package/dist/dataStores.d.ts.map +1 -1
- package/dist/dataStores.js +11 -3
- package/dist/dataStores.js.map +1 -1
- package/dist/garbageCollection.d.ts.map +1 -1
- package/dist/garbageCollection.js +17 -12
- package/dist/garbageCollection.js.map +1 -1
- package/dist/opProperties.d.ts +7 -0
- package/dist/opProperties.d.ts.map +1 -0
- package/dist/opProperties.js +20 -0
- package/dist/opProperties.js.map +1 -0
- 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.d.ts +14 -4
- package/dist/runningSummarizer.d.ts.map +1 -1
- package/dist/runningSummarizer.js +68 -26
- package/dist/runningSummarizer.js.map +1 -1
- package/dist/summarizer.d.ts +0 -2
- package/dist/summarizer.d.ts.map +1 -1
- package/dist/summarizer.js +1 -12
- package/dist/summarizer.js.map +1 -1
- package/dist/summarizerHeuristics.d.ts +26 -4
- package/dist/summarizerHeuristics.d.ts.map +1 -1
- package/dist/summarizerHeuristics.js +95 -18
- package/dist/summarizerHeuristics.js.map +1 -1
- package/dist/summarizerTypes.d.ts +30 -10
- package/dist/summarizerTypes.d.ts.map +1 -1
- package/dist/summarizerTypes.js.map +1 -1
- package/dist/summaryCollection.js +1 -1
- package/dist/summaryCollection.js.map +1 -1
- package/dist/summaryFormat.d.ts +0 -5
- package/dist/summaryFormat.d.ts.map +1 -1
- package/dist/summaryFormat.js.map +1 -1
- package/dist/summaryGenerator.d.ts +1 -0
- package/dist/summaryGenerator.d.ts.map +1 -1
- package/dist/summaryGenerator.js +11 -9
- package/dist/summaryGenerator.js.map +1 -1
- package/lib/blobManager.d.ts +81 -25
- package/lib/blobManager.d.ts.map +1 -1
- package/lib/blobManager.js +302 -101
- package/lib/blobManager.js.map +1 -1
- package/lib/containerRuntime.d.ts +65 -11
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +103 -84
- 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 +32 -26
- package/lib/dataStore.js.map +1 -1
- package/lib/dataStoreContext.d.ts +3 -4
- package/lib/dataStoreContext.d.ts.map +1 -1
- package/lib/dataStoreContext.js +17 -24
- package/lib/dataStoreContext.js.map +1 -1
- package/lib/dataStores.d.ts +5 -2
- package/lib/dataStores.d.ts.map +1 -1
- package/lib/dataStores.js +11 -3
- package/lib/dataStores.js.map +1 -1
- package/lib/garbageCollection.d.ts.map +1 -1
- package/lib/garbageCollection.js +17 -12
- package/lib/garbageCollection.js.map +1 -1
- package/lib/opProperties.d.ts +7 -0
- package/lib/opProperties.d.ts.map +1 -0
- package/lib/opProperties.js +16 -0
- package/lib/opProperties.js.map +1 -0
- 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.d.ts +14 -4
- package/lib/runningSummarizer.d.ts.map +1 -1
- package/lib/runningSummarizer.js +68 -26
- package/lib/runningSummarizer.js.map +1 -1
- package/lib/summarizer.d.ts +0 -2
- package/lib/summarizer.d.ts.map +1 -1
- package/lib/summarizer.js +1 -12
- package/lib/summarizer.js.map +1 -1
- package/lib/summarizerHeuristics.d.ts +26 -4
- package/lib/summarizerHeuristics.d.ts.map +1 -1
- package/lib/summarizerHeuristics.js +95 -18
- package/lib/summarizerHeuristics.js.map +1 -1
- package/lib/summarizerTypes.d.ts +30 -10
- package/lib/summarizerTypes.d.ts.map +1 -1
- package/lib/summarizerTypes.js.map +1 -1
- package/lib/summaryCollection.js +1 -1
- package/lib/summaryCollection.js.map +1 -1
- package/lib/summaryFormat.d.ts +0 -5
- package/lib/summaryFormat.d.ts.map +1 -1
- package/lib/summaryFormat.js.map +1 -1
- package/lib/summaryGenerator.d.ts +1 -0
- package/lib/summaryGenerator.d.ts.map +1 -1
- package/lib/summaryGenerator.js +11 -9
- package/lib/summaryGenerator.js.map +1 -1
- package/package.json +45 -20
- package/src/blobManager.ts +360 -119
- package/src/containerRuntime.ts +203 -103
- package/src/dataStore.ts +53 -38
- package/src/dataStoreContext.ts +16 -23
- package/src/dataStores.ts +14 -3
- package/src/garbageCollection.ts +13 -7
- package/src/opProperties.ts +19 -0
- package/src/packageVersion.ts +1 -1
- package/src/runningSummarizer.ts +75 -22
- package/src/summarizer.ts +1 -18
- package/src/summarizerHeuristics.ts +133 -19
- package/src/summarizerTypes.ts +37 -10
- package/src/summaryCollection.ts +1 -1
- package/src/summaryFormat.ts +0 -6
- package/src/summaryGenerator.ts +40 -22
- package/dist/opTelemetry.d.ts +0 -22
- package/dist/opTelemetry.d.ts.map +0 -1
- package/dist/opTelemetry.js +0 -59
- package/dist/opTelemetry.js.map +0 -1
- package/lib/opTelemetry.d.ts +0 -22
- package/lib/opTelemetry.d.ts.map +0 -1
- package/lib/opTelemetry.js +0 -55
- package/lib/opTelemetry.js.map +0 -1
- package/src/opTelemetry.ts +0 -71
package/src/containerRuntime.ts
CHANGED
|
@@ -43,9 +43,13 @@ import {
|
|
|
43
43
|
TaggedLoggerAdapter,
|
|
44
44
|
MonitoringContext,
|
|
45
45
|
loggerToMonitoringContext,
|
|
46
|
-
TelemetryDataTag,
|
|
47
46
|
} from "@fluidframework/telemetry-utils";
|
|
48
|
-
import {
|
|
47
|
+
import {
|
|
48
|
+
DriverHeader,
|
|
49
|
+
FetchSource,
|
|
50
|
+
IDocumentStorageService,
|
|
51
|
+
ISummaryContext,
|
|
52
|
+
} from "@fluidframework/driver-definitions";
|
|
49
53
|
import { readAndParse, isUnpackedRuntimeMessage } from "@fluidframework/driver-utils";
|
|
50
54
|
import {
|
|
51
55
|
DataCorruptionError,
|
|
@@ -160,7 +164,6 @@ import {
|
|
|
160
164
|
} from "./dataStore";
|
|
161
165
|
import { BindBatchTracker } from "./batchTracker";
|
|
162
166
|
import { ISerializedBaseSnapshotBlobs, SerializedSnapshotStorage } from "./serializedSnapshotStorage";
|
|
163
|
-
import { OpTracker } from "./opTelemetry";
|
|
164
167
|
|
|
165
168
|
export enum ContainerMessageType {
|
|
166
169
|
// An op to be delivered to store
|
|
@@ -224,12 +227,14 @@ export interface ISummaryBaseConfiguration {
|
|
|
224
227
|
export interface ISummaryConfigurationHeuristics extends ISummaryBaseConfiguration {
|
|
225
228
|
state: "enabled";
|
|
226
229
|
/**
|
|
227
|
-
*
|
|
230
|
+
* @deprecated - please move all implementation to minIdleTime and maxIdleTime
|
|
228
231
|
*/
|
|
229
232
|
idleTime: number;
|
|
230
233
|
/**
|
|
231
|
-
* Defines the maximum allowed time, since the last received Ack,
|
|
234
|
+
* Defines the maximum allowed time, since the last received Ack, before running the summary
|
|
232
235
|
* with reason maxTime.
|
|
236
|
+
* For example, say we receive ops one by one just before the idle time is triggered.
|
|
237
|
+
* In this case, we still want to run a summary since it's been a while since the last summary.
|
|
233
238
|
*/
|
|
234
239
|
maxTime: number;
|
|
235
240
|
/**
|
|
@@ -242,6 +247,34 @@ export interface ISummaryConfigurationHeuristics extends ISummaryBaseConfigurati
|
|
|
242
247
|
* before running the last summary.
|
|
243
248
|
*/
|
|
244
249
|
minOpsForLastSummaryAttempt: number;
|
|
250
|
+
/**
|
|
251
|
+
* Defines the lower boundary for the allowed time in between summarizations.
|
|
252
|
+
* Pairs with maxIdleTime to form a range.
|
|
253
|
+
* For example, if we only receive 1 op, we don't want to have the same idle time as say 100 ops.
|
|
254
|
+
* Based on the boundaries we set in minIdleTime and maxIdleTime, the idle time will change
|
|
255
|
+
* linearly depending on the number of ops we receive.
|
|
256
|
+
*/
|
|
257
|
+
minIdleTime: number;
|
|
258
|
+
/**
|
|
259
|
+
* Defines the upper boundary for the allowed time in between summarizations.
|
|
260
|
+
* Pairs with minIdleTime to form a range.
|
|
261
|
+
* For example, if we only receive 1 op, we don't want to have the same idle time as say 100 ops.
|
|
262
|
+
* Based on the boundaries we set in minIdleTime and maxIdleTime, the idle time will change
|
|
263
|
+
* linearly depending on the number of ops we receive.
|
|
264
|
+
*/
|
|
265
|
+
maxIdleTime: number;
|
|
266
|
+
/**
|
|
267
|
+
* Runtime op weight to use in heuristic summarizing.
|
|
268
|
+
* This number is a multiplier on the number of runtime ops we process when running summarize heuristics.
|
|
269
|
+
* For example: (multiplier) * (number of runtime ops) = weighted number of runtime ops
|
|
270
|
+
*/
|
|
271
|
+
runtimeOpWeight: number;
|
|
272
|
+
/**
|
|
273
|
+
* Non-runtime op weight to use in heuristic summarizing
|
|
274
|
+
* This number is a multiplier on the number of non-runtime ops we process when running summarize heuristics.
|
|
275
|
+
* For example: (multiplier) * (number of non-runtime ops) = weighted number of non-runtime ops
|
|
276
|
+
*/
|
|
277
|
+
nonRuntimeOpWeight: number;
|
|
245
278
|
}
|
|
246
279
|
|
|
247
280
|
export interface ISummaryConfigurationDisableSummarizer {
|
|
@@ -260,44 +293,57 @@ export type ISummaryConfiguration =
|
|
|
260
293
|
export const DefaultSummaryConfiguration: ISummaryConfiguration = {
|
|
261
294
|
state: "enabled",
|
|
262
295
|
|
|
263
|
-
idleTime:
|
|
296
|
+
idleTime: 15 * 1000, // 15 secs.
|
|
297
|
+
|
|
298
|
+
minIdleTime: 0,
|
|
299
|
+
|
|
300
|
+
maxIdleTime: 30 * 1000, // 30 secs.
|
|
264
301
|
|
|
265
|
-
maxTime:
|
|
302
|
+
maxTime: 60 * 1000, // 1 min.
|
|
266
303
|
|
|
267
|
-
maxOps: 100, // Summarize if 100 ops received since last snapshot.
|
|
304
|
+
maxOps: 100, // Summarize if 100 weighted ops received since last snapshot.
|
|
268
305
|
|
|
269
306
|
minOpsForLastSummaryAttempt: 10,
|
|
270
307
|
|
|
271
|
-
maxAckWaitTime:
|
|
308
|
+
maxAckWaitTime: 10 * 60 * 1000, // 10 mins.
|
|
272
309
|
|
|
273
310
|
maxOpsSinceLastSummary: 7000,
|
|
274
311
|
|
|
275
|
-
initialSummarizerDelayMs:
|
|
312
|
+
initialSummarizerDelayMs: 5 * 1000, // 5 secs.
|
|
276
313
|
|
|
277
314
|
summarizerClientElection: false,
|
|
315
|
+
|
|
316
|
+
nonRuntimeOpWeight: 0.1,
|
|
317
|
+
|
|
318
|
+
runtimeOpWeight: 1.0,
|
|
278
319
|
};
|
|
279
320
|
|
|
280
321
|
export interface IGCRuntimeOptions {
|
|
281
322
|
/**
|
|
282
|
-
* Flag that if true, will enable running garbage collection (GC)
|
|
283
|
-
*
|
|
284
|
-
* mark phase.
|
|
323
|
+
* Flag that if true, will enable running garbage collection (GC) for a new container.
|
|
324
|
+
*
|
|
325
|
+
* GC has mark phase and sweep phase. In mark phase, unreferenced objects are identified
|
|
326
|
+
* and marked as such in the summary. This option enables the mark phase.
|
|
285
327
|
* In sweep phase, unreferenced objects are eventually deleted from the container if they meet certain conditions.
|
|
286
328
|
* Sweep phase can be enabled via the "sweepAllowed" option.
|
|
287
|
-
*
|
|
329
|
+
*
|
|
330
|
+
* Note: This setting is persisted in the container's summary and cannot be changed.
|
|
288
331
|
*/
|
|
289
332
|
gcAllowed?: boolean;
|
|
290
333
|
|
|
291
334
|
/**
|
|
292
|
-
* Flag that if true, enables GC's sweep phase
|
|
335
|
+
* Flag that if true, enables GC's sweep phase for a new container.
|
|
336
|
+
*
|
|
337
|
+
* This will allow GC to eventually delete unreferenced objects from the container.
|
|
293
338
|
* This flag should only be set to true if "gcAllowed" is true.
|
|
294
|
-
*
|
|
339
|
+
*
|
|
340
|
+
* Note: This setting is persisted in the container's summary and cannot be changed.
|
|
295
341
|
*/
|
|
296
342
|
sweepAllowed?: boolean;
|
|
297
343
|
|
|
298
344
|
/**
|
|
299
|
-
* Flag that will disable garbage collection
|
|
300
|
-
* is allowed via the gcAllowed option.
|
|
345
|
+
* Flag that if true, will disable garbage collection for the session.
|
|
346
|
+
* Can be used to disable running GC on containers where it is allowed via the gcAllowed option.
|
|
301
347
|
*/
|
|
302
348
|
disableGC?: boolean;
|
|
303
349
|
|
|
@@ -307,6 +353,13 @@ export interface IGCRuntimeOptions {
|
|
|
307
353
|
*/
|
|
308
354
|
runFullGC?: boolean;
|
|
309
355
|
|
|
356
|
+
/**
|
|
357
|
+
* Maximum session duration for a new container. If not present, a default value will be used.
|
|
358
|
+
*
|
|
359
|
+
* Note: This setting is persisted in the container's summary and cannot be changed.
|
|
360
|
+
*/
|
|
361
|
+
sessionExpiryTimeoutMs?: number;
|
|
362
|
+
|
|
310
363
|
/**
|
|
311
364
|
* Allows additional GC options to be passed.
|
|
312
365
|
*/
|
|
@@ -318,9 +371,12 @@ export interface ISummaryRuntimeOptions {
|
|
|
318
371
|
/** Override summary configurations set by the server. */
|
|
319
372
|
summaryConfigOverrides?: ISummaryConfiguration;
|
|
320
373
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
374
|
+
/**
|
|
375
|
+
* @deprecated - this option will not be supported on the next versions.
|
|
376
|
+
* Flag that disables putting channels in isolated subtrees for each data store
|
|
377
|
+
* and the root node when generating a summary if set to true.
|
|
378
|
+
* Defaults to FALSE (enabled) for now.
|
|
379
|
+
*/
|
|
324
380
|
disableIsolatedChannels?: boolean;
|
|
325
381
|
|
|
326
382
|
/**
|
|
@@ -466,11 +522,6 @@ const maxOpSizeInBytesKey = "Fluid.ContainerRuntime.MaxOpSizeInBytes";
|
|
|
466
522
|
// to not reach the 1MB limits in socket.io and Kafka.
|
|
467
523
|
const defaultMaxOpSizeInBytes = 768000;
|
|
468
524
|
|
|
469
|
-
// By default, the size of the contents for the incoming ops is tracked.
|
|
470
|
-
// However, in certain situations, this may incur a performance hit.
|
|
471
|
-
// The feature-gate below can be used to disable this feature.
|
|
472
|
-
const disableOpTrackingKey = "Fluid.ContainerRuntime.DisableOpTracking";
|
|
473
|
-
|
|
474
525
|
const defaultFlushMode = FlushMode.TurnBased;
|
|
475
526
|
|
|
476
527
|
export enum RuntimeMessage {
|
|
@@ -1036,6 +1087,12 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1036
1087
|
|
|
1037
1088
|
private consecutiveReconnects = 0;
|
|
1038
1089
|
|
|
1090
|
+
/**
|
|
1091
|
+
* Used to delay transition to "connected" state while we upload
|
|
1092
|
+
* attachment blobs that were added while disconnected
|
|
1093
|
+
*/
|
|
1094
|
+
private delayConnectClientId?: string;
|
|
1095
|
+
|
|
1039
1096
|
public get connected(): boolean {
|
|
1040
1097
|
return this._connected;
|
|
1041
1098
|
}
|
|
@@ -1160,7 +1217,6 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1160
1217
|
* a summary is generated.
|
|
1161
1218
|
*/
|
|
1162
1219
|
private nextSummaryNumber: number;
|
|
1163
|
-
private readonly opTracker: OpTracker;
|
|
1164
1220
|
|
|
1165
1221
|
private constructor(
|
|
1166
1222
|
private readonly context: IContainerContext,
|
|
@@ -1197,6 +1253,10 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1197
1253
|
this.mc = loggerToMonitoringContext(
|
|
1198
1254
|
ChildLogger.create(this.logger, "ContainerRuntime"));
|
|
1199
1255
|
|
|
1256
|
+
if (this.summaryConfiguration.state === "enabled") {
|
|
1257
|
+
this.validateSummaryHeuristicConfiguration(this.summaryConfiguration);
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1200
1260
|
this.summariesDisabled = this.isSummariesDisabled();
|
|
1201
1261
|
this.heuristicsDisabled = this.isHeuristicsDisabled();
|
|
1202
1262
|
this.summarizerClientElectionEnabled = this.isSummarizerClientElectionEnabled();
|
|
@@ -1288,10 +1348,10 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1288
1348
|
this.handleContext,
|
|
1289
1349
|
blobManagerSnapshot,
|
|
1290
1350
|
() => this.storage,
|
|
1291
|
-
(blobId
|
|
1351
|
+
(blobId, localId) => this.submit(
|
|
1352
|
+
ContainerMessageType.BlobAttach, undefined, undefined, { blobId, localId }),
|
|
1292
1353
|
(blobPath: string) => this.garbageCollector.nodeUpdated(blobPath, "Loaded"),
|
|
1293
1354
|
this,
|
|
1294
|
-
this.logger,
|
|
1295
1355
|
);
|
|
1296
1356
|
|
|
1297
1357
|
this.scheduleManager = new ScheduleManager(
|
|
@@ -1442,9 +1502,9 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1442
1502
|
createContainerRuntimeVersion: metadata?.createContainerRuntimeVersion,
|
|
1443
1503
|
createContainerTimestamp: metadata?.createContainerTimestamp,
|
|
1444
1504
|
};
|
|
1445
|
-
//
|
|
1446
|
-
//
|
|
1447
|
-
loadSummaryNumber = metadata?.summaryNumber ??
|
|
1505
|
+
// summaryNumber was renamed from summaryCount. For older docs that haven't been opened for a long time,
|
|
1506
|
+
// the count is reset to 0.
|
|
1507
|
+
loadSummaryNumber = metadata?.summaryNumber ?? 0;
|
|
1448
1508
|
} else {
|
|
1449
1509
|
this.createContainerMetadata = {
|
|
1450
1510
|
createContainerRuntimeVersion: pkgVersion,
|
|
@@ -1466,7 +1526,6 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1466
1526
|
|
|
1467
1527
|
ReportOpPerfTelemetry(this.context.clientId, this.deltaManager, this.logger);
|
|
1468
1528
|
BindBatchTracker(this, this.logger);
|
|
1469
|
-
this.opTracker = new OpTracker(this.deltaManager, this.mc.config.getBoolean(disableOpTrackingKey) === true);
|
|
1470
1529
|
}
|
|
1471
1530
|
|
|
1472
1531
|
public dispose(error?: Error): void {
|
|
@@ -1546,12 +1605,12 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1546
1605
|
}
|
|
1547
1606
|
|
|
1548
1607
|
if (id === BlobManager.basePath && requestParser.isLeaf(2)) {
|
|
1549
|
-
const
|
|
1550
|
-
if (
|
|
1608
|
+
const blob = await this.blobManager.getBlob(requestParser.pathParts[1]);
|
|
1609
|
+
if (blob) {
|
|
1551
1610
|
return {
|
|
1552
1611
|
status: 200,
|
|
1553
1612
|
mimeType: "fluid/object",
|
|
1554
|
-
value:
|
|
1613
|
+
value: blob,
|
|
1555
1614
|
};
|
|
1556
1615
|
} else {
|
|
1557
1616
|
return create404Response(request);
|
|
@@ -1573,7 +1632,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1573
1632
|
}
|
|
1574
1633
|
|
|
1575
1634
|
private internalId(maybeAlias: string): string {
|
|
1576
|
-
return this.dataStores.aliases
|
|
1635
|
+
return this.dataStores.aliases.get(maybeAlias) ?? maybeAlias;
|
|
1577
1636
|
}
|
|
1578
1637
|
|
|
1579
1638
|
private async getDataStoreFromRequest(id: string, request: IRequest): Promise<IFluidRouter> {
|
|
@@ -1581,6 +1640,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1581
1640
|
? request.headers?.[RuntimeHeaders.wait]
|
|
1582
1641
|
: true;
|
|
1583
1642
|
|
|
1643
|
+
await this.dataStores.waitIfPendingAlias(id);
|
|
1584
1644
|
const internalId = this.internalId(id);
|
|
1585
1645
|
const dataStoreContext = await this.dataStores.getDataStore(internalId, wait);
|
|
1586
1646
|
|
|
@@ -1620,8 +1680,6 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1620
1680
|
private addMetadataToSummary(summaryTree: ISummaryTreeWithStats) {
|
|
1621
1681
|
const metadata: IContainerRuntimeMetadata = {
|
|
1622
1682
|
...this.createContainerMetadata,
|
|
1623
|
-
// back-compat 0.59.3000: This is renamed to summaryNumber. Can be removed when 0.59.3000 saturates.
|
|
1624
|
-
summaryCount: this.nextSummaryNumber,
|
|
1625
1683
|
// Increment the summary number for the next summary that will be generated.
|
|
1626
1684
|
summaryNumber: this.nextSummaryNumber++,
|
|
1627
1685
|
summaryFormatVersion: 1,
|
|
@@ -1647,7 +1705,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1647
1705
|
addBlobToSummary(summaryTree, chunksBlobName, content);
|
|
1648
1706
|
}
|
|
1649
1707
|
|
|
1650
|
-
const dataStoreAliases = this.dataStores.aliases
|
|
1708
|
+
const dataStoreAliases = this.dataStores.aliases;
|
|
1651
1709
|
if (dataStoreAliases.size > 0) {
|
|
1652
1710
|
addBlobToSummary(summaryTree, aliasBlobName, JSON.stringify([...dataStoreAliases]));
|
|
1653
1711
|
}
|
|
@@ -1757,6 +1815,38 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1757
1815
|
}
|
|
1758
1816
|
|
|
1759
1817
|
public setConnectionState(connected: boolean, clientId?: string) {
|
|
1818
|
+
if (connected === false && this.delayConnectClientId !== undefined) {
|
|
1819
|
+
this.delayConnectClientId = undefined;
|
|
1820
|
+
this.mc.logger.sendTelemetryEvent({
|
|
1821
|
+
eventName: "UnsuccessfulConnectedTransition",
|
|
1822
|
+
});
|
|
1823
|
+
// Don't propagate "disconnected" event because we didn't propagate the previous "connected" event
|
|
1824
|
+
return;
|
|
1825
|
+
}
|
|
1826
|
+
|
|
1827
|
+
// If attachment blobs were added while disconnected, we need to delay
|
|
1828
|
+
// propagation of the "connected" event until we have uploaded them to
|
|
1829
|
+
// ensure we don't submit ops referencing a blob that has not been uploaded
|
|
1830
|
+
const connecting = connected && !this._connected && !this.deltaManager.readOnlyInfo.readonly;
|
|
1831
|
+
if (connecting && this.blobManager.hasPendingOfflineUploads) {
|
|
1832
|
+
assert(!this.delayConnectClientId, "Connect event delay must be canceled before subsequent connect event");
|
|
1833
|
+
assert(!!clientId, "Must have clientId when connecting");
|
|
1834
|
+
this.delayConnectClientId = clientId;
|
|
1835
|
+
this.blobManager.onConnected().then(() => {
|
|
1836
|
+
// make sure we didn't reconnect before the promise resolved
|
|
1837
|
+
if (this.delayConnectClientId === clientId && !this.disposed) {
|
|
1838
|
+
this.delayConnectClientId = undefined;
|
|
1839
|
+
this.setConnectionStateCore(connected, clientId);
|
|
1840
|
+
}
|
|
1841
|
+
}, (error) => this.closeFn(error));
|
|
1842
|
+
return;
|
|
1843
|
+
}
|
|
1844
|
+
|
|
1845
|
+
this.setConnectionStateCore(connected, clientId);
|
|
1846
|
+
}
|
|
1847
|
+
|
|
1848
|
+
private setConnectionStateCore(connected: boolean, clientId?: string) {
|
|
1849
|
+
assert(!this.delayConnectClientId, "connect event delay must be cleared before propagating connect event");
|
|
1760
1850
|
this.verifyNotClosed();
|
|
1761
1851
|
|
|
1762
1852
|
// There might be no change of state due to Container calling this API after loading runtime.
|
|
@@ -1854,8 +1944,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1854
1944
|
this.dataStores.processFluidDataStoreOp(message, local, localOpMetadata);
|
|
1855
1945
|
break;
|
|
1856
1946
|
case ContainerMessageType.BlobAttach:
|
|
1857
|
-
|
|
1858
|
-
this.blobManager.processBlobAttachOp(message.metadata.blobId, local);
|
|
1947
|
+
this.blobManager.processBlobAttachOp(message, local);
|
|
1859
1948
|
break;
|
|
1860
1949
|
default:
|
|
1861
1950
|
}
|
|
@@ -1937,6 +2026,11 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1937
2026
|
}
|
|
1938
2027
|
|
|
1939
2028
|
public async getRootDataStore(id: string, wait = true): Promise<IFluidRouter> {
|
|
2029
|
+
return this.getRootDataStoreChannel(id, wait);
|
|
2030
|
+
}
|
|
2031
|
+
|
|
2032
|
+
private async getRootDataStoreChannel(id: string, wait = true): Promise<IFluidDataStoreChannel> {
|
|
2033
|
+
await this.dataStores.waitIfPendingAlias(id);
|
|
1940
2034
|
const internalId = this.internalId(id);
|
|
1941
2035
|
const context = await this.dataStores.getDataStore(internalId, wait);
|
|
1942
2036
|
assert(await context.isRoot(), 0x12b /* "did not get root data store" */);
|
|
@@ -2058,13 +2152,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2058
2152
|
*/
|
|
2059
2153
|
private async createRootDataStoreLegacy(pkg: string | string[], rootDataStoreId: string): Promise<IFluidRouter> {
|
|
2060
2154
|
const fluidDataStore = await this._createDataStore(pkg, true /* isRoot */, rootDataStoreId);
|
|
2061
|
-
|
|
2062
|
-
// older versions, we still have to call bindToContext.
|
|
2063
|
-
if (fluidDataStore.makeVisibleAndAttachGraph !== undefined) {
|
|
2064
|
-
fluidDataStore.makeVisibleAndAttachGraph();
|
|
2065
|
-
} else {
|
|
2066
|
-
fluidDataStore.bindToContext();
|
|
2067
|
-
}
|
|
2155
|
+
fluidDataStore.makeVisibleAndAttachGraph();
|
|
2068
2156
|
return fluidDataStore;
|
|
2069
2157
|
}
|
|
2070
2158
|
|
|
@@ -2093,27 +2181,37 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2093
2181
|
*/
|
|
2094
2182
|
private async createAndAliasDataStore(pkg: string | string[], alias: string, props?: any): Promise<IDataStore> {
|
|
2095
2183
|
const internalId = uuid();
|
|
2096
|
-
const dataStore = await this._createDataStore(pkg, false /* isRoot */, internalId, props);
|
|
2097
|
-
const aliasedDataStore = channelToDataStore(dataStore, internalId, this, this.dataStores, this.mc.logger);
|
|
2098
|
-
const result = await aliasedDataStore.trySetAlias(alias);
|
|
2099
|
-
if (result !== "Success") {
|
|
2100
|
-
throw new GenericError(
|
|
2101
|
-
"dataStoreAliasFailure",
|
|
2102
|
-
undefined /* error */,
|
|
2103
|
-
{
|
|
2104
|
-
alias: {
|
|
2105
|
-
value: alias,
|
|
2106
|
-
tag: TelemetryDataTag.UserData,
|
|
2107
|
-
},
|
|
2108
|
-
internalId: {
|
|
2109
|
-
value: internalId,
|
|
2110
|
-
tag: TelemetryDataTag.PackageData,
|
|
2111
|
-
},
|
|
2112
|
-
aliasResult: result,
|
|
2113
|
-
});
|
|
2114
|
-
}
|
|
2115
2184
|
|
|
2116
|
-
|
|
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
|
+
}
|
|
2117
2215
|
}
|
|
2118
2216
|
|
|
2119
2217
|
public createDetachedRootDataStore(
|
|
@@ -2144,13 +2242,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2144
2242
|
const fluidDataStore = await this.dataStores._createFluidDataStoreContext(
|
|
2145
2243
|
Array.isArray(pkg) ? pkg : [pkg], id, isRoot, props).realize();
|
|
2146
2244
|
if (isRoot) {
|
|
2147
|
-
|
|
2148
|
-
// For older versions, we still have to call bindToContext.
|
|
2149
|
-
if (fluidDataStore.makeVisibleAndAttachGraph !== undefined) {
|
|
2150
|
-
fluidDataStore.makeVisibleAndAttachGraph();
|
|
2151
|
-
} else {
|
|
2152
|
-
fluidDataStore.bindToContext();
|
|
2153
|
-
}
|
|
2245
|
+
fluidDataStore.makeVisibleAndAttachGraph();
|
|
2154
2246
|
this.logger.sendTelemetryEvent({
|
|
2155
2247
|
eventName: "Root datastore with props",
|
|
2156
2248
|
hasProps: props !== undefined,
|
|
@@ -2560,15 +2652,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2560
2652
|
const summaryRefSeqNum = this.deltaManager.lastSequenceNumber;
|
|
2561
2653
|
const minimumSequenceNumber = this.deltaManager.minimumSequenceNumber;
|
|
2562
2654
|
const message = `Summary @${summaryRefSeqNum}:${this.deltaManager.minimumSequenceNumber}`;
|
|
2563
|
-
|
|
2564
|
-
// We should be here is we haven't processed be here. If we are of if the last message's sequence number
|
|
2565
|
-
// doesn't match the last processed sequence number, log an error.
|
|
2566
|
-
if (summaryRefSeqNum !== this.deltaManager.lastMessage?.sequenceNumber) {
|
|
2567
|
-
summaryNumberLogger.sendErrorEvent({
|
|
2568
|
-
eventName: "LastSequenceMismatch",
|
|
2569
|
-
error: message,
|
|
2570
|
-
});
|
|
2571
|
-
}
|
|
2655
|
+
const lastAck = this.summaryCollection.latestAck;
|
|
2572
2656
|
|
|
2573
2657
|
this.summarizerNode.startSummary(summaryRefSeqNum, summaryNumberLogger);
|
|
2574
2658
|
|
|
@@ -2598,6 +2682,16 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2598
2682
|
error: `lastSequenceNumber changed before uploading to storage. ${this.deltaManager.lastSequenceNumber} !== ${summaryRefSeqNum}`,
|
|
2599
2683
|
};
|
|
2600
2684
|
}
|
|
2685
|
+
assert(summaryRefSeqNum === this.deltaManager.lastMessage?.sequenceNumber,
|
|
2686
|
+
"it's one and the same thing");
|
|
2687
|
+
|
|
2688
|
+
if (lastAck !== this.summaryCollection.latestAck) {
|
|
2689
|
+
return {
|
|
2690
|
+
continue: false,
|
|
2691
|
+
// eslint-disable-next-line max-len
|
|
2692
|
+
error: `Last summary changed while summarizing. ${this.summaryCollection.latestAck} !== ${lastAck}`,
|
|
2693
|
+
};
|
|
2694
|
+
}
|
|
2601
2695
|
return { continue: true };
|
|
2602
2696
|
};
|
|
2603
2697
|
|
|
@@ -2654,8 +2748,6 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2654
2748
|
gcStateUpdatedDataStoreCount: summarizeResult.gcStats?.updatedDataStoreCount,
|
|
2655
2749
|
gcBlobNodeCount: gcSummaryTreeStats?.blobNodeCount,
|
|
2656
2750
|
gcTotalBlobsSize: gcSummaryTreeStats?.totalBlobSize,
|
|
2657
|
-
opsSizesSinceLastSummary: this.opTracker.opsSizeAccumulator,
|
|
2658
|
-
nonSystemOpsSinceLastSummary: this.opTracker.nonSystemOpCount,
|
|
2659
2751
|
summaryNumber,
|
|
2660
2752
|
...partialStats,
|
|
2661
2753
|
};
|
|
@@ -2673,7 +2765,6 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2673
2765
|
return { stage: "generate", ...generateSummaryData, error: continueResult.error };
|
|
2674
2766
|
}
|
|
2675
2767
|
|
|
2676
|
-
const lastAck = this.summaryCollection.latestAck;
|
|
2677
2768
|
const summaryContext: ISummaryContext =
|
|
2678
2769
|
lastAck === undefined
|
|
2679
2770
|
? {
|
|
@@ -2728,7 +2819,6 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2728
2819
|
} as const;
|
|
2729
2820
|
|
|
2730
2821
|
this.summarizerNode.completeSummary(handle);
|
|
2731
|
-
this.opTracker.reset();
|
|
2732
2822
|
return submitData;
|
|
2733
2823
|
} finally {
|
|
2734
2824
|
// Cleanup wip summary in case of failure
|
|
@@ -2892,14 +2982,8 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2892
2982
|
"OpTooLarge",
|
|
2893
2983
|
/* error */ undefined,
|
|
2894
2984
|
{
|
|
2895
|
-
length:
|
|
2896
|
-
|
|
2897
|
-
tag: TelemetryDataTag.PackageData,
|
|
2898
|
-
},
|
|
2899
|
-
limit: {
|
|
2900
|
-
value: this._maxOpSizeInBytes,
|
|
2901
|
-
tag: TelemetryDataTag.PackageData,
|
|
2902
|
-
},
|
|
2985
|
+
length: serializedContent.length,
|
|
2986
|
+
limit: this._maxOpSizeInBytes,
|
|
2903
2987
|
}));
|
|
2904
2988
|
return -1;
|
|
2905
2989
|
}
|
|
@@ -3005,7 +3089,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
3005
3089
|
case ContainerMessageType.ChunkedOp:
|
|
3006
3090
|
throw new Error(`chunkedOp not expected here`);
|
|
3007
3091
|
case ContainerMessageType.BlobAttach:
|
|
3008
|
-
this.
|
|
3092
|
+
this.blobManager.reSubmit(opMetadata);
|
|
3009
3093
|
break;
|
|
3010
3094
|
case ContainerMessageType.Rejoin:
|
|
3011
3095
|
this.submit(type, content);
|
|
@@ -3064,9 +3148,11 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
3064
3148
|
*/
|
|
3065
3149
|
private async refreshLatestSummaryAckFromServer(summaryLogger: ITelemetryLogger): Promise<number> {
|
|
3066
3150
|
const snapshot = await this.fetchSnapshotFromStorage(null, summaryLogger, {
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
|
|
3151
|
+
eventName: "RefreshLatestSummaryGetSnapshot",
|
|
3152
|
+
fetchLatest: true,
|
|
3153
|
+
},
|
|
3154
|
+
FetchSource.noCache,
|
|
3155
|
+
);
|
|
3070
3156
|
|
|
3071
3157
|
const readAndParseBlob = async <T>(id: string) => readAndParse<T>(this.storage, id);
|
|
3072
3158
|
const snapshotRefSeq = await seqFromTree(snapshot, readAndParseBlob);
|
|
@@ -3086,7 +3172,11 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
3086
3172
|
}
|
|
3087
3173
|
|
|
3088
3174
|
private async fetchSnapshotFromStorage(
|
|
3089
|
-
versionId: string | null,
|
|
3175
|
+
versionId: string | null,
|
|
3176
|
+
logger: ITelemetryLogger,
|
|
3177
|
+
event: ITelemetryGenericEvent,
|
|
3178
|
+
fetchSource?: FetchSource,
|
|
3179
|
+
) {
|
|
3090
3180
|
return PerformanceEvent.timedExecAsync(
|
|
3091
3181
|
logger, event, async (perfEvent: {
|
|
3092
3182
|
end: (arg0: {
|
|
@@ -3097,7 +3187,8 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
3097
3187
|
const stats: { getVersionDuration?: number; getSnapshotDuration?: number; } = {};
|
|
3098
3188
|
const trace = Trace.start();
|
|
3099
3189
|
|
|
3100
|
-
const versions = await this.storage.getVersions(
|
|
3190
|
+
const versions = await this.storage.getVersions(
|
|
3191
|
+
versionId, 1, "refreshLatestSummaryAckFromServer", fetchSource);
|
|
3101
3192
|
assert(!!versions && !!versions[0], 0x137 /* "Failed to get version from storage" */);
|
|
3102
3193
|
stats.getVersionDuration = trace.trace().duration;
|
|
3103
3194
|
|
|
@@ -3218,6 +3309,15 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
3218
3309
|
// don't have any more saved ops
|
|
3219
3310
|
await this.pendingStateManager.applyStashedOpsAt();
|
|
3220
3311
|
}
|
|
3312
|
+
|
|
3313
|
+
private validateSummaryHeuristicConfiguration(configuration: ISummaryConfigurationHeuristics) {
|
|
3314
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
3315
|
+
for (const prop in configuration) {
|
|
3316
|
+
if (typeof configuration[prop] === "number" && configuration[prop] < 0) {
|
|
3317
|
+
throw new UsageError(`Summary heuristic configuration property "${prop}" cannot be less than 0`);
|
|
3318
|
+
}
|
|
3319
|
+
}
|
|
3320
|
+
}
|
|
3221
3321
|
}
|
|
3222
3322
|
|
|
3223
3323
|
/**
|