@fluidframework/container-runtime 0.59.4001 → 1.1.0-75972
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/.eslintrc.js +1 -1
- package/dist/blobManager.d.ts +2 -2
- package/dist/blobManager.d.ts.map +1 -1
- package/dist/blobManager.js +12 -11
- package/dist/blobManager.js.map +1 -1
- package/dist/connectionTelemetry.d.ts +19 -0
- package/dist/connectionTelemetry.d.ts.map +1 -1
- package/dist/connectionTelemetry.js +23 -23
- package/dist/connectionTelemetry.js.map +1 -1
- package/dist/containerRuntime.d.ts +137 -29
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +338 -118
- package/dist/containerRuntime.js.map +1 -1
- package/dist/dataStore.d.ts.map +1 -1
- package/dist/dataStore.js +14 -3
- package/dist/dataStore.js.map +1 -1
- package/dist/dataStoreContext.d.ts +4 -2
- package/dist/dataStoreContext.d.ts.map +1 -1
- package/dist/dataStoreContext.js +16 -5
- package/dist/dataStoreContext.js.map +1 -1
- package/dist/dataStoreRegistry.d.ts +0 -4
- package/dist/dataStoreRegistry.d.ts.map +1 -1
- package/dist/dataStoreRegistry.js +12 -1
- package/dist/dataStoreRegistry.js.map +1 -1
- package/dist/dataStores.d.ts +4 -3
- package/dist/dataStores.d.ts.map +1 -1
- package/dist/dataStores.js +13 -7
- package/dist/dataStores.js.map +1 -1
- package/dist/garbageCollection.d.ts +23 -27
- package/dist/garbageCollection.d.ts.map +1 -1
- package/dist/garbageCollection.js +44 -119
- package/dist/garbageCollection.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/orderedClientElection.js +0 -4
- package/dist/orderedClientElection.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.d.ts.map +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/pendingStateManager.d.ts +30 -29
- package/dist/pendingStateManager.d.ts.map +1 -1
- package/dist/pendingStateManager.js +72 -109
- package/dist/pendingStateManager.js.map +1 -1
- package/dist/runningSummarizer.d.ts +4 -3
- package/dist/runningSummarizer.d.ts.map +1 -1
- package/dist/runningSummarizer.js +11 -6
- package/dist/runningSummarizer.js.map +1 -1
- package/dist/serializedSnapshotStorage.d.ts +58 -0
- package/dist/serializedSnapshotStorage.d.ts.map +1 -0
- package/dist/serializedSnapshotStorage.js +108 -0
- package/dist/serializedSnapshotStorage.js.map +1 -0
- package/dist/summarizer.d.ts +11 -4
- package/dist/summarizer.d.ts.map +1 -1
- package/dist/summarizer.js +18 -9
- package/dist/summarizer.js.map +1 -1
- package/dist/summarizerHeuristics.d.ts +5 -3
- package/dist/summarizerHeuristics.d.ts.map +1 -1
- package/dist/summarizerHeuristics.js +10 -3
- package/dist/summarizerHeuristics.js.map +1 -1
- package/dist/summarizerTypes.d.ts +4 -2
- package/dist/summarizerTypes.d.ts.map +1 -1
- package/dist/summarizerTypes.js.map +1 -1
- package/dist/summaryManager.d.ts +3 -3
- package/dist/summaryManager.d.ts.map +1 -1
- package/dist/summaryManager.js +7 -7
- package/dist/summaryManager.js.map +1 -1
- package/garbageCollection.md +9 -1
- package/lib/blobManager.d.ts +2 -2
- package/lib/blobManager.d.ts.map +1 -1
- package/lib/blobManager.js +12 -11
- package/lib/blobManager.js.map +1 -1
- package/lib/connectionTelemetry.d.ts +19 -0
- package/lib/connectionTelemetry.d.ts.map +1 -1
- package/lib/connectionTelemetry.js +23 -23
- package/lib/connectionTelemetry.js.map +1 -1
- package/lib/containerRuntime.d.ts +137 -29
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +341 -121
- package/lib/containerRuntime.js.map +1 -1
- package/lib/dataStore.d.ts.map +1 -1
- package/lib/dataStore.js +15 -4
- package/lib/dataStore.js.map +1 -1
- package/lib/dataStoreContext.d.ts +4 -2
- package/lib/dataStoreContext.d.ts.map +1 -1
- package/lib/dataStoreContext.js +16 -5
- package/lib/dataStoreContext.js.map +1 -1
- package/lib/dataStoreRegistry.d.ts +0 -4
- package/lib/dataStoreRegistry.d.ts.map +1 -1
- package/lib/dataStoreRegistry.js +12 -1
- package/lib/dataStoreRegistry.js.map +1 -1
- package/lib/dataStores.d.ts +4 -3
- package/lib/dataStores.d.ts.map +1 -1
- package/lib/dataStores.js +14 -8
- package/lib/dataStores.js.map +1 -1
- package/lib/garbageCollection.d.ts +23 -27
- package/lib/garbageCollection.d.ts.map +1 -1
- package/lib/garbageCollection.js +43 -117
- package/lib/garbageCollection.js.map +1 -1
- package/lib/index.d.ts +2 -2
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -1
- package/lib/index.js.map +1 -1
- package/lib/orderedClientElection.js +0 -4
- package/lib/orderedClientElection.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.d.ts.map +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/pendingStateManager.d.ts +30 -29
- package/lib/pendingStateManager.d.ts.map +1 -1
- package/lib/pendingStateManager.js +72 -109
- package/lib/pendingStateManager.js.map +1 -1
- package/lib/runningSummarizer.d.ts +4 -3
- package/lib/runningSummarizer.d.ts.map +1 -1
- package/lib/runningSummarizer.js +11 -6
- package/lib/runningSummarizer.js.map +1 -1
- package/lib/serializedSnapshotStorage.d.ts +58 -0
- package/lib/serializedSnapshotStorage.d.ts.map +1 -0
- package/lib/serializedSnapshotStorage.js +104 -0
- package/lib/serializedSnapshotStorage.js.map +1 -0
- package/lib/summarizer.d.ts +11 -4
- package/lib/summarizer.d.ts.map +1 -1
- package/lib/summarizer.js +18 -9
- package/lib/summarizer.js.map +1 -1
- package/lib/summarizerHeuristics.d.ts +5 -3
- package/lib/summarizerHeuristics.d.ts.map +1 -1
- package/lib/summarizerHeuristics.js +10 -3
- package/lib/summarizerHeuristics.js.map +1 -1
- package/lib/summarizerTypes.d.ts +4 -2
- package/lib/summarizerTypes.d.ts.map +1 -1
- package/lib/summarizerTypes.js.map +1 -1
- package/lib/summaryManager.d.ts +3 -3
- package/lib/summaryManager.d.ts.map +1 -1
- package/lib/summaryManager.js +7 -7
- package/lib/summaryManager.js.map +1 -1
- package/package.json +19 -32
- package/src/blobManager.ts +29 -15
- package/src/connectionTelemetry.ts +60 -39
- package/src/containerRuntime.ts +502 -156
- package/src/dataStore.ts +21 -4
- package/src/dataStoreContext.ts +27 -5
- package/src/dataStoreRegistry.ts +8 -1
- package/src/dataStores.ts +21 -8
- package/src/garbageCollection.ts +81 -166
- package/src/index.ts +7 -1
- package/src/orderedClientElection.ts +1 -1
- package/src/packageVersion.ts +1 -1
- package/src/pendingStateManager.ts +104 -123
- package/src/runningSummarizer.ts +20 -10
- package/src/serializedSnapshotStorage.ts +146 -0
- package/src/summarizer.ts +20 -16
- package/src/summarizerHeuristics.ts +21 -5
- package/src/summarizerTypes.ts +4 -2
- package/src/summaryManager.ts +5 -6
package/src/containerRuntime.ts
CHANGED
|
@@ -2,8 +2,6 @@
|
|
|
2
2
|
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
|
-
// See #9219
|
|
6
|
-
/* eslint-disable max-lines */
|
|
7
5
|
import { EventEmitter } from "events";
|
|
8
6
|
import { ITelemetryBaseLogger, ITelemetryGenericEvent, ITelemetryLogger } from "@fluidframework/common-definitions";
|
|
9
7
|
import {
|
|
@@ -25,6 +23,7 @@ import {
|
|
|
25
23
|
AttachState,
|
|
26
24
|
ILoaderOptions,
|
|
27
25
|
LoaderHeader,
|
|
26
|
+
ISnapshotTreeWithBlobContents,
|
|
28
27
|
} from "@fluidframework/container-definitions";
|
|
29
28
|
import {
|
|
30
29
|
IContainerRuntime,
|
|
@@ -41,7 +40,6 @@ import {
|
|
|
41
40
|
ChildLogger,
|
|
42
41
|
raiseConnectedEvent,
|
|
43
42
|
PerformanceEvent,
|
|
44
|
-
normalizeError,
|
|
45
43
|
TaggedLoggerAdapter,
|
|
46
44
|
MonitoringContext,
|
|
47
45
|
loggerToMonitoringContext,
|
|
@@ -51,6 +49,7 @@ import { DriverHeader, IDocumentStorageService, ISummaryContext } from "@fluidfr
|
|
|
51
49
|
import { readAndParse } from "@fluidframework/driver-utils";
|
|
52
50
|
import {
|
|
53
51
|
DataCorruptionError,
|
|
52
|
+
DataProcessingError,
|
|
54
53
|
GenericError,
|
|
55
54
|
UsageError,
|
|
56
55
|
extractSafePropertiesFromMessage,
|
|
@@ -61,7 +60,7 @@ import {
|
|
|
61
60
|
IQuorumClients,
|
|
62
61
|
ISequencedDocumentMessage,
|
|
63
62
|
ISignalMessage,
|
|
64
|
-
|
|
63
|
+
ISnapshotTree,
|
|
65
64
|
ISummaryContent,
|
|
66
65
|
ISummaryTree,
|
|
67
66
|
MessageType,
|
|
@@ -86,9 +85,11 @@ import {
|
|
|
86
85
|
channelsTreeName,
|
|
87
86
|
IAttachMessage,
|
|
88
87
|
IDataStore,
|
|
88
|
+
ITelemetryContext,
|
|
89
89
|
} from "@fluidframework/runtime-definitions";
|
|
90
90
|
import {
|
|
91
91
|
addBlobToSummary,
|
|
92
|
+
addSummarizeResultToSummary,
|
|
92
93
|
addTreeToSummary,
|
|
93
94
|
createRootSummarizerNodeWithGC,
|
|
94
95
|
IRootSummarizerNodeWithGC,
|
|
@@ -99,7 +100,7 @@ import {
|
|
|
99
100
|
responseToException,
|
|
100
101
|
seqFromTree,
|
|
101
102
|
calculateStats,
|
|
102
|
-
|
|
103
|
+
TelemetryContext,
|
|
103
104
|
} from "@fluidframework/runtime-utils";
|
|
104
105
|
import { GCDataBuilder, trimLeadingAndTrailingSlashes } from "@fluidframework/garbage-collector";
|
|
105
106
|
import { v4 as uuid } from "uuid";
|
|
@@ -108,7 +109,11 @@ import { FluidDataStoreRegistry } from "./dataStoreRegistry";
|
|
|
108
109
|
import { Summarizer } from "./summarizer";
|
|
109
110
|
import { SummaryManager } from "./summaryManager";
|
|
110
111
|
import { DeltaScheduler } from "./deltaScheduler";
|
|
111
|
-
import {
|
|
112
|
+
import {
|
|
113
|
+
ReportOpPerfTelemetry,
|
|
114
|
+
latencyThreshold,
|
|
115
|
+
IPerfSignalReport,
|
|
116
|
+
} from "./connectionTelemetry";
|
|
112
117
|
import { IPendingLocalState, PendingStateManager } from "./pendingStateManager";
|
|
113
118
|
import { pkgVersion } from "./packageVersion";
|
|
114
119
|
import { BlobManager, IBlobManagerLoadInfo } from "./blobManager";
|
|
@@ -154,6 +159,7 @@ import {
|
|
|
154
159
|
isDataStoreAliasMessage,
|
|
155
160
|
} from "./dataStore";
|
|
156
161
|
import { BindBatchTracker } from "./batchTracker";
|
|
162
|
+
import { ISerializedBaseSnapshotBlobs, SerializedSnapshotStorage } from "./serializedSnapshotStorage";
|
|
157
163
|
import { OpTracker } from "./opTelemetry";
|
|
158
164
|
|
|
159
165
|
export enum ContainerMessageType {
|
|
@@ -190,22 +196,85 @@ export interface ContainerRuntimeMessage {
|
|
|
190
196
|
contents: any;
|
|
191
197
|
type: ContainerMessageType;
|
|
192
198
|
}
|
|
199
|
+
export interface ISummaryBaseConfiguration {
|
|
200
|
+
/**
|
|
201
|
+
* Delay before first attempt to spawn summarizing container.
|
|
202
|
+
*/
|
|
203
|
+
initialSummarizerDelayMs: number;
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Flag that will enable changing elected summarizer client after maxOpsSinceLastSummary.
|
|
207
|
+
* This defaults to false (disabled) and must be explicitly set to true to enable.
|
|
208
|
+
*/
|
|
209
|
+
summarizerClientElection: boolean;
|
|
193
210
|
|
|
194
|
-
|
|
195
|
-
|
|
211
|
+
/**
|
|
212
|
+
* Defines the maximum allowed time to wait for a pending summary ack.
|
|
213
|
+
* The maximum amount of time client will wait for a summarize is the minimum of
|
|
214
|
+
* maxSummarizeAckWaitTime (currently 10 * 60 * 1000) and maxAckWaitTime.
|
|
215
|
+
*/
|
|
216
|
+
maxAckWaitTime: number;
|
|
217
|
+
/**
|
|
218
|
+
* Defines the maximum number of Ops in between Summaries that can be
|
|
219
|
+
* allowed before forcibly electing a new summarizer client.
|
|
220
|
+
*/
|
|
221
|
+
maxOpsSinceLastSummary: number;
|
|
222
|
+
}
|
|
196
223
|
|
|
197
|
-
|
|
198
|
-
|
|
224
|
+
export interface ISummaryConfigurationHeuristics extends ISummaryBaseConfiguration {
|
|
225
|
+
state: "enabled";
|
|
226
|
+
/**
|
|
227
|
+
* Defines the maximum allowed time in between summarizations.
|
|
228
|
+
*/
|
|
229
|
+
idleTime: number;
|
|
230
|
+
/**
|
|
231
|
+
* Defines the maximum allowed time, since the last received Ack, before running the summary
|
|
232
|
+
* with reason maxTime.
|
|
233
|
+
*/
|
|
234
|
+
maxTime: number;
|
|
235
|
+
/**
|
|
236
|
+
* Defines the maximum number of Ops, since the last received Ack, that can be allowed
|
|
237
|
+
* before running the summary with reason maxOps.
|
|
238
|
+
*/
|
|
239
|
+
maxOps: number;
|
|
240
|
+
/**
|
|
241
|
+
* Defines the minimum number of Ops, since the last received Ack, that can be allowed
|
|
242
|
+
* before running the last summary.
|
|
243
|
+
*/
|
|
244
|
+
minOpsForLastSummaryAttempt: number;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
export interface ISummaryConfigurationDisableSummarizer {
|
|
248
|
+
state: "disabled";
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
export interface ISummaryConfigurationDisableHeuristics extends ISummaryBaseConfiguration {
|
|
252
|
+
state: "disableHeuristics";
|
|
253
|
+
}
|
|
199
254
|
|
|
200
|
-
|
|
255
|
+
export type ISummaryConfiguration =
|
|
256
|
+
| ISummaryConfigurationDisableSummarizer
|
|
257
|
+
| ISummaryConfigurationDisableHeuristics
|
|
258
|
+
| ISummaryConfigurationHeuristics;
|
|
201
259
|
|
|
202
|
-
|
|
203
|
-
|
|
260
|
+
export const DefaultSummaryConfiguration: ISummaryConfiguration = {
|
|
261
|
+
state: "enabled",
|
|
204
262
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
263
|
+
idleTime: 5000 * 3,
|
|
264
|
+
|
|
265
|
+
maxTime: 5000 * 12,
|
|
266
|
+
|
|
267
|
+
maxOps: 100, // Summarize if 100 ops received since last snapshot.
|
|
268
|
+
|
|
269
|
+
minOpsForLastSummaryAttempt: 10,
|
|
270
|
+
|
|
271
|
+
maxAckWaitTime: 6 * 10 * 1000, // 6 min.
|
|
272
|
+
|
|
273
|
+
maxOpsSinceLastSummary: 7000,
|
|
274
|
+
|
|
275
|
+
initialSummarizerDelayMs: 5000, // 5 secs.
|
|
276
|
+
|
|
277
|
+
summarizerClientElection: false,
|
|
209
278
|
};
|
|
210
279
|
|
|
211
280
|
export interface IGCRuntimeOptions {
|
|
@@ -245,39 +314,43 @@ export interface IGCRuntimeOptions {
|
|
|
245
314
|
}
|
|
246
315
|
|
|
247
316
|
export interface ISummaryRuntimeOptions {
|
|
248
|
-
/**
|
|
249
|
-
* Flag that disables summaries if it is set to true.
|
|
250
|
-
*/
|
|
251
|
-
disableSummaries?: boolean;
|
|
252
|
-
|
|
253
|
-
/**
|
|
254
|
-
* @deprecated - To disable summaries, please set disableSummaries===true.
|
|
255
|
-
* Flag that will generate summaries if connected to a service that supports them.
|
|
256
|
-
* This defaults to true and must be explicitly set to false to disable.
|
|
257
|
-
*/
|
|
258
|
-
generateSummaries?: boolean;
|
|
259
|
-
|
|
260
|
-
/* Delay before first attempt to spawn summarizing container. */
|
|
261
|
-
initialSummarizerDelayMs?: number;
|
|
262
317
|
|
|
263
318
|
/** Override summary configurations set by the server. */
|
|
264
|
-
summaryConfigOverrides?:
|
|
319
|
+
summaryConfigOverrides?: ISummaryConfiguration;
|
|
265
320
|
|
|
266
321
|
// Flag that disables putting channels in isolated subtrees for each data store
|
|
267
322
|
// and the root node when generating a summary if set to true.
|
|
268
323
|
// Defaults to FALSE (enabled) for now.
|
|
269
324
|
disableIsolatedChannels?: boolean;
|
|
270
325
|
|
|
271
|
-
|
|
272
|
-
|
|
326
|
+
/**
|
|
327
|
+
* @deprecated - use `summaryConfigOverrides.initialSummarizerDelayMs` instead.
|
|
328
|
+
* Delay before first attempt to spawn summarizing container.
|
|
329
|
+
*/
|
|
330
|
+
initialSummarizerDelayMs?: number;
|
|
273
331
|
|
|
274
332
|
/**
|
|
333
|
+
* @deprecated - use `summaryConfigOverrides.disableSummaries` instead.
|
|
334
|
+
* Flag that disables summaries if it is set to true.
|
|
335
|
+
*/
|
|
336
|
+
disableSummaries?: boolean;
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* @deprecated - use `summaryConfigOverrides.maxOpsSinceLastSummary` instead.
|
|
340
|
+
* Defaults to 7000 ops
|
|
341
|
+
*/
|
|
342
|
+
maxOpsSinceLastSummary?: number;
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* @deprecated - use `summaryConfigOverrides.summarizerClientElection` instead.
|
|
275
346
|
* Flag that will enable changing elected summarizer client after maxOpsSinceLastSummary.
|
|
276
|
-
*
|
|
347
|
+
* This defaults to false (disabled) and must be explicitly set to true to enable.
|
|
277
348
|
*/
|
|
278
349
|
summarizerClientElection?: boolean;
|
|
279
350
|
|
|
280
|
-
/**
|
|
351
|
+
/**
|
|
352
|
+
* @deprecated - use `summaryConfigOverrides.state = "DisableHeuristics"` instead.
|
|
353
|
+
* Options that control the running summarizer behavior. */
|
|
281
354
|
summarizerOptions?: Readonly<Partial<ISummarizerOptions>>;
|
|
282
355
|
}
|
|
283
356
|
|
|
@@ -309,6 +382,10 @@ export interface IContainerRuntimeOptions {
|
|
|
309
382
|
* By default, flush mode is TurnBased.
|
|
310
383
|
*/
|
|
311
384
|
readonly flushMode?: FlushMode;
|
|
385
|
+
/**
|
|
386
|
+
* Save enough runtime state to be able to serialize upon request and load to the same state in a new container.
|
|
387
|
+
*/
|
|
388
|
+
readonly enableOfflineLoad?: boolean;
|
|
312
389
|
}
|
|
313
390
|
|
|
314
391
|
type IRuntimeMessageMetadata = undefined | {
|
|
@@ -349,6 +426,33 @@ interface OldContainerContextWithLogger extends Omit<IContainerContext, "taggedL
|
|
|
349
426
|
taggedLogger: undefined;
|
|
350
427
|
}
|
|
351
428
|
|
|
429
|
+
/**
|
|
430
|
+
* State saved when the container closes, to be given back to a newly
|
|
431
|
+
* instantiated runtime in a new instance of the container, so it can load to the
|
|
432
|
+
* same state
|
|
433
|
+
*/
|
|
434
|
+
export interface IPendingRuntimeState {
|
|
435
|
+
/**
|
|
436
|
+
* Pending ops from PendingStateManager
|
|
437
|
+
*/
|
|
438
|
+
pending?: IPendingLocalState;
|
|
439
|
+
/**
|
|
440
|
+
* A base snapshot at a sequence number prior to the first pending op
|
|
441
|
+
*/
|
|
442
|
+
baseSnapshot: ISnapshotTree;
|
|
443
|
+
/**
|
|
444
|
+
* Serialized blobs from the base snapshot. Used to load offline since
|
|
445
|
+
* storage is not available.
|
|
446
|
+
*/
|
|
447
|
+
snapshotBlobs: ISerializedBaseSnapshotBlobs;
|
|
448
|
+
/**
|
|
449
|
+
* All runtime ops since base snapshot sequence number up to the latest op
|
|
450
|
+
* seen when the container was closed. Used to apply stashed (saved pending)
|
|
451
|
+
* ops at the same sequence number at which they were made.
|
|
452
|
+
*/
|
|
453
|
+
savedOps: ISequencedDocumentMessage[];
|
|
454
|
+
}
|
|
455
|
+
|
|
352
456
|
const useDataStoreAliasingKey = "Fluid.ContainerRuntime.UseDataStoreAliasing";
|
|
353
457
|
const maxConsecutiveReconnectsKey = "Fluid.ContainerRuntime.MaxConsecutiveReconnects";
|
|
354
458
|
|
|
@@ -507,7 +611,7 @@ class ScheduleManagerCore {
|
|
|
507
611
|
|
|
508
612
|
private resumeQueue(startBatch: number, messageEndBatch: ISequencedDocumentMessage) {
|
|
509
613
|
const endBatch = messageEndBatch.sequenceNumber;
|
|
510
|
-
const duration = performance.now() - this.timePaused;
|
|
614
|
+
const duration = this.localPaused ? (performance.now() - this.timePaused) : undefined;
|
|
511
615
|
|
|
512
616
|
this.batchCount++;
|
|
513
617
|
if (this.batchCount % 1000 === 1) {
|
|
@@ -530,7 +634,7 @@ class ScheduleManagerCore {
|
|
|
530
634
|
this.localPaused = false;
|
|
531
635
|
|
|
532
636
|
// Random round number - we want to know when batch waiting paused op processing.
|
|
533
|
-
if (duration > latencyThreshold) {
|
|
637
|
+
if (duration !== undefined && duration > latencyThreshold) {
|
|
534
638
|
this.logger.sendErrorEvent({
|
|
535
639
|
eventName: "MaxBatchWaitTimeExceeded",
|
|
536
640
|
duration,
|
|
@@ -749,15 +853,20 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
749
853
|
loadSequenceNumberVerification = "close",
|
|
750
854
|
useDataStoreAliasing = false,
|
|
751
855
|
flushMode = defaultFlushMode,
|
|
856
|
+
enableOfflineLoad = false,
|
|
752
857
|
} = runtimeOptions;
|
|
753
858
|
|
|
754
|
-
const
|
|
859
|
+
const pendingRuntimeState = context.pendingLocalState as IPendingRuntimeState | undefined;
|
|
860
|
+
const baseSnapshot: ISnapshotTree | undefined = pendingRuntimeState?.baseSnapshot ?? context.baseSnapshot;
|
|
861
|
+
const storage = !pendingRuntimeState ?
|
|
862
|
+
context.storage :
|
|
863
|
+
new SerializedSnapshotStorage(() => { return context.storage; }, pendingRuntimeState.snapshotBlobs);
|
|
755
864
|
|
|
756
865
|
const registry = new FluidDataStoreRegistry(registryEntries);
|
|
757
866
|
|
|
758
867
|
const tryFetchBlob = async <T>(blobName: string): Promise<T | undefined> => {
|
|
759
|
-
const blobId =
|
|
760
|
-
if (
|
|
868
|
+
const blobId = baseSnapshot?.blobs[blobName];
|
|
869
|
+
if (baseSnapshot && blobId) {
|
|
761
870
|
// IContainerContext storage api return type still has undefined in 0.39 package version.
|
|
762
871
|
// So once we release 0.40 container-defn package we can remove this check.
|
|
763
872
|
assert(storage !== undefined, 0x1f5 /* "Attached state should have storage" */);
|
|
@@ -776,7 +885,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
776
885
|
|
|
777
886
|
// read snapshot blobs needed for BlobManager to load
|
|
778
887
|
const blobManagerSnapshot = await BlobManager.load(
|
|
779
|
-
|
|
888
|
+
baseSnapshot?.trees[blobsTreeName],
|
|
780
889
|
async (id) => {
|
|
781
890
|
// IContainerContext storage api return type still has undefined in 0.39 package version.
|
|
782
891
|
// So once we release 0.40 container-defn package we can remove this check.
|
|
@@ -787,7 +896,8 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
787
896
|
|
|
788
897
|
// Verify summary runtime sequence number matches protocol sequence number.
|
|
789
898
|
const runtimeSequenceNumber = metadata?.message?.sequenceNumber;
|
|
790
|
-
|
|
899
|
+
// When we load with pending state, we reuse an old snapshot so we don't expect these numbers to match
|
|
900
|
+
if (!pendingRuntimeState && runtimeSequenceNumber !== undefined) {
|
|
791
901
|
const protocolSequenceNumber = context.deltaManager.initialSequenceNumber;
|
|
792
902
|
// Unless bypass is explicitly set, then take action when sequence numbers mismatch.
|
|
793
903
|
if (loadSequenceNumberVerification !== "bypass" && runtimeSequenceNumber !== protocolSequenceNumber) {
|
|
@@ -819,14 +929,24 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
819
929
|
loadSequenceNumberVerification,
|
|
820
930
|
useDataStoreAliasing,
|
|
821
931
|
flushMode,
|
|
932
|
+
enableOfflineLoad,
|
|
822
933
|
},
|
|
823
934
|
containerScope,
|
|
824
935
|
logger,
|
|
825
936
|
loadExisting,
|
|
826
937
|
blobManagerSnapshot,
|
|
938
|
+
storage,
|
|
827
939
|
requestHandler,
|
|
828
940
|
);
|
|
829
941
|
|
|
942
|
+
if (pendingRuntimeState) {
|
|
943
|
+
await runtime.processSavedOps(pendingRuntimeState);
|
|
944
|
+
// delete these once runtime has seen them to save space
|
|
945
|
+
pendingRuntimeState.savedOps = [];
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
await runtime.getSnapshotBlobs();
|
|
949
|
+
|
|
830
950
|
return runtime;
|
|
831
951
|
}
|
|
832
952
|
|
|
@@ -847,7 +967,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
847
967
|
}
|
|
848
968
|
|
|
849
969
|
public get storage(): IDocumentStorageService {
|
|
850
|
-
return this.
|
|
970
|
+
return this._storage;
|
|
851
971
|
}
|
|
852
972
|
|
|
853
973
|
public get reSubmitFn(): (
|
|
@@ -910,7 +1030,8 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
910
1030
|
|
|
911
1031
|
private _connected: boolean;
|
|
912
1032
|
|
|
913
|
-
private
|
|
1033
|
+
private readonly savedOps: ISequencedDocumentMessage[] = [];
|
|
1034
|
+
private baseSnapshotBlobs?: ISerializedBaseSnapshotBlobs;
|
|
914
1035
|
|
|
915
1036
|
private consecutiveReconnects = 0;
|
|
916
1037
|
|
|
@@ -923,21 +1044,20 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
923
1044
|
return this.summarizerClientElection?.electedClientId;
|
|
924
1045
|
}
|
|
925
1046
|
|
|
926
|
-
private get summaryConfiguration() {
|
|
927
|
-
return {
|
|
928
|
-
// the defaults
|
|
929
|
-
... DefaultSummaryConfiguration,
|
|
930
|
-
// the runtime configuration overrides
|
|
931
|
-
... this.runtimeOptions.summaryOptions?.summaryConfigOverrides,
|
|
932
|
-
};
|
|
933
|
-
}
|
|
934
|
-
|
|
935
1047
|
private _disposed = false;
|
|
936
1048
|
public get disposed() { return this._disposed; }
|
|
937
1049
|
|
|
938
1050
|
private dirtyContainer: boolean;
|
|
939
1051
|
private emitDirtyDocumentEvent = true;
|
|
940
1052
|
|
|
1053
|
+
private readonly defaultTelemetrySignalSampleCount = 100;
|
|
1054
|
+
private _perfSignalData: IPerfSignalReport = {
|
|
1055
|
+
signalsLost: 0,
|
|
1056
|
+
signalSequenceNumber: 0,
|
|
1057
|
+
signalTimestamp: 0,
|
|
1058
|
+
trackingSignalSequenceNumber: undefined,
|
|
1059
|
+
};
|
|
1060
|
+
|
|
941
1061
|
/**
|
|
942
1062
|
* Summarizer is responsible for coordinating when to send generate and send summaries.
|
|
943
1063
|
* It is the main entry point for summary work.
|
|
@@ -969,9 +1089,68 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
969
1089
|
return this._summarizer;
|
|
970
1090
|
}
|
|
971
1091
|
|
|
972
|
-
private
|
|
973
|
-
|
|
974
|
-
|
|
1092
|
+
private readonly summariesDisabled: boolean;
|
|
1093
|
+
private isSummariesDisabled(): boolean {
|
|
1094
|
+
// back-compat: disableSummaries was moved from ISummaryRuntimeOptions
|
|
1095
|
+
// to ISummaryConfiguration in 0.60.
|
|
1096
|
+
if (this.runtimeOptions.summaryOptions.disableSummaries === true) {
|
|
1097
|
+
return true;
|
|
1098
|
+
}
|
|
1099
|
+
return this.summaryConfiguration.state === "disabled";
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
private readonly heuristicsDisabled: boolean;
|
|
1103
|
+
private isHeuristicsDisabled(): boolean {
|
|
1104
|
+
// back-compat: disableHeuristics was moved from ISummarizerOptions
|
|
1105
|
+
// to ISummaryConfiguration in 0.60.
|
|
1106
|
+
if (this.runtimeOptions.summaryOptions.summarizerOptions?.disableHeuristics === true) {
|
|
1107
|
+
return true;
|
|
1108
|
+
}
|
|
1109
|
+
return this.summaryConfiguration.state === "disableHeuristics";
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
private readonly summarizerClientElectionEnabled: boolean;
|
|
1113
|
+
private isSummarizerClientElectionEnabled(): boolean {
|
|
1114
|
+
if (this.mc.config.getBoolean("Fluid.ContainerRuntime.summarizerClientElection")) {
|
|
1115
|
+
return this.mc.config.getBoolean("Fluid.ContainerRuntime.summarizerClientElection") ?? true;
|
|
1116
|
+
}
|
|
1117
|
+
// back-compat: summarizerClientElection was moved from ISummaryRuntimeOptions
|
|
1118
|
+
// to ISummaryConfiguration in 0.60.
|
|
1119
|
+
if (this.runtimeOptions.summaryOptions.summarizerClientElection === true) {
|
|
1120
|
+
return true;
|
|
1121
|
+
}
|
|
1122
|
+
if (this.summaryConfiguration.state !== "disabled") {
|
|
1123
|
+
return this.summaryConfiguration.summarizerClientElection === true;
|
|
1124
|
+
} else {
|
|
1125
|
+
return false;
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
private readonly maxOpsSinceLastSummary: number;
|
|
1129
|
+
private getMaxOpsSinceLastSummary(): number {
|
|
1130
|
+
// back-compat: maxOpsSinceLastSummary was moved from ISummaryRuntimeOptions
|
|
1131
|
+
// to ISummaryConfiguration in 0.60.
|
|
1132
|
+
if (this.runtimeOptions.summaryOptions.maxOpsSinceLastSummary !== undefined) {
|
|
1133
|
+
return this.runtimeOptions.summaryOptions.maxOpsSinceLastSummary;
|
|
1134
|
+
}
|
|
1135
|
+
if (this.summaryConfiguration.state !== "disabled") {
|
|
1136
|
+
return this.summaryConfiguration.maxOpsSinceLastSummary;
|
|
1137
|
+
} else {
|
|
1138
|
+
return 0;
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
private readonly initialSummarizerDelayMs: number;
|
|
1143
|
+
private getInitialSummarizerDelayMs(): number {
|
|
1144
|
+
// back-compat: initialSummarizerDelayMs was moved from ISummaryRuntimeOptions
|
|
1145
|
+
// to ISummaryConfiguration in 0.60.
|
|
1146
|
+
if (this.runtimeOptions.summaryOptions.initialSummarizerDelayMs !== undefined) {
|
|
1147
|
+
return this.runtimeOptions.summaryOptions.initialSummarizerDelayMs;
|
|
1148
|
+
}
|
|
1149
|
+
if (this.summaryConfiguration.state !== "disabled") {
|
|
1150
|
+
return this.summaryConfiguration.initialSummarizerDelayMs;
|
|
1151
|
+
} else {
|
|
1152
|
+
return 0;
|
|
1153
|
+
}
|
|
975
1154
|
}
|
|
976
1155
|
|
|
977
1156
|
private readonly createContainerMetadata: ICreateContainerMetadata;
|
|
@@ -994,10 +1173,16 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
994
1173
|
public readonly logger: ITelemetryLogger,
|
|
995
1174
|
existing: boolean,
|
|
996
1175
|
blobManagerSnapshot: IBlobManagerLoadInfo,
|
|
1176
|
+
private readonly _storage: IDocumentStorageService,
|
|
997
1177
|
private readonly requestHandler?: (request: IRequest, runtime: IContainerRuntime) => Promise<IResponse>,
|
|
1178
|
+
private readonly summaryConfiguration: ISummaryConfiguration = {
|
|
1179
|
+
// the defaults
|
|
1180
|
+
... DefaultSummaryConfiguration,
|
|
1181
|
+
// the runtime configuration overrides
|
|
1182
|
+
... runtimeOptions.summaryOptions?.summaryConfigOverrides,
|
|
1183
|
+
},
|
|
998
1184
|
) {
|
|
999
1185
|
super();
|
|
1000
|
-
|
|
1001
1186
|
this.messageAtLastSummary = metadata?.message;
|
|
1002
1187
|
|
|
1003
1188
|
// Default to false (enabled).
|
|
@@ -1011,6 +1196,12 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1011
1196
|
this.mc = loggerToMonitoringContext(
|
|
1012
1197
|
ChildLogger.create(this.logger, "ContainerRuntime"));
|
|
1013
1198
|
|
|
1199
|
+
this.summariesDisabled = this.isSummariesDisabled();
|
|
1200
|
+
this.heuristicsDisabled = this.isHeuristicsDisabled();
|
|
1201
|
+
this.summarizerClientElectionEnabled = this.isSummarizerClientElectionEnabled();
|
|
1202
|
+
this.maxOpsSinceLastSummary = this.getMaxOpsSinceLastSummary();
|
|
1203
|
+
this.initialSummarizerDelayMs = this.getInitialSummarizerDelayMs();
|
|
1204
|
+
|
|
1014
1205
|
this._aliasingEnabled =
|
|
1015
1206
|
(this.mc.config.getBoolean(useDataStoreAliasingKey) ?? false) ||
|
|
1016
1207
|
(runtimeOptions.useDataStoreAliasing ?? false);
|
|
@@ -1020,28 +1211,33 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1020
1211
|
this.mc.config.getNumber(maxConsecutiveReconnectsKey) ?? this.defaultMaxConsecutiveReconnects;
|
|
1021
1212
|
|
|
1022
1213
|
this._flushMode = runtimeOptions.flushMode;
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1214
|
+
|
|
1215
|
+
const pendingRuntimeState = context.pendingLocalState as IPendingRuntimeState | undefined;
|
|
1216
|
+
const baseSnapshot: ISnapshotTree | undefined = pendingRuntimeState?.baseSnapshot ?? context.baseSnapshot;
|
|
1217
|
+
|
|
1218
|
+
this.garbageCollector = GarbageCollector.create({
|
|
1219
|
+
runtime: this,
|
|
1220
|
+
gcOptions: this.runtimeOptions.gcOptions,
|
|
1221
|
+
baseSnapshot,
|
|
1222
|
+
baseLogger: this.mc.logger,
|
|
1031
1223
|
existing,
|
|
1032
1224
|
metadata,
|
|
1033
|
-
this.context.clientDetails.type === summarizerClientType,
|
|
1034
|
-
|
|
1225
|
+
isSummarizerClient: this.context.clientDetails.type === summarizerClientType,
|
|
1226
|
+
getNodePackagePath: (nodePath: string) => this.getGCNodePackagePath(nodePath),
|
|
1227
|
+
getLastSummaryTimestampMs: () => this.messageAtLastSummary?.timestamp,
|
|
1228
|
+
readAndParseBlob: async <T>(id: string) => readAndParse<T>(this.storage, id),
|
|
1229
|
+
});
|
|
1035
1230
|
|
|
1036
1231
|
const loadedFromSequenceNumber = this.deltaManager.initialSequenceNumber;
|
|
1037
1232
|
this.summarizerNode = createRootSummarizerNodeWithGC(
|
|
1038
1233
|
ChildLogger.create(this.logger, "SummarizerNode"),
|
|
1039
1234
|
// Summarize function to call when summarize is called. Summarizer node always tracks summary state.
|
|
1040
|
-
async (fullTree: boolean, trackState: boolean) =>
|
|
1235
|
+
async (fullTree: boolean, trackState: boolean, telemetryContext?: ITelemetryContext) =>
|
|
1236
|
+
this.summarizeInternal(fullTree, trackState, telemetryContext),
|
|
1041
1237
|
// Latest change sequence number, no changes since summary applied yet
|
|
1042
1238
|
loadedFromSequenceNumber,
|
|
1043
1239
|
// Summary reference sequence number, undefined if no summary yet
|
|
1044
|
-
|
|
1240
|
+
baseSnapshot ? loadedFromSequenceNumber : undefined,
|
|
1045
1241
|
{
|
|
1046
1242
|
// Must set to false to prevent sending summary handle which would be pointing to
|
|
1047
1243
|
// a summary with an older protocol state.
|
|
@@ -1054,12 +1250,12 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1054
1250
|
},
|
|
1055
1251
|
);
|
|
1056
1252
|
|
|
1057
|
-
if (
|
|
1058
|
-
this.summarizerNode.loadBaseSummaryWithoutDifferential(
|
|
1253
|
+
if (baseSnapshot) {
|
|
1254
|
+
this.summarizerNode.loadBaseSummaryWithoutDifferential(baseSnapshot);
|
|
1059
1255
|
}
|
|
1060
1256
|
|
|
1061
1257
|
this.dataStores = new DataStores(
|
|
1062
|
-
getSummaryForDatastores(
|
|
1258
|
+
getSummaryForDatastores(baseSnapshot, metadata),
|
|
1063
1259
|
this,
|
|
1064
1260
|
(attachMsg) => this.submit(ContainerMessageType.Attach, attachMsg),
|
|
1065
1261
|
(id: string, createParam: CreateChildSummarizerNodeParam) => (
|
|
@@ -1106,10 +1302,19 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1106
1302
|
this.deltaSender = this.deltaManager;
|
|
1107
1303
|
|
|
1108
1304
|
this.pendingStateManager = new PendingStateManager(
|
|
1109
|
-
|
|
1110
|
-
|
|
1305
|
+
{
|
|
1306
|
+
applyStashedOp: this.applyStashedOp.bind(this),
|
|
1307
|
+
clientId: () => this.clientId,
|
|
1308
|
+
close: this.closeFn,
|
|
1309
|
+
connected: () => this.connected,
|
|
1310
|
+
flush: this.flush.bind(this),
|
|
1311
|
+
flushMode: () => this.flushMode,
|
|
1312
|
+
reSubmit: this.reSubmit.bind(this),
|
|
1313
|
+
rollback: this.rollback.bind(this),
|
|
1314
|
+
setFlushMode: (mode) => this.setFlushMode(mode),
|
|
1315
|
+
},
|
|
1111
1316
|
this._flushMode,
|
|
1112
|
-
|
|
1317
|
+
pendingRuntimeState?.pending);
|
|
1113
1318
|
|
|
1114
1319
|
this.context.quorum.on("removeMember", (clientId: string) => {
|
|
1115
1320
|
this.clearPartialChunks(clientId);
|
|
@@ -1117,16 +1322,10 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1117
1322
|
|
|
1118
1323
|
this.summaryCollection = new SummaryCollection(this.deltaManager, this.logger);
|
|
1119
1324
|
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|| (pendingLocalState as IPendingLocalState)?.pendingStates.length > 0;
|
|
1325
|
+
this.dirtyContainer = this.context.attachState !== AttachState.Attached
|
|
1326
|
+
|| this.pendingStateManager.hasPendingMessages();
|
|
1123
1327
|
this.context.updateDirtyContainerState(this.dirtyContainer);
|
|
1124
1328
|
|
|
1125
|
-
// Map the deprecated generateSummaries flag to disableSummaries.
|
|
1126
|
-
if (this.runtimeOptions.summaryOptions.generateSummaries === false) {
|
|
1127
|
-
this.runtimeOptions.summaryOptions.disableSummaries = true;
|
|
1128
|
-
}
|
|
1129
|
-
|
|
1130
1329
|
if (this.summariesDisabled) {
|
|
1131
1330
|
this.mc.logger.sendTelemetryEvent({ eventName: "SummariesDisabled" });
|
|
1132
1331
|
} else {
|
|
@@ -1143,16 +1342,13 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1143
1342
|
electedSummarizerData ?? this.context.deltaManager.lastSequenceNumber,
|
|
1144
1343
|
SummarizerClientElection.isClientEligible,
|
|
1145
1344
|
);
|
|
1146
|
-
|
|
1147
|
-
this.mc.config.getBoolean("Fluid.ContainerRuntime.summarizerClientElection") ??
|
|
1148
|
-
this.runtimeOptions.summaryOptions?.summarizerClientElection === true;
|
|
1149
|
-
const maxOpsSinceLastSummary = this.runtimeOptions.summaryOptions.maxOpsSinceLastSummary ?? 7000;
|
|
1345
|
+
|
|
1150
1346
|
this.summarizerClientElection = new SummarizerClientElection(
|
|
1151
1347
|
orderedClientLogger,
|
|
1152
1348
|
this.summaryCollection,
|
|
1153
1349
|
orderedClientElectionForSummarizer,
|
|
1154
|
-
maxOpsSinceLastSummary,
|
|
1155
|
-
summarizerClientElectionEnabled,
|
|
1350
|
+
this.maxOpsSinceLastSummary,
|
|
1351
|
+
this.summarizerClientElectionEnabled,
|
|
1156
1352
|
);
|
|
1157
1353
|
|
|
1158
1354
|
if (this.context.clientDetails.type === summarizerClientType) {
|
|
@@ -1169,7 +1365,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1169
1365
|
// Only create a SummaryManager and SummarizerClientElection
|
|
1170
1366
|
// if summaries are enabled and we are not the summarizer client.
|
|
1171
1367
|
const defaultAction = () => {
|
|
1172
|
-
if (this.summaryCollection.opsSinceLastAck > maxOpsSinceLastSummary) {
|
|
1368
|
+
if (this.summaryCollection.opsSinceLastAck > this.maxOpsSinceLastSummary) {
|
|
1173
1369
|
this.logger.sendErrorEvent({ eventName: "SummaryStatus:Behind" });
|
|
1174
1370
|
// unregister default to no log on every op after falling behind
|
|
1175
1371
|
// and register summary ack handler to re-register this handler
|
|
@@ -1200,9 +1396,9 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1200
1396
|
formExponentialFn({ coefficient: 20, initialDelay: 0 }),
|
|
1201
1397
|
),
|
|
1202
1398
|
{
|
|
1203
|
-
initialDelayMs: this.
|
|
1399
|
+
initialDelayMs: this.initialSummarizerDelayMs,
|
|
1204
1400
|
},
|
|
1205
|
-
this.
|
|
1401
|
+
this.heuristicsDisabled,
|
|
1206
1402
|
);
|
|
1207
1403
|
this.summaryManager.start();
|
|
1208
1404
|
}
|
|
@@ -1231,10 +1427,6 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1231
1427
|
this.replayPendingStates();
|
|
1232
1428
|
});
|
|
1233
1429
|
|
|
1234
|
-
if (context.pendingLocalState !== undefined) {
|
|
1235
|
-
this.deltaManager.on("op", this.onOp);
|
|
1236
|
-
}
|
|
1237
|
-
|
|
1238
1430
|
// logging hardware telemetry
|
|
1239
1431
|
logger.sendTelemetryEvent({
|
|
1240
1432
|
eventName: "DeviceSpec",
|
|
@@ -1379,11 +1571,17 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1379
1571
|
}
|
|
1380
1572
|
}
|
|
1381
1573
|
|
|
1574
|
+
private internalId(maybeAlias: string): string {
|
|
1575
|
+
return this.dataStores.aliases().get(maybeAlias) ?? maybeAlias;
|
|
1576
|
+
}
|
|
1577
|
+
|
|
1382
1578
|
private async getDataStoreFromRequest(id: string, request: IRequest): Promise<IFluidRouter> {
|
|
1383
1579
|
const wait = typeof request.headers?.[RuntimeHeaders.wait] === "boolean"
|
|
1384
1580
|
? request.headers?.[RuntimeHeaders.wait]
|
|
1385
1581
|
: true;
|
|
1386
|
-
|
|
1582
|
+
|
|
1583
|
+
const internalId = this.internalId(id);
|
|
1584
|
+
const dataStoreContext = await this.dataStores.getDataStore(internalId, wait);
|
|
1387
1585
|
|
|
1388
1586
|
/**
|
|
1389
1587
|
* If GC should run and this an external app request with "externalRequest" header, we need to return
|
|
@@ -1439,6 +1637,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1439
1637
|
summaryTree: ISummaryTreeWithStats,
|
|
1440
1638
|
fullTree: boolean,
|
|
1441
1639
|
trackState: boolean,
|
|
1640
|
+
telemetryContext?: ITelemetryContext,
|
|
1442
1641
|
) {
|
|
1443
1642
|
this.addMetadataToSummary(summaryTree);
|
|
1444
1643
|
|
|
@@ -1465,7 +1664,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1465
1664
|
}
|
|
1466
1665
|
|
|
1467
1666
|
if (this.garbageCollector.writeDataAtRoot) {
|
|
1468
|
-
const gcSummary = this.garbageCollector.summarize(fullTree, trackState);
|
|
1667
|
+
const gcSummary = this.garbageCollector.summarize(fullTree, trackState, telemetryContext);
|
|
1469
1668
|
if (gcSummary !== undefined) {
|
|
1470
1669
|
addSummarizeResultToSummary(summaryTree, gcTreeKey, gcSummary);
|
|
1471
1670
|
}
|
|
@@ -1489,7 +1688,6 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1489
1688
|
return true;
|
|
1490
1689
|
}
|
|
1491
1690
|
|
|
1492
|
-
this.consecutiveReconnects++;
|
|
1493
1691
|
if (this.consecutiveReconnects === Math.floor(this.maxConsecutiveReconnects / 2)) {
|
|
1494
1692
|
// If we're halfway through the max reconnects, send an event in order
|
|
1495
1693
|
// to better identify false positives, if any. If the rate of this event
|
|
@@ -1498,6 +1696,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1498
1696
|
this.mc.logger.sendTelemetryEvent({
|
|
1499
1697
|
eventName: "ReconnectsWithNoProgress",
|
|
1500
1698
|
attempts: this.consecutiveReconnects,
|
|
1699
|
+
pendingMessages: this.pendingStateManager.pendingMessagesCount,
|
|
1501
1700
|
});
|
|
1502
1701
|
}
|
|
1503
1702
|
|
|
@@ -1538,28 +1737,6 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1538
1737
|
this.updateDocumentDirtyState(newState);
|
|
1539
1738
|
}
|
|
1540
1739
|
|
|
1541
|
-
/**
|
|
1542
|
-
* Used to apply stashed ops at their reference sequence number.
|
|
1543
|
-
* Normal op processing is synchronous, but applying stashed ops is async since the
|
|
1544
|
-
* data store may not be loaded yet, so we pause DeltaManager between ops.
|
|
1545
|
-
* It's also important that we see each op so we know all stashed ops have
|
|
1546
|
-
* been applied by "connected" event, but process() doesn't see system ops,
|
|
1547
|
-
* so we listen directly from DeltaManager instead.
|
|
1548
|
-
*/
|
|
1549
|
-
private readonly onOp = (op: ISequencedDocumentMessage) => {
|
|
1550
|
-
assert(!this.paused, 0x128 /* "Container should not already be paused before applying stashed ops" */);
|
|
1551
|
-
this.paused = true;
|
|
1552
|
-
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
1553
|
-
this.context.deltaManager.inbound.pause();
|
|
1554
|
-
const stashP = this.pendingStateManager.applyStashedOpsAt(op.sequenceNumber);
|
|
1555
|
-
stashP.then(() => {
|
|
1556
|
-
this.paused = false;
|
|
1557
|
-
this.context.deltaManager.inbound.resume();
|
|
1558
|
-
}, (error) => {
|
|
1559
|
-
this.closeFn(normalizeError(error));
|
|
1560
|
-
});
|
|
1561
|
-
};
|
|
1562
|
-
|
|
1563
1740
|
private async applyStashedOp(type: ContainerMessageType, op: ISequencedDocumentMessage): Promise<unknown> {
|
|
1564
1741
|
switch (type) {
|
|
1565
1742
|
case ContainerMessageType.FluidDataStoreOp:
|
|
@@ -1583,20 +1760,35 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1583
1760
|
|
|
1584
1761
|
// There might be no change of state due to Container calling this API after loading runtime.
|
|
1585
1762
|
const changeOfState = this._connected !== connected;
|
|
1763
|
+
const reconnection = changeOfState && connected;
|
|
1586
1764
|
this._connected = connected;
|
|
1587
1765
|
|
|
1588
|
-
if (
|
|
1589
|
-
this.
|
|
1590
|
-
this.
|
|
1766
|
+
if (!connected) {
|
|
1767
|
+
this._perfSignalData.signalsLost = 0;
|
|
1768
|
+
this._perfSignalData.signalTimestamp = 0;
|
|
1769
|
+
this._perfSignalData.trackingSignalSequenceNumber = undefined;
|
|
1770
|
+
}
|
|
1771
|
+
|
|
1772
|
+
if (reconnection) {
|
|
1773
|
+
this.consecutiveReconnects++;
|
|
1774
|
+
|
|
1591
1775
|
if (!this.shouldContinueReconnecting()) {
|
|
1592
|
-
this.closeFn(
|
|
1776
|
+
this.closeFn(
|
|
1593
1777
|
// pre-0.58 error message: MaxReconnectsWithNoProgress
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1778
|
+
DataProcessingError.create(
|
|
1779
|
+
"Runtime detected too many reconnects with no progress syncing local ops",
|
|
1780
|
+
"setConnectionState",
|
|
1781
|
+
undefined,
|
|
1782
|
+
{
|
|
1783
|
+
dataLoss: 1,
|
|
1784
|
+
attempts: this.consecutiveReconnects,
|
|
1785
|
+
pendingMessages: this.pendingStateManager.pendingMessagesCount,
|
|
1786
|
+
}));
|
|
1597
1787
|
return;
|
|
1598
1788
|
}
|
|
1789
|
+
}
|
|
1599
1790
|
|
|
1791
|
+
if (changeOfState) {
|
|
1600
1792
|
this.replayPendingStates();
|
|
1601
1793
|
}
|
|
1602
1794
|
|
|
@@ -1613,6 +1805,10 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1613
1805
|
return;
|
|
1614
1806
|
}
|
|
1615
1807
|
|
|
1808
|
+
if (this.mc.config.getBoolean("enableOfflineLoad") ?? this.runtimeOptions.enableOfflineLoad) {
|
|
1809
|
+
this.savedOps.push(messageArg);
|
|
1810
|
+
}
|
|
1811
|
+
|
|
1616
1812
|
// Do shallow copy of message, as methods below will modify it.
|
|
1617
1813
|
// There might be multiple container instances receiving same message
|
|
1618
1814
|
// We do not need to make deep copy, as each layer will just replace message.content itself,
|
|
@@ -1631,8 +1827,14 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1631
1827
|
// once all pieces are available
|
|
1632
1828
|
message = this.processRemoteChunkedMessage(message);
|
|
1633
1829
|
|
|
1634
|
-
|
|
1635
|
-
|
|
1830
|
+
let localOpMetadata: unknown;
|
|
1831
|
+
if (local) {
|
|
1832
|
+
// Call the PendingStateManager to process local messages.
|
|
1833
|
+
// Do not process local chunked ops until all pieces are available.
|
|
1834
|
+
if (message.type !== ContainerMessageType.ChunkedOp) {
|
|
1835
|
+
localOpMetadata = this.pendingStateManager.processPendingLocalMessage(message);
|
|
1836
|
+
}
|
|
1837
|
+
}
|
|
1636
1838
|
|
|
1637
1839
|
// If there are no more pending messages after processing a local message,
|
|
1638
1840
|
// the document is no longer dirty.
|
|
@@ -1642,14 +1844,13 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1642
1844
|
|
|
1643
1845
|
switch (message.type) {
|
|
1644
1846
|
case ContainerMessageType.Attach:
|
|
1645
|
-
this.dataStores.processAttachMessage(message, local
|
|
1847
|
+
this.dataStores.processAttachMessage(message, local);
|
|
1646
1848
|
break;
|
|
1647
1849
|
case ContainerMessageType.Alias:
|
|
1648
1850
|
this.processAliasMessage(message, localOpMetadata, local);
|
|
1649
1851
|
break;
|
|
1650
1852
|
case ContainerMessageType.FluidDataStoreOp:
|
|
1651
|
-
|
|
1652
|
-
this.dataStores.processFluidDataStoreOp(message, local || localAck, localOpMetadata);
|
|
1853
|
+
this.dataStores.processFluidDataStoreOp(message, local, localOpMetadata);
|
|
1653
1854
|
break;
|
|
1654
1855
|
case ContainerMessageType.BlobAttach:
|
|
1655
1856
|
assert(message?.metadata?.blobId, 0x12a /* "Missing blob id on metadata" */);
|
|
@@ -1681,6 +1882,22 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1681
1882
|
this.dataStores.processAliasMessage(message, localOpMetadata, local);
|
|
1682
1883
|
}
|
|
1683
1884
|
|
|
1885
|
+
/**
|
|
1886
|
+
* Emits the Signal event and update the perf signal data.
|
|
1887
|
+
* @param clientSignalSequenceNumber - is the client signal sequence number to be uploaded.
|
|
1888
|
+
*/
|
|
1889
|
+
private sendSignalTelemetryEvent(clientSignalSequenceNumber: number) {
|
|
1890
|
+
const duration = Date.now() - this._perfSignalData.signalTimestamp;
|
|
1891
|
+
this.logger.sendPerformanceEvent({
|
|
1892
|
+
eventName: "SignalLatency",
|
|
1893
|
+
duration,
|
|
1894
|
+
signalsLost: this._perfSignalData.signalsLost,
|
|
1895
|
+
});
|
|
1896
|
+
|
|
1897
|
+
this._perfSignalData.signalsLost = 0;
|
|
1898
|
+
this._perfSignalData.signalTimestamp = 0;
|
|
1899
|
+
}
|
|
1900
|
+
|
|
1684
1901
|
public processSignal(message: ISignalMessage, local: boolean) {
|
|
1685
1902
|
const envelope = message.content as ISignalEnvelope;
|
|
1686
1903
|
const transformed: IInboundSignalMessage = {
|
|
@@ -1689,6 +1906,26 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1689
1906
|
type: envelope.contents.type,
|
|
1690
1907
|
};
|
|
1691
1908
|
|
|
1909
|
+
// Only collect signal telemetry for messages sent by the current client.
|
|
1910
|
+
if (message.clientId === this.clientId && this.connected) {
|
|
1911
|
+
// Check to see if the signal was lost.
|
|
1912
|
+
if (this._perfSignalData.trackingSignalSequenceNumber !== undefined &&
|
|
1913
|
+
envelope.clientSignalSequenceNumber > this._perfSignalData.trackingSignalSequenceNumber) {
|
|
1914
|
+
this._perfSignalData.signalsLost++;
|
|
1915
|
+
this._perfSignalData.trackingSignalSequenceNumber = undefined;
|
|
1916
|
+
this.logger.sendErrorEvent({
|
|
1917
|
+
eventName: "SignalLost",
|
|
1918
|
+
type: envelope.contents.type,
|
|
1919
|
+
signalsLost: this._perfSignalData.signalsLost,
|
|
1920
|
+
trackingSequenceNumber: this._perfSignalData.trackingSignalSequenceNumber,
|
|
1921
|
+
clientSignalSequenceNumber: envelope.clientSignalSequenceNumber,
|
|
1922
|
+
});
|
|
1923
|
+
} else if (envelope.clientSignalSequenceNumber === this._perfSignalData.trackingSignalSequenceNumber) {
|
|
1924
|
+
this.sendSignalTelemetryEvent(envelope.clientSignalSequenceNumber);
|
|
1925
|
+
this._perfSignalData.trackingSignalSequenceNumber = undefined;
|
|
1926
|
+
}
|
|
1927
|
+
}
|
|
1928
|
+
|
|
1692
1929
|
if (envelope.address === undefined) {
|
|
1693
1930
|
// No address indicates a container signal message.
|
|
1694
1931
|
this.emit("signal", transformed, local);
|
|
@@ -1699,7 +1936,8 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1699
1936
|
}
|
|
1700
1937
|
|
|
1701
1938
|
public async getRootDataStore(id: string, wait = true): Promise<IFluidRouter> {
|
|
1702
|
-
const
|
|
1939
|
+
const internalId = this.internalId(id);
|
|
1940
|
+
const context = await this.dataStores.getDataStore(internalId, wait);
|
|
1703
1941
|
assert(await context.isRoot(), 0x12b /* "did not get root data store" */);
|
|
1704
1942
|
return context.realize();
|
|
1705
1943
|
}
|
|
@@ -1770,18 +2008,31 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1770
2008
|
const savedFlushMode = this.flushMode;
|
|
1771
2009
|
this.setFlushMode(FlushMode.TurnBased);
|
|
1772
2010
|
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
2011
|
+
try {
|
|
2012
|
+
this.trackOrderSequentiallyCalls(callback);
|
|
2013
|
+
this.flush();
|
|
2014
|
+
} finally {
|
|
2015
|
+
this.setFlushMode(savedFlushMode);
|
|
2016
|
+
}
|
|
1776
2017
|
}
|
|
1777
2018
|
|
|
1778
2019
|
private trackOrderSequentiallyCalls(callback: () => void): void {
|
|
2020
|
+
let checkpoint: { rollback: () => void; } | undefined;
|
|
2021
|
+
if (this.mc.config.getBoolean("Fluid.ContainerRuntime.EnableRollback")) {
|
|
2022
|
+
checkpoint = this.pendingStateManager.checkpoint();
|
|
2023
|
+
}
|
|
2024
|
+
|
|
1779
2025
|
try {
|
|
1780
2026
|
this._orderSequentiallyCalls++;
|
|
1781
2027
|
callback();
|
|
1782
2028
|
} catch (error) {
|
|
1783
|
-
|
|
1784
|
-
|
|
2029
|
+
if (checkpoint) {
|
|
2030
|
+
// This will throw and close the container if rollback fails
|
|
2031
|
+
checkpoint.rollback();
|
|
2032
|
+
} else {
|
|
2033
|
+
// pre-0.58 error message: orderSequentiallyCallbackException
|
|
2034
|
+
this.closeFn(new GenericError("orderSequentially callback exception", error));
|
|
2035
|
+
}
|
|
1785
2036
|
throw error; // throw the original error for the consumer of the runtime
|
|
1786
2037
|
} finally {
|
|
1787
2038
|
this._orderSequentiallyCalls--;
|
|
@@ -1816,7 +2067,13 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1816
2067
|
return fluidDataStore;
|
|
1817
2068
|
}
|
|
1818
2069
|
|
|
2070
|
+
/**
|
|
2071
|
+
* @deprecated - will be removed in an upcoming release. See #9660.
|
|
2072
|
+
*/
|
|
1819
2073
|
public async createRootDataStore(pkg: string | string[], rootDataStoreId: string): Promise<IFluidRouter> {
|
|
2074
|
+
if (rootDataStoreId.includes("/")) {
|
|
2075
|
+
throw new UsageError(`Id cannot contain slashes: '${rootDataStoreId}'`);
|
|
2076
|
+
}
|
|
1820
2077
|
return this._aliasingEnabled === true ?
|
|
1821
2078
|
this.createAndAliasDataStore(pkg, rootDataStoreId) :
|
|
1822
2079
|
this.createRootDataStoreLegacy(pkg, rootDataStoreId);
|
|
@@ -1861,6 +2118,9 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1861
2118
|
public createDetachedRootDataStore(
|
|
1862
2119
|
pkg: Readonly<string[]>,
|
|
1863
2120
|
rootDataStoreId: string): IFluidDataStoreContextDetached {
|
|
2121
|
+
if (rootDataStoreId.includes("/")) {
|
|
2122
|
+
throw new UsageError(`Id cannot contain slashes: '${rootDataStoreId}'`);
|
|
2123
|
+
}
|
|
1864
2124
|
return this.dataStores.createDetachedDataStoreCore(pkg, true, rootDataStoreId);
|
|
1865
2125
|
}
|
|
1866
2126
|
|
|
@@ -1958,6 +2218,24 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1958
2218
|
return true;
|
|
1959
2219
|
}
|
|
1960
2220
|
|
|
2221
|
+
private createNewSignalEnvelope(address: string | undefined, type: string, content: any): ISignalEnvelope {
|
|
2222
|
+
const newSequenceNumber = ++this._perfSignalData.signalSequenceNumber;
|
|
2223
|
+
const newEnvelope: ISignalEnvelope = {
|
|
2224
|
+
address,
|
|
2225
|
+
clientSignalSequenceNumber: newSequenceNumber,
|
|
2226
|
+
contents: { type, content },
|
|
2227
|
+
};
|
|
2228
|
+
|
|
2229
|
+
// We should not track any signals in case we already have a tracking number.
|
|
2230
|
+
if (newSequenceNumber % this.defaultTelemetrySignalSampleCount === 1 &&
|
|
2231
|
+
this._perfSignalData.trackingSignalSequenceNumber === undefined) {
|
|
2232
|
+
this._perfSignalData.signalTimestamp = Date.now();
|
|
2233
|
+
this._perfSignalData.trackingSignalSequenceNumber = newSequenceNumber;
|
|
2234
|
+
}
|
|
2235
|
+
|
|
2236
|
+
return newEnvelope;
|
|
2237
|
+
}
|
|
2238
|
+
|
|
1961
2239
|
/**
|
|
1962
2240
|
* Submits the signal to be sent to other clients.
|
|
1963
2241
|
* @param type - Type of the signal.
|
|
@@ -1965,13 +2243,13 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1965
2243
|
*/
|
|
1966
2244
|
public submitSignal(type: string, content: any) {
|
|
1967
2245
|
this.verifyNotClosed();
|
|
1968
|
-
const envelope
|
|
2246
|
+
const envelope = this.createNewSignalEnvelope(undefined /* address */, type, content);
|
|
1969
2247
|
return this.context.submitSignalFn(envelope);
|
|
1970
2248
|
}
|
|
1971
2249
|
|
|
1972
2250
|
public submitDataStoreSignal(address: string, type: string, content: any) {
|
|
1973
|
-
const envelope
|
|
1974
|
-
|
|
2251
|
+
const envelope = this.createNewSignalEnvelope(address, type, content);
|
|
2252
|
+
return this.context.submitSignalFn(envelope);
|
|
1975
2253
|
}
|
|
1976
2254
|
|
|
1977
2255
|
public setAttachState(attachState: AttachState.Attaching | AttachState.Attached): void {
|
|
@@ -1996,13 +2274,14 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1996
2274
|
* @param blobRedirectTable - A table passed during the attach process. While detached, blob upload is supported
|
|
1997
2275
|
* using IDs generated locally. After attach, these IDs cannot be used, so this table maps the old local IDs to the
|
|
1998
2276
|
* new storage IDs so requests can be redirected.
|
|
2277
|
+
* @param telemetryContext - summary data passed through the layers for telemetry purposes
|
|
1999
2278
|
*/
|
|
2000
|
-
public createSummary(blobRedirectTable?: Map<string, string
|
|
2279
|
+
public createSummary(blobRedirectTable?: Map<string, string>, telemetryContext?: ITelemetryContext): ISummaryTree {
|
|
2001
2280
|
if (blobRedirectTable) {
|
|
2002
2281
|
this.blobManager.setRedirectTable(blobRedirectTable);
|
|
2003
2282
|
}
|
|
2004
2283
|
|
|
2005
|
-
const summarizeResult = this.dataStores.createSummary();
|
|
2284
|
+
const summarizeResult = this.dataStores.createSummary(telemetryContext);
|
|
2006
2285
|
if (!this.disableIsolatedChannels) {
|
|
2007
2286
|
// Wrap data store summaries in .channels subtree.
|
|
2008
2287
|
wrapSummaryInChannelsTree(summarizeResult);
|
|
@@ -2010,7 +2289,9 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2010
2289
|
this.addContainerStateToSummary(
|
|
2011
2290
|
summarizeResult,
|
|
2012
2291
|
true /* fullTree */,
|
|
2013
|
-
false /* trackState
|
|
2292
|
+
false /* trackState */,
|
|
2293
|
+
telemetryContext,
|
|
2294
|
+
);
|
|
2014
2295
|
return summarizeResult.summary;
|
|
2015
2296
|
}
|
|
2016
2297
|
|
|
@@ -2024,8 +2305,12 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2024
2305
|
return this.context.getAbsoluteUrl(relativeUrl);
|
|
2025
2306
|
}
|
|
2026
2307
|
|
|
2027
|
-
private async summarizeInternal(
|
|
2028
|
-
|
|
2308
|
+
private async summarizeInternal(
|
|
2309
|
+
fullTree: boolean,
|
|
2310
|
+
trackState: boolean,
|
|
2311
|
+
telemetryContext?: ITelemetryContext,
|
|
2312
|
+
): Promise<ISummarizeInternalResult> {
|
|
2313
|
+
const summarizeResult = await this.dataStores.summarize(fullTree, trackState, telemetryContext);
|
|
2029
2314
|
let pathPartsForChildren: string[] | undefined;
|
|
2030
2315
|
|
|
2031
2316
|
if (!this.disableIsolatedChannels) {
|
|
@@ -2033,7 +2318,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2033
2318
|
wrapSummaryInChannelsTree(summarizeResult);
|
|
2034
2319
|
pathPartsForChildren = [channelsTreeName];
|
|
2035
2320
|
}
|
|
2036
|
-
this.addContainerStateToSummary(summarizeResult, fullTree, trackState);
|
|
2321
|
+
this.addContainerStateToSummary(summarizeResult, fullTree, trackState, telemetryContext);
|
|
2037
2322
|
return {
|
|
2038
2323
|
...summarizeResult,
|
|
2039
2324
|
id: "",
|
|
@@ -2074,7 +2359,10 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2074
2359
|
gcStats = await this.collectGarbage({ logger: summaryLogger, runSweep, fullGC });
|
|
2075
2360
|
}
|
|
2076
2361
|
|
|
2077
|
-
const
|
|
2362
|
+
const telemetryContext = new TelemetryContext();
|
|
2363
|
+
const { stats, summary } = await this.summarizerNode.summarize(fullTree, trackState, telemetryContext);
|
|
2364
|
+
|
|
2365
|
+
this.logger.sendTelemetryEvent({ eventName: "SummarizeTelemetry", details: telemetryContext.serialize() });
|
|
2078
2366
|
|
|
2079
2367
|
assert(summary.type === SummaryType.Tree,
|
|
2080
2368
|
0x12f /* "Container Runtime's summarize should always return a tree" */);
|
|
@@ -2531,9 +2819,6 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2531
2819
|
): void {
|
|
2532
2820
|
this.verifyNotClosed();
|
|
2533
2821
|
|
|
2534
|
-
if (this.context.pendingLocalState !== undefined) {
|
|
2535
|
-
this.closeFn(new GenericError("containerRuntimeSubmitWithPendingLocalState"));
|
|
2536
|
-
}
|
|
2537
2822
|
// There should be no ops in detached container state!
|
|
2538
2823
|
assert(this.attachState !== AttachState.Detached, 0x132 /* "sending ops in detached container" */);
|
|
2539
2824
|
|
|
@@ -2729,6 +3014,22 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2729
3014
|
}
|
|
2730
3015
|
}
|
|
2731
3016
|
|
|
3017
|
+
private rollback(
|
|
3018
|
+
type: ContainerMessageType,
|
|
3019
|
+
content: any,
|
|
3020
|
+
localOpMetadata: unknown,
|
|
3021
|
+
) {
|
|
3022
|
+
switch (type) {
|
|
3023
|
+
case ContainerMessageType.FluidDataStoreOp:
|
|
3024
|
+
// For operations, call rollbackDataStoreOp which will find the right store
|
|
3025
|
+
// and trigger rollback on it.
|
|
3026
|
+
this.dataStores.rollbackDataStoreOp(content, localOpMetadata);
|
|
3027
|
+
break;
|
|
3028
|
+
default:
|
|
3029
|
+
throw new Error(`Can't rollback ${type}`);
|
|
3030
|
+
}
|
|
3031
|
+
}
|
|
3032
|
+
|
|
2732
3033
|
/** Implementation of ISummarizerInternalsProvider.refreshLatestSummaryAck */
|
|
2733
3034
|
public async refreshLatestSummaryAck(
|
|
2734
3035
|
proposalHandle: string | undefined,
|
|
@@ -2807,8 +3108,43 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2807
3108
|
});
|
|
2808
3109
|
}
|
|
2809
3110
|
|
|
2810
|
-
public
|
|
2811
|
-
|
|
3111
|
+
public notifyAttaching(snapshot: ISnapshotTreeWithBlobContents) {
|
|
3112
|
+
if (this.mc.config.getBoolean("enableOfflineLoad") ?? this.runtimeOptions.enableOfflineLoad) {
|
|
3113
|
+
this.baseSnapshotBlobs = SerializedSnapshotStorage.serializeTreeWithBlobContents(snapshot);
|
|
3114
|
+
}
|
|
3115
|
+
}
|
|
3116
|
+
|
|
3117
|
+
public async getSnapshotBlobs(): Promise<void> {
|
|
3118
|
+
if (!(this.mc.config.getBoolean("enableOfflineLoad") ?? this.runtimeOptions.enableOfflineLoad) ||
|
|
3119
|
+
this.attachState !== AttachState.Attached || this.context.pendingLocalState) {
|
|
3120
|
+
return;
|
|
3121
|
+
}
|
|
3122
|
+
assert(!!this.context.baseSnapshot, 0x2e5 /* "Must have a base snapshot" */);
|
|
3123
|
+
this.baseSnapshotBlobs = await SerializedSnapshotStorage.serializeTree(this.context.baseSnapshot, this.storage);
|
|
3124
|
+
}
|
|
3125
|
+
|
|
3126
|
+
public getPendingLocalState(): IPendingRuntimeState {
|
|
3127
|
+
if (!(this.mc.config.getBoolean("enableOfflineLoad") ?? this.runtimeOptions.enableOfflineLoad)) {
|
|
3128
|
+
throw new UsageError("can't get state when offline load disabled");
|
|
3129
|
+
}
|
|
3130
|
+
|
|
3131
|
+
const previousPendingState = this.context.pendingLocalState as IPendingRuntimeState | undefined;
|
|
3132
|
+
if (previousPendingState) {
|
|
3133
|
+
return {
|
|
3134
|
+
pending: this.pendingStateManager.getLocalState(),
|
|
3135
|
+
snapshotBlobs: previousPendingState.snapshotBlobs,
|
|
3136
|
+
baseSnapshot: previousPendingState.baseSnapshot,
|
|
3137
|
+
savedOps: this.savedOps,
|
|
3138
|
+
};
|
|
3139
|
+
}
|
|
3140
|
+
assert(!!this.context.baseSnapshot, 0x2e6 /* "Must have a base snapshot" */);
|
|
3141
|
+
assert(!!this.baseSnapshotBlobs, 0x2e7 /* "Must serialize base snapshot blobs before getting runtime state" */);
|
|
3142
|
+
return {
|
|
3143
|
+
pending: this.pendingStateManager.getLocalState(),
|
|
3144
|
+
snapshotBlobs: this.baseSnapshotBlobs,
|
|
3145
|
+
baseSnapshot: this.context.baseSnapshot,
|
|
3146
|
+
savedOps: this.savedOps,
|
|
3147
|
+
};
|
|
2812
3148
|
}
|
|
2813
3149
|
|
|
2814
3150
|
public readonly summarizeOnDemand: ISummarizer["summarizeOnDemand"] = (...args) => {
|
|
@@ -2837,7 +3173,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2837
3173
|
// because it is a misuse of the API rather than an expected failure.
|
|
2838
3174
|
throw new UsageError(
|
|
2839
3175
|
`Can't summarize, disableSummaries: ${this.summariesDisabled}`,
|
|
2840
|
-
|
|
3176
|
+
);
|
|
2841
3177
|
}
|
|
2842
3178
|
};
|
|
2843
3179
|
|
|
@@ -2870,6 +3206,16 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2870
3206
|
return summarizer;
|
|
2871
3207
|
};
|
|
2872
3208
|
}
|
|
3209
|
+
|
|
3210
|
+
private async processSavedOps(state: IPendingRuntimeState) {
|
|
3211
|
+
for (const op of state.savedOps) {
|
|
3212
|
+
this.process(op, false);
|
|
3213
|
+
await this.pendingStateManager.applyStashedOpsAt(op.sequenceNumber);
|
|
3214
|
+
}
|
|
3215
|
+
// we may not have seen every sequence number (because of system ops) so apply everything once we
|
|
3216
|
+
// don't have any more saved ops
|
|
3217
|
+
await this.pendingStateManager.applyStashedOpsAt();
|
|
3218
|
+
}
|
|
2873
3219
|
}
|
|
2874
3220
|
|
|
2875
3221
|
/**
|