@fluidframework/container-runtime 1.2.3-83900 → 2.0.0-internal.1.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/batchTracker.js +1 -1
- package/dist/batchTracker.js.map +1 -1
- 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 +66 -49
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +129 -164
- package/dist/containerRuntime.js.map +1 -1
- package/dist/dataStore.js +29 -24
- 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 +6 -3
- package/dist/dataStores.d.ts.map +1 -1
- package/dist/dataStores.js +13 -5
- 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 +28 -4
- package/dist/runningSummarizer.d.ts.map +1 -1
- package/dist/runningSummarizer.js +93 -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 +34 -15
- 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/batchTracker.js +1 -1
- package/lib/batchTracker.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 +66 -49
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +131 -166
- package/lib/containerRuntime.js.map +1 -1
- package/lib/dataStore.js +29 -24
- 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 +6 -3
- package/lib/dataStores.d.ts.map +1 -1
- package/lib/dataStores.js +13 -5
- 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 +28 -4
- package/lib/runningSummarizer.d.ts.map +1 -1
- package/lib/runningSummarizer.js +93 -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 +36 -17
- 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 +55 -21
- package/src/batchTracker.ts +1 -1
- package/src/blobManager.ts +364 -119
- package/src/containerRuntime.ts +232 -216
- package/src/dataStore.ts +49 -37
- package/src/dataStoreContext.ts +16 -23
- package/src/dataStores.ts +27 -16
- package/src/garbageCollection.ts +13 -7
- package/src/opProperties.ts +19 -0
- package/src/packageVersion.ts +1 -1
- package/src/runningSummarizer.ts +108 -23
- package/src/summarizer.ts +47 -28
- 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.
|
|
264
297
|
|
|
265
|
-
|
|
298
|
+
minIdleTime: 0,
|
|
266
299
|
|
|
267
|
-
|
|
300
|
+
maxIdleTime: 30 * 1000, // 30 secs.
|
|
301
|
+
|
|
302
|
+
maxTime: 60 * 1000, // 1 min.
|
|
303
|
+
|
|
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
|
/**
|
|
@@ -369,12 +425,6 @@ export interface IContainerRuntimeOptions {
|
|
|
369
425
|
* 3. "bypass" will skip the check entirely. This is not recommended.
|
|
370
426
|
*/
|
|
371
427
|
readonly loadSequenceNumberVerification?: "close" | "log" | "bypass";
|
|
372
|
-
/**
|
|
373
|
-
* Should the runtime use data store aliasing for creating root datastores.
|
|
374
|
-
* In case of aliasing conflicts, the runtime will raise an exception which does
|
|
375
|
-
* not effect the status of the container.
|
|
376
|
-
*/
|
|
377
|
-
readonly useDataStoreAliasing?: boolean;
|
|
378
428
|
/**
|
|
379
429
|
* Sets the flush mode for the runtime. In Immediate flush mode the runtime will immediately
|
|
380
430
|
* send all operations to the driver layer, while in TurnBased the operations will be buffered
|
|
@@ -453,7 +503,6 @@ export interface IPendingRuntimeState {
|
|
|
453
503
|
savedOps: ISequencedDocumentMessage[];
|
|
454
504
|
}
|
|
455
505
|
|
|
456
|
-
const useDataStoreAliasingKey = "Fluid.ContainerRuntime.UseDataStoreAliasing";
|
|
457
506
|
const maxConsecutiveReconnectsKey = "Fluid.ContainerRuntime.MaxConsecutiveReconnects";
|
|
458
507
|
|
|
459
508
|
// Feature gate for the max op size. If the value is negative, chunking is enabled
|
|
@@ -466,11 +515,6 @@ const maxOpSizeInBytesKey = "Fluid.ContainerRuntime.MaxOpSizeInBytes";
|
|
|
466
515
|
// to not reach the 1MB limits in socket.io and Kafka.
|
|
467
516
|
const defaultMaxOpSizeInBytes = 768000;
|
|
468
517
|
|
|
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
518
|
const defaultFlushMode = FlushMode.TurnBased;
|
|
475
519
|
|
|
476
520
|
export enum RuntimeMessage {
|
|
@@ -852,7 +896,6 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
852
896
|
summaryOptions = {},
|
|
853
897
|
gcOptions = {},
|
|
854
898
|
loadSequenceNumberVerification = "close",
|
|
855
|
-
useDataStoreAliasing = false,
|
|
856
899
|
flushMode = defaultFlushMode,
|
|
857
900
|
enableOfflineLoad = false,
|
|
858
901
|
} = runtimeOptions;
|
|
@@ -928,7 +971,6 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
928
971
|
summaryOptions,
|
|
929
972
|
gcOptions,
|
|
930
973
|
loadSequenceNumberVerification,
|
|
931
|
-
useDataStoreAliasing,
|
|
932
974
|
flushMode,
|
|
933
975
|
enableOfflineLoad,
|
|
934
976
|
},
|
|
@@ -1018,7 +1060,6 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1018
1060
|
private readonly summaryCollection: SummaryCollection;
|
|
1019
1061
|
|
|
1020
1062
|
private readonly summarizerNode: IRootSummarizerNodeWithGC;
|
|
1021
|
-
private readonly _aliasingEnabled: boolean;
|
|
1022
1063
|
private readonly _maxOpSizeInBytes: number;
|
|
1023
1064
|
|
|
1024
1065
|
private readonly maxConsecutiveReconnects: number;
|
|
@@ -1036,6 +1077,12 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1036
1077
|
|
|
1037
1078
|
private consecutiveReconnects = 0;
|
|
1038
1079
|
|
|
1080
|
+
/**
|
|
1081
|
+
* Used to delay transition to "connected" state while we upload
|
|
1082
|
+
* attachment blobs that were added while disconnected
|
|
1083
|
+
*/
|
|
1084
|
+
private delayConnectClientId?: string;
|
|
1085
|
+
|
|
1039
1086
|
public get connected(): boolean {
|
|
1040
1087
|
return this._connected;
|
|
1041
1088
|
}
|
|
@@ -1160,7 +1207,6 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1160
1207
|
* a summary is generated.
|
|
1161
1208
|
*/
|
|
1162
1209
|
private nextSummaryNumber: number;
|
|
1163
|
-
private readonly opTracker: OpTracker;
|
|
1164
1210
|
|
|
1165
1211
|
private constructor(
|
|
1166
1212
|
private readonly context: IContainerContext,
|
|
@@ -1197,16 +1243,16 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1197
1243
|
this.mc = loggerToMonitoringContext(
|
|
1198
1244
|
ChildLogger.create(this.logger, "ContainerRuntime"));
|
|
1199
1245
|
|
|
1246
|
+
if (this.summaryConfiguration.state === "enabled") {
|
|
1247
|
+
this.validateSummaryHeuristicConfiguration(this.summaryConfiguration);
|
|
1248
|
+
}
|
|
1249
|
+
|
|
1200
1250
|
this.summariesDisabled = this.isSummariesDisabled();
|
|
1201
1251
|
this.heuristicsDisabled = this.isHeuristicsDisabled();
|
|
1202
1252
|
this.summarizerClientElectionEnabled = this.isSummarizerClientElectionEnabled();
|
|
1203
1253
|
this.maxOpsSinceLastSummary = this.getMaxOpsSinceLastSummary();
|
|
1204
1254
|
this.initialSummarizerDelayMs = this.getInitialSummarizerDelayMs();
|
|
1205
1255
|
|
|
1206
|
-
this._aliasingEnabled =
|
|
1207
|
-
(this.mc.config.getBoolean(useDataStoreAliasingKey) ?? false) ||
|
|
1208
|
-
(runtimeOptions.useDataStoreAliasing ?? false);
|
|
1209
|
-
|
|
1210
1256
|
this._maxOpSizeInBytes = (this.mc.config.getNumber(maxOpSizeInBytesKey) ?? defaultMaxOpSizeInBytes);
|
|
1211
1257
|
this.maxConsecutiveReconnects =
|
|
1212
1258
|
this.mc.config.getNumber(maxConsecutiveReconnectsKey) ?? this.defaultMaxConsecutiveReconnects;
|
|
@@ -1288,10 +1334,10 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1288
1334
|
this.handleContext,
|
|
1289
1335
|
blobManagerSnapshot,
|
|
1290
1336
|
() => this.storage,
|
|
1291
|
-
(blobId
|
|
1337
|
+
(blobId, localId) => this.submit(
|
|
1338
|
+
ContainerMessageType.BlobAttach, undefined, undefined, { blobId, localId }),
|
|
1292
1339
|
(blobPath: string) => this.garbageCollector.nodeUpdated(blobPath, "Loaded"),
|
|
1293
1340
|
this,
|
|
1294
|
-
this.logger,
|
|
1295
1341
|
);
|
|
1296
1342
|
|
|
1297
1343
|
this.scheduleManager = new ScheduleManager(
|
|
@@ -1442,9 +1488,9 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1442
1488
|
createContainerRuntimeVersion: metadata?.createContainerRuntimeVersion,
|
|
1443
1489
|
createContainerTimestamp: metadata?.createContainerTimestamp,
|
|
1444
1490
|
};
|
|
1445
|
-
//
|
|
1446
|
-
//
|
|
1447
|
-
loadSummaryNumber = metadata?.summaryNumber ??
|
|
1491
|
+
// summaryNumber was renamed from summaryCount. For older docs that haven't been opened for a long time,
|
|
1492
|
+
// the count is reset to 0.
|
|
1493
|
+
loadSummaryNumber = metadata?.summaryNumber ?? 0;
|
|
1448
1494
|
} else {
|
|
1449
1495
|
this.createContainerMetadata = {
|
|
1450
1496
|
createContainerRuntimeVersion: pkgVersion,
|
|
@@ -1466,7 +1512,6 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1466
1512
|
|
|
1467
1513
|
ReportOpPerfTelemetry(this.context.clientId, this.deltaManager, this.logger);
|
|
1468
1514
|
BindBatchTracker(this, this.logger);
|
|
1469
|
-
this.opTracker = new OpTracker(this.deltaManager, this.mc.config.getBoolean(disableOpTrackingKey) === true);
|
|
1470
1515
|
}
|
|
1471
1516
|
|
|
1472
1517
|
public dispose(error?: Error): void {
|
|
@@ -1546,12 +1591,12 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1546
1591
|
}
|
|
1547
1592
|
|
|
1548
1593
|
if (id === BlobManager.basePath && requestParser.isLeaf(2)) {
|
|
1549
|
-
const
|
|
1550
|
-
if (
|
|
1594
|
+
const blob = await this.blobManager.getBlob(requestParser.pathParts[1]);
|
|
1595
|
+
if (blob) {
|
|
1551
1596
|
return {
|
|
1552
1597
|
status: 200,
|
|
1553
1598
|
mimeType: "fluid/object",
|
|
1554
|
-
value:
|
|
1599
|
+
value: blob,
|
|
1555
1600
|
};
|
|
1556
1601
|
} else {
|
|
1557
1602
|
return create404Response(request);
|
|
@@ -1573,7 +1618,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1573
1618
|
}
|
|
1574
1619
|
|
|
1575
1620
|
private internalId(maybeAlias: string): string {
|
|
1576
|
-
return this.dataStores.aliases
|
|
1621
|
+
return this.dataStores.aliases.get(maybeAlias) ?? maybeAlias;
|
|
1577
1622
|
}
|
|
1578
1623
|
|
|
1579
1624
|
private async getDataStoreFromRequest(id: string, request: IRequest): Promise<IFluidRouter> {
|
|
@@ -1581,6 +1626,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1581
1626
|
? request.headers?.[RuntimeHeaders.wait]
|
|
1582
1627
|
: true;
|
|
1583
1628
|
|
|
1629
|
+
await this.dataStores.waitIfPendingAlias(id);
|
|
1584
1630
|
const internalId = this.internalId(id);
|
|
1585
1631
|
const dataStoreContext = await this.dataStores.getDataStore(internalId, wait);
|
|
1586
1632
|
|
|
@@ -1620,8 +1666,6 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1620
1666
|
private addMetadataToSummary(summaryTree: ISummaryTreeWithStats) {
|
|
1621
1667
|
const metadata: IContainerRuntimeMetadata = {
|
|
1622
1668
|
...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
1669
|
// Increment the summary number for the next summary that will be generated.
|
|
1626
1670
|
summaryNumber: this.nextSummaryNumber++,
|
|
1627
1671
|
summaryFormatVersion: 1,
|
|
@@ -1647,7 +1691,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1647
1691
|
addBlobToSummary(summaryTree, chunksBlobName, content);
|
|
1648
1692
|
}
|
|
1649
1693
|
|
|
1650
|
-
const dataStoreAliases = this.dataStores.aliases
|
|
1694
|
+
const dataStoreAliases = this.dataStores.aliases;
|
|
1651
1695
|
if (dataStoreAliases.size > 0) {
|
|
1652
1696
|
addBlobToSummary(summaryTree, aliasBlobName, JSON.stringify([...dataStoreAliases]));
|
|
1653
1697
|
}
|
|
@@ -1757,6 +1801,40 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1757
1801
|
}
|
|
1758
1802
|
|
|
1759
1803
|
public setConnectionState(connected: boolean, clientId?: string) {
|
|
1804
|
+
if (connected === false && this.delayConnectClientId !== undefined) {
|
|
1805
|
+
this.delayConnectClientId = undefined;
|
|
1806
|
+
this.mc.logger.sendTelemetryEvent({
|
|
1807
|
+
eventName: "UnsuccessfulConnectedTransition",
|
|
1808
|
+
});
|
|
1809
|
+
// Don't propagate "disconnected" event because we didn't propagate the previous "connected" event
|
|
1810
|
+
return;
|
|
1811
|
+
}
|
|
1812
|
+
|
|
1813
|
+
// If attachment blobs were added while disconnected, we need to delay
|
|
1814
|
+
// propagation of the "connected" event until we have uploaded them to
|
|
1815
|
+
// ensure we don't submit ops referencing a blob that has not been uploaded
|
|
1816
|
+
const connecting = connected && !this._connected && !this.deltaManager.readOnlyInfo.readonly;
|
|
1817
|
+
if (connecting && this.blobManager.hasPendingOfflineUploads) {
|
|
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 */);
|
|
1821
|
+
this.delayConnectClientId = clientId;
|
|
1822
|
+
this.blobManager.onConnected().then(() => {
|
|
1823
|
+
// make sure we didn't reconnect before the promise resolved
|
|
1824
|
+
if (this.delayConnectClientId === clientId && !this.disposed) {
|
|
1825
|
+
this.delayConnectClientId = undefined;
|
|
1826
|
+
this.setConnectionStateCore(connected, clientId);
|
|
1827
|
+
}
|
|
1828
|
+
}, (error) => this.closeFn(error));
|
|
1829
|
+
return;
|
|
1830
|
+
}
|
|
1831
|
+
|
|
1832
|
+
this.setConnectionStateCore(connected, clientId);
|
|
1833
|
+
}
|
|
1834
|
+
|
|
1835
|
+
private setConnectionStateCore(connected: boolean, clientId?: string) {
|
|
1836
|
+
assert(!this.delayConnectClientId,
|
|
1837
|
+
0x394 /* connect event delay must be cleared before propagating connect event */);
|
|
1760
1838
|
this.verifyNotClosed();
|
|
1761
1839
|
|
|
1762
1840
|
// There might be no change of state due to Container calling this API after loading runtime.
|
|
@@ -1780,11 +1858,11 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1780
1858
|
"Runtime detected too many reconnects with no progress syncing local ops",
|
|
1781
1859
|
"setConnectionState",
|
|
1782
1860
|
undefined,
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1861
|
+
{
|
|
1862
|
+
dataLoss: 1,
|
|
1863
|
+
attempts: this.consecutiveReconnects,
|
|
1864
|
+
pendingMessages: this.pendingStateManager.pendingMessagesCount,
|
|
1865
|
+
}));
|
|
1788
1866
|
return;
|
|
1789
1867
|
}
|
|
1790
1868
|
}
|
|
@@ -1854,8 +1932,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1854
1932
|
this.dataStores.processFluidDataStoreOp(message, local, localOpMetadata);
|
|
1855
1933
|
break;
|
|
1856
1934
|
case ContainerMessageType.BlobAttach:
|
|
1857
|
-
|
|
1858
|
-
this.blobManager.processBlobAttachOp(message.metadata.blobId, local);
|
|
1935
|
+
this.blobManager.processBlobAttachOp(message, local);
|
|
1859
1936
|
break;
|
|
1860
1937
|
default:
|
|
1861
1938
|
}
|
|
@@ -1937,6 +2014,11 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1937
2014
|
}
|
|
1938
2015
|
|
|
1939
2016
|
public async getRootDataStore(id: string, wait = true): Promise<IFluidRouter> {
|
|
2017
|
+
return this.getRootDataStoreChannel(id, wait);
|
|
2018
|
+
}
|
|
2019
|
+
|
|
2020
|
+
private async getRootDataStoreChannel(id: string, wait = true): Promise<IFluidDataStoreChannel> {
|
|
2021
|
+
await this.dataStores.waitIfPendingAlias(id);
|
|
1940
2022
|
const internalId = this.internalId(id);
|
|
1941
2023
|
const context = await this.dataStores.getDataStore(internalId, wait);
|
|
1942
2024
|
assert(await context.isRoot(), 0x12b /* "did not get root data store" */);
|
|
@@ -2043,79 +2125,13 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2043
2125
|
public async createDataStore(pkg: string | string[]): Promise<IDataStore> {
|
|
2044
2126
|
const internalId = uuid();
|
|
2045
2127
|
return channelToDataStore(
|
|
2046
|
-
await this._createDataStore(pkg,
|
|
2128
|
+
await this._createDataStore(pkg, internalId),
|
|
2047
2129
|
internalId,
|
|
2048
2130
|
this,
|
|
2049
2131
|
this.dataStores,
|
|
2050
2132
|
this.mc.logger);
|
|
2051
2133
|
}
|
|
2052
2134
|
|
|
2053
|
-
/**
|
|
2054
|
-
* Creates a root datastore directly with a user generated id and attaches it to storage.
|
|
2055
|
-
* It is vulnerable to name collisions and should not be used.
|
|
2056
|
-
*
|
|
2057
|
-
* This method will be removed. See #6465.
|
|
2058
|
-
*/
|
|
2059
|
-
private async createRootDataStoreLegacy(pkg: string | string[], rootDataStoreId: string): Promise<IFluidRouter> {
|
|
2060
|
-
const fluidDataStore = await this._createDataStore(pkg, true /* isRoot */, rootDataStoreId);
|
|
2061
|
-
// back-compat 0.59.1000 - makeVisibleAndAttachGraph was added in this version to IFluidDataStoreChannel. For
|
|
2062
|
-
// older versions, we still have to call bindToContext.
|
|
2063
|
-
if (fluidDataStore.makeVisibleAndAttachGraph !== undefined) {
|
|
2064
|
-
fluidDataStore.makeVisibleAndAttachGraph();
|
|
2065
|
-
} else {
|
|
2066
|
-
fluidDataStore.bindToContext();
|
|
2067
|
-
}
|
|
2068
|
-
return fluidDataStore;
|
|
2069
|
-
}
|
|
2070
|
-
|
|
2071
|
-
/**
|
|
2072
|
-
* @deprecated - will be removed in an upcoming release. See #9660.
|
|
2073
|
-
*/
|
|
2074
|
-
public async createRootDataStore(pkg: string | string[], rootDataStoreId: string): Promise<IFluidRouter> {
|
|
2075
|
-
if (rootDataStoreId.includes("/")) {
|
|
2076
|
-
throw new UsageError(`Id cannot contain slashes: '${rootDataStoreId}'`);
|
|
2077
|
-
}
|
|
2078
|
-
return this._aliasingEnabled === true ?
|
|
2079
|
-
this.createAndAliasDataStore(pkg, rootDataStoreId) :
|
|
2080
|
-
this.createRootDataStoreLegacy(pkg, rootDataStoreId);
|
|
2081
|
-
}
|
|
2082
|
-
|
|
2083
|
-
/**
|
|
2084
|
-
* Creates a data store then attempts to alias it.
|
|
2085
|
-
* If aliasing fails, it will raise an exception.
|
|
2086
|
-
*
|
|
2087
|
-
* This method will be removed. See #6465.
|
|
2088
|
-
*
|
|
2089
|
-
* @param pkg - Package name of the data store
|
|
2090
|
-
* @param alias - Alias to be assigned to the data store
|
|
2091
|
-
* @param props - Properties for the data store
|
|
2092
|
-
* @returns - An aliased data store which can can be found / loaded by alias.
|
|
2093
|
-
*/
|
|
2094
|
-
private async createAndAliasDataStore(pkg: string | string[], alias: string, props?: any): Promise<IDataStore> {
|
|
2095
|
-
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
|
-
|
|
2116
|
-
return aliasedDataStore;
|
|
2117
|
-
}
|
|
2118
|
-
|
|
2119
2135
|
public createDetachedRootDataStore(
|
|
2120
2136
|
pkg: Readonly<string[]>,
|
|
2121
2137
|
rootDataStoreId: string): IFluidDataStoreContextDetached {
|
|
@@ -2129,55 +2145,23 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2129
2145
|
return this.dataStores.createDetachedDataStoreCore(pkg, false);
|
|
2130
2146
|
}
|
|
2131
2147
|
|
|
2132
|
-
|
|
2133
|
-
* Creates a possibly root datastore directly with a possibly user generated id and attaches it to storage.
|
|
2134
|
-
* It is vulnerable to name collisions if both aforementioned conditions are true, and should not be used.
|
|
2135
|
-
*
|
|
2136
|
-
* This method will be removed. See #6465.
|
|
2137
|
-
*/
|
|
2138
|
-
private async _createDataStoreWithPropsLegacy(
|
|
2148
|
+
public async _createDataStoreWithProps(
|
|
2139
2149
|
pkg: string | string[],
|
|
2140
2150
|
props?: any,
|
|
2141
2151
|
id = uuid(),
|
|
2142
|
-
isRoot = false,
|
|
2143
2152
|
): Promise<IDataStore> {
|
|
2144
2153
|
const fluidDataStore = await this.dataStores._createFluidDataStoreContext(
|
|
2145
|
-
Array.isArray(pkg) ? pkg : [pkg], id,
|
|
2146
|
-
if (isRoot) {
|
|
2147
|
-
// back-compat 0.59.1000 - makeVisibleAndAttachGraph was added in this version to IFluidDataStoreChannel.
|
|
2148
|
-
// For older versions, we still have to call bindToContext.
|
|
2149
|
-
if (fluidDataStore.makeVisibleAndAttachGraph !== undefined) {
|
|
2150
|
-
fluidDataStore.makeVisibleAndAttachGraph();
|
|
2151
|
-
} else {
|
|
2152
|
-
fluidDataStore.bindToContext();
|
|
2153
|
-
}
|
|
2154
|
-
this.logger.sendTelemetryEvent({
|
|
2155
|
-
eventName: "Root datastore with props",
|
|
2156
|
-
hasProps: props !== undefined,
|
|
2157
|
-
});
|
|
2158
|
-
}
|
|
2154
|
+
Array.isArray(pkg) ? pkg : [pkg], id, props).realize();
|
|
2159
2155
|
return channelToDataStore(fluidDataStore, id, this, this.dataStores, this.mc.logger);
|
|
2160
2156
|
}
|
|
2161
2157
|
|
|
2162
|
-
public async _createDataStoreWithProps(
|
|
2163
|
-
pkg: string | string[],
|
|
2164
|
-
props?: any,
|
|
2165
|
-
id = uuid(),
|
|
2166
|
-
isRoot = false,
|
|
2167
|
-
): Promise<IDataStore> {
|
|
2168
|
-
return this._aliasingEnabled === true && isRoot ?
|
|
2169
|
-
this.createAndAliasDataStore(pkg, id, props) :
|
|
2170
|
-
this._createDataStoreWithPropsLegacy(pkg, props, id, isRoot);
|
|
2171
|
-
}
|
|
2172
|
-
|
|
2173
2158
|
private async _createDataStore(
|
|
2174
2159
|
pkg: string | string[],
|
|
2175
|
-
isRoot: boolean,
|
|
2176
2160
|
id = uuid(),
|
|
2177
2161
|
props?: any,
|
|
2178
2162
|
): Promise<IFluidDataStoreChannel> {
|
|
2179
2163
|
return this.dataStores
|
|
2180
|
-
._createFluidDataStoreContext(Array.isArray(pkg) ? pkg : [pkg], id,
|
|
2164
|
+
._createFluidDataStoreContext(Array.isArray(pkg) ? pkg : [pkg], id, props)
|
|
2181
2165
|
.realize();
|
|
2182
2166
|
}
|
|
2183
2167
|
|
|
@@ -2534,21 +2518,24 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2534
2518
|
},
|
|
2535
2519
|
);
|
|
2536
2520
|
|
|
2521
|
+
let latestSnapshotVersionId: string | undefined;
|
|
2537
2522
|
if (refreshLatestAck) {
|
|
2538
|
-
const
|
|
2523
|
+
const latestSnapshotInfo = await this.refreshLatestSummaryAckFromServer(
|
|
2539
2524
|
ChildLogger.create(summaryNumberLogger, undefined, { all: { safeSummary: true } }));
|
|
2525
|
+
const latestSnapshotRefSeq = latestSnapshotInfo.latestSnapshotRefSeq;
|
|
2526
|
+
latestSnapshotVersionId = latestSnapshotInfo.latestSnapshotVersionId;
|
|
2540
2527
|
|
|
2541
|
-
if (
|
|
2528
|
+
if (latestSnapshotRefSeq > this.deltaManager.lastSequenceNumber) {
|
|
2542
2529
|
// We need to catch up to the latest summary's reference sequence number before pausing.
|
|
2543
2530
|
await PerformanceEvent.timedExecAsync(
|
|
2544
2531
|
summaryNumberLogger,
|
|
2545
2532
|
{
|
|
2546
2533
|
eventName: "WaitingForSeq",
|
|
2547
2534
|
lastSequenceNumber: this.deltaManager.lastSequenceNumber,
|
|
2548
|
-
targetSequenceNumber:
|
|
2535
|
+
targetSequenceNumber: latestSnapshotRefSeq,
|
|
2549
2536
|
lastKnownSeqNumber: this.deltaManager.lastKnownSeqNumber,
|
|
2550
2537
|
},
|
|
2551
|
-
async () => waitForSeq(this.deltaManager,
|
|
2538
|
+
async () => waitForSeq(this.deltaManager, latestSnapshotRefSeq),
|
|
2552
2539
|
{ start: true, end: true, cancel: "error" }, // definitely want start event
|
|
2553
2540
|
);
|
|
2554
2541
|
}
|
|
@@ -2560,15 +2547,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2560
2547
|
const summaryRefSeqNum = this.deltaManager.lastSequenceNumber;
|
|
2561
2548
|
const minimumSequenceNumber = this.deltaManager.minimumSequenceNumber;
|
|
2562
2549
|
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
|
-
}
|
|
2550
|
+
const lastAck = this.summaryCollection.latestAck;
|
|
2572
2551
|
|
|
2573
2552
|
this.summarizerNode.startSummary(summaryRefSeqNum, summaryNumberLogger);
|
|
2574
2553
|
|
|
@@ -2598,6 +2577,16 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2598
2577
|
error: `lastSequenceNumber changed before uploading to storage. ${this.deltaManager.lastSequenceNumber} !== ${summaryRefSeqNum}`,
|
|
2599
2578
|
};
|
|
2600
2579
|
}
|
|
2580
|
+
assert(summaryRefSeqNum === this.deltaManager.lastMessage?.sequenceNumber,
|
|
2581
|
+
0x395 /* it's one and the same thing */);
|
|
2582
|
+
|
|
2583
|
+
if (lastAck !== this.summaryCollection.latestAck) {
|
|
2584
|
+
return {
|
|
2585
|
+
continue: false,
|
|
2586
|
+
// eslint-disable-next-line max-len
|
|
2587
|
+
error: `Last summary changed while summarizing. ${this.summaryCollection.latestAck} !== ${lastAck}`,
|
|
2588
|
+
};
|
|
2589
|
+
}
|
|
2601
2590
|
return { continue: true };
|
|
2602
2591
|
};
|
|
2603
2592
|
|
|
@@ -2654,8 +2643,6 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2654
2643
|
gcStateUpdatedDataStoreCount: summarizeResult.gcStats?.updatedDataStoreCount,
|
|
2655
2644
|
gcBlobNodeCount: gcSummaryTreeStats?.blobNodeCount,
|
|
2656
2645
|
gcTotalBlobsSize: gcSummaryTreeStats?.totalBlobSize,
|
|
2657
|
-
opsSizesSinceLastSummary: this.opTracker.opsSizeAccumulator,
|
|
2658
|
-
nonSystemOpsSinceLastSummary: this.opTracker.nonSystemOpCount,
|
|
2659
2646
|
summaryNumber,
|
|
2660
2647
|
...partialStats,
|
|
2661
2648
|
};
|
|
@@ -2673,19 +2660,32 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2673
2660
|
return { stage: "generate", ...generateSummaryData, error: continueResult.error };
|
|
2674
2661
|
}
|
|
2675
2662
|
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
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
|
+
}
|
|
2689
2689
|
|
|
2690
2690
|
let handle: string;
|
|
2691
2691
|
try {
|
|
@@ -2728,7 +2728,6 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2728
2728
|
} as const;
|
|
2729
2729
|
|
|
2730
2730
|
this.summarizerNode.completeSummary(handle);
|
|
2731
|
-
this.opTracker.reset();
|
|
2732
2731
|
return submitData;
|
|
2733
2732
|
} finally {
|
|
2734
2733
|
// Cleanup wip summary in case of failure
|
|
@@ -2892,14 +2891,8 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2892
2891
|
"OpTooLarge",
|
|
2893
2892
|
/* error */ undefined,
|
|
2894
2893
|
{
|
|
2895
|
-
length:
|
|
2896
|
-
|
|
2897
|
-
tag: TelemetryDataTag.PackageData,
|
|
2898
|
-
},
|
|
2899
|
-
limit: {
|
|
2900
|
-
value: this._maxOpSizeInBytes,
|
|
2901
|
-
tag: TelemetryDataTag.PackageData,
|
|
2902
|
-
},
|
|
2894
|
+
length: serializedContent.length,
|
|
2895
|
+
limit: this._maxOpSizeInBytes,
|
|
2903
2896
|
}));
|
|
2904
2897
|
return -1;
|
|
2905
2898
|
}
|
|
@@ -3005,7 +2998,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
3005
2998
|
case ContainerMessageType.ChunkedOp:
|
|
3006
2999
|
throw new Error(`chunkedOp not expected here`);
|
|
3007
3000
|
case ContainerMessageType.BlobAttach:
|
|
3008
|
-
this.
|
|
3001
|
+
this.blobManager.reSubmit(opMetadata);
|
|
3009
3002
|
break;
|
|
3010
3003
|
case ContainerMessageType.Rejoin:
|
|
3011
3004
|
this.submit(type, content);
|
|
@@ -3039,15 +3032,20 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
3039
3032
|
summaryLogger: ITelemetryLogger,
|
|
3040
3033
|
) {
|
|
3041
3034
|
const readAndParseBlob = async <T>(id: string) => readAndParse<T>(this.storage, id);
|
|
3042
|
-
const
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3035
|
+
const { snapshotTree } = await this.fetchSnapshotFromStorage(
|
|
3036
|
+
ackHandle,
|
|
3037
|
+
summaryLogger,
|
|
3038
|
+
{
|
|
3046
3039
|
eventName: "RefreshLatestSummaryGetSnapshot",
|
|
3047
3040
|
ackHandle,
|
|
3048
3041
|
summaryRefSeq,
|
|
3049
3042
|
fetchLatest: false,
|
|
3050
|
-
}
|
|
3043
|
+
},
|
|
3044
|
+
);
|
|
3045
|
+
const result = await this.summarizerNode.refreshLatestSummary(
|
|
3046
|
+
proposalHandle,
|
|
3047
|
+
summaryRefSeq,
|
|
3048
|
+
async () => snapshotTree,
|
|
3051
3049
|
readAndParseBlob,
|
|
3052
3050
|
summaryLogger,
|
|
3053
3051
|
);
|
|
@@ -3062,19 +3060,23 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
3062
3060
|
* @param summaryLogger - logger to use when fetching snapshot from storage
|
|
3063
3061
|
* @returns downloaded snapshot's reference sequence number
|
|
3064
3062
|
*/
|
|
3065
|
-
private async refreshLatestSummaryAckFromServer(
|
|
3066
|
-
|
|
3063
|
+
private async refreshLatestSummaryAckFromServer(
|
|
3064
|
+
summaryLogger: ITelemetryLogger,
|
|
3065
|
+
): Promise<{ latestSnapshotRefSeq: number; latestSnapshotVersionId: string | undefined; }> {
|
|
3066
|
+
const { snapshotTree, versionId } = await this.fetchSnapshotFromStorage(null, summaryLogger, {
|
|
3067
3067
|
eventName: "RefreshLatestSummaryGetSnapshot",
|
|
3068
3068
|
fetchLatest: true,
|
|
3069
|
-
}
|
|
3069
|
+
},
|
|
3070
|
+
FetchSource.noCache,
|
|
3071
|
+
);
|
|
3070
3072
|
|
|
3071
3073
|
const readAndParseBlob = async <T>(id: string) => readAndParse<T>(this.storage, id);
|
|
3072
|
-
const
|
|
3074
|
+
const latestSnapshotRefSeq = await seqFromTree(snapshotTree, readAndParseBlob);
|
|
3073
3075
|
|
|
3074
3076
|
const result = await this.summarizerNode.refreshLatestSummary(
|
|
3075
3077
|
undefined,
|
|
3076
|
-
|
|
3077
|
-
async () =>
|
|
3078
|
+
latestSnapshotRefSeq,
|
|
3079
|
+
async () => snapshotTree,
|
|
3078
3080
|
readAndParseBlob,
|
|
3079
3081
|
summaryLogger,
|
|
3080
3082
|
);
|
|
@@ -3082,11 +3084,15 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
3082
3084
|
// Notify the garbage collector so it can update its latest summary state.
|
|
3083
3085
|
await this.garbageCollector.latestSummaryStateRefreshed(result, readAndParseBlob);
|
|
3084
3086
|
|
|
3085
|
-
return
|
|
3087
|
+
return { latestSnapshotRefSeq, latestSnapshotVersionId: versionId };
|
|
3086
3088
|
}
|
|
3087
3089
|
|
|
3088
3090
|
private async fetchSnapshotFromStorage(
|
|
3089
|
-
versionId: string | null,
|
|
3091
|
+
versionId: string | null,
|
|
3092
|
+
logger: ITelemetryLogger,
|
|
3093
|
+
event: ITelemetryGenericEvent,
|
|
3094
|
+
fetchSource?: FetchSource,
|
|
3095
|
+
): Promise<{ snapshotTree: ISnapshotTree; versionId: string; }> {
|
|
3090
3096
|
return PerformanceEvent.timedExecAsync(
|
|
3091
3097
|
logger, event, async (perfEvent: {
|
|
3092
3098
|
end: (arg0: {
|
|
@@ -3097,7 +3103,8 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
3097
3103
|
const stats: { getVersionDuration?: number; getSnapshotDuration?: number; } = {};
|
|
3098
3104
|
const trace = Trace.start();
|
|
3099
3105
|
|
|
3100
|
-
const versions = await this.storage.getVersions(
|
|
3106
|
+
const versions = await this.storage.getVersions(
|
|
3107
|
+
versionId, 1, "refreshLatestSummaryAckFromServer", fetchSource);
|
|
3101
3108
|
assert(!!versions && !!versions[0], 0x137 /* "Failed to get version from storage" */);
|
|
3102
3109
|
stats.getVersionDuration = trace.trace().duration;
|
|
3103
3110
|
|
|
@@ -3106,7 +3113,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
3106
3113
|
stats.getSnapshotDuration = trace.trace().duration;
|
|
3107
3114
|
|
|
3108
3115
|
perfEvent.end(stats);
|
|
3109
|
-
return maybeSnapshot;
|
|
3116
|
+
return { snapshotTree: maybeSnapshot, versionId: versions[0].id };
|
|
3110
3117
|
});
|
|
3111
3118
|
}
|
|
3112
3119
|
|
|
@@ -3218,6 +3225,15 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
3218
3225
|
// don't have any more saved ops
|
|
3219
3226
|
await this.pendingStateManager.applyStashedOpsAt();
|
|
3220
3227
|
}
|
|
3228
|
+
|
|
3229
|
+
private validateSummaryHeuristicConfiguration(configuration: ISummaryConfigurationHeuristics) {
|
|
3230
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
3231
|
+
for (const prop in configuration) {
|
|
3232
|
+
if (typeof configuration[prop] === "number" && configuration[prop] < 0) {
|
|
3233
|
+
throw new UsageError(`Summary heuristic configuration property "${prop}" cannot be less than 0`);
|
|
3234
|
+
}
|
|
3235
|
+
}
|
|
3236
|
+
}
|
|
3221
3237
|
}
|
|
3222
3238
|
|
|
3223
3239
|
/**
|