@fluidframework/container-runtime 2.0.0-internal.1.2.0.93071 → 2.0.0-internal.1.2.2
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/batchManager.d.ts +7 -2
- package/dist/batchManager.d.ts.map +1 -1
- package/dist/batchManager.js +19 -17
- package/dist/batchManager.js.map +1 -1
- package/dist/batchTracker.d.ts +1 -2
- package/dist/batchTracker.d.ts.map +1 -1
- package/dist/batchTracker.js +1 -2
- package/dist/batchTracker.js.map +1 -1
- package/dist/containerRuntime.d.ts +10 -5
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +87 -63
- package/dist/containerRuntime.js.map +1 -1
- package/dist/dataStoreContext.d.ts +14 -5
- package/dist/dataStoreContext.d.ts.map +1 -1
- package/dist/dataStoreContext.js +19 -11
- package/dist/dataStoreContext.js.map +1 -1
- package/dist/dataStores.d.ts +6 -2
- package/dist/dataStores.d.ts.map +1 -1
- package/dist/dataStores.js +7 -9
- package/dist/dataStores.js.map +1 -1
- package/dist/deltaScheduler.d.ts +6 -4
- package/dist/deltaScheduler.d.ts.map +1 -1
- package/dist/deltaScheduler.js +6 -4
- package/dist/deltaScheduler.js.map +1 -1
- package/dist/garbageCollection.d.ts +41 -12
- package/dist/garbageCollection.d.ts.map +1 -1
- package/dist/garbageCollection.js +176 -98
- package/dist/garbageCollection.js.map +1 -1
- package/dist/gcSweepReadyUsageDetection.d.ts +53 -0
- package/dist/gcSweepReadyUsageDetection.d.ts.map +1 -0
- package/dist/gcSweepReadyUsageDetection.js +135 -0
- package/dist/gcSweepReadyUsageDetection.js.map +1 -0
- package/dist/orderedClientElection.d.ts +28 -10
- package/dist/orderedClientElection.d.ts.map +1 -1
- package/dist/orderedClientElection.js +14 -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.map +1 -1
- package/dist/pendingStateManager.js +4 -2
- package/dist/pendingStateManager.js.map +1 -1
- package/dist/scheduleManager.d.ts +6 -3
- package/dist/scheduleManager.d.ts.map +1 -1
- package/dist/scheduleManager.js +21 -13
- package/dist/scheduleManager.js.map +1 -1
- package/dist/summarizerTypes.d.ts +13 -6
- package/dist/summarizerTypes.d.ts.map +1 -1
- package/dist/summarizerTypes.js.map +1 -1
- package/dist/summaryCollection.d.ts.map +1 -1
- package/dist/summaryCollection.js +3 -6
- package/dist/summaryCollection.js.map +1 -1
- package/dist/summaryManager.d.ts +2 -2
- package/dist/summaryManager.js +2 -2
- package/dist/summaryManager.js.map +1 -1
- package/lib/batchManager.d.ts +7 -2
- package/lib/batchManager.d.ts.map +1 -1
- package/lib/batchManager.js +19 -17
- package/lib/batchManager.js.map +1 -1
- package/lib/batchTracker.d.ts +1 -2
- package/lib/batchTracker.d.ts.map +1 -1
- package/lib/batchTracker.js +1 -2
- package/lib/batchTracker.js.map +1 -1
- package/lib/containerRuntime.d.ts +10 -5
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +87 -63
- package/lib/containerRuntime.js.map +1 -1
- package/lib/dataStoreContext.d.ts +14 -5
- package/lib/dataStoreContext.d.ts.map +1 -1
- package/lib/dataStoreContext.js +20 -12
- package/lib/dataStoreContext.js.map +1 -1
- package/lib/dataStores.d.ts +6 -2
- package/lib/dataStores.d.ts.map +1 -1
- package/lib/dataStores.js +7 -9
- package/lib/dataStores.js.map +1 -1
- package/lib/deltaScheduler.d.ts +6 -4
- package/lib/deltaScheduler.d.ts.map +1 -1
- package/lib/deltaScheduler.js +6 -4
- package/lib/deltaScheduler.js.map +1 -1
- package/lib/garbageCollection.d.ts +41 -12
- package/lib/garbageCollection.d.ts.map +1 -1
- package/lib/garbageCollection.js +175 -97
- package/lib/garbageCollection.js.map +1 -1
- package/lib/gcSweepReadyUsageDetection.d.ts +53 -0
- package/lib/gcSweepReadyUsageDetection.d.ts.map +1 -0
- package/lib/gcSweepReadyUsageDetection.js +130 -0
- package/lib/gcSweepReadyUsageDetection.js.map +1 -0
- package/lib/orderedClientElection.d.ts +28 -10
- package/lib/orderedClientElection.d.ts.map +1 -1
- package/lib/orderedClientElection.js +14 -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.map +1 -1
- package/lib/pendingStateManager.js +4 -2
- package/lib/pendingStateManager.js.map +1 -1
- package/lib/scheduleManager.d.ts +6 -3
- package/lib/scheduleManager.d.ts.map +1 -1
- package/lib/scheduleManager.js +22 -14
- package/lib/scheduleManager.js.map +1 -1
- package/lib/summarizerTypes.d.ts +13 -6
- package/lib/summarizerTypes.d.ts.map +1 -1
- package/lib/summarizerTypes.js.map +1 -1
- package/lib/summaryCollection.d.ts.map +1 -1
- package/lib/summaryCollection.js +3 -6
- package/lib/summaryCollection.js.map +1 -1
- package/lib/summaryManager.d.ts +2 -2
- package/lib/summaryManager.js +2 -2
- package/lib/summaryManager.js.map +1 -1
- package/package.json +19 -16
- package/src/batchManager.ts +22 -19
- package/src/batchTracker.ts +1 -2
- package/src/containerRuntime.ts +118 -80
- package/src/dataStoreContext.ts +20 -10
- package/src/dataStores.ts +7 -8
- package/src/deltaScheduler.ts +6 -4
- package/src/garbageCollection.ts +224 -134
- package/src/gcSweepReadyUsageDetection.ts +147 -0
- package/src/orderedClientElection.ts +31 -10
- package/src/packageVersion.ts +1 -1
- package/src/pendingStateManager.ts +4 -2
- package/src/scheduleManager.ts +30 -10
- package/src/summarizerTypes.ts +14 -6
- package/src/summaryCollection.ts +3 -5
- package/src/summaryManager.ts +2 -2
package/src/containerRuntime.ts
CHANGED
|
@@ -204,7 +204,7 @@ export interface ContainerRuntimeMessage {
|
|
|
204
204
|
|
|
205
205
|
export interface ISummaryBaseConfiguration {
|
|
206
206
|
/**
|
|
207
|
-
*
|
|
207
|
+
* Delay before first attempt to spawn summarizing container.
|
|
208
208
|
*/
|
|
209
209
|
initialSummarizerDelayMs: number;
|
|
210
210
|
|
|
@@ -540,9 +540,12 @@ export function isRuntimeMessage(message: ISequencedDocumentMessage): boolean {
|
|
|
540
540
|
|
|
541
541
|
/**
|
|
542
542
|
* Unpacks runtime messages
|
|
543
|
-
*
|
|
543
|
+
*
|
|
544
|
+
* @remarks This API makes no promises regarding backward-compatability. This is internal API.
|
|
544
545
|
* @param message - message (as it observed in storage / service)
|
|
545
546
|
* @returns unpacked runtime message
|
|
547
|
+
*
|
|
548
|
+
* @internal
|
|
546
549
|
*/
|
|
547
550
|
export function unpackRuntimeMessage(message: ISequencedDocumentMessage) {
|
|
548
551
|
if (message.type === MessageType.Operation) {
|
|
@@ -850,7 +853,14 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
850
853
|
private readonly scheduleManager: ScheduleManager;
|
|
851
854
|
private readonly blobManager: BlobManager;
|
|
852
855
|
private readonly pendingStateManager: PendingStateManager;
|
|
853
|
-
|
|
856
|
+
|
|
857
|
+
// Provide lower soft limit - we want to have some number of ops to get efficiency in compression & bandwidth usage,
|
|
858
|
+
// but at the same time we want to send these ops sooner, to reduce overall latency of processing a batch.
|
|
859
|
+
// So there is some ballance here, that depends on compression algorithm and its efficiency working with smaller
|
|
860
|
+
// payloads. That number represents final (compressed) bits (once compression is implemented).
|
|
861
|
+
private readonly pendingAttachBatch = new BatchManager(64 * 1024);
|
|
862
|
+
private readonly pendingBatch = new BatchManager();
|
|
863
|
+
|
|
854
864
|
private readonly garbageCollector: IGarbageCollector;
|
|
855
865
|
|
|
856
866
|
// Local copy of incomplete received chunks.
|
|
@@ -861,6 +871,10 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
861
871
|
/** The last message processed at the time of the last summary. */
|
|
862
872
|
private messageAtLastSummary: ISummaryMetadataMessage | undefined;
|
|
863
873
|
|
|
874
|
+
private get emptyBatch() {
|
|
875
|
+
return this.pendingBatch.empty && this.pendingAttachBatch.empty;
|
|
876
|
+
}
|
|
877
|
+
|
|
864
878
|
private get summarizer(): Summarizer {
|
|
865
879
|
assert(this._summarizer !== undefined, 0x257 /* "This is not summarizing container" */);
|
|
866
880
|
return this._summarizer;
|
|
@@ -896,11 +910,9 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
896
910
|
if (this.runtimeOptions.summaryOptions.summarizerClientElection === true) {
|
|
897
911
|
return true;
|
|
898
912
|
}
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
return false;
|
|
903
|
-
}
|
|
913
|
+
return this.summaryConfiguration.state !== "disabled"
|
|
914
|
+
? this.summaryConfiguration.summarizerClientElection === true
|
|
915
|
+
: false;
|
|
904
916
|
}
|
|
905
917
|
private readonly maxOpsSinceLastSummary: number;
|
|
906
918
|
private getMaxOpsSinceLastSummary(): number {
|
|
@@ -909,11 +921,9 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
909
921
|
if (this.runtimeOptions.summaryOptions.maxOpsSinceLastSummary !== undefined) {
|
|
910
922
|
return this.runtimeOptions.summaryOptions.maxOpsSinceLastSummary;
|
|
911
923
|
}
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
return 0;
|
|
916
|
-
}
|
|
924
|
+
return this.summaryConfiguration.state !== "disabled"
|
|
925
|
+
? this.summaryConfiguration.maxOpsSinceLastSummary
|
|
926
|
+
: 0;
|
|
917
927
|
}
|
|
918
928
|
|
|
919
929
|
private readonly initialSummarizerDelayMs: number;
|
|
@@ -923,11 +933,9 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
923
933
|
if (this.runtimeOptions.summaryOptions.initialSummarizerDelayMs !== undefined) {
|
|
924
934
|
return this.runtimeOptions.summaryOptions.initialSummarizerDelayMs;
|
|
925
935
|
}
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
return 0;
|
|
930
|
-
}
|
|
936
|
+
return this.summaryConfiguration.state !== "disabled"
|
|
937
|
+
? this.summaryConfiguration.initialSummarizerDelayMs
|
|
938
|
+
: 0;
|
|
931
939
|
}
|
|
932
940
|
|
|
933
941
|
private readonly createContainerMetadata: ICreateContainerMetadata;
|
|
@@ -998,6 +1006,8 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
998
1006
|
getNodePackagePath: async (nodePath: string) => this.getGCNodePackagePath(nodePath),
|
|
999
1007
|
getLastSummaryTimestampMs: () => this.messageAtLastSummary?.timestamp,
|
|
1000
1008
|
readAndParseBlob: async <T>(id: string) => readAndParse<T>(this.storage, id),
|
|
1009
|
+
getContainerDiagnosticId: () => this.context.id,
|
|
1010
|
+
activeConnection: () => this.deltaManager.active,
|
|
1001
1011
|
});
|
|
1002
1012
|
|
|
1003
1013
|
const loadedFromSequenceNumber = this.deltaManager.initialSequenceNumber;
|
|
@@ -1072,6 +1082,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1072
1082
|
this.scheduleManager = new ScheduleManager(
|
|
1073
1083
|
context.deltaManager,
|
|
1074
1084
|
this,
|
|
1085
|
+
() => this.clientId,
|
|
1075
1086
|
ChildLogger.create(this.logger, "ScheduleManager"),
|
|
1076
1087
|
);
|
|
1077
1088
|
|
|
@@ -1320,15 +1331,12 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1320
1331
|
|
|
1321
1332
|
if (id === BlobManager.basePath && requestParser.isLeaf(2)) {
|
|
1322
1333
|
const blob = await this.blobManager.getBlob(requestParser.pathParts[1]);
|
|
1323
|
-
|
|
1324
|
-
|
|
1334
|
+
return blob
|
|
1335
|
+
? {
|
|
1325
1336
|
status: 200,
|
|
1326
1337
|
mimeType: "fluid/object",
|
|
1327
1338
|
value: blob,
|
|
1328
|
-
};
|
|
1329
|
-
} else {
|
|
1330
|
-
return create404Response(request);
|
|
1331
|
-
}
|
|
1339
|
+
} : create404Response(request);
|
|
1332
1340
|
} else if (requestParser.pathParts.length > 0) {
|
|
1333
1341
|
const dataStore = await this.getDataStoreFromRequest(id, request);
|
|
1334
1342
|
const subRequest = requestParser.createSubRequest(1);
|
|
@@ -1575,7 +1583,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1575
1583
|
this._perfSignalData.trackingSignalSequenceNumber = undefined;
|
|
1576
1584
|
} else {
|
|
1577
1585
|
assert(this.attachState === AttachState.Attached,
|
|
1578
|
-
|
|
1586
|
+
0x3cd /* Connection is possible only if container exists in storage */);
|
|
1579
1587
|
}
|
|
1580
1588
|
|
|
1581
1589
|
// Fail while disconnected
|
|
@@ -1603,6 +1611,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1603
1611
|
}
|
|
1604
1612
|
|
|
1605
1613
|
this.dataStores.setConnectionState(connected, clientId);
|
|
1614
|
+
this.garbageCollector.setConnectionState(connected, clientId);
|
|
1606
1615
|
|
|
1607
1616
|
raiseConnectedEvent(this.mc.logger, this, connected, clientId);
|
|
1608
1617
|
}
|
|
@@ -1670,7 +1679,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1670
1679
|
case ContainerMessageType.Rejoin:
|
|
1671
1680
|
break;
|
|
1672
1681
|
default:
|
|
1673
|
-
assert(!runtimeMessage,
|
|
1682
|
+
assert(!runtimeMessage, 0x3ce /* Runtime message of unknown type */);
|
|
1674
1683
|
}
|
|
1675
1684
|
|
|
1676
1685
|
// For back-compat, notify only about runtime messages for now.
|
|
@@ -1791,10 +1800,10 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1791
1800
|
assert(this._orderSequentiallyCalls === 0,
|
|
1792
1801
|
0x24c /* "Cannot call `flush()` from `orderSequentially`'s callback" */);
|
|
1793
1802
|
|
|
1794
|
-
|
|
1795
|
-
this.flushBatch(
|
|
1803
|
+
this.flushBatch(this.pendingAttachBatch.popBatch());
|
|
1804
|
+
this.flushBatch(this.pendingBatch.popBatch());
|
|
1796
1805
|
|
|
1797
|
-
assert(this.
|
|
1806
|
+
assert(this.emptyBatch, 0x3cf /* reentrancy */);
|
|
1798
1807
|
}
|
|
1799
1808
|
|
|
1800
1809
|
protected flushBatch(batch: BatchMessage[]): void {
|
|
@@ -1843,7 +1852,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1843
1852
|
|
|
1844
1853
|
// Convert from clientSequenceNumber of last message in the batch to clientSequenceNumber of first message.
|
|
1845
1854
|
clientSequenceNumber -= batch.length - 1;
|
|
1846
|
-
assert(clientSequenceNumber >= 0,
|
|
1855
|
+
assert(clientSequenceNumber >= 0, 0x3d0 /* clientSequenceNumber can't be negative */);
|
|
1847
1856
|
}
|
|
1848
1857
|
|
|
1849
1858
|
// Let the PendingStateManager know that a message was submitted.
|
|
@@ -1888,7 +1897,10 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1888
1897
|
private trackOrderSequentiallyCalls(callback: () => void): void {
|
|
1889
1898
|
let checkpoint: { rollback: (action: (message: BatchMessage) => void) => void; } | undefined;
|
|
1890
1899
|
if (this.mc.config.getBoolean("Fluid.ContainerRuntime.EnableRollback")) {
|
|
1891
|
-
|
|
1900
|
+
// Note: we are not touching this.pendingAttachBatch here, for two reasons:
|
|
1901
|
+
// 1. It would not help, as we flush attach ops as they become available.
|
|
1902
|
+
// 2. There is no way to undo process of data store creation.
|
|
1903
|
+
checkpoint = this.pendingBatch.checkpoint();
|
|
1892
1904
|
}
|
|
1893
1905
|
|
|
1894
1906
|
try {
|
|
@@ -2270,7 +2282,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2270
2282
|
|
|
2271
2283
|
/**
|
|
2272
2284
|
* Runs garbage collection and updates the reference / used state of the nodes in the container.
|
|
2273
|
-
* @returns the statistics of the garbage collection run.
|
|
2285
|
+
* @returns the statistics of the garbage collection run; undefined if GC did not run.
|
|
2274
2286
|
*/
|
|
2275
2287
|
public async collectGarbage(
|
|
2276
2288
|
options: {
|
|
@@ -2281,7 +2293,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2281
2293
|
/** True to generate full GC data */
|
|
2282
2294
|
fullGC?: boolean;
|
|
2283
2295
|
},
|
|
2284
|
-
): Promise<IGCStats> {
|
|
2296
|
+
): Promise<IGCStats | undefined> {
|
|
2285
2297
|
return this.garbageCollector.collectGarbage(options);
|
|
2286
2298
|
}
|
|
2287
2299
|
|
|
@@ -2316,7 +2328,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2316
2328
|
},
|
|
2317
2329
|
);
|
|
2318
2330
|
|
|
2319
|
-
assert(this.
|
|
2331
|
+
assert(this.emptyBatch, 0x3d1 /* Can't trigger summary in the middle of a batch */);
|
|
2320
2332
|
|
|
2321
2333
|
let latestSnapshotVersionId: string | undefined;
|
|
2322
2334
|
if (refreshLatestAck) {
|
|
@@ -2407,7 +2419,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2407
2419
|
const forcedFullTree = this.garbageCollector.summaryStateNeedsReset;
|
|
2408
2420
|
try {
|
|
2409
2421
|
summarizeResult = await this.summarize({
|
|
2410
|
-
fullTree: fullTree
|
|
2422
|
+
fullTree: fullTree ?? forcedFullTree,
|
|
2411
2423
|
trackState: true,
|
|
2412
2424
|
summaryLogger: summaryNumberLogger,
|
|
2413
2425
|
runGC: this.garbageCollector.shouldRunGC,
|
|
@@ -2575,16 +2587,16 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2575
2587
|
}
|
|
2576
2588
|
|
|
2577
2589
|
private hasPendingMessages() {
|
|
2578
|
-
return this.pendingStateManager.hasPendingMessages() || !this.
|
|
2590
|
+
return this.pendingStateManager.hasPendingMessages() || !this.emptyBatch;
|
|
2579
2591
|
}
|
|
2580
2592
|
|
|
2581
2593
|
private updateDocumentDirtyState(dirty: boolean) {
|
|
2582
2594
|
if (this.attachState !== AttachState.Attached) {
|
|
2583
|
-
assert(dirty,
|
|
2595
|
+
assert(dirty, 0x3d2 /* Non-attached container is dirty */);
|
|
2584
2596
|
} else {
|
|
2585
2597
|
// Other way is not true = see this.isContainerMessageDirtyable()
|
|
2586
2598
|
assert(!dirty || this.hasPendingMessages(),
|
|
2587
|
-
|
|
2599
|
+
0x3d3 /* if doc is dirty, there has to be pending ops */);
|
|
2588
2600
|
}
|
|
2589
2601
|
|
|
2590
2602
|
if (this.dirtyContainer === dirty) {
|
|
@@ -2661,29 +2673,57 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2661
2673
|
// failure (batch is too large). Pushing them earlier and outside of main batch should alleviate
|
|
2662
2674
|
// these issues.
|
|
2663
2675
|
// Cons:
|
|
2664
|
-
// With large batches, relay service may throttle clients. Clients may disconnect while throttled.
|
|
2676
|
+
// 1. With large batches, relay service may throttle clients. Clients may disconnect while throttled.
|
|
2665
2677
|
// This change creates new possibility of a lot of newly created data stores never being referenced
|
|
2666
2678
|
// because client died before it had a change to submit the rest of the ops. This will create more
|
|
2667
2679
|
// garbage that needs to be collected leveraging GC (Garbage Collection) feature.
|
|
2680
|
+
// 2. Sending ops out of order means they are excluded from rollback functionality. This is not an issue
|
|
2681
|
+
// today as rollback can't undo creation of data store. To some extent not sending them is a bigger
|
|
2682
|
+
// issue than sending.
|
|
2668
2683
|
// Please note that this does not change file format, so it can be disabled in the future if this
|
|
2669
2684
|
// optimization no longer makes sense (for example, batch compression may make it less appealing).
|
|
2670
|
-
if (
|
|
2671
|
-
|
|
2672
|
-
this.
|
|
2685
|
+
if (type === ContainerMessageType.Attach &&
|
|
2686
|
+
this.mc.config.getBoolean("Fluid.ContainerRuntime.disableAttachOpReorder") !== true) {
|
|
2687
|
+
if (!this.pendingAttachBatch.push(message)) {
|
|
2688
|
+
// BatchManager has two limits - soft limit & hard limit. Soft limit is only engaged
|
|
2689
|
+
// when queue is not empty.
|
|
2690
|
+
// Flush queue & retry. Failure on retry would mean - single message is bigger than hard limit
|
|
2691
|
+
this.flushBatch(this.pendingAttachBatch.popBatch());
|
|
2692
|
+
if (!this.pendingAttachBatch.push(message)) {
|
|
2693
|
+
throw new GenericError(
|
|
2694
|
+
"BatchTooLarge",
|
|
2695
|
+
/* error */ undefined,
|
|
2696
|
+
{
|
|
2697
|
+
opSize: message.contents.length,
|
|
2698
|
+
count: this.pendingAttachBatch.length,
|
|
2699
|
+
limit: this.pendingAttachBatch.limit,
|
|
2700
|
+
});
|
|
2701
|
+
}
|
|
2702
|
+
}
|
|
2673
2703
|
} else {
|
|
2674
|
-
this.
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
this.flush();
|
|
2684
|
-
});
|
|
2704
|
+
if (!this.pendingBatch.push(message)) {
|
|
2705
|
+
throw new GenericError(
|
|
2706
|
+
"BatchTooLarge",
|
|
2707
|
+
/* error */ undefined,
|
|
2708
|
+
{
|
|
2709
|
+
opSize: message.contents.length,
|
|
2710
|
+
count: this.pendingBatch.length,
|
|
2711
|
+
limit: this.pendingBatch.limit,
|
|
2712
|
+
});
|
|
2685
2713
|
}
|
|
2686
2714
|
}
|
|
2715
|
+
|
|
2716
|
+
if (this._flushMode !== FlushMode.TurnBased) {
|
|
2717
|
+
this.flush();
|
|
2718
|
+
} else if (!this.flushTrigger) {
|
|
2719
|
+
this.flushTrigger = true;
|
|
2720
|
+
// Queue a microtask to detect the end of the turn and force a flush.
|
|
2721
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
2722
|
+
Promise.resolve().then(() => {
|
|
2723
|
+
this.flushTrigger = false;
|
|
2724
|
+
this.flush();
|
|
2725
|
+
});
|
|
2726
|
+
}
|
|
2687
2727
|
} catch (error) {
|
|
2688
2728
|
this.closeFn(error as GenericError);
|
|
2689
2729
|
throw error;
|
|
@@ -2699,17 +2739,15 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2699
2739
|
assert(this.connected, 0x133 /* "Container disconnected when trying to submit system message" */);
|
|
2700
2740
|
|
|
2701
2741
|
// System message should not be sent in the middle of the batch.
|
|
2702
|
-
assert(this.
|
|
2742
|
+
assert(this.emptyBatch, 0x3d4 /* System op in the middle of a batch */);
|
|
2703
2743
|
|
|
2704
2744
|
// back-compat: ADO #1385: Make this call unconditional in the future
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
return this.context.submitFn(
|
|
2745
|
+
return this.context.submitSummaryFn !== undefined
|
|
2746
|
+
? this.context.submitSummaryFn(contents)
|
|
2747
|
+
: this.context.submitFn(
|
|
2709
2748
|
MessageType.Summarize,
|
|
2710
2749
|
contents,
|
|
2711
|
-
false);
|
|
2712
|
-
}
|
|
2750
|
+
false);
|
|
2713
2751
|
}
|
|
2714
2752
|
|
|
2715
2753
|
/**
|
|
@@ -2785,25 +2823,25 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2785
2823
|
// It should only be done by the summarizerNode, if required.
|
|
2786
2824
|
const snapshotTreeFetcher = async () => {
|
|
2787
2825
|
const fetchResult = await this.fetchSnapshotFromStorage(
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2826
|
+
ackHandle,
|
|
2827
|
+
summaryLogger,
|
|
2828
|
+
{
|
|
2829
|
+
eventName: "RefreshLatestSummaryGetSnapshot",
|
|
2830
|
+
ackHandle,
|
|
2831
|
+
summaryRefSeq,
|
|
2832
|
+
fetchLatest: false,
|
|
2833
|
+
});
|
|
2834
|
+
return fetchResult.snapshotTree;
|
|
2835
|
+
};
|
|
2836
|
+
const result = await this.summarizerNode.refreshLatestSummary(
|
|
2837
|
+
proposalHandle,
|
|
2838
|
+
summaryRefSeq,
|
|
2839
|
+
snapshotTreeFetcher,
|
|
2840
|
+
readAndParseBlob,
|
|
2841
|
+
summaryLogger,
|
|
2842
|
+
);
|
|
2843
|
+
|
|
2844
|
+
// Notify the garbage collector so it can update its latest summary state.
|
|
2807
2845
|
await this.garbageCollector.latestSummaryStateRefreshed(result, readAndParseBlob);
|
|
2808
2846
|
}
|
|
2809
2847
|
|
|
@@ -2987,7 +3025,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2987
3025
|
|
|
2988
3026
|
// If it's not the case, we should take it into account when calculating dirty state.
|
|
2989
3027
|
assert(this.context.attachState === AttachState.Attached,
|
|
2990
|
-
|
|
3028
|
+
0x3d5 /* this function is called for attached containers only */);
|
|
2991
3029
|
if (!this.hasPendingMessages()) {
|
|
2992
3030
|
this.updateDocumentDirtyState(false);
|
|
2993
3031
|
}
|
package/src/dataStoreContext.ts
CHANGED
|
@@ -37,6 +37,7 @@ import {
|
|
|
37
37
|
IContainerRuntime,
|
|
38
38
|
} from "@fluidframework/container-runtime-definitions";
|
|
39
39
|
import {
|
|
40
|
+
BindState,
|
|
40
41
|
channelsTreeName,
|
|
41
42
|
CreateChildSummarizerNodeFn,
|
|
42
43
|
CreateChildSummarizerNodeParam,
|
|
@@ -224,9 +225,8 @@ export abstract class FluidDataStoreContext extends TypedEventEmitter<IFluidData
|
|
|
224
225
|
protected registry: IFluidDataStoreRegistry | undefined;
|
|
225
226
|
|
|
226
227
|
protected detachedRuntimeCreation = false;
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
private readonly bindToContext: () => void;
|
|
228
|
+
/** @deprecated - To be replaced by calling makeLocallyVisible directly */
|
|
229
|
+
public readonly bindToContext: () => void;
|
|
230
230
|
protected channel: IFluidDataStoreChannel | undefined;
|
|
231
231
|
private loaded = false;
|
|
232
232
|
protected pending: ISequencedDocumentMessage[] | undefined = [];
|
|
@@ -253,6 +253,7 @@ export abstract class FluidDataStoreContext extends TypedEventEmitter<IFluidData
|
|
|
253
253
|
constructor(
|
|
254
254
|
props: IFluidDataStoreContextProps,
|
|
255
255
|
private readonly existing: boolean,
|
|
256
|
+
private bindState: BindState, // Used to assert for state tracking purposes
|
|
256
257
|
public readonly isLocalDataStore: boolean,
|
|
257
258
|
private readonly makeLocallyVisibleFn: () => void,
|
|
258
259
|
) {
|
|
@@ -273,8 +274,11 @@ export abstract class FluidDataStoreContext extends TypedEventEmitter<IFluidData
|
|
|
273
274
|
this.containerRuntime.attachState : AttachState.Detached;
|
|
274
275
|
|
|
275
276
|
this.bindToContext = () => {
|
|
277
|
+
assert(this.bindState === BindState.NotBound, 0x13b /* "datastore context is already in bound state" */);
|
|
278
|
+
this.bindState = BindState.Binding;
|
|
276
279
|
assert(this.channel !== undefined, 0x13c /* "undefined channel on datastore context" */);
|
|
277
280
|
this.makeLocallyVisible();
|
|
281
|
+
this.bindState = BindState.Bound;
|
|
278
282
|
};
|
|
279
283
|
|
|
280
284
|
const thisSummarizeInternal =
|
|
@@ -515,12 +519,18 @@ export abstract class FluidDataStoreContext extends TypedEventEmitter<IFluidData
|
|
|
515
519
|
|
|
516
520
|
/**
|
|
517
521
|
* After GC has run, called to notify the data store of routes used in it. These are used for the following:
|
|
522
|
+
*
|
|
518
523
|
* 1. To identify if this data store is being referenced in the document or not.
|
|
524
|
+
*
|
|
519
525
|
* 2. To determine if it needs to re-summarize in case used routes changed since last summary.
|
|
526
|
+
*
|
|
520
527
|
* 3. These are added to the summary generated by the data store.
|
|
521
|
-
*
|
|
522
|
-
*
|
|
528
|
+
*
|
|
529
|
+
* 4. To notify child contexts of their used routes. This is done immediately if the data store is loaded.
|
|
530
|
+
* Else, it is done when realizing the data store.
|
|
531
|
+
*
|
|
523
532
|
* 5. To update the timestamp when this data store or any children are marked as unreferenced.
|
|
533
|
+
*
|
|
524
534
|
* @param usedRoutes - The routes that are used in this data store.
|
|
525
535
|
* @param gcTimestamp - The time when GC was run that generated these used routes. If any node becomes unreferenced
|
|
526
536
|
* as part of this GC run, this should be used to update the time when it happens.
|
|
@@ -776,6 +786,7 @@ export class RemoteFluidDataStoreContext extends FluidDataStoreContext {
|
|
|
776
786
|
super(
|
|
777
787
|
props,
|
|
778
788
|
true /* existing */,
|
|
789
|
+
BindState.Bound,
|
|
779
790
|
false /* isLocalDataStore */,
|
|
780
791
|
() => {
|
|
781
792
|
throw new Error("Already attached");
|
|
@@ -814,11 +825,9 @@ export class RemoteFluidDataStoreContext extends FluidDataStoreContext {
|
|
|
814
825
|
// For snapshotFormatVersion = "0.1" (1) or above, pkg is jsonified, otherwise it is just a string.
|
|
815
826
|
const formatVersion = getAttributesFormatVersion(attributes);
|
|
816
827
|
if (formatVersion < 1) {
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
pkgFromSnapshot = [attributes.pkg];
|
|
821
|
-
}
|
|
828
|
+
pkgFromSnapshot = attributes.pkg.startsWith("[\"") && attributes.pkg.endsWith("\"]")
|
|
829
|
+
? JSON.parse(attributes.pkg) as string[]
|
|
830
|
+
: [attributes.pkg];
|
|
822
831
|
} else {
|
|
823
832
|
pkgFromSnapshot = JSON.parse(attributes.pkg) as string[];
|
|
824
833
|
}
|
|
@@ -880,6 +889,7 @@ export class LocalFluidDataStoreContextBase extends FluidDataStoreContext {
|
|
|
880
889
|
super(
|
|
881
890
|
props,
|
|
882
891
|
props.snapshotTree !== undefined ? true : false /* existing */,
|
|
892
|
+
props.snapshotTree ? BindState.Bound : BindState.NotBound,
|
|
883
893
|
true /* isLocalDataStore */,
|
|
884
894
|
props.makeLocallyVisibleFn,
|
|
885
895
|
);
|
package/src/dataStores.ts
CHANGED
|
@@ -469,12 +469,7 @@ export class DataStores implements IDisposable {
|
|
|
469
469
|
}
|
|
470
470
|
|
|
471
471
|
public setAttachState(attachState: AttachState.Attaching | AttachState.Attached): void {
|
|
472
|
-
|
|
473
|
-
if (attachState === AttachState.Attaching) {
|
|
474
|
-
eventName = "attaching";
|
|
475
|
-
} else {
|
|
476
|
-
eventName = "attached";
|
|
477
|
-
}
|
|
472
|
+
const eventName = attachState === AttachState.Attaching ? "attaching" : "attached";
|
|
478
473
|
for (const [, context] of this.contexts) {
|
|
479
474
|
// Fire only for bounded stores.
|
|
480
475
|
if (!this.contexts.isNotBound(context.id)) {
|
|
@@ -566,11 +561,15 @@ export class DataStores implements IDisposable {
|
|
|
566
561
|
|
|
567
562
|
/**
|
|
568
563
|
* Generates data used for garbage collection. It does the following:
|
|
564
|
+
*
|
|
569
565
|
* 1. Calls into each child data store context to get its GC data.
|
|
566
|
+
*
|
|
570
567
|
* 2. Prefixes the child context's id to the GC nodes in the child's GC data. This makes sure that the node can be
|
|
571
|
-
*
|
|
568
|
+
* identified as belonging to the child.
|
|
569
|
+
*
|
|
572
570
|
* 3. Adds a GC node for this channel to the nodes received from the children. All these nodes together represent
|
|
573
|
-
*
|
|
571
|
+
* the GC data of this channel.
|
|
572
|
+
*
|
|
574
573
|
* @param fullGC - true to bypass optimizations and force full generation of GC data.
|
|
575
574
|
*/
|
|
576
575
|
public async getGCData(fullGC: boolean = false): Promise<IGarbageCollectionData> {
|
package/src/deltaScheduler.ts
CHANGED
|
@@ -18,12 +18,14 @@ import {
|
|
|
18
18
|
* DeltaScheduler is responsible for the scheduling of inbound delta queue in cases where there
|
|
19
19
|
* is more than one op a particular run of the queue. It does not schedule if there is just one
|
|
20
20
|
* op or just one batch in the run. It does the following two things:
|
|
21
|
+
*
|
|
21
22
|
* 1. If the ops have been processed for more than a specific amount of time, it pauses the queue
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
23
|
+
* and calls setTimeout to schedule a resume of the queue. This ensures that we don't block
|
|
24
|
+
* the JS thread for a long time processing ops synchronously (for example, when catching up
|
|
25
|
+
* ops right after boot or catching up ops / delayed realizing data stores by summarizer).
|
|
26
|
+
*
|
|
25
27
|
* 2. If we scheduled a particular run of the queue, it logs telemetry for the number of ops
|
|
26
|
-
*
|
|
28
|
+
* processed, the time and number of turns it took to process the ops.
|
|
27
29
|
*/
|
|
28
30
|
export class DeltaScheduler {
|
|
29
31
|
private readonly deltaManager: IDeltaManager<ISequencedDocumentMessage, IDocumentMessage>;
|