@fluidframework/container-runtime 2.118.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +68 -4
- package/README.md +53 -50
- package/{api-extractor.json → api-extractor/api-extractor-model.json} +1 -1
- package/api-report/container-runtime.legacy.alpha.api.md +3 -1
- package/api-report/container-runtime.legacy.beta.api.md +3 -1
- package/container-runtime.test-files.tar +0 -0
- package/dist/batchTracker.d.ts.map +1 -1
- package/dist/batchTracker.js +5 -1
- package/dist/batchTracker.js.map +1 -1
- package/dist/blobManager/blobManager.d.ts +1 -1
- package/dist/blobManager/blobManager.d.ts.map +1 -1
- package/dist/blobManager/blobManager.js +264 -231
- package/dist/blobManager/blobManager.js.map +1 -1
- package/dist/blobManager/blobManagerSnapSum.d.ts.map +1 -1
- package/dist/channelCollection.d.ts.map +1 -1
- package/dist/channelCollection.js +70 -58
- package/dist/channelCollection.js.map +1 -1
- package/dist/connectionTelemetry.js +38 -20
- package/dist/connectionTelemetry.js.map +1 -1
- package/dist/containerCompatibility.d.ts +3 -3
- package/dist/containerCompatibility.d.ts.map +1 -1
- package/dist/containerCompatibility.js +36 -45
- package/dist/containerCompatibility.js.map +1 -1
- package/dist/containerHandleContext.d.ts.map +1 -1
- package/dist/containerHandleContext.js +4 -0
- package/dist/containerHandleContext.js.map +1 -1
- package/dist/containerRuntime.d.ts +4 -4
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +346 -223
- package/dist/containerRuntime.js.map +1 -1
- package/dist/dataStore.d.ts.map +1 -1
- package/dist/dataStore.js +9 -1
- package/dist/dataStore.js.map +1 -1
- package/dist/dataStoreContext.d.ts.map +1 -1
- package/dist/dataStoreContext.js +184 -127
- package/dist/dataStoreContext.js.map +1 -1
- package/dist/dataStoreContexts.js +43 -42
- package/dist/dataStoreContexts.js.map +1 -1
- package/dist/dataStoreRegistry.js +1 -0
- package/dist/dataStoreRegistry.js.map +1 -1
- package/dist/deltaManagerProxies.js +5 -0
- package/dist/deltaManagerProxies.js.map +1 -1
- package/dist/deltaScheduler.js +104 -99
- package/dist/deltaScheduler.js.map +1 -1
- package/dist/error.js +2 -1
- package/dist/error.js.map +1 -1
- package/dist/gc/garbageCollection.js +85 -55
- package/dist/gc/garbageCollection.js.map +1 -1
- package/dist/gc/gcConfigs.js +2 -3
- package/dist/gc/gcConfigs.js.map +1 -1
- package/dist/gc/gcHelpers.js +12 -13
- package/dist/gc/gcHelpers.js.map +1 -1
- package/dist/gc/gcReferenceGraphAlgorithm.js +1 -2
- package/dist/gc/gcReferenceGraphAlgorithm.js.map +1 -1
- package/dist/gc/gcSummaryStateTracker.js +8 -3
- package/dist/gc/gcSummaryStateTracker.js.map +1 -1
- package/dist/gc/gcTelemetry.js +12 -5
- package/dist/gc/gcTelemetry.js.map +1 -1
- package/dist/gc/gcUnreferencedStateTracker.js +18 -1
- package/dist/gc/gcUnreferencedStateTracker.js.map +1 -1
- package/dist/inboundBatchAggregator.js +122 -117
- package/dist/inboundBatchAggregator.js.map +1 -1
- package/dist/metadata.d.ts.map +1 -1
- package/dist/metadata.js +3 -3
- package/dist/metadata.js.map +1 -1
- package/dist/opLifecycle/batchManager.d.ts.map +1 -1
- package/dist/opLifecycle/batchManager.js +11 -5
- package/dist/opLifecycle/batchManager.js.map +1 -1
- package/dist/opLifecycle/duplicateBatchDetector.js +18 -18
- package/dist/opLifecycle/duplicateBatchDetector.js.map +1 -1
- package/dist/opLifecycle/index.d.ts +2 -2
- package/dist/opLifecycle/index.d.ts.map +1 -1
- package/dist/opLifecycle/index.js +2 -1
- package/dist/opLifecycle/index.js.map +1 -1
- package/dist/opLifecycle/opCompressor.js +2 -1
- package/dist/opLifecycle/opCompressor.js.map +1 -1
- package/dist/opLifecycle/opDecompressor.js +10 -6
- package/dist/opLifecycle/opDecompressor.js.map +1 -1
- package/dist/opLifecycle/opGroupingManager.js +5 -3
- package/dist/opLifecycle/opGroupingManager.js.map +1 -1
- package/dist/opLifecycle/opSerialization.d.ts +11 -1
- package/dist/opLifecycle/opSerialization.d.ts.map +1 -1
- package/dist/opLifecycle/opSerialization.js +22 -6
- package/dist/opLifecycle/opSerialization.js.map +1 -1
- package/dist/opLifecycle/opSplitter.d.ts +17 -1
- package/dist/opLifecycle/opSplitter.d.ts.map +1 -1
- package/dist/opLifecycle/opSplitter.js +46 -9
- package/dist/opLifecycle/opSplitter.js.map +1 -1
- package/dist/opLifecycle/outbox.d.ts.map +1 -1
- package/dist/opLifecycle/outbox.js +17 -13
- package/dist/opLifecycle/outbox.js.map +1 -1
- package/dist/opLifecycle/remoteMessageProcessor.js +6 -3
- package/dist/opLifecycle/remoteMessageProcessor.js.map +1 -1
- package/dist/opProperties.d.ts.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.js +54 -47
- package/dist/pendingStateManager.js.map +1 -1
- package/dist/runCounter.js +10 -30
- package/dist/runCounter.js.map +1 -1
- package/dist/runtimeLayerCompatState.d.ts +1 -1
- package/dist/runtimeLayerCompatState.d.ts.map +1 -1
- package/dist/runtimeLayerCompatState.js +31 -11
- package/dist/runtimeLayerCompatState.js.map +1 -1
- package/dist/signalTelemetryProcessing.js +15 -17
- package/dist/signalTelemetryProcessing.js.map +1 -1
- package/dist/storageServiceWithAttachBlobs.js +2 -0
- package/dist/storageServiceWithAttachBlobs.js.map +1 -1
- package/dist/summary/documentSchema.d.ts.map +1 -1
- package/dist/summary/documentSchema.js +51 -13
- package/dist/summary/documentSchema.js.map +1 -1
- package/dist/summary/orderedClientElection.js +25 -17
- package/dist/summary/orderedClientElection.js.map +1 -1
- package/dist/summary/summarizerClientElection.d.ts.map +1 -1
- package/dist/summary/summarizerClientElection.js +18 -7
- package/dist/summary/summarizerClientElection.js.map +1 -1
- package/dist/summary/summarizerNode/summarizerNode.d.ts.map +1 -1
- package/dist/summary/summarizerNode/summarizerNode.js +22 -13
- package/dist/summary/summarizerNode/summarizerNode.js.map +1 -1
- package/dist/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
- package/dist/summary/summarizerNode/summarizerNodeWithGc.js +20 -6
- package/dist/summary/summarizerNode/summarizerNodeWithGc.js.map +1 -1
- package/dist/summary/summarizerUtils.d.ts.map +1 -1
- package/dist/summary/summarizerUtils.js +4 -3
- package/dist/summary/summarizerUtils.js.map +1 -1
- package/dist/summary/summaryCollection.js +23 -11
- package/dist/summary/summaryCollection.js.map +1 -1
- package/dist/summary/summaryDelayLoadedModule/runWhileConnectedCoordinator.js +4 -2
- package/dist/summary/summaryDelayLoadedModule/runWhileConnectedCoordinator.js.map +1 -1
- package/dist/summary/summaryDelayLoadedModule/runningSummarizer.d.ts.map +1 -1
- package/dist/summary/summaryDelayLoadedModule/runningSummarizer.js +65 -46
- package/dist/summary/summaryDelayLoadedModule/runningSummarizer.js.map +1 -1
- package/dist/summary/summaryDelayLoadedModule/summarizer.d.ts.map +1 -1
- package/dist/summary/summaryDelayLoadedModule/summarizer.js +15 -6
- package/dist/summary/summaryDelayLoadedModule/summarizer.js.map +1 -1
- package/dist/summary/summaryDelayLoadedModule/summarizerHeuristics.js +30 -25
- package/dist/summary/summaryDelayLoadedModule/summarizerHeuristics.js.map +1 -1
- package/dist/summary/summaryDelayLoadedModule/summaryGenerator.js +9 -0
- package/dist/summary/summaryDelayLoadedModule/summaryGenerator.js.map +1 -1
- package/dist/summary/summaryDelayLoadedModule/summaryResultBuilder.js +3 -5
- package/dist/summary/summaryDelayLoadedModule/summaryResultBuilder.js.map +1 -1
- package/dist/summary/summaryFormat.d.ts.map +1 -1
- package/dist/summary/summaryFormat.js +7 -7
- package/dist/summary/summaryFormat.js.map +1 -1
- package/dist/summary/summaryHelpers.js +5 -5
- package/dist/summary/summaryHelpers.js.map +1 -1
- package/dist/summary/summaryManager.js +66 -54
- package/dist/summary/summaryManager.js.map +1 -1
- package/dist/throttler.d.ts.map +1 -1
- package/dist/throttler.js +4 -1
- package/dist/throttler.js.map +1 -1
- package/dist/versionMarks/inboundBatch.d.ts +8 -6
- package/dist/versionMarks/inboundBatch.d.ts.map +1 -1
- package/dist/versionMarks/inboundBatch.js +11 -7
- package/dist/versionMarks/inboundBatch.js.map +1 -1
- package/dist/versionMarks/versionMarkResolver.d.ts +30 -23
- package/dist/versionMarks/versionMarkResolver.d.ts.map +1 -1
- package/dist/versionMarks/versionMarkResolver.js +112 -63
- package/dist/versionMarks/versionMarkResolver.js.map +1 -1
- package/lib/batchTracker.d.ts.map +1 -1
- package/lib/batchTracker.js +5 -1
- package/lib/batchTracker.js.map +1 -1
- package/lib/blobManager/blobManager.d.ts +1 -1
- package/lib/blobManager/blobManager.d.ts.map +1 -1
- package/lib/blobManager/blobManager.js +264 -231
- package/lib/blobManager/blobManager.js.map +1 -1
- package/lib/blobManager/blobManagerSnapSum.d.ts.map +1 -1
- package/lib/channelCollection.d.ts.map +1 -1
- package/lib/channelCollection.js +65 -53
- package/lib/channelCollection.js.map +1 -1
- package/lib/connectionTelemetry.js +36 -18
- package/lib/connectionTelemetry.js.map +1 -1
- package/lib/containerCompatibility.d.ts +3 -3
- package/lib/containerCompatibility.d.ts.map +1 -1
- package/lib/containerCompatibility.js +33 -42
- package/lib/containerCompatibility.js.map +1 -1
- package/lib/containerHandleContext.d.ts.map +1 -1
- package/lib/containerHandleContext.js +4 -0
- package/lib/containerHandleContext.js.map +1 -1
- package/lib/containerRuntime.d.ts +4 -4
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +341 -218
- package/lib/containerRuntime.js.map +1 -1
- package/lib/dataStore.d.ts.map +1 -1
- package/lib/dataStore.js +9 -1
- package/lib/dataStore.js.map +1 -1
- package/lib/dataStoreContext.d.ts.map +1 -1
- package/lib/dataStoreContext.js +182 -125
- package/lib/dataStoreContext.js.map +1 -1
- package/lib/dataStoreContexts.js +43 -42
- package/lib/dataStoreContexts.js.map +1 -1
- package/lib/dataStoreRegistry.js +1 -0
- package/lib/dataStoreRegistry.js.map +1 -1
- package/lib/deltaManagerProxies.js +5 -0
- package/lib/deltaManagerProxies.js.map +1 -1
- package/lib/deltaScheduler.js +104 -99
- package/lib/deltaScheduler.js.map +1 -1
- package/lib/error.js +2 -1
- package/lib/error.js.map +1 -1
- package/lib/gc/garbageCollection.js +85 -55
- package/lib/gc/garbageCollection.js.map +1 -1
- package/lib/gc/gcSummaryStateTracker.js +8 -3
- package/lib/gc/gcSummaryStateTracker.js.map +1 -1
- package/lib/gc/gcTelemetry.js +12 -5
- package/lib/gc/gcTelemetry.js.map +1 -1
- package/lib/gc/gcUnreferencedStateTracker.js +18 -1
- package/lib/gc/gcUnreferencedStateTracker.js.map +1 -1
- package/lib/inboundBatchAggregator.js +122 -117
- package/lib/inboundBatchAggregator.js.map +1 -1
- package/lib/legacy.d.ts +1 -1
- package/lib/legacyAlpha.d.ts +1 -1
- package/lib/metadata.d.ts.map +1 -1
- package/lib/opLifecycle/batchManager.d.ts.map +1 -1
- package/lib/opLifecycle/batchManager.js +8 -2
- package/lib/opLifecycle/batchManager.js.map +1 -1
- package/lib/opLifecycle/duplicateBatchDetector.js +18 -18
- package/lib/opLifecycle/duplicateBatchDetector.js.map +1 -1
- package/lib/opLifecycle/index.d.ts +2 -2
- package/lib/opLifecycle/index.d.ts.map +1 -1
- package/lib/opLifecycle/index.js +2 -2
- package/lib/opLifecycle/index.js.map +1 -1
- package/lib/opLifecycle/opCompressor.js +3 -2
- package/lib/opLifecycle/opCompressor.js.map +1 -1
- package/lib/opLifecycle/opDecompressor.js +10 -6
- package/lib/opLifecycle/opDecompressor.js.map +1 -1
- package/lib/opLifecycle/opGroupingManager.js +3 -1
- package/lib/opLifecycle/opGroupingManager.js.map +1 -1
- package/lib/opLifecycle/opSerialization.d.ts +11 -1
- package/lib/opLifecycle/opSerialization.d.ts.map +1 -1
- package/lib/opLifecycle/opSerialization.js +16 -0
- package/lib/opLifecycle/opSerialization.js.map +1 -1
- package/lib/opLifecycle/opSplitter.d.ts +17 -1
- package/lib/opLifecycle/opSplitter.d.ts.map +1 -1
- package/lib/opLifecycle/opSplitter.js +44 -7
- package/lib/opLifecycle/opSplitter.js.map +1 -1
- package/lib/opLifecycle/outbox.d.ts.map +1 -1
- package/lib/opLifecycle/outbox.js +14 -10
- package/lib/opLifecycle/outbox.js.map +1 -1
- package/lib/opLifecycle/remoteMessageProcessor.js +4 -1
- package/lib/opLifecycle/remoteMessageProcessor.js.map +1 -1
- package/lib/opProperties.d.ts.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.js +52 -45
- package/lib/pendingStateManager.js.map +1 -1
- package/lib/public.d.ts +1 -1
- package/lib/runCounter.js +10 -30
- package/lib/runCounter.js.map +1 -1
- package/lib/runtimeLayerCompatState.d.ts +1 -1
- package/lib/runtimeLayerCompatState.d.ts.map +1 -1
- package/lib/runtimeLayerCompatState.js +28 -8
- package/lib/runtimeLayerCompatState.js.map +1 -1
- package/lib/signalTelemetryProcessing.js +15 -17
- package/lib/signalTelemetryProcessing.js.map +1 -1
- package/lib/storageServiceWithAttachBlobs.js +2 -0
- package/lib/storageServiceWithAttachBlobs.js.map +1 -1
- package/lib/summary/documentSchema.d.ts.map +1 -1
- package/lib/summary/documentSchema.js +51 -13
- package/lib/summary/documentSchema.js.map +1 -1
- package/lib/summary/orderedClientElection.js +25 -17
- package/lib/summary/orderedClientElection.js.map +1 -1
- package/lib/summary/summarizerClientElection.d.ts.map +1 -1
- package/lib/summary/summarizerClientElection.js +18 -7
- package/lib/summary/summarizerClientElection.js.map +1 -1
- package/lib/summary/summarizerNode/summarizerNode.d.ts.map +1 -1
- package/lib/summary/summarizerNode/summarizerNode.js +22 -13
- package/lib/summary/summarizerNode/summarizerNode.js.map +1 -1
- package/lib/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
- package/lib/summary/summarizerNode/summarizerNodeWithGc.js +20 -6
- package/lib/summary/summarizerNode/summarizerNodeWithGc.js.map +1 -1
- package/lib/summary/summarizerUtils.d.ts.map +1 -1
- package/lib/summary/summarizerUtils.js +1 -0
- package/lib/summary/summarizerUtils.js.map +1 -1
- package/lib/summary/summaryCollection.js +23 -11
- package/lib/summary/summaryCollection.js.map +1 -1
- package/lib/summary/summaryDelayLoadedModule/runWhileConnectedCoordinator.js +4 -2
- package/lib/summary/summaryDelayLoadedModule/runWhileConnectedCoordinator.js.map +1 -1
- package/lib/summary/summaryDelayLoadedModule/runningSummarizer.d.ts.map +1 -1
- package/lib/summary/summaryDelayLoadedModule/runningSummarizer.js +65 -46
- package/lib/summary/summaryDelayLoadedModule/runningSummarizer.js.map +1 -1
- package/lib/summary/summaryDelayLoadedModule/summarizer.d.ts.map +1 -1
- package/lib/summary/summaryDelayLoadedModule/summarizer.js +15 -6
- package/lib/summary/summaryDelayLoadedModule/summarizer.js.map +1 -1
- package/lib/summary/summaryDelayLoadedModule/summarizerHeuristics.js +30 -25
- package/lib/summary/summaryDelayLoadedModule/summarizerHeuristics.js.map +1 -1
- package/lib/summary/summaryDelayLoadedModule/summaryGenerator.js +9 -0
- package/lib/summary/summaryDelayLoadedModule/summaryGenerator.js.map +1 -1
- package/lib/summary/summaryDelayLoadedModule/summaryResultBuilder.js +3 -5
- package/lib/summary/summaryDelayLoadedModule/summaryResultBuilder.js.map +1 -1
- package/lib/summary/summaryFormat.d.ts.map +1 -1
- package/lib/summary/summaryManager.js +66 -54
- package/lib/summary/summaryManager.js.map +1 -1
- package/lib/throttler.d.ts.map +1 -1
- package/lib/throttler.js +4 -1
- package/lib/throttler.js.map +1 -1
- package/lib/versionMarks/inboundBatch.d.ts +8 -6
- package/lib/versionMarks/inboundBatch.d.ts.map +1 -1
- package/lib/versionMarks/inboundBatch.js +10 -5
- package/lib/versionMarks/inboundBatch.js.map +1 -1
- package/lib/versionMarks/versionMarkResolver.d.ts +30 -23
- package/lib/versionMarks/versionMarkResolver.d.ts.map +1 -1
- package/lib/versionMarks/versionMarkResolver.js +112 -63
- package/lib/versionMarks/versionMarkResolver.js.map +1 -1
- package/package.json +60 -102
- package/src/containerCompatibility.ts +33 -42
- package/src/containerRuntime.ts +15 -12
- package/src/opLifecycle/index.ts +7 -1
- package/src/opLifecycle/opCompressor.ts +2 -2
- package/src/opLifecycle/opSerialization.ts +22 -1
- package/src/opLifecycle/opSplitter.ts +63 -6
- package/src/packageVersion.ts +1 -1
- package/src/runtimeLayerCompatState.ts +28 -21
- package/src/summary/documentSchema.ts +31 -6
- package/src/versionMarks/DEV.md +595 -157
- package/src/versionMarks/inboundBatch.ts +18 -8
- package/src/versionMarks/versionMarkResolver.ts +144 -75
- package/tsconfig.json +1 -1
- package/internal.d.ts +0 -11
- package/legacy/alpha.d.ts +0 -11
- package/legacy.d.ts +0 -11
- /package/api-extractor/{api-extractor.current.json → api-extractor-report.current.json} +0 -0
- /package/api-extractor/{api-extractor.legacy.json → api-extractor-report.legacy.json} +0 -0
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
7
|
exports.VersionMarkResolver = void 0;
|
|
8
8
|
const internal_1 = require("@fluidframework/core-utils/internal");
|
|
9
|
-
const metadata_js_1 = require("../metadata.js");
|
|
10
9
|
const inboundBatch_js_1 = require("./inboundBatch.js");
|
|
11
10
|
/**
|
|
12
11
|
* Resolves a mark's batchId to a global sequence number. The app owns mark storage and promotion; the
|
|
@@ -15,16 +14,17 @@ const inboundBatch_js_1 = require("./inboundBatch.js");
|
|
|
15
14
|
* @internal
|
|
16
15
|
*/
|
|
17
16
|
class VersionMarkResolver {
|
|
17
|
+
hooks;
|
|
18
|
+
resolvedBatchById = new Map();
|
|
19
|
+
batchListeners = new Set();
|
|
20
|
+
/**
|
|
21
|
+
* Sticky flag: false until the feature is first used this session (a pending capture or a listener).
|
|
22
|
+
* While false the runtime skips all per-inbound-batch tracking, so a container that never uses version
|
|
23
|
+
* marks pays nothing on the hot path (mirrors #22497 gating `DuplicateBatchDetector` on offline load).
|
|
24
|
+
*/
|
|
25
|
+
tracking = false;
|
|
18
26
|
constructor(hooks) {
|
|
19
27
|
this.hooks = hooks;
|
|
20
|
-
this.sequenceNumberByBatchId = new Map();
|
|
21
|
-
this.batchListeners = new Set();
|
|
22
|
-
/**
|
|
23
|
-
* Sticky flag: false until the feature is first used this session (a pending capture or a listener).
|
|
24
|
-
* While false the runtime skips all per-inbound-batch tracking, so a container that never uses version
|
|
25
|
-
* marks pays nothing on the hot path (mirrors #22497 gating `DuplicateBatchDetector` on offline load).
|
|
26
|
-
*/
|
|
27
|
-
this.tracking = false;
|
|
28
28
|
}
|
|
29
29
|
/** Whether inbound batches need tracking (see {@link tracking}). Read by the runtime to gate the hot path. */
|
|
30
30
|
get isTracking() {
|
|
@@ -34,50 +34,106 @@ class VersionMarkResolver {
|
|
|
34
34
|
// Seal the current batch so a just-submitted local edit is flushed into the pending state with a
|
|
35
35
|
// stable batchId (which is only assigned at flush) before we read it.
|
|
36
36
|
this.hooks.flushPendingBatch();
|
|
37
|
-
const
|
|
37
|
+
const referenceSequenceNumber = this.hooks.getCurrentSequenceNumber();
|
|
38
38
|
const batchId = this.hooks.getCurrentPendingBatchId();
|
|
39
|
+
let result;
|
|
39
40
|
if (batchId === undefined) {
|
|
40
41
|
// No unacked local batch: the mark already points at a durable sequence number.
|
|
41
|
-
|
|
42
|
+
result = {
|
|
43
|
+
kind: "resolved",
|
|
44
|
+
sequenceNumber: referenceSequenceNumber,
|
|
45
|
+
timestamp: this.hooks.getCurrentTimestamp(),
|
|
46
|
+
};
|
|
42
47
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
48
|
+
else {
|
|
49
|
+
// A pending mark needs its batch tracked so resolve() can promote it from the live map.
|
|
50
|
+
this.tracking = true;
|
|
51
|
+
// The pending batch is sequenced after the reference point, so its first possible sequence
|
|
52
|
+
// number is `referenceSequenceNumber + 1`. Store that as an inclusive lower bound so resolve()
|
|
53
|
+
// scans directly from it.
|
|
54
|
+
result = {
|
|
55
|
+
kind: "pending",
|
|
56
|
+
batchId,
|
|
57
|
+
sequenceNumberLowerBound: referenceSequenceNumber + 1,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
this.hooks.logger.sendTelemetryEvent({ eventName: "Capture", kind: result.kind });
|
|
61
|
+
return result;
|
|
46
62
|
}
|
|
47
63
|
async resolve(batchId, sequenceNumberLowerBound) {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
64
|
+
const startTime = Date.now();
|
|
65
|
+
// Track from here so inbound batches are recorded even without a prior capture/subscribe; otherwise
|
|
66
|
+
// a batch sequencing during the scan (or live, in the no-reader case) is skipped and unrecoverable.
|
|
67
|
+
this.tracking = true;
|
|
68
|
+
// Defaults cover the throw path (only the history scan can throw — e.g. an unpacker
|
|
69
|
+
// DataCorruptionError or the 0xd1c reader-contract assert): the Resolve event still fires via
|
|
70
|
+
// `finally`, with outcome "error".
|
|
71
|
+
let path = "history";
|
|
72
|
+
let outcome = "error";
|
|
73
|
+
let resolvedSequenceNumber;
|
|
74
|
+
try {
|
|
75
|
+
// Fast path: batch sequenced live this session.
|
|
76
|
+
const resolvedBatch = this.sessionResolutionFor(batchId);
|
|
77
|
+
if (resolvedBatch === undefined) {
|
|
78
|
+
const reader = this.hooks.getHistoricalOpReader?.();
|
|
79
|
+
if (reader === undefined) {
|
|
80
|
+
// No reader: the batch may still sequence live, so report pending.
|
|
81
|
+
path = "noReader";
|
|
82
|
+
outcome = "pending";
|
|
83
|
+
return { kind: "pending" };
|
|
84
|
+
}
|
|
85
|
+
// Otherwise scan history from the mark's reference point.
|
|
86
|
+
path = "history";
|
|
87
|
+
let result = await this.resolveFromHistory(reader, batchId, sequenceNumberLowerBound);
|
|
88
|
+
if (result.kind !== "resolved") {
|
|
89
|
+
// The batch may have sequenced live while the history scan was in progress. Prefer that
|
|
90
|
+
// authoritative in-session result over a stale history miss.
|
|
91
|
+
const liveResult = this.sessionResolutionFor(batchId);
|
|
92
|
+
if (liveResult !== undefined) {
|
|
93
|
+
path = "session";
|
|
94
|
+
result = { kind: "resolved", ...liveResult };
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
outcome = result.kind;
|
|
98
|
+
if (result.kind === "resolved") {
|
|
99
|
+
resolvedSequenceNumber = result.sequenceNumber;
|
|
100
|
+
}
|
|
101
|
+
return result;
|
|
102
|
+
}
|
|
103
|
+
path = "session";
|
|
104
|
+
outcome = "resolved";
|
|
105
|
+
resolvedSequenceNumber = resolvedBatch.sequenceNumber;
|
|
106
|
+
return { kind: "resolved", ...resolvedBatch };
|
|
52
107
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
108
|
+
finally {
|
|
109
|
+
this.hooks.logger.sendTelemetryEvent({
|
|
110
|
+
eventName: "Resolve",
|
|
111
|
+
outcome,
|
|
112
|
+
path,
|
|
113
|
+
durationMs: Date.now() - startTime,
|
|
114
|
+
...(resolvedSequenceNumber === undefined
|
|
115
|
+
? {}
|
|
116
|
+
: { sequenceNumber: resolvedSequenceNumber }),
|
|
117
|
+
});
|
|
58
118
|
}
|
|
59
|
-
return this.resolveFromHistory(reader, batchId, sequenceNumberLowerBound);
|
|
60
119
|
}
|
|
61
120
|
/**
|
|
62
|
-
* Scans sequenced ops
|
|
121
|
+
* Scans sequenced ops from `sequenceNumberLowerBound` (inclusive) for `batchId`, returning its last op's
|
|
63
122
|
* sequence number when found and aborting the fetch once found or exhausted. Each op is routed through
|
|
64
123
|
* the same unpack pipeline the live inbound path uses (chunk reassembly, ungroup, decompress) and
|
|
65
|
-
* {@link inboundVersionMarkUpdate} derives its batch identity, so
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
* See DEV.md.
|
|
124
|
+
* {@link inboundVersionMarkUpdate} derives its batch identity, so virtualized batches resolve here too.
|
|
125
|
+
* The injected historical unpacker owns virtualization details, including tolerating a requested range
|
|
126
|
+
* that begins within an incomplete chunk stream. On a miss, {@link classifyMiss} distinguishes
|
|
127
|
+
* `pending` (not sequenced yet) from `unresolvable` (ops trimmed).
|
|
70
128
|
*/
|
|
71
129
|
async resolveFromHistory(reader, batchId, sequenceNumberLowerBound) {
|
|
72
|
-
const from = sequenceNumberLowerBound
|
|
130
|
+
const from = sequenceNumberLowerBound;
|
|
73
131
|
const unpack = this.hooks.createHistoricalOpUnpacker?.();
|
|
74
132
|
const abortController = new AbortController();
|
|
75
133
|
try {
|
|
76
134
|
const stream = await reader.fetchMessages(from, undefined, abortController.signal);
|
|
77
135
|
let firstScannedSequenceNumber;
|
|
78
136
|
let carriedBatchId;
|
|
79
|
-
// Mirrors the processor's batch-in-progress state so we can drop an orphan end marker (below).
|
|
80
|
-
let inBatch = false;
|
|
81
137
|
while (true) {
|
|
82
138
|
const result = await stream.read();
|
|
83
139
|
if (result.done) {
|
|
@@ -85,31 +141,21 @@ class VersionMarkResolver {
|
|
|
85
141
|
}
|
|
86
142
|
for (const op of result.value) {
|
|
87
143
|
firstScannedSequenceNumber ??= op.sequenceNumber;
|
|
88
|
-
|
|
89
|
-
// The anchor (`sequenceNumberLowerBound + 1`) may land inside a batch whose start
|
|
90
|
-
// precedes the window; its orphan end marker would trip the processor's 0x9d5 assert.
|
|
91
|
-
// Drop the clipped tail. The target batch is fully in-window, so this can't skip it.
|
|
92
|
-
continue;
|
|
93
|
-
}
|
|
94
|
-
// undefined = a system op, or an incomplete chunk awaiting later fragments.
|
|
144
|
+
// undefined = a system op or virtualized input that has not produced a complete result.
|
|
95
145
|
const inboundResult = unpack?.(op);
|
|
96
146
|
if (inboundResult === undefined) {
|
|
97
147
|
continue;
|
|
98
148
|
}
|
|
99
|
-
// Track batch progress with the processor so the guard above skips only orphan ends.
|
|
100
|
-
if (inboundResult.type === "batchStartingMessage") {
|
|
101
|
-
inBatch = true;
|
|
102
|
-
}
|
|
103
|
-
else if (inboundResult.type === "nextBatchMessage" &&
|
|
104
|
-
inboundResult.batchEnd === true) {
|
|
105
|
-
inBatch = false;
|
|
106
|
-
}
|
|
107
149
|
// Derive batch identity exactly as the live inbound path does, carrying the id across a
|
|
108
150
|
// piecemeal batch's messages.
|
|
109
151
|
const update = (0, inboundBatch_js_1.inboundVersionMarkUpdate)(inboundResult, carriedBatchId);
|
|
110
152
|
carriedBatchId = update.carriedBatchId;
|
|
111
153
|
if (update.sequenced?.batchId === batchId) {
|
|
112
|
-
return {
|
|
154
|
+
return {
|
|
155
|
+
kind: "resolved",
|
|
156
|
+
sequenceNumber: update.sequenced.sequenceNumber,
|
|
157
|
+
timestamp: update.sequenced.timestamp,
|
|
158
|
+
};
|
|
113
159
|
}
|
|
114
160
|
}
|
|
115
161
|
}
|
|
@@ -130,7 +176,7 @@ class VersionMarkResolver {
|
|
|
130
176
|
(0, internal_1.assert)(firstScannedSequenceNumber === undefined || firstScannedSequenceNumber >= from, 0xd1c /* historical op reader returned an op before the requested range start */);
|
|
131
177
|
const tip = this.hooks.getCurrentSequenceNumber();
|
|
132
178
|
if (from > tip) {
|
|
133
|
-
// Nothing is sequenced at/after the mark's
|
|
179
|
+
// Nothing is sequenced at/after the mark's lower bound yet, so the batch cannot have landed.
|
|
134
180
|
return { kind: "pending" };
|
|
135
181
|
}
|
|
136
182
|
if (firstScannedSequenceNumber === undefined) {
|
|
@@ -140,10 +186,10 @@ class VersionMarkResolver {
|
|
|
140
186
|
return { kind: "unresolvable" };
|
|
141
187
|
}
|
|
142
188
|
if (firstScannedSequenceNumber > from) {
|
|
143
|
-
// A trim gap at the anchor: the mark's ops (
|
|
189
|
+
// A trim gap at the anchor: the mark's ops (at/after its lower bound) are gone.
|
|
144
190
|
return { kind: "unresolvable" };
|
|
145
191
|
}
|
|
146
|
-
// Ops are present from the
|
|
192
|
+
// Ops are present from the lower bound and the batch is not among them: not yet sequenced.
|
|
147
193
|
return { kind: "pending" };
|
|
148
194
|
}
|
|
149
195
|
onBatchSequenced(listener) {
|
|
@@ -154,21 +200,24 @@ class VersionMarkResolver {
|
|
|
154
200
|
this.batchListeners.delete(listener);
|
|
155
201
|
};
|
|
156
202
|
}
|
|
157
|
-
/** The
|
|
158
|
-
|
|
159
|
-
return this.
|
|
203
|
+
/** The resolved point for a batch seen live this session, or undefined. */
|
|
204
|
+
sessionResolutionFor(batchId) {
|
|
205
|
+
return this.resolvedBatchById.get(batchId);
|
|
160
206
|
}
|
|
161
207
|
/** Records an inbound batch and notifies listeners so any client can promote matching pending marks. */
|
|
162
|
-
processInboundBatch(batchId, sequenceNumber) {
|
|
163
|
-
const
|
|
164
|
-
if (
|
|
208
|
+
processInboundBatch(batchId, sequenceNumber, timestamp) {
|
|
209
|
+
const existingResolution = this.resolvedBatchById.get(batchId);
|
|
210
|
+
if (existingResolution !== undefined) {
|
|
211
|
+
// NOTE: It's possible (but exceedingly rare) that this is a spurious duplicated batch,
|
|
212
|
+
// with a different sequence number/timestamp.
|
|
213
|
+
// Rather than asserting or overwriting, we let the earlier version mark stand, it is sufficient.
|
|
165
214
|
return;
|
|
166
215
|
}
|
|
167
|
-
this.
|
|
216
|
+
this.resolvedBatchById.set(batchId, { sequenceNumber, timestamp });
|
|
168
217
|
this.evictBelowMinimumSequenceNumber();
|
|
169
218
|
for (const listener of this.batchListeners) {
|
|
170
219
|
try {
|
|
171
|
-
listener(batchId, sequenceNumber);
|
|
220
|
+
listener(batchId, sequenceNumber, timestamp);
|
|
172
221
|
}
|
|
173
222
|
catch (error) {
|
|
174
223
|
// Isolate each app listener (like the container's EventEmitterWithErrorHandling): one throw
|
|
@@ -186,11 +235,11 @@ class VersionMarkResolver {
|
|
|
186
235
|
*/
|
|
187
236
|
evictBelowMinimumSequenceNumber() {
|
|
188
237
|
const minimumSequenceNumber = this.hooks.getCurrentMinimumSequenceNumber();
|
|
189
|
-
for (const [id,
|
|
190
|
-
if (sequenceNumber >= minimumSequenceNumber) {
|
|
238
|
+
for (const [id, resolution] of this.resolvedBatchById) {
|
|
239
|
+
if (resolution.sequenceNumber >= minimumSequenceNumber) {
|
|
191
240
|
break;
|
|
192
241
|
}
|
|
193
|
-
this.
|
|
242
|
+
this.resolvedBatchById.delete(id);
|
|
194
243
|
}
|
|
195
244
|
}
|
|
196
245
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"versionMarkResolver.js","sourceRoot":"","sources":["../../src/versionMarks/versionMarkResolver.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport type {\n\tISequencedDocumentMessage,\n\tIStream,\n} from \"@fluidframework/driver-definitions/internal\";\n\nimport { assert } from \"@fluidframework/core-utils/internal\";\nimport type { TelemetryLoggerExt } from \"@fluidframework/telemetry-utils/internal\";\n\nimport type { InboundMessageResult } from \"../opLifecycle/index.js\";\nimport { asBatchMetadata } from \"../metadata.js\";\n\nimport { inboundVersionMarkUpdate } from \"./inboundBatch.js\";\n\n/**\n * Reads sequenced ops in `[from, to)` from delta storage, injected by the container so the runtime stays\n * driver-agnostic. `abortSignal` cancels an in-flight fetch.\n *\n * @internal\n */\nexport interface IHistoricalOpReader {\n\tfetchMessages(\n\t\tfrom: number,\n\t\tto?: number,\n\t\tabortSignal?: AbortSignal,\n\t): Promise<IStream<ISequencedDocumentMessage[]>>;\n}\n\n/**\n * Result of resolving a pending batchId.\n *\n * @legacy @beta\n */\nexport type ResolveResult =\n\t| { readonly kind: \"resolved\"; readonly sequenceNumber: number }\n\t| { readonly kind: \"pending\" }\n\t| { readonly kind: \"unresolvable\" };\n\n/**\n * The data captured for a version mark. `pending` when the captured edit is local and not yet sequenced\n * (resolve it later via {@link IVersionMarkResolver.resolve}); `resolved` when there is no in-flight local\n * work, so the mark already points at a durable sequence number. The app packs its own stored record from\n * this — the runtime does not define the stored locator shape.\n *\n * @legacy @beta\n */\nexport type VersionMarkCapture =\n\t| {\n\t\t\treadonly kind: \"pending\";\n\t\t\treadonly batchId: string;\n\t\t\treadonly sequenceNumberLowerBound: number;\n\t }\n\t| { readonly kind: \"resolved\"; readonly sequenceNumber: number };\n\n/**\n * Runtime-owned resolver for app-stored version mark locators.\n *\n * @legacy @beta\n */\nexport interface IVersionMarkResolver {\n\t/**\n\t * Captures a version mark at the current point. Seals the current outbound batch first (so a just-made\n\t * local edit has a stable `batchId`, which is only assigned when a batch is flushed), then returns the\n\t * mark data atomically: a `pending` capture (`batchId` + `sequenceNumberLowerBound`) when there is an\n\t * unacked local batch, or a `resolved` capture (`sequenceNumber`) when there is no in-flight local work.\n\t *\n\t * @remarks Sealing the batch is a side effect (it submits the current batch), so capture at savepoint\n\t * boundaries, not per keystroke.\n\t *\n\t * @returns The pending batch identity and exclusive sequence number lower bound, or the current sequence number\n\t * when there is no pending local batch.\n\t */\n\tsealAndCaptureVersionMark(): VersionMarkCapture;\n\t/**\n\t * Resolves a pending mark's batchId to a global sequence number (`sequenceNumberLowerBound` is the\n\t * exclusive lower bound for a history read). A `resolved` sequence number feeds the loader's\n\t * `loadContainerToSequenceNumber`.\n\t *\n\t * @param batchId - The stable identity of the pending batch.\n\t * @param sequenceNumberLowerBound - The exclusive lower bound for the historical op search.\n\t * @returns The resolved sequence number, or a result indicating that the batch remains pending or can\n\t * no longer be resolved.\n\t */\n\tresolve(batchId: string, sequenceNumberLowerBound: number): Promise<ResolveResult>;\n\t/**\n\t * Subscribes to inbound batch sequencing: fires `(batchId, sequenceNumber)` per batch so any connected\n\t * client can promote a matching pending mark. Returns an unsubscribe function.\n\t *\n\t * @param listener - Called with the stable batch identity and its final sequence number.\n\t * @returns A function that unsubscribes the listener.\n\t */\n\tonBatchSequenced(listener: (batchId: string, sequenceNumber: number) => void): () => void;\n}\n\n/**\n * @internal\n */\nexport interface VersionMarkResolverRuntimeHooks {\n\treadonly getCurrentSequenceNumber: () => number;\n\treadonly getCurrentMinimumSequenceNumber: () => number;\n\treadonly getCurrentPendingBatchId: () => string | undefined;\n\t/** Logs faults from app-supplied `onBatchSequenced` listeners (see {@link VersionMarkResolver.processInboundBatch}). */\n\treadonly logger: TelemetryLoggerExt;\n\t/**\n\t * Seals the current outbound batch (flushes the runtime), so a just-submitted local edit is moved into\n\t * the pending state with a stable `batchId` before\n\t * {@link IVersionMarkResolver.sealAndCaptureVersionMark} reads it.\n\t */\n\treadonly flushPendingBatch: () => void;\n\t/**\n\t * Injected by the container, backed by delta storage. When absent, unknown batchIds resolve as `pending`.\n\t */\n\treadonly getHistoricalOpReader?: () => IHistoricalOpReader | undefined;\n\t/**\n\t * Creates a per-scan op unpacker that mirrors the live inbound path (chunk reassembly, ungroup,\n\t * decompress), so the history scan observes the same restored `batchId` — in particular for chunked\n\t * batches, whose id is stripped from the final chunk's wire metadata and restored only after\n\t * reassembly. A fresh unpacker is created per scan since it holds reassembly state. When absent, the\n\t * history scan cannot identify batches and reports `pending`/`unresolvable`.\n\t */\n\treadonly createHistoricalOpUnpacker?: () => (\n\t\top: ISequencedDocumentMessage,\n\t) => InboundMessageResult | undefined;\n}\n\n/**\n * Resolves a mark's batchId to a global sequence number. The app owns mark storage and promotion; the\n * runtime exposes the capture point and an ephemeral in-session `batchId -> sequenceNumber` map.\n *\n * @internal\n */\nexport class VersionMarkResolver implements IVersionMarkResolver {\n\tprivate readonly sequenceNumberByBatchId = new Map<string, number>();\n\tprivate readonly batchListeners = new Set<\n\t\t(batchId: string, sequenceNumber: number) => void\n\t>();\n\t/**\n\t * Sticky flag: false until the feature is first used this session (a pending capture or a listener).\n\t * While false the runtime skips all per-inbound-batch tracking, so a container that never uses version\n\t * marks pays nothing on the hot path (mirrors #22497 gating `DuplicateBatchDetector` on offline load).\n\t */\n\tprivate tracking = false;\n\n\tpublic constructor(private readonly hooks: VersionMarkResolverRuntimeHooks) {}\n\n\t/** Whether inbound batches need tracking (see {@link tracking}). Read by the runtime to gate the hot path. */\n\tpublic get isTracking(): boolean {\n\t\treturn this.tracking;\n\t}\n\n\tpublic sealAndCaptureVersionMark(): VersionMarkCapture {\n\t\t// Seal the current batch so a just-submitted local edit is flushed into the pending state with a\n\t\t// stable batchId (which is only assigned at flush) before we read it.\n\t\tthis.hooks.flushPendingBatch();\n\t\tconst sequenceNumberLowerBound = this.hooks.getCurrentSequenceNumber();\n\t\tconst batchId = this.hooks.getCurrentPendingBatchId();\n\t\tif (batchId === undefined) {\n\t\t\t// No unacked local batch: the mark already points at a durable sequence number.\n\t\t\treturn { kind: \"resolved\", sequenceNumber: sequenceNumberLowerBound };\n\t\t}\n\t\t// A pending mark needs its batch tracked so resolve() can promote it from the live map.\n\t\tthis.tracking = true;\n\t\treturn { kind: \"pending\", batchId, sequenceNumberLowerBound };\n\t}\n\n\tpublic async resolve(\n\t\tbatchId: string,\n\t\tsequenceNumberLowerBound: number,\n\t): Promise<ResolveResult> {\n\t\t// Fast path: batch sequenced live this session.\n\t\tconst sequenceNumber = this.sessionSequenceFor(batchId);\n\t\tif (sequenceNumber !== undefined) {\n\t\t\treturn { kind: \"resolved\", sequenceNumber };\n\t\t}\n\n\t\t// Otherwise scan history from the mark's reference point.\n\t\tconst reader = this.hooks.getHistoricalOpReader?.();\n\t\tif (reader === undefined) {\n\t\t\t// No reader: the batch may still sequence live, so report pending.\n\t\t\treturn { kind: \"pending\" };\n\t\t}\n\t\treturn this.resolveFromHistory(reader, batchId, sequenceNumberLowerBound);\n\t}\n\n\t/**\n\t * Scans sequenced ops after `sequenceNumberLowerBound` for `batchId`, returning its last op's\n\t * sequence number when found and aborting the fetch once found or exhausted. Each op is routed through\n\t * the same unpack pipeline the live inbound path uses (chunk reassembly, ungroup, decompress) and\n\t * {@link inboundVersionMarkUpdate} derives its batch identity, so chunked batches resolve here too. The\n\t * scan anchor is not guaranteed to be a batch boundary, so a leading batch clipped by the anchor is\n\t * tolerated (its orphan end marker is dropped rather than fed to the processor). On a miss,\n\t * {@link classifyMiss} distinguishes `pending` (not sequenced yet) from `unresolvable` (ops trimmed).\n\t * See DEV.md.\n\t */\n\tprivate async resolveFromHistory(\n\t\treader: IHistoricalOpReader,\n\t\tbatchId: string,\n\t\tsequenceNumberLowerBound: number,\n\t): Promise<ResolveResult> {\n\t\tconst from = sequenceNumberLowerBound + 1;\n\t\tconst unpack = this.hooks.createHistoricalOpUnpacker?.();\n\t\tconst abortController = new AbortController();\n\t\ttry {\n\t\t\tconst stream = await reader.fetchMessages(from, undefined, abortController.signal);\n\t\t\tlet firstScannedSequenceNumber: number | undefined;\n\t\t\tlet carriedBatchId: string | undefined;\n\t\t\t// Mirrors the processor's batch-in-progress state so we can drop an orphan end marker (below).\n\t\t\tlet inBatch = false;\n\t\t\twhile (true) {\n\t\t\t\tconst result = await stream.read();\n\t\t\t\tif (result.done) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tfor (const op of result.value) {\n\t\t\t\t\tfirstScannedSequenceNumber ??= op.sequenceNumber;\n\t\t\t\t\tif (!inBatch && asBatchMetadata(op.metadata)?.batch === false) {\n\t\t\t\t\t\t// The anchor (`sequenceNumberLowerBound + 1`) may land inside a batch whose start\n\t\t\t\t\t\t// precedes the window; its orphan end marker would trip the processor's 0x9d5 assert.\n\t\t\t\t\t\t// Drop the clipped tail. The target batch is fully in-window, so this can't skip it.\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\t// undefined = a system op, or an incomplete chunk awaiting later fragments.\n\t\t\t\t\tconst inboundResult = unpack?.(op);\n\t\t\t\t\tif (inboundResult === undefined) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\t// Track batch progress with the processor so the guard above skips only orphan ends.\n\t\t\t\t\tif (inboundResult.type === \"batchStartingMessage\") {\n\t\t\t\t\t\tinBatch = true;\n\t\t\t\t\t} else if (\n\t\t\t\t\t\tinboundResult.type === \"nextBatchMessage\" &&\n\t\t\t\t\t\tinboundResult.batchEnd === true\n\t\t\t\t\t) {\n\t\t\t\t\t\tinBatch = false;\n\t\t\t\t\t}\n\t\t\t\t\t// Derive batch identity exactly as the live inbound path does, carrying the id across a\n\t\t\t\t\t// piecemeal batch's messages.\n\t\t\t\t\tconst update = inboundVersionMarkUpdate(inboundResult, carriedBatchId);\n\t\t\t\t\tcarriedBatchId = update.carriedBatchId;\n\t\t\t\t\tif (update.sequenced?.batchId === batchId) {\n\t\t\t\t\t\treturn { kind: \"resolved\", sequenceNumber: update.sequenced.sequenceNumber };\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Not found: distinguish \"not sequenced yet\" from \"trimmed\" via the tip and where the scan landed.\n\t\t\treturn this.classifyMiss(from, firstScannedSequenceNumber);\n\t\t} finally {\n\t\t\t// Cancel any in-flight fetch once we stop reading (found or exhausted).\n\t\t\tabortController.abort();\n\t\t}\n\t}\n\n\t/**\n\t * Classifies a history-scan miss as `pending` or `unresolvable`. See {@link resolveFromHistory}.\n\t */\n\tprivate classifyMiss(\n\t\tfrom: number,\n\t\tfirstScannedSequenceNumber: number | undefined,\n\t): ResolveResult {\n\t\t// Invariant: a reader must never return an op before the requested range start. If it does, the\n\t\t// trim inference below is meaningless, so fail loudly rather than misclassify.\n\t\tassert(\n\t\t\tfirstScannedSequenceNumber === undefined || firstScannedSequenceNumber >= from,\n\t\t\t0xd1c /* historical op reader returned an op before the requested range start */,\n\t\t);\n\t\tconst tip = this.hooks.getCurrentSequenceNumber();\n\t\tif (from > tip) {\n\t\t\t// Nothing is sequenced at/after the mark's reference point yet, so the batch cannot have landed.\n\t\t\treturn { kind: \"pending\" };\n\t\t}\n\t\tif (firstScannedSequenceNumber === undefined) {\n\t\t\t// Empty read though ops should exist in `[from, tip]` → trimmed. ODSP-specific: a strict driver\n\t\t\t// empties a `from`-misaligned trimmed range (validateMessages); a return-from-earliest driver\n\t\t\t// would instead surface the trim via the `firstScannedSequenceNumber > from` branch below.\n\t\t\treturn { kind: \"unresolvable\" };\n\t\t}\n\t\tif (firstScannedSequenceNumber > from) {\n\t\t\t// A trim gap at the anchor: the mark's ops (just after the reference point) are gone.\n\t\t\treturn { kind: \"unresolvable\" };\n\t\t}\n\t\t// Ops are present from the reference point and the batch is not among them: not yet sequenced.\n\t\treturn { kind: \"pending\" };\n\t}\n\n\tpublic onBatchSequenced(\n\t\tlistener: (batchId: string, sequenceNumber: number) => void,\n\t): () => void {\n\t\t// A subscriber wants live promotions, so start tracking inbound batches from here on.\n\t\tthis.tracking = true;\n\t\tthis.batchListeners.add(listener);\n\t\treturn () => {\n\t\t\tthis.batchListeners.delete(listener);\n\t\t};\n\t}\n\n\t/** The sequence number for a batch seen live this session, or undefined. */\n\tprivate sessionSequenceFor(batchId: string): number | undefined {\n\t\treturn this.sequenceNumberByBatchId.get(batchId);\n\t}\n\n\t/** Records an inbound batch and notifies listeners so any client can promote matching pending marks. */\n\tpublic processInboundBatch(batchId: string, sequenceNumber: number): void {\n\t\tconst existingSequenceNumber = this.sequenceNumberByBatchId.get(batchId);\n\t\tif (existingSequenceNumber === sequenceNumber) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis.sequenceNumberByBatchId.set(batchId, sequenceNumber);\n\t\tthis.evictBelowMinimumSequenceNumber();\n\t\tfor (const listener of this.batchListeners) {\n\t\t\ttry {\n\t\t\t\tlistener(batchId, sequenceNumber);\n\t\t\t} catch (error) {\n\t\t\t\t// Isolate each app listener (like the container's EventEmitterWithErrorHandling): one throw\n\t\t\t\t// must not abort op processing or starve the rest. Log and continue rather than fault the\n\t\t\t\t// container — a missed promotion is recoverable via resolve()'s history scan.\n\t\t\t\tthis.hooks.logger.sendErrorEvent({ eventName: \"VersionMarkListenerException\" }, error);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Bounds the map to the collaboration window by dropping entries below the MSN (mirrors\n\t * `DuplicateBatchDetector`). Safe because below the MSN every client has processed the batch, so its\n\t * `onBatchSequenced` already fired; a dropped entry falls back to the history scan. Entries insert in\n\t * sequence order, so iteration stops at the first retained one.\n\t */\n\tprivate evictBelowMinimumSequenceNumber(): void {\n\t\tconst minimumSequenceNumber = this.hooks.getCurrentMinimumSequenceNumber();\n\t\tfor (const [id, sequenceNumber] of this.sequenceNumberByBatchId) {\n\t\t\tif (sequenceNumber >= minimumSequenceNumber) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tthis.sequenceNumberByBatchId.delete(id);\n\t\t}\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"versionMarkResolver.js","sourceRoot":"","sources":["../../src/versionMarks/versionMarkResolver.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport type {\n\tISequencedDocumentMessage,\n\tIStream,\n} from \"@fluidframework/driver-definitions/internal\";\n\nimport { assert } from \"@fluidframework/core-utils/internal\";\nimport type { TelemetryLoggerExt } from \"@fluidframework/telemetry-utils/internal\";\n\nimport type { InboundMessageResult } from \"../opLifecycle/index.js\";\n\nimport { inboundVersionMarkUpdate } from \"./inboundBatch.js\";\n\n/**\n * Reads sequenced ops in `[from, to)` from delta storage, injected by the container so the runtime stays\n * driver-agnostic. `abortSignal` cancels an in-flight fetch.\n *\n * @internal\n */\nexport interface IHistoricalOpReader {\n\tfetchMessages(\n\t\tfrom: number,\n\t\tto?: number,\n\t\tabortSignal?: AbortSignal,\n\t): Promise<IStream<ISequencedDocumentMessage[]>>;\n}\n\n/**\n * Result of resolving a pending batchId. A resolved result includes the matched batch's last op server\n * timestamp when available. The property is optional for compatibility with previously stored results.\n *\n * @legacy @beta\n */\nexport type ResolveResult =\n\t| {\n\t\t\treadonly kind: \"resolved\";\n\t\t\treadonly sequenceNumber: number;\n\t\t\treadonly timestamp?: number;\n\t }\n\t| { readonly kind: \"pending\" }\n\t| { readonly kind: \"unresolvable\" };\n\n/**\n * The data captured for a version mark. `pending` when the captured edit is local and not yet sequenced\n * (resolve it later via {@link IVersionMarkResolver.resolve}); `resolved` when there is no in-flight local\n * work, so the mark already points at a durable sequence number. The app packs its own stored record from\n * this — the runtime does not define the stored locator shape.\n *\n * @legacy @beta\n */\nexport type VersionMarkCapture =\n\t| {\n\t\t\treadonly kind: \"pending\";\n\t\t\treadonly batchId: string;\n\t\t\treadonly sequenceNumberLowerBound: number;\n\t }\n\t| {\n\t\t\treadonly kind: \"resolved\";\n\t\t\treadonly sequenceNumber: number;\n\t\t\treadonly timestamp?: number;\n\t };\n\n/**\n * Runtime-owned resolver for app-stored version mark locators.\n *\n * @legacy @beta\n */\nexport interface IVersionMarkResolver {\n\t/**\n\t * Captures a version mark at the current point. Seals the current outbound batch first (so a just-made\n\t * local edit has a stable `batchId`, which is only assigned when a batch is flushed), then returns the\n\t * mark data atomically: a `pending` capture (`batchId` + `sequenceNumberLowerBound`) when there is an\n\t * unacked local batch, or a `resolved` capture (`sequenceNumber` + the last processed op's server\n\t * `timestamp`) when there is no in-flight local work. The timestamp property is optional both for\n\t * compatibility with previously stored captures and because it is `undefined` when neither a last\n\t * processed message nor a last-summary message is available.\n\t *\n\t * @remarks Sealing the batch is a side effect (it submits the current batch), so capture at savepoint\n\t * boundaries, not per keystroke.\n\t *\n\t * @returns The pending batch identity and inclusive sequence number lower bound, or the current sequence\n\t * number and corresponding op timestamp (when available) when there is no pending local batch.\n\t */\n\tsealAndCaptureVersionMark(): VersionMarkCapture;\n\t/**\n\t * Resolves a pending mark's batchId to a global sequence number (`sequenceNumberLowerBound` is the\n\t * inclusive lower bound for a history read). A `resolved` sequence number feeds the loader's\n\t * `loadContainerToSequenceNumber`.\n\t *\n\t * @param batchId - The stable identity of the pending batch.\n\t * @param sequenceNumberLowerBound - The inclusive lower bound for the historical op search.\n\t * @returns The resolved sequence number and server timestamp, or a result indicating that the batch\n\t * remains pending or can no longer be resolved.\n\t */\n\tresolve(batchId: string, sequenceNumberLowerBound: number): Promise<ResolveResult>;\n\t/**\n\t * Subscribes to inbound batch sequencing: fires `(batchId, sequenceNumber, timestamp)` per batch so any\n\t * connected client can promote a matching pending mark. Returns an unsubscribe function.\n\t *\n\t * @param listener - Called with the stable batch identity, its final sequence number, and the final op's\n\t * server timestamp.\n\t * @returns A function that unsubscribes the listener.\n\t */\n\tonBatchSequenced(\n\t\tlistener: (batchId: string, sequenceNumber: number, timestamp?: number) => void,\n\t): () => void;\n}\n\n/**\n * @internal\n */\nexport interface VersionMarkResolverRuntimeHooks {\n\treadonly getCurrentSequenceNumber: () => number;\n\treadonly getCurrentTimestamp: () => number | undefined;\n\treadonly getCurrentMinimumSequenceNumber: () => number;\n\treadonly getCurrentPendingBatchId: () => string | undefined;\n\t/** Logs faults from app-supplied `onBatchSequenced` listeners (see {@link VersionMarkResolver.processInboundBatch}). */\n\treadonly logger: TelemetryLoggerExt;\n\t/**\n\t * Seals the current outbound batch (flushes the runtime), so a just-submitted local edit is moved into\n\t * the pending state with a stable `batchId` before\n\t * {@link IVersionMarkResolver.sealAndCaptureVersionMark} reads it.\n\t */\n\treadonly flushPendingBatch: () => void;\n\t/**\n\t * Injected by the container, backed by delta storage. When absent, unknown batchIds resolve as `pending`.\n\t */\n\treadonly getHistoricalOpReader?: () => IHistoricalOpReader | undefined;\n\t/**\n\t * Creates a per-scan op unpacker that mirrors the live inbound path (chunk reassembly, ungroup,\n\t * decompress), so the history scan observes the same restored `batchId` — in particular for chunked\n\t * batches, whose id is stripped from the final chunk's wire metadata and restored only after\n\t * reassembly. A fresh unpacker is created per scan since it holds reassembly state. When absent, the\n\t * history scan cannot identify batches and reports `pending`/`unresolvable`.\n\t */\n\treadonly createHistoricalOpUnpacker?: () => (\n\t\top: ISequencedDocumentMessage,\n\t) => InboundMessageResult | undefined;\n}\n\n/**\n * Resolves a mark's batchId to a global sequence number. The app owns mark storage and promotion; the\n * runtime exposes the capture point and an ephemeral in-session `batchId -> sequenceNumber` map.\n *\n * @internal\n */\nexport class VersionMarkResolver implements IVersionMarkResolver {\n\tprivate readonly resolvedBatchById = new Map<\n\t\tstring,\n\t\t{ readonly sequenceNumber: number; readonly timestamp: number }\n\t>();\n\tprivate readonly batchListeners = new Set<\n\t\t(batchId: string, sequenceNumber: number, timestamp: number) => void\n\t>();\n\t/**\n\t * Sticky flag: false until the feature is first used this session (a pending capture or a listener).\n\t * While false the runtime skips all per-inbound-batch tracking, so a container that never uses version\n\t * marks pays nothing on the hot path (mirrors #22497 gating `DuplicateBatchDetector` on offline load).\n\t */\n\tprivate tracking = false;\n\n\tpublic constructor(private readonly hooks: VersionMarkResolverRuntimeHooks) {}\n\n\t/** Whether inbound batches need tracking (see {@link tracking}). Read by the runtime to gate the hot path. */\n\tpublic get isTracking(): boolean {\n\t\treturn this.tracking;\n\t}\n\n\tpublic sealAndCaptureVersionMark(): VersionMarkCapture {\n\t\t// Seal the current batch so a just-submitted local edit is flushed into the pending state with a\n\t\t// stable batchId (which is only assigned at flush) before we read it.\n\t\tthis.hooks.flushPendingBatch();\n\t\tconst referenceSequenceNumber = this.hooks.getCurrentSequenceNumber();\n\t\tconst batchId = this.hooks.getCurrentPendingBatchId();\n\t\tlet result: VersionMarkCapture;\n\t\tif (batchId === undefined) {\n\t\t\t// No unacked local batch: the mark already points at a durable sequence number.\n\t\t\tresult = {\n\t\t\t\tkind: \"resolved\",\n\t\t\t\tsequenceNumber: referenceSequenceNumber,\n\t\t\t\ttimestamp: this.hooks.getCurrentTimestamp(),\n\t\t\t};\n\t\t} else {\n\t\t\t// A pending mark needs its batch tracked so resolve() can promote it from the live map.\n\t\t\tthis.tracking = true;\n\t\t\t// The pending batch is sequenced after the reference point, so its first possible sequence\n\t\t\t// number is `referenceSequenceNumber + 1`. Store that as an inclusive lower bound so resolve()\n\t\t\t// scans directly from it.\n\t\t\tresult = {\n\t\t\t\tkind: \"pending\",\n\t\t\t\tbatchId,\n\t\t\t\tsequenceNumberLowerBound: referenceSequenceNumber + 1,\n\t\t\t};\n\t\t}\n\t\tthis.hooks.logger.sendTelemetryEvent({ eventName: \"Capture\", kind: result.kind });\n\t\treturn result;\n\t}\n\n\tpublic async resolve(\n\t\tbatchId: string,\n\t\tsequenceNumberLowerBound: number,\n\t): Promise<ResolveResult> {\n\t\tconst startTime = Date.now();\n\t\t// Track from here so inbound batches are recorded even without a prior capture/subscribe; otherwise\n\t\t// a batch sequencing during the scan (or live, in the no-reader case) is skipped and unrecoverable.\n\t\tthis.tracking = true;\n\t\t// Defaults cover the throw path (only the history scan can throw — e.g. an unpacker\n\t\t// DataCorruptionError or the 0xd1c reader-contract assert): the Resolve event still fires via\n\t\t// `finally`, with outcome \"error\".\n\t\tlet path: \"session\" | \"history\" | \"noReader\" = \"history\";\n\t\tlet outcome: ResolveResult[\"kind\"] | \"error\" = \"error\";\n\t\tlet resolvedSequenceNumber: number | undefined;\n\t\ttry {\n\t\t\t// Fast path: batch sequenced live this session.\n\t\t\tconst resolvedBatch = this.sessionResolutionFor(batchId);\n\t\t\tif (resolvedBatch === undefined) {\n\t\t\t\tconst reader = this.hooks.getHistoricalOpReader?.();\n\t\t\t\tif (reader === undefined) {\n\t\t\t\t\t// No reader: the batch may still sequence live, so report pending.\n\t\t\t\t\tpath = \"noReader\";\n\t\t\t\t\toutcome = \"pending\";\n\t\t\t\t\treturn { kind: \"pending\" };\n\t\t\t\t}\n\t\t\t\t// Otherwise scan history from the mark's reference point.\n\t\t\t\tpath = \"history\";\n\t\t\t\tlet result = await this.resolveFromHistory(reader, batchId, sequenceNumberLowerBound);\n\t\t\t\tif (result.kind !== \"resolved\") {\n\t\t\t\t\t// The batch may have sequenced live while the history scan was in progress. Prefer that\n\t\t\t\t\t// authoritative in-session result over a stale history miss.\n\t\t\t\t\tconst liveResult = this.sessionResolutionFor(batchId);\n\t\t\t\t\tif (liveResult !== undefined) {\n\t\t\t\t\t\tpath = \"session\";\n\t\t\t\t\t\tresult = { kind: \"resolved\", ...liveResult };\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\toutcome = result.kind;\n\t\t\t\tif (result.kind === \"resolved\") {\n\t\t\t\t\tresolvedSequenceNumber = result.sequenceNumber;\n\t\t\t\t}\n\t\t\t\treturn result;\n\t\t\t}\n\t\t\tpath = \"session\";\n\t\t\toutcome = \"resolved\";\n\t\t\tresolvedSequenceNumber = resolvedBatch.sequenceNumber;\n\t\t\treturn { kind: \"resolved\", ...resolvedBatch };\n\t\t} finally {\n\t\t\tthis.hooks.logger.sendTelemetryEvent({\n\t\t\t\teventName: \"Resolve\",\n\t\t\t\toutcome,\n\t\t\t\tpath,\n\t\t\t\tdurationMs: Date.now() - startTime,\n\t\t\t\t...(resolvedSequenceNumber === undefined\n\t\t\t\t\t? {}\n\t\t\t\t\t: { sequenceNumber: resolvedSequenceNumber }),\n\t\t\t});\n\t\t}\n\t}\n\n\t/**\n\t * Scans sequenced ops from `sequenceNumberLowerBound` (inclusive) for `batchId`, returning its last op's\n\t * sequence number when found and aborting the fetch once found or exhausted. Each op is routed through\n\t * the same unpack pipeline the live inbound path uses (chunk reassembly, ungroup, decompress) and\n\t * {@link inboundVersionMarkUpdate} derives its batch identity, so virtualized batches resolve here too.\n\t * The injected historical unpacker owns virtualization details, including tolerating a requested range\n\t * that begins within an incomplete chunk stream. On a miss, {@link classifyMiss} distinguishes\n\t * `pending` (not sequenced yet) from `unresolvable` (ops trimmed).\n\t */\n\tprivate async resolveFromHistory(\n\t\treader: IHistoricalOpReader,\n\t\tbatchId: string,\n\t\tsequenceNumberLowerBound: number,\n\t): Promise<ResolveResult> {\n\t\tconst from = sequenceNumberLowerBound;\n\t\tconst unpack = this.hooks.createHistoricalOpUnpacker?.();\n\t\tconst abortController = new AbortController();\n\t\ttry {\n\t\t\tconst stream = await reader.fetchMessages(from, undefined, abortController.signal);\n\t\t\tlet firstScannedSequenceNumber: number | undefined;\n\t\t\tlet carriedBatchId: string | undefined;\n\t\t\twhile (true) {\n\t\t\t\tconst result = await stream.read();\n\t\t\t\tif (result.done) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tfor (const op of result.value) {\n\t\t\t\t\tfirstScannedSequenceNumber ??= op.sequenceNumber;\n\t\t\t\t\t// undefined = a system op or virtualized input that has not produced a complete result.\n\t\t\t\t\tconst inboundResult = unpack?.(op);\n\t\t\t\t\tif (inboundResult === undefined) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\t// Derive batch identity exactly as the live inbound path does, carrying the id across a\n\t\t\t\t\t// piecemeal batch's messages.\n\t\t\t\t\tconst update = inboundVersionMarkUpdate(inboundResult, carriedBatchId);\n\t\t\t\t\tcarriedBatchId = update.carriedBatchId;\n\t\t\t\t\tif (update.sequenced?.batchId === batchId) {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tkind: \"resolved\",\n\t\t\t\t\t\t\tsequenceNumber: update.sequenced.sequenceNumber,\n\t\t\t\t\t\t\ttimestamp: update.sequenced.timestamp,\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Not found: distinguish \"not sequenced yet\" from \"trimmed\" via the tip and where the scan landed.\n\t\t\treturn this.classifyMiss(from, firstScannedSequenceNumber);\n\t\t} finally {\n\t\t\t// Cancel any in-flight fetch once we stop reading (found or exhausted).\n\t\t\tabortController.abort();\n\t\t}\n\t}\n\n\t/**\n\t * Classifies a history-scan miss as `pending` or `unresolvable`. See {@link resolveFromHistory}.\n\t */\n\tprivate classifyMiss(\n\t\tfrom: number,\n\t\tfirstScannedSequenceNumber: number | undefined,\n\t): ResolveResult {\n\t\t// Invariant: a reader must never return an op before the requested range start. If it does, the\n\t\t// trim inference below is meaningless, so fail loudly rather than misclassify.\n\t\tassert(\n\t\t\tfirstScannedSequenceNumber === undefined || firstScannedSequenceNumber >= from,\n\t\t\t0xd1c /* historical op reader returned an op before the requested range start */,\n\t\t);\n\t\tconst tip = this.hooks.getCurrentSequenceNumber();\n\t\tif (from > tip) {\n\t\t\t// Nothing is sequenced at/after the mark's lower bound yet, so the batch cannot have landed.\n\t\t\treturn { kind: \"pending\" };\n\t\t}\n\t\tif (firstScannedSequenceNumber === undefined) {\n\t\t\t// Empty read though ops should exist in `[from, tip]` → trimmed. ODSP-specific: a strict driver\n\t\t\t// empties a `from`-misaligned trimmed range (validateMessages); a return-from-earliest driver\n\t\t\t// would instead surface the trim via the `firstScannedSequenceNumber > from` branch below.\n\t\t\treturn { kind: \"unresolvable\" };\n\t\t}\n\t\tif (firstScannedSequenceNumber > from) {\n\t\t\t// A trim gap at the anchor: the mark's ops (at/after its lower bound) are gone.\n\t\t\treturn { kind: \"unresolvable\" };\n\t\t}\n\t\t// Ops are present from the lower bound and the batch is not among them: not yet sequenced.\n\t\treturn { kind: \"pending\" };\n\t}\n\n\tpublic onBatchSequenced(\n\t\tlistener: (batchId: string, sequenceNumber: number, timestamp?: number) => void,\n\t): () => void {\n\t\t// A subscriber wants live promotions, so start tracking inbound batches from here on.\n\t\tthis.tracking = true;\n\t\tthis.batchListeners.add(listener);\n\t\treturn () => {\n\t\t\tthis.batchListeners.delete(listener);\n\t\t};\n\t}\n\n\t/** The resolved point for a batch seen live this session, or undefined. */\n\tprivate sessionResolutionFor(\n\t\tbatchId: string,\n\t): { readonly sequenceNumber: number; readonly timestamp: number } | undefined {\n\t\treturn this.resolvedBatchById.get(batchId);\n\t}\n\n\t/** Records an inbound batch and notifies listeners so any client can promote matching pending marks. */\n\tpublic processInboundBatch(\n\t\tbatchId: string,\n\t\tsequenceNumber: number,\n\t\ttimestamp: number,\n\t): void {\n\t\tconst existingResolution = this.resolvedBatchById.get(batchId);\n\t\tif (existingResolution !== undefined) {\n\t\t\t// NOTE: It's possible (but exceedingly rare) that this is a spurious duplicated batch,\n\t\t\t// with a different sequence number/timestamp.\n\t\t\t// Rather than asserting or overwriting, we let the earlier version mark stand, it is sufficient.\n\t\t\treturn;\n\t\t}\n\n\t\tthis.resolvedBatchById.set(batchId, { sequenceNumber, timestamp });\n\t\tthis.evictBelowMinimumSequenceNumber();\n\t\tfor (const listener of this.batchListeners) {\n\t\t\ttry {\n\t\t\t\tlistener(batchId, sequenceNumber, timestamp);\n\t\t\t} catch (error) {\n\t\t\t\t// Isolate each app listener (like the container's EventEmitterWithErrorHandling): one throw\n\t\t\t\t// must not abort op processing or starve the rest. Log and continue rather than fault the\n\t\t\t\t// container — a missed promotion is recoverable via resolve()'s history scan.\n\t\t\t\tthis.hooks.logger.sendErrorEvent({ eventName: \"VersionMarkListenerException\" }, error);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Bounds the map to the collaboration window by dropping entries below the MSN (mirrors\n\t * `DuplicateBatchDetector`). Safe because below the MSN every client has processed the batch, so its\n\t * `onBatchSequenced` already fired; a dropped entry falls back to the history scan. Entries insert in\n\t * sequence order, so iteration stops at the first retained one.\n\t */\n\tprivate evictBelowMinimumSequenceNumber(): void {\n\t\tconst minimumSequenceNumber = this.hooks.getCurrentMinimumSequenceNumber();\n\t\tfor (const [id, resolution] of this.resolvedBatchById) {\n\t\t\tif (resolution.sequenceNumber >= minimumSequenceNumber) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tthis.resolvedBatchById.delete(id);\n\t\t}\n\t}\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"batchTracker.d.ts","sourceRoot":"","sources":["../src/batchTracker.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAEjE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,iCAAiC,CAAC;AAU5E,qBAAa,YAAY;IAOvB,OAAO,CAAC,QAAQ,CAAC,iBAAiB;IANnC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqB;IAC5C,OAAO,CAAC,wBAAwB,CAAqB;IACrD,OAAO,CAAC,iBAAiB,CAAa;IACtC,OAAO,CAAC,6BAA6B,CAAqB;gBAGxC,iBAAiB,EAAE,YAAY,EAChD,MAAM,EAAE,oBAAoB,EAC5B,oBAAoB,EAAE,MAAM,EAC5B,sBAAsB,EAAE,MAAM,EAC9B,gBAAgB,GAAE,MAAM,MAA+B;CA2CxD;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,gBAAgB,
|
|
1
|
+
{"version":3,"file":"batchTracker.d.ts","sourceRoot":"","sources":["../src/batchTracker.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAEjE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,iCAAiC,CAAC;AAU5E,qBAAa,YAAY;IAOvB,OAAO,CAAC,QAAQ,CAAC,iBAAiB;IANnC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqB;IAC5C,OAAO,CAAC,wBAAwB,CAAqB;IACrD,OAAO,CAAC,iBAAiB,CAAa;IACtC,OAAO,CAAC,6BAA6B,CAAqB;gBAGxC,iBAAiB,EAAE,YAAY,EAChD,MAAM,EAAE,oBAAoB,EAC5B,oBAAoB,EAAE,MAAM,EAC5B,sBAAsB,EAAE,MAAM,EAC9B,gBAAgB,GAAE,MAAM,MAA+B;CA2CxD;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,gBAAgB,GAC5B,mBAAmB,YAAY,EAC/B,QAAQ,oBAAoB,EAC5B,uBAAsB,MAAa,EACnC,yBAAwB,MAAa,KACnC,YACuF,CAAC"}
|
package/lib/batchTracker.js
CHANGED
|
@@ -6,9 +6,13 @@ import { performanceNow } from "@fluid-internal/client-utils";
|
|
|
6
6
|
import { assert } from "@fluidframework/core-utils/internal";
|
|
7
7
|
import { createChildLogger, } from "@fluidframework/telemetry-utils/internal";
|
|
8
8
|
export class BatchTracker {
|
|
9
|
+
batchEventEmitter;
|
|
10
|
+
logger;
|
|
11
|
+
startBatchSequenceNumber;
|
|
12
|
+
trackedBatchCount = 0;
|
|
13
|
+
batchProcessingStartTimeStamp;
|
|
9
14
|
constructor(batchEventEmitter, logger, batchLengthThreshold, batchCountSamplingRate, dateTimeProvider = () => performanceNow()) {
|
|
10
15
|
this.batchEventEmitter = batchEventEmitter;
|
|
11
|
-
this.trackedBatchCount = 0;
|
|
12
16
|
this.logger = createChildLogger({ logger, namespace: "Batching" });
|
|
13
17
|
this.batchEventEmitter.on("batchBegin", (message) => {
|
|
14
18
|
this.startBatchSequenceNumber = message.sequenceNumber;
|
package/lib/batchTracker.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"batchTracker.js","sourceRoot":"","sources":["../src/batchTracker.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAE9D,OAAO,EAAE,MAAM,EAAE,MAAM,qCAAqC,CAAC;AAE7D,OAAO,EACN,iBAAiB,GAEjB,MAAM,0CAA0C,CAAC;AAIlD,MAAM,OAAO,YAAY;
|
|
1
|
+
{"version":3,"file":"batchTracker.js","sourceRoot":"","sources":["../src/batchTracker.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAE9D,OAAO,EAAE,MAAM,EAAE,MAAM,qCAAqC,CAAC;AAE7D,OAAO,EACN,iBAAiB,GAEjB,MAAM,0CAA0C,CAAC;AAIlD,MAAM,OAAO,YAAY;IAON;IAND,MAAM,CAAqB;IACpC,wBAAwB,CAAqB;IAC7C,iBAAiB,GAAW,CAAC,CAAC;IAC9B,6BAA6B,CAAqB;IAE1D,YACkB,iBAA+B,EAChD,MAA4B,EAC5B,oBAA4B,EAC5B,sBAA8B,EAC9B,mBAAiC,GAAG,EAAE,CAAC,cAAc,EAAE;QAJtC,sBAAiB,GAAjB,iBAAiB,CAAc;QAMhD,IAAI,CAAC,MAAM,GAAG,iBAAiB,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,CAAC,CAAC;QAEnE,IAAI,CAAC,iBAAiB,CAAC,EAAE,CAAC,YAAY,EAAE,CAAC,OAA4B,EAAE,EAAE;YACxE,IAAI,CAAC,wBAAwB,GAAG,OAAO,CAAC,cAAc,CAAC;YACvD,IAAI,CAAC,6BAA6B,GAAG,gBAAgB,EAAE,CAAC;YACxD,IAAI,CAAC,iBAAiB,EAAE,CAAC;QAC1B,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,iBAAiB,CAAC,EAAE,CAAC,UAAU,EAAE,CAAC,KAAc,EAAE,OAA4B,EAAE,EAAE;YACtF,MAAM,CACL,IAAI,CAAC,wBAAwB,KAAK,SAAS;gBAC1C,IAAI,CAAC,6BAA6B,KAAK,SAAS,EACjD,KAAK,CAAC,4CAA4C,CAClD,CAAC;YAEF,MAAM,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,wBAAwB,GAAG,CAAC,CAAC;YAC1E,IAAI,MAAM,IAAI,oBAAoB,EAAE,CAAC;gBACpC,IAAI,CAAC,MAAM,CAAC,oBAAoB,CAAC;oBAChC,SAAS,EAAE,cAAc;oBACzB,MAAM;oBACN,SAAS,EAAE,oBAAoB;oBAC/B,sBAAsB,EAAE,OAAO,CAAC,cAAc;oBAC9C,QAAQ,EAAE,gBAAgB,EAAE,GAAG,IAAI,CAAC,6BAA6B;oBACjE,UAAU,EAAE,KAAK,KAAK,SAAS;iBAC/B,CAAC,CAAC;YACJ,CAAC;YAED,IAAI,IAAI,CAAC,iBAAiB,GAAG,sBAAsB,KAAK,CAAC,EAAE,CAAC;gBAC3D,IAAI,CAAC,MAAM,CAAC,oBAAoB,CAAC;oBAChC,SAAS,EAAE,QAAQ;oBACnB,MAAM;oBACN,YAAY,EAAE,sBAAsB;oBACpC,sBAAsB,EAAE,OAAO,CAAC,cAAc;oBAC9C,QAAQ,EAAE,gBAAgB,EAAE,GAAG,IAAI,CAAC,6BAA6B;iBACjE,CAAC,CAAC;YACJ,CAAC;YAED,IAAI,CAAC,wBAAwB,GAAG,SAAS,CAAC;YAC1C,IAAI,CAAC,6BAA6B,GAAG,SAAS,CAAC;QAChD,CAAC,CAAC,CAAC;IACJ,CAAC;CACD;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAC/B,iBAA+B,EAC/B,MAA4B,EAC5B,uBAA+B,IAAI,EACnC,yBAAiC,IAAI,EACtB,EAAE,CACjB,IAAI,YAAY,CAAC,iBAAiB,EAAE,MAAM,EAAE,oBAAoB,EAAE,sBAAsB,CAAC,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport type { EventEmitter } from \"@fluid-internal/client-utils\";\nimport { performanceNow } from \"@fluid-internal/client-utils\";\nimport type { ITelemetryBaseLogger } from \"@fluidframework/core-interfaces\";\nimport { assert } from \"@fluidframework/core-utils/internal\";\nimport type { ISequencedDocumentMessage } from \"@fluidframework/driver-definitions/internal\";\nimport {\n\tcreateChildLogger,\n\ttype TelemetryLoggerExt,\n} from \"@fluidframework/telemetry-utils/internal\";\n\ntype BatchTrackerMessage = Pick<ISequencedDocumentMessage, \"sequenceNumber\">;\n\nexport class BatchTracker {\n\tprivate readonly logger: TelemetryLoggerExt;\n\tprivate startBatchSequenceNumber: number | undefined;\n\tprivate trackedBatchCount: number = 0;\n\tprivate batchProcessingStartTimeStamp: number | undefined;\n\n\tconstructor(\n\t\tprivate readonly batchEventEmitter: EventEmitter,\n\t\tlogger: ITelemetryBaseLogger,\n\t\tbatchLengthThreshold: number,\n\t\tbatchCountSamplingRate: number,\n\t\tdateTimeProvider: () => number = () => performanceNow(),\n\t) {\n\t\tthis.logger = createChildLogger({ logger, namespace: \"Batching\" });\n\n\t\tthis.batchEventEmitter.on(\"batchBegin\", (message: BatchTrackerMessage) => {\n\t\t\tthis.startBatchSequenceNumber = message.sequenceNumber;\n\t\t\tthis.batchProcessingStartTimeStamp = dateTimeProvider();\n\t\t\tthis.trackedBatchCount++;\n\t\t});\n\n\t\tthis.batchEventEmitter.on(\"batchEnd\", (error: unknown, message: BatchTrackerMessage) => {\n\t\t\tassert(\n\t\t\t\tthis.startBatchSequenceNumber !== undefined &&\n\t\t\t\t\tthis.batchProcessingStartTimeStamp !== undefined,\n\t\t\t\t0x2ba /* \"batchBegin must fire before batchEnd\" */,\n\t\t\t);\n\n\t\t\tconst length = message.sequenceNumber - this.startBatchSequenceNumber + 1;\n\t\t\tif (length >= batchLengthThreshold) {\n\t\t\t\tthis.logger.sendPerformanceEvent({\n\t\t\t\t\teventName: \"LengthTooBig\",\n\t\t\t\t\tlength,\n\t\t\t\t\tthreshold: batchLengthThreshold,\n\t\t\t\t\tbatchEndSequenceNumber: message.sequenceNumber,\n\t\t\t\t\tduration: dateTimeProvider() - this.batchProcessingStartTimeStamp,\n\t\t\t\t\tbatchError: error !== undefined,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (this.trackedBatchCount % batchCountSamplingRate === 0) {\n\t\t\t\tthis.logger.sendPerformanceEvent({\n\t\t\t\t\teventName: \"Length\",\n\t\t\t\t\tlength,\n\t\t\t\t\tsamplingRate: batchCountSamplingRate,\n\t\t\t\t\tbatchEndSequenceNumber: message.sequenceNumber,\n\t\t\t\t\tduration: dateTimeProvider() - this.batchProcessingStartTimeStamp,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tthis.startBatchSequenceNumber = undefined;\n\t\t\tthis.batchProcessingStartTimeStamp = undefined;\n\t\t});\n\t}\n}\n\n/**\n * Track batch sizes in terms of op counts and processing times\n *\n * @param batchEventEmitter - event emitter which tracks the lifecycle of batch operations\n * @param logger - See {@link @fluidframework/core-interfaces#TelemetryLoggerExt}\n * @param batchLengthThreshold - threshold for the length of a batch when to send an error event\n * @param batchCountSamplingRate - rate for batches for which to send an event with its characteristics\n */\nexport const BindBatchTracker = (\n\tbatchEventEmitter: EventEmitter,\n\tlogger: ITelemetryBaseLogger,\n\tbatchLengthThreshold: number = 1000,\n\tbatchCountSamplingRate: number = 1000,\n): BatchTracker =>\n\tnew BatchTracker(batchEventEmitter, logger, batchLengthThreshold, batchCountSamplingRate);\n"]}
|
|
@@ -236,6 +236,6 @@ export declare const getGCNodePathFromLocalId: (localId: string) => string;
|
|
|
236
236
|
/**
|
|
237
237
|
* Returns whether a given path is for attachment blobs that are in the format - "/blobManagerBasePath/...".
|
|
238
238
|
*/
|
|
239
|
-
export declare const isBlobPath: (path: string) => path is
|
|
239
|
+
export declare const isBlobPath: (path: string) => path is `/${typeof blobManagerBasePath}/${string}`;
|
|
240
240
|
export {};
|
|
241
241
|
//# sourceMappingURL=blobManager.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"blobManager.d.ts","sourceRoot":"","sources":["../../src/blobManager/blobManager.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAEN,KAAK,wBAAwB,EAC7B,MAAM,gDAAgD,CAAC;AACxD,OAAO,KAAK,EACX,iBAAiB,EACjB,uBAAuB,EACvB,MAAM,wDAAwD,CAAC;AAChE,OAAO,KAAK,EAEX,cAAc,EACd,mBAAmB,EACnB,kCAAkC,EAClC,iBAAiB,EACjB,uBAAuB,EACvB,UAAU,EACV,YAAY,EACZ,MAAM,0CAA0C,CAAC;AAElD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,6CAA6C,CAAC;AACvF,OAAO,KAAK,EACX,sBAAsB,EACtB,qBAAqB,EACrB,iBAAiB,EACjB,yBAAyB,EACzB,MAAM,8CAA8C,CAAC;AACtD,OAAO,EACN,eAAe,EAIf,MAAM,wCAAwC,CAAC;AAWhD,OAAO,EAGN,KAAK,oBAAoB,EACzB,MAAM,yBAAyB,CAAC;AAEjC;;;;;;GAMG;AACH,qBAAa,UACZ,SAAQ,eAAe,CAAC,eAAe,CACvC,YACC,iBAAiB,CAAC,eAAe,CAAC,EAClC,kCAAkC,CAAC,eAAe,CAAC;aAiCnC,IAAI,EAAE,MAAM;aACZ,YAAY,EAAE,mBAAmB;IAE1C,GAAG,EAAE,MAAM,OAAO,CAAC,eAAe,CAAC;aAC1B,cAAc,EAAE,OAAO;IACvC,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAC;IACtC,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAC;IArCtC,OAAO,CAAC,QAAQ,CAAkB;IAClC,OAAO,CAAC,qBAAqB,CAAkB;IAE/C,IAAW,UAAU,IAAI,OAAO,CAE/B;IAED,OAAO,CAAC,OAAO,CAEF;IACb,IAAW,MAAM,IAAI,UAAU,CAAC,uBAAuB,CAAC,CAEvD;IAED,OAAO,CAAC,aAAa,CAAe;IACpC,IAAW,YAAY,IAAI,YAAY,CAEtC;IAED;;;OAGG;IACH,OAAO,CAAC,kBAAkB,CAAU;IACpC,IAAW,iBAAiB,IAAI,OAAO,CAEtC;IAED,SAAgB,YAAY,EAAE,MAAM,CAAC;gBAGpB,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,mBAAmB,EAE1C,GAAG,EAAE,MAAM,OAAO,CAAC,eAAe,CAAC,EAC1B,cAAc,EAAE,OAAO,EACtB,oBAAoB,CAAC,
|
|
1
|
+
{"version":3,"file":"blobManager.d.ts","sourceRoot":"","sources":["../../src/blobManager/blobManager.ts"],"names":[],"mappings":"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"}
|