@fluidframework/container-runtime 2.111.0 → 2.112.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +27 -0
- package/container-runtime.test-files.tar +0 -0
- package/dist/blobManager/blobManager.d.ts +6 -3
- package/dist/blobManager/blobManager.d.ts.map +1 -1
- package/dist/blobManager/blobManager.js +19 -5
- package/dist/blobManager/blobManager.js.map +1 -1
- package/dist/containerRuntime.d.ts +31 -0
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +69 -10
- package/dist/containerRuntime.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/pendingStateManager.d.ts +7 -0
- package/dist/pendingStateManager.d.ts.map +1 -1
- package/dist/pendingStateManager.js +15 -0
- package/dist/pendingStateManager.js.map +1 -1
- package/dist/runtimeLayerCompatState.d.ts +1 -1
- package/lib/blobManager/blobManager.d.ts +6 -3
- package/lib/blobManager/blobManager.d.ts.map +1 -1
- package/lib/blobManager/blobManager.js +19 -5
- package/lib/blobManager/blobManager.js.map +1 -1
- package/lib/containerRuntime.d.ts +31 -0
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +69 -10
- package/lib/containerRuntime.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/pendingStateManager.d.ts +7 -0
- package/lib/pendingStateManager.d.ts.map +1 -1
- package/lib/pendingStateManager.js +15 -0
- package/lib/pendingStateManager.js.map +1 -1
- package/lib/runtimeLayerCompatState.d.ts +1 -1
- package/package.json +18 -18
- package/src/blobManager/blobManager.ts +20 -4
- package/src/containerRuntime.ts +78 -11
- package/src/packageVersion.ts +1 -1
- package/src/pendingStateManager.ts +16 -0
package/dist/containerRuntime.js
CHANGED
|
@@ -754,6 +754,7 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
754
754
|
this.rollbackStagedChange(runtimeOp, localOpMetadata);
|
|
755
755
|
});
|
|
756
756
|
this.updateDocumentDirtyState();
|
|
757
|
+
this.updateHasStagedChangesState();
|
|
757
758
|
return batchInfos;
|
|
758
759
|
}, "discard"),
|
|
759
760
|
commitChanges: (options) => {
|
|
@@ -761,10 +762,12 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
761
762
|
exitStagingMode(() => {
|
|
762
763
|
// Replay all staged batches in typical FIFO order.
|
|
763
764
|
// We'll be out of staging mode so they'll be sent to the service finally.
|
|
764
|
-
|
|
765
|
+
const batchInfos = this.pendingStateManager.replayPendingStates({
|
|
765
766
|
committingStagedBatches: true,
|
|
766
767
|
squash,
|
|
767
768
|
});
|
|
769
|
+
this.updateHasStagedChangesState();
|
|
770
|
+
return batchInfos;
|
|
768
771
|
}, "commit");
|
|
769
772
|
},
|
|
770
773
|
};
|
|
@@ -1226,6 +1229,8 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
1226
1229
|
// We haven't emitted dirty/saved yet, but this is the baseline so we know to emit when it changes
|
|
1227
1230
|
this.lastEmittedDirty = this.computeCurrentDirtyState();
|
|
1228
1231
|
context.updateDirtyContainerState(this.lastEmittedDirty);
|
|
1232
|
+
// We haven't emitted hasStagedChangesChanged yet, but this is the baseline so we know to emit when it changes
|
|
1233
|
+
this.lastEmittedHasStagedChanges = this.computeCurrentHasStagedChanges();
|
|
1229
1234
|
// Reference Sequence Number may have just changed, and it must be consistent across a batch,
|
|
1230
1235
|
// so we should flush now to clear the way for the next ops.
|
|
1231
1236
|
// NOTE: This will be redundant whenever CR.process was called for the op (since we flush there too) -
|
|
@@ -2009,11 +2014,13 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
2009
2014
|
const result = this.duplicateBatchDetector?.processInboundBatch(batchStart);
|
|
2010
2015
|
if (result?.duplicate === true) {
|
|
2011
2016
|
const error = new internal_8.DataCorruptionError("Duplicate batch - The same batch was sequenced twice", { batchId: batchStart.batchId });
|
|
2017
|
+
const batchIdExplicit = batchStart.batchId !== undefined;
|
|
2018
|
+
const otherBatchIdExplicit = result.otherBatchInfo?.batchIdExplicit ?? false;
|
|
2012
2019
|
this.mc.logger.sendTelemetryEvent({
|
|
2013
2020
|
eventName: "DuplicateBatch",
|
|
2014
2021
|
details: {
|
|
2015
2022
|
batchId: batchStart.batchId,
|
|
2016
|
-
batchIdExplicit
|
|
2023
|
+
batchIdExplicit,
|
|
2017
2024
|
clientId: batchStart.clientId,
|
|
2018
2025
|
batchStartCsn: batchStart.batchStartCsn,
|
|
2019
2026
|
size: inboundResult.length,
|
|
@@ -2024,7 +2031,7 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
2024
2031
|
// loaded from a summary snapshot rather than seen at runtime.
|
|
2025
2032
|
otherClientId: result.otherBatchInfo?.clientId,
|
|
2026
2033
|
otherBatchStartCsn: result.otherBatchInfo?.batchStartCsn,
|
|
2027
|
-
otherBatchIdExplicit
|
|
2034
|
+
otherBatchIdExplicit,
|
|
2028
2035
|
otherFromSnapshot: result.otherBatchInfo === undefined,
|
|
2029
2036
|
...(0, internal_8.extractSafePropertiesFromMessage)(batchStart.keyMessage),
|
|
2030
2037
|
// For grouped batches, `keyMessage` is one of the sub-messages produced by
|
|
@@ -2035,10 +2042,12 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
2035
2042
|
messageClientSequenceNumber: batchStart.batchStartCsn,
|
|
2036
2043
|
},
|
|
2037
2044
|
}, error);
|
|
2038
|
-
//
|
|
2039
|
-
//
|
|
2040
|
-
|
|
2041
|
-
|
|
2045
|
+
// Only throw the error if either the current batch or the other batch has an explicit batchId since that indicates a meaningful duplication scenario
|
|
2046
|
+
// coming from our batch readings rather than a server outage scenario.
|
|
2047
|
+
const shouldThrowOnDuplicate = batchIdExplicit || otherBatchIdExplicit;
|
|
2048
|
+
if (shouldThrowOnDuplicate) {
|
|
2049
|
+
throw error;
|
|
2050
|
+
}
|
|
2042
2051
|
}
|
|
2043
2052
|
}
|
|
2044
2053
|
// Reach out to PendingStateManager, either to zip localOpMetadata into the *local* message list,
|
|
@@ -2357,6 +2366,11 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
2357
2366
|
this.closeFn(error2);
|
|
2358
2367
|
throw error2;
|
|
2359
2368
|
}
|
|
2369
|
+
// Flushing moves any staged batch from the Outbox into the PendingStateManager. Since
|
|
2370
|
+
// computeCurrentHasStagedChanges() now also considers a non-empty Outbox in Staging Mode, the
|
|
2371
|
+
// externally-visible value shouldn't change here, but re-checking is cheap and keeps this
|
|
2372
|
+
// method robust to future changes in how the two are tracked.
|
|
2373
|
+
this.updateHasStagedChangesState();
|
|
2360
2374
|
}
|
|
2361
2375
|
/**
|
|
2362
2376
|
* {@inheritDoc @fluidframework/runtime-definitions#IContainerRuntimeBase.orderSequentially}
|
|
@@ -2499,6 +2513,34 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
2499
2513
|
this.pendingStateManager.hasPendingUserChanges() ||
|
|
2500
2514
|
this.outbox.containsUserChanges());
|
|
2501
2515
|
}
|
|
2516
|
+
/**
|
|
2517
|
+
* Returns true if there are any staged changes, i.e. changes submitted while in Staging Mode
|
|
2518
|
+
* (see {@link @fluidframework/runtime-definitions#IContainerRuntimeBaseInternal.enterStagingMode})
|
|
2519
|
+
* that have not yet been discarded or committed.
|
|
2520
|
+
*
|
|
2521
|
+
* @remarks This is distinct from {@link ContainerRuntime.isDirty}: a container may be dirty due to
|
|
2522
|
+
* ordinary unacknowledged local changes without having any staged changes.
|
|
2523
|
+
*/
|
|
2524
|
+
get hasStagedChanges() {
|
|
2525
|
+
// Rather than recomputing this in the moment, just regurgitate the last emitted state.
|
|
2526
|
+
return this.lastEmittedHasStagedChanges;
|
|
2527
|
+
}
|
|
2528
|
+
/**
|
|
2529
|
+
* Returns true if there are currently any staged (not yet discarded or committed) changes pending.
|
|
2530
|
+
*
|
|
2531
|
+
* @remarks While in Staging Mode, newly submitted ops sit in the Outbox until the next flush before
|
|
2532
|
+
* they're moved to the PendingStateManager (where `pendingStateManager.hasStagedChanges()` looks).
|
|
2533
|
+
* So we also check the Outbox here, otherwise there would be a window between submit and flush where
|
|
2534
|
+
* staged changes exist but this would incorrectly report false.
|
|
2535
|
+
*
|
|
2536
|
+
* @remarks We don't care about the type of ops in the Outbox here (unlike dirty state), just whether
|
|
2537
|
+
* there's anything queued at all, for consistency with how `pendingStateManager.hasStagedChanges()`
|
|
2538
|
+
* doesn't discriminate by op type either.
|
|
2539
|
+
*/
|
|
2540
|
+
computeCurrentHasStagedChanges() {
|
|
2541
|
+
return (this.pendingStateManager.hasStagedChanges() ||
|
|
2542
|
+
(this.inStagingMode && !this.outbox.isEmpty));
|
|
2543
|
+
}
|
|
2502
2544
|
/**
|
|
2503
2545
|
* Submits the signal to be sent to other clients.
|
|
2504
2546
|
* @param type - Type of the signal.
|
|
@@ -3103,12 +3145,28 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
3103
3145
|
*/
|
|
3104
3146
|
updateDocumentDirtyState() {
|
|
3105
3147
|
const dirty = this.computeCurrentDirtyState();
|
|
3106
|
-
if (this.lastEmittedDirty
|
|
3148
|
+
if (this.lastEmittedDirty !== dirty) {
|
|
3149
|
+
this.lastEmittedDirty = dirty;
|
|
3150
|
+
if (this.emitDirtyDocumentEvent) {
|
|
3151
|
+
this.emit(dirty ? "dirty" : "saved");
|
|
3152
|
+
}
|
|
3153
|
+
}
|
|
3154
|
+
}
|
|
3155
|
+
/**
|
|
3156
|
+
* Emit "hasStagedChangesChanged" if the current staged-changes state differs from what was last emitted.
|
|
3157
|
+
* This must be called explicitly at each place staged changes can be added or removed (submit, flush,
|
|
3158
|
+
* discardChanges, commitChanges) -- unlike {@link ContainerRuntime.updateDocumentDirtyState}, it is not
|
|
3159
|
+
* safe to call this unconditionally alongside dirty tracking, since most dirty-state transitions (e.g.
|
|
3160
|
+
* acking ops, reconnecting) can't affect staged changes.
|
|
3161
|
+
*/
|
|
3162
|
+
updateHasStagedChangesState() {
|
|
3163
|
+
const hasStagedChanges = this.computeCurrentHasStagedChanges();
|
|
3164
|
+
if (this.lastEmittedHasStagedChanges === hasStagedChanges) {
|
|
3107
3165
|
return;
|
|
3108
3166
|
}
|
|
3109
|
-
this.
|
|
3167
|
+
this.lastEmittedHasStagedChanges = hasStagedChanges;
|
|
3110
3168
|
if (this.emitDirtyDocumentEvent) {
|
|
3111
|
-
this.emit(
|
|
3169
|
+
this.emit("hasStagedChangesChanged", hasStagedChanges);
|
|
3112
3170
|
}
|
|
3113
3171
|
}
|
|
3114
3172
|
// Keep in sync with IFluidRootParentContextPrivate.submitMessage.
|
|
@@ -3243,6 +3301,7 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
3243
3301
|
throw dpe;
|
|
3244
3302
|
}
|
|
3245
3303
|
this.updateDocumentDirtyState();
|
|
3304
|
+
this.updateHasStagedChangesState();
|
|
3246
3305
|
}
|
|
3247
3306
|
scheduleFlush() {
|
|
3248
3307
|
// During staging mode, suppress automatic flush scheduling until the main batch
|