@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
package/lib/throttler.js
CHANGED
|
@@ -7,6 +7,10 @@
|
|
|
7
7
|
* Delay is based on previous attempts within specified time window, subtracting delay time.
|
|
8
8
|
*/
|
|
9
9
|
export class Throttler {
|
|
10
|
+
delayWindowMs;
|
|
11
|
+
maxDelayMs;
|
|
12
|
+
delayFn;
|
|
13
|
+
startTimes = [];
|
|
10
14
|
get numAttempts() {
|
|
11
15
|
return this.startTimes.length;
|
|
12
16
|
}
|
|
@@ -44,7 +48,6 @@ export class Throttler {
|
|
|
44
48
|
this.delayWindowMs = delayWindowMs;
|
|
45
49
|
this.maxDelayMs = maxDelayMs;
|
|
46
50
|
this.delayFn = delayFn;
|
|
47
|
-
this.startTimes = [];
|
|
48
51
|
}
|
|
49
52
|
getDelay() {
|
|
50
53
|
const now = Date.now();
|
package/lib/throttler.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"throttler.js","sourceRoot":"","sources":["../src/throttler.ts"],"names":[],"mappings":"AAAA;;;GAGG;AA+BH;;;GAGG;AACH,MAAM,OAAO,SAAS;
|
|
1
|
+
{"version":3,"file":"throttler.js","sourceRoot":"","sources":["../src/throttler.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\nexport interface IThrottler {\n\t/**\n\t * Computes what the throttle delay should be, and records an attempt\n\t * which will be used for calculating future attempt delays.\n\t */\n\tgetDelay(): number;\n\n\t/**\n\t * Number of attempts that occurred within the sliding window as of\n\t * the most recent delay computation.\n\t */\n\treadonly numAttempts: number;\n\n\t/**\n\t * Width of sliding delay window in milliseconds.\n\t */\n\treadonly delayWindowMs: number;\n\t/**\n\t * Maximum delay allowed in milliseconds.\n\t */\n\treadonly maxDelayMs: number;\n\t/**\n\t * Delay function used to calculate what the delay should be.\n\t * The input is the number of attempts that occurred within the sliding window.\n\t * The result is the calculated delay in milliseconds.\n\t */\n\treadonly delayFn: (numAttempts: number) => number;\n}\n\n/**\n * Used to give increasing delay times for throttling a single functionality.\n * Delay is based on previous attempts within specified time window, subtracting delay time.\n */\nexport class Throttler implements IThrottler {\n\tprivate startTimes: number[] = [];\n\n\tpublic get numAttempts(): number {\n\t\treturn this.startTimes.length;\n\t}\n\n\t/**\n\t * Gets all attempt start times after compensating for the delay times\n\t * by adding the delay times to the actual times.\n\t */\n\tpublic getAttempts(): readonly number[] {\n\t\treturn [...this.startTimes];\n\t}\n\n\t/**\n\t * Latest attempt time after compensating for the delay time itself\n\t * by adding the delay time to the actual time.\n\t */\n\tpublic get latestAttemptTime(): number | undefined {\n\t\treturn this.startTimes.length > 0\n\t\t\t? this.startTimes[this.startTimes.length - 1]\n\t\t\t: undefined;\n\t}\n\n\tconstructor(\n\t\t/**\n\t\t * Width of sliding delay window in milliseconds.\n\t\t */\n\t\tpublic readonly delayWindowMs: number,\n\t\t/**\n\t\t * Maximum delay allowed in milliseconds.\n\t\t */\n\t\tpublic readonly maxDelayMs: number,\n\t\t/**\n\t\t * Delay function used to calculate what the delay should be.\n\t\t * The input is the number of attempts that occurred within the sliding window.\n\t\t * The result is the calculated delay in milliseconds.\n\t\t */\n\t\tpublic readonly delayFn: (numAttempts: number) => number,\n\t) {}\n\n\tpublic getDelay(): number {\n\t\tconst now = Date.now();\n\n\t\tconst latestAttemptTime = this.latestAttemptTime;\n\t\tif (latestAttemptTime !== undefined) {\n\t\t\t// If getDelay was called sooner than the most recent delay,\n\t\t\t// subtract the remaining time, since we previously added it.\n\t\t\tconst earlyMs = latestAttemptTime - now;\n\t\t\tif (earlyMs > 0) {\n\t\t\t\tthis.startTimes = this.startTimes.map((t) => t - earlyMs);\n\t\t\t}\n\t\t}\n\n\t\t// Remove all attempts that have already fallen out of the window.\n\t\tthis.startTimes = this.startTimes.filter((t) => now - t < this.delayWindowMs);\n\n\t\t// Compute delay, but do not exceed the specified max delay.\n\t\tconst delayMs = Math.min(this.delayFn(this.startTimes.length), this.maxDelayMs);\n\n\t\t// Record this attempt start time.\n\t\tthis.startTimes.push(now);\n\n\t\t// Account for the delay time, by effectively removing it from the delay window.\n\t\tthis.startTimes = this.startTimes.map((t) => t + delayMs);\n\n\t\tif (delayMs === this.maxDelayMs) {\n\t\t\t// We hit max delay, so adding more won't affect anything.\n\t\t\t// Shift off oldest time to stop this array from growing forever.\n\t\t\tthis.startTimes.shift();\n\t\t}\n\n\t\treturn delayMs;\n\t}\n}\n\n/**\n * Helper function to generate simple exponential throttle functions.\n * f(n) = [coefficient] x ([multiplier]^n) + [flatOffset]\n * where n = number of attempts, and f(n) = delay time in milliseconds.\n * If not provided, coefficient will default to 1, multiplier to 2,\n * minimum delay to 0, and the offset to 0, yielding:\n * 0 ms, 2 ms, 4 ms, 8 ms, ..., 2^n ms\n * where M = multiplier; an exponential back-off.\n * Use initialDelay to decide what should happen when numAttempts is 0,\n * leave it undefined to not special case.\n */\nexport const formExponentialFn =\n\t({\n\t\tmultiplier = 2,\n\t\tcoefficient = 1,\n\t\toffset = 0,\n\t\tinitialDelay = undefined as number | undefined,\n\t} = {}): IThrottler[\"delayFn\"] =>\n\t(numAttempts) =>\n\t\tMath.max(\n\t\t\t0,\n\t\t\tnumAttempts <= 0 && initialDelay !== undefined\n\t\t\t\t? initialDelay\n\t\t\t\t: coefficient * Math.pow(multiplier, numAttempts) + offset,\n\t\t);\n\n/**\n * f(n) = C x (B^(n+A)) + F = (C x B^A) x B^n + F\n */\nexport const formExponentialFnWithAttemptOffset = (\n\tattemptOffset: number,\n\t{\n\t\tmultiplier = 2,\n\t\tcoefficient = 1,\n\t\toffset = 0,\n\t\tinitialDelay = undefined as number | undefined,\n\t} = {},\n): IThrottler[\"delayFn\"] =>\n\tformExponentialFn({\n\t\tmultiplier,\n\t\tcoefficient: coefficient * Math.pow(multiplier, attemptOffset),\n\t\toffset,\n\t\tinitialDelay,\n\t});\n\n/**\n * Helper function to generate simple linear throttle functions.\n * f(n) = [coefficient] x n + [flatOffset]\n * where n = number of attempts, and f(n) = delay time in milliseconds.\n * If not provided, coefficient will default to 1, and offset to 0, yielding:\n * 0 ms, 1 ms, 2 ms, 3 ms, ..., n ms delays; a linear back-off.\n */\nexport const formLinearFn =\n\t({ coefficient = 1, offset = 0 } = {}): IThrottler[\"delayFn\"] =>\n\t(numAttempts) =>\n\t\tMath.max(0, coefficient * numAttempts + offset);\n\n/**\n * f(n) = C x (n+A) + F = C x n + (C x A + F)\n */\nexport const formLinearFnWithAttemptOffset = (\n\tattemptOffset: number,\n\t{ coefficient = 1, offset = 0 } = {},\n): IThrottler[\"delayFn\"] =>\n\tformLinearFn({\n\t\tcoefficient,\n\t\toffset: coefficient * attemptOffset + offset,\n\t});\n"]}
|
|
@@ -10,12 +10,14 @@ import { type InboundMessageResult } from "../opLifecycle/index.js";
|
|
|
10
10
|
*/
|
|
11
11
|
export interface InboundVersionMarkUpdate {
|
|
12
12
|
/**
|
|
13
|
-
* A completed batch to record via `processInboundBatch`: its id and its last op's sequence
|
|
14
|
-
* Undefined when no batch completed on this message (a piecemeal batch
|
|
13
|
+
* A completed batch to record via `processInboundBatch`: its id and its last op's sequence
|
|
14
|
+
* number and timestamp. Undefined when no batch completed on this message (a piecemeal batch
|
|
15
|
+
* still in progress).
|
|
15
16
|
*/
|
|
16
17
|
readonly sequenced?: {
|
|
17
18
|
readonly batchId: string;
|
|
18
19
|
readonly sequenceNumber: number;
|
|
20
|
+
readonly timestamp: number;
|
|
19
21
|
};
|
|
20
22
|
/**
|
|
21
23
|
* The batch id to carry for a piecemeal batch still in progress, or undefined when none is pending.
|
|
@@ -25,10 +27,10 @@ export interface InboundVersionMarkUpdate {
|
|
|
25
27
|
}
|
|
26
28
|
/**
|
|
27
29
|
* Maps an inbound batch result to the version-mark resolver update. A batch's resolved point is its last
|
|
28
|
-
* op's sequence number, so this reports `(batchId, sequenceNumber)` only once a
|
|
29
|
-
* both a batch delivered whole (`fullBatch`, incl. an empty grouped batch via
|
|
30
|
-
* and one delivered piecemeal (`batchStartingMessage` then `nextBatchMessage`
|
|
31
|
-
* batch id across the piecemeal messages via `carriedBatchId`.
|
|
30
|
+
* op's sequence number and timestamp, so this reports `(batchId, sequenceNumber, timestamp)` only once a
|
|
31
|
+
* batch completes. It handles both a batch delivered whole (`fullBatch`, incl. an empty grouped batch via
|
|
32
|
+
* the batch-start key message) and one delivered piecemeal (`batchStartingMessage` then `nextBatchMessage`
|
|
33
|
+
* with `batchEnd`), carrying the batch id across the piecemeal messages via `carriedBatchId`.
|
|
32
34
|
*
|
|
33
35
|
* @internal
|
|
34
36
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inboundBatch.d.ts","sourceRoot":"","sources":["../../src/versionMarks/inboundBatch.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAuB,KAAK,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAEzF;;;;GAIG;AACH,MAAM,WAAW,wBAAwB;IACxC
|
|
1
|
+
{"version":3,"file":"inboundBatch.d.ts","sourceRoot":"","sources":["../../src/versionMarks/inboundBatch.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAuB,KAAK,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAEzF;;;;GAIG;AACH,MAAM,WAAW,wBAAwB;IACxC;;;;OAIG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE;QACpB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;QACzB,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;QAChC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;KAC3B,CAAC;IACF;;;OAGG;IACH,QAAQ,CAAC,cAAc,EAAE,MAAM,GAAG,SAAS,CAAC;CAC5C;AAED;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CACvC,aAAa,EAAE,oBAAoB,EACnC,cAAc,EAAE,MAAM,GAAG,SAAS,GAChC,wBAAwB,CAqC1B"}
|
|
@@ -5,10 +5,10 @@
|
|
|
5
5
|
import { getEffectiveBatchId } from "../opLifecycle/index.js";
|
|
6
6
|
/**
|
|
7
7
|
* Maps an inbound batch result to the version-mark resolver update. A batch's resolved point is its last
|
|
8
|
-
* op's sequence number, so this reports `(batchId, sequenceNumber)` only once a
|
|
9
|
-
* both a batch delivered whole (`fullBatch`, incl. an empty grouped batch via
|
|
10
|
-
* and one delivered piecemeal (`batchStartingMessage` then `nextBatchMessage`
|
|
11
|
-
* batch id across the piecemeal messages via `carriedBatchId`.
|
|
8
|
+
* op's sequence number and timestamp, so this reports `(batchId, sequenceNumber, timestamp)` only once a
|
|
9
|
+
* batch completes. It handles both a batch delivered whole (`fullBatch`, incl. an empty grouped batch via
|
|
10
|
+
* the batch-start key message) and one delivered piecemeal (`batchStartingMessage` then `nextBatchMessage`
|
|
11
|
+
* with `batchEnd`), carrying the batch id across the piecemeal messages via `carriedBatchId`.
|
|
12
12
|
*
|
|
13
13
|
* @internal
|
|
14
14
|
*/
|
|
@@ -22,7 +22,11 @@ export function inboundVersionMarkUpdate(inboundResult, carriedBatchId) {
|
|
|
22
22
|
inboundResult.messages[inboundResult.messages.length - 1] ??
|
|
23
23
|
inboundResult.batchStart.keyMessage;
|
|
24
24
|
return {
|
|
25
|
-
sequenced: {
|
|
25
|
+
sequenced: {
|
|
26
|
+
batchId,
|
|
27
|
+
sequenceNumber: lastMessage.sequenceNumber,
|
|
28
|
+
timestamp: lastMessage.timestamp,
|
|
29
|
+
},
|
|
26
30
|
carriedBatchId: undefined,
|
|
27
31
|
};
|
|
28
32
|
}
|
|
@@ -36,6 +40,7 @@ export function inboundVersionMarkUpdate(inboundResult, carriedBatchId) {
|
|
|
36
40
|
sequenced: {
|
|
37
41
|
batchId: carriedBatchId,
|
|
38
42
|
sequenceNumber: inboundResult.nextMessage.sequenceNumber,
|
|
43
|
+
timestamp: inboundResult.nextMessage.timestamp,
|
|
39
44
|
},
|
|
40
45
|
carriedBatchId: undefined,
|
|
41
46
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inboundBatch.js","sourceRoot":"","sources":["../../src/versionMarks/inboundBatch.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,mBAAmB,EAA6B,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"inboundBatch.js","sourceRoot":"","sources":["../../src/versionMarks/inboundBatch.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,mBAAmB,EAA6B,MAAM,yBAAyB,CAAC;AAyBzF;;;;;;;;GAQG;AACH,MAAM,UAAU,wBAAwB,CACvC,aAAmC,EACnC,cAAkC;IAElC,IAAI,YAAY,IAAI,aAAa,EAAE,CAAC;QACnC,MAAM,OAAO,GAAG,mBAAmB,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC;QAC9D,IAAI,aAAa,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;YACxC,uGAAuG;YACvG,MAAM,WAAW;YAChB,uGAAuG;YACvG,aAAa,CAAC,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC;gBACzD,aAAa,CAAC,UAAU,CAAC,UAAU,CAAC;YACrC,OAAO;gBACN,SAAS,EAAE;oBACV,OAAO;oBACP,cAAc,EAAE,WAAW,CAAC,cAAc;oBAC1C,SAAS,EAAE,WAAW,CAAC,SAAS;iBAChC;gBACD,cAAc,EAAE,SAAS;aACzB,CAAC;QACH,CAAC;QACD,4EAA4E;QAC5E,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,CAAC;IACpC,CAAC;IACD,IACC,aAAa,CAAC,IAAI,KAAK,kBAAkB;QACzC,aAAa,CAAC,QAAQ,KAAK,IAAI;QAC/B,cAAc,KAAK,SAAS,EAC3B,CAAC;QACF,OAAO;YACN,SAAS,EAAE;gBACV,OAAO,EAAE,cAAc;gBACvB,cAAc,EAAE,aAAa,CAAC,WAAW,CAAC,cAAc;gBACxD,SAAS,EAAE,aAAa,CAAC,WAAW,CAAC,SAAS;aAC9C;YACD,cAAc,EAAE,SAAS;SACzB,CAAC;IACH,CAAC;IACD,qEAAqE;IACrE,OAAO,EAAE,cAAc,EAAE,CAAC;AAC3B,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { getEffectiveBatchId, type InboundMessageResult } from \"../opLifecycle/index.js\";\n\n/**\n * The version-mark resolver update derived from an inbound batch result.\n *\n * @internal\n */\nexport interface InboundVersionMarkUpdate {\n\t/**\n\t * A completed batch to record via `processInboundBatch`: its id and its last op's sequence\n\t * number and timestamp. Undefined when no batch completed on this message (a piecemeal batch\n\t * still in progress).\n\t */\n\treadonly sequenced?: {\n\t\treadonly batchId: string;\n\t\treadonly sequenceNumber: number;\n\t\treadonly timestamp: number;\n\t};\n\t/**\n\t * The batch id to carry for a piecemeal batch still in progress, or undefined when none is pending.\n\t * The caller stores this and passes it back on the next inbound result.\n\t */\n\treadonly carriedBatchId: string | undefined;\n}\n\n/**\n * Maps an inbound batch result to the version-mark resolver update. A batch's resolved point is its last\n * op's sequence number and timestamp, so this reports `(batchId, sequenceNumber, timestamp)` only once a\n * batch completes. It handles both a batch delivered whole (`fullBatch`, incl. an empty grouped batch via\n * the batch-start key message) and one delivered piecemeal (`batchStartingMessage` then `nextBatchMessage`\n * with `batchEnd`), carrying the batch id across the piecemeal messages via `carriedBatchId`.\n *\n * @internal\n */\nexport function inboundVersionMarkUpdate(\n\tinboundResult: InboundMessageResult,\n\tcarriedBatchId: string | undefined,\n): InboundVersionMarkUpdate {\n\tif (\"batchStart\" in inboundResult) {\n\t\tconst batchId = getEffectiveBatchId(inboundResult.batchStart);\n\t\tif (inboundResult.type === \"fullBatch\") {\n\t\t\t// The last op is the batch's resolved point; an empty batch falls back to the batch-start key message.\n\t\t\tconst lastMessage =\n\t\t\t\t// eslint-disable-next-line unicorn/prefer-at -- Array.prototype.at is not in this package's lib target\n\t\t\t\tinboundResult.messages[inboundResult.messages.length - 1] ??\n\t\t\t\tinboundResult.batchStart.keyMessage;\n\t\t\treturn {\n\t\t\t\tsequenced: {\n\t\t\t\t\tbatchId,\n\t\t\t\t\tsequenceNumber: lastMessage.sequenceNumber,\n\t\t\t\t\ttimestamp: lastMessage.timestamp,\n\t\t\t\t},\n\t\t\t\tcarriedBatchId: undefined,\n\t\t\t};\n\t\t}\n\t\t// batchStartingMessage: carry the id until the batch's end message arrives.\n\t\treturn { carriedBatchId: batchId };\n\t}\n\tif (\n\t\tinboundResult.type === \"nextBatchMessage\" &&\n\t\tinboundResult.batchEnd === true &&\n\t\tcarriedBatchId !== undefined\n\t) {\n\t\treturn {\n\t\t\tsequenced: {\n\t\t\t\tbatchId: carriedBatchId,\n\t\t\t\tsequenceNumber: inboundResult.nextMessage.sequenceNumber,\n\t\t\t\ttimestamp: inboundResult.nextMessage.timestamp,\n\t\t\t},\n\t\t\tcarriedBatchId: undefined,\n\t\t};\n\t}\n\t// A mid-batch message, or a batch end with no carried id: no change.\n\treturn { carriedBatchId };\n}\n"]}
|
|
@@ -15,13 +15,15 @@ export interface IHistoricalOpReader {
|
|
|
15
15
|
fetchMessages(from: number, to?: number, abortSignal?: AbortSignal): Promise<IStream<ISequencedDocumentMessage[]>>;
|
|
16
16
|
}
|
|
17
17
|
/**
|
|
18
|
-
* Result of resolving a pending batchId.
|
|
18
|
+
* Result of resolving a pending batchId. A resolved result includes the matched batch's last op server
|
|
19
|
+
* timestamp when available. The property is optional for compatibility with previously stored results.
|
|
19
20
|
*
|
|
20
21
|
* @legacy @beta
|
|
21
22
|
*/
|
|
22
23
|
export type ResolveResult = {
|
|
23
24
|
readonly kind: "resolved";
|
|
24
25
|
readonly sequenceNumber: number;
|
|
26
|
+
readonly timestamp?: number;
|
|
25
27
|
} | {
|
|
26
28
|
readonly kind: "pending";
|
|
27
29
|
} | {
|
|
@@ -42,6 +44,7 @@ export type VersionMarkCapture = {
|
|
|
42
44
|
} | {
|
|
43
45
|
readonly kind: "resolved";
|
|
44
46
|
readonly sequenceNumber: number;
|
|
47
|
+
readonly timestamp?: number;
|
|
45
48
|
};
|
|
46
49
|
/**
|
|
47
50
|
* Runtime-owned resolver for app-stored version mark locators.
|
|
@@ -53,40 +56,45 @@ export interface IVersionMarkResolver {
|
|
|
53
56
|
* Captures a version mark at the current point. Seals the current outbound batch first (so a just-made
|
|
54
57
|
* local edit has a stable `batchId`, which is only assigned when a batch is flushed), then returns the
|
|
55
58
|
* mark data atomically: a `pending` capture (`batchId` + `sequenceNumberLowerBound`) when there is an
|
|
56
|
-
* unacked local batch, or a `resolved` capture (`sequenceNumber`
|
|
59
|
+
* unacked local batch, or a `resolved` capture (`sequenceNumber` + the last processed op's server
|
|
60
|
+
* `timestamp`) when there is no in-flight local work. The timestamp property is optional both for
|
|
61
|
+
* compatibility with previously stored captures and because it is `undefined` when neither a last
|
|
62
|
+
* processed message nor a last-summary message is available.
|
|
57
63
|
*
|
|
58
64
|
* @remarks Sealing the batch is a side effect (it submits the current batch), so capture at savepoint
|
|
59
65
|
* boundaries, not per keystroke.
|
|
60
66
|
*
|
|
61
|
-
* @returns The pending batch identity and
|
|
62
|
-
* when there is no pending local batch.
|
|
67
|
+
* @returns The pending batch identity and inclusive sequence number lower bound, or the current sequence
|
|
68
|
+
* number and corresponding op timestamp (when available) when there is no pending local batch.
|
|
63
69
|
*/
|
|
64
70
|
sealAndCaptureVersionMark(): VersionMarkCapture;
|
|
65
71
|
/**
|
|
66
72
|
* Resolves a pending mark's batchId to a global sequence number (`sequenceNumberLowerBound` is the
|
|
67
|
-
*
|
|
73
|
+
* inclusive lower bound for a history read). A `resolved` sequence number feeds the loader's
|
|
68
74
|
* `loadContainerToSequenceNumber`.
|
|
69
75
|
*
|
|
70
76
|
* @param batchId - The stable identity of the pending batch.
|
|
71
|
-
* @param sequenceNumberLowerBound - The
|
|
72
|
-
* @returns The resolved sequence number, or a result indicating that the batch
|
|
73
|
-
* no longer be resolved.
|
|
77
|
+
* @param sequenceNumberLowerBound - The inclusive lower bound for the historical op search.
|
|
78
|
+
* @returns The resolved sequence number and server timestamp, or a result indicating that the batch
|
|
79
|
+
* remains pending or can no longer be resolved.
|
|
74
80
|
*/
|
|
75
81
|
resolve(batchId: string, sequenceNumberLowerBound: number): Promise<ResolveResult>;
|
|
76
82
|
/**
|
|
77
|
-
* Subscribes to inbound batch sequencing: fires `(batchId, sequenceNumber)` per batch so any
|
|
78
|
-
* client can promote a matching pending mark. Returns an unsubscribe function.
|
|
83
|
+
* Subscribes to inbound batch sequencing: fires `(batchId, sequenceNumber, timestamp)` per batch so any
|
|
84
|
+
* connected client can promote a matching pending mark. Returns an unsubscribe function.
|
|
79
85
|
*
|
|
80
|
-
* @param listener - Called with the stable batch identity
|
|
86
|
+
* @param listener - Called with the stable batch identity, its final sequence number, and the final op's
|
|
87
|
+
* server timestamp.
|
|
81
88
|
* @returns A function that unsubscribes the listener.
|
|
82
89
|
*/
|
|
83
|
-
onBatchSequenced(listener: (batchId: string, sequenceNumber: number) => void): () => void;
|
|
90
|
+
onBatchSequenced(listener: (batchId: string, sequenceNumber: number, timestamp?: number) => void): () => void;
|
|
84
91
|
}
|
|
85
92
|
/**
|
|
86
93
|
* @internal
|
|
87
94
|
*/
|
|
88
95
|
export interface VersionMarkResolverRuntimeHooks {
|
|
89
96
|
readonly getCurrentSequenceNumber: () => number;
|
|
97
|
+
readonly getCurrentTimestamp: () => number | undefined;
|
|
90
98
|
readonly getCurrentMinimumSequenceNumber: () => number;
|
|
91
99
|
readonly getCurrentPendingBatchId: () => string | undefined;
|
|
92
100
|
/** Logs faults from app-supplied `onBatchSequenced` listeners (see {@link VersionMarkResolver.processInboundBatch}). */
|
|
@@ -118,7 +126,7 @@ export interface VersionMarkResolverRuntimeHooks {
|
|
|
118
126
|
*/
|
|
119
127
|
export declare class VersionMarkResolver implements IVersionMarkResolver {
|
|
120
128
|
private readonly hooks;
|
|
121
|
-
private readonly
|
|
129
|
+
private readonly resolvedBatchById;
|
|
122
130
|
private readonly batchListeners;
|
|
123
131
|
/**
|
|
124
132
|
* Sticky flag: false until the feature is first used this session (a pending capture or a listener).
|
|
@@ -132,25 +140,24 @@ export declare class VersionMarkResolver implements IVersionMarkResolver {
|
|
|
132
140
|
sealAndCaptureVersionMark(): VersionMarkCapture;
|
|
133
141
|
resolve(batchId: string, sequenceNumberLowerBound: number): Promise<ResolveResult>;
|
|
134
142
|
/**
|
|
135
|
-
* Scans sequenced ops
|
|
143
|
+
* Scans sequenced ops from `sequenceNumberLowerBound` (inclusive) for `batchId`, returning its last op's
|
|
136
144
|
* sequence number when found and aborting the fetch once found or exhausted. Each op is routed through
|
|
137
145
|
* the same unpack pipeline the live inbound path uses (chunk reassembly, ungroup, decompress) and
|
|
138
|
-
* {@link inboundVersionMarkUpdate} derives its batch identity, so
|
|
139
|
-
*
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
* See DEV.md.
|
|
146
|
+
* {@link inboundVersionMarkUpdate} derives its batch identity, so virtualized batches resolve here too.
|
|
147
|
+
* The injected historical unpacker owns virtualization details, including tolerating a requested range
|
|
148
|
+
* that begins within an incomplete chunk stream. On a miss, {@link classifyMiss} distinguishes
|
|
149
|
+
* `pending` (not sequenced yet) from `unresolvable` (ops trimmed).
|
|
143
150
|
*/
|
|
144
151
|
private resolveFromHistory;
|
|
145
152
|
/**
|
|
146
153
|
* Classifies a history-scan miss as `pending` or `unresolvable`. See {@link resolveFromHistory}.
|
|
147
154
|
*/
|
|
148
155
|
private classifyMiss;
|
|
149
|
-
onBatchSequenced(listener: (batchId: string, sequenceNumber: number) => void): () => void;
|
|
150
|
-
/** The
|
|
151
|
-
private
|
|
156
|
+
onBatchSequenced(listener: (batchId: string, sequenceNumber: number, timestamp?: number) => void): () => void;
|
|
157
|
+
/** The resolved point for a batch seen live this session, or undefined. */
|
|
158
|
+
private sessionResolutionFor;
|
|
152
159
|
/** Records an inbound batch and notifies listeners so any client can promote matching pending marks. */
|
|
153
|
-
processInboundBatch(batchId: string, sequenceNumber: number): void;
|
|
160
|
+
processInboundBatch(batchId: string, sequenceNumber: number, timestamp: number): void;
|
|
154
161
|
/**
|
|
155
162
|
* Bounds the map to the collaboration window by dropping entries below the MSN (mirrors
|
|
156
163
|
* `DuplicateBatchDetector`). Safe because below the MSN every client has processed the batch, so its
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"versionMarkResolver.d.ts","sourceRoot":"","sources":["../../src/versionMarks/versionMarkResolver.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EACX,yBAAyB,EACzB,OAAO,EACP,MAAM,6CAA6C,CAAC;AAGrD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0CAA0C,CAAC;AAEnF,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"versionMarkResolver.d.ts","sourceRoot":"","sources":["../../src/versionMarks/versionMarkResolver.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
import { assert } from "@fluidframework/core-utils/internal";
|
|
6
|
-
import { asBatchMetadata } from "../metadata.js";
|
|
7
6
|
import { inboundVersionMarkUpdate } from "./inboundBatch.js";
|
|
8
7
|
/**
|
|
9
8
|
* Resolves a mark's batchId to a global sequence number. The app owns mark storage and promotion; the
|
|
@@ -12,16 +11,17 @@ import { inboundVersionMarkUpdate } from "./inboundBatch.js";
|
|
|
12
11
|
* @internal
|
|
13
12
|
*/
|
|
14
13
|
export class VersionMarkResolver {
|
|
14
|
+
hooks;
|
|
15
|
+
resolvedBatchById = new Map();
|
|
16
|
+
batchListeners = new Set();
|
|
17
|
+
/**
|
|
18
|
+
* Sticky flag: false until the feature is first used this session (a pending capture or a listener).
|
|
19
|
+
* While false the runtime skips all per-inbound-batch tracking, so a container that never uses version
|
|
20
|
+
* marks pays nothing on the hot path (mirrors #22497 gating `DuplicateBatchDetector` on offline load).
|
|
21
|
+
*/
|
|
22
|
+
tracking = false;
|
|
15
23
|
constructor(hooks) {
|
|
16
24
|
this.hooks = hooks;
|
|
17
|
-
this.sequenceNumberByBatchId = new Map();
|
|
18
|
-
this.batchListeners = new Set();
|
|
19
|
-
/**
|
|
20
|
-
* Sticky flag: false until the feature is first used this session (a pending capture or a listener).
|
|
21
|
-
* While false the runtime skips all per-inbound-batch tracking, so a container that never uses version
|
|
22
|
-
* marks pays nothing on the hot path (mirrors #22497 gating `DuplicateBatchDetector` on offline load).
|
|
23
|
-
*/
|
|
24
|
-
this.tracking = false;
|
|
25
25
|
}
|
|
26
26
|
/** Whether inbound batches need tracking (see {@link tracking}). Read by the runtime to gate the hot path. */
|
|
27
27
|
get isTracking() {
|
|
@@ -31,50 +31,106 @@ export class VersionMarkResolver {
|
|
|
31
31
|
// Seal the current batch so a just-submitted local edit is flushed into the pending state with a
|
|
32
32
|
// stable batchId (which is only assigned at flush) before we read it.
|
|
33
33
|
this.hooks.flushPendingBatch();
|
|
34
|
-
const
|
|
34
|
+
const referenceSequenceNumber = this.hooks.getCurrentSequenceNumber();
|
|
35
35
|
const batchId = this.hooks.getCurrentPendingBatchId();
|
|
36
|
+
let result;
|
|
36
37
|
if (batchId === undefined) {
|
|
37
38
|
// No unacked local batch: the mark already points at a durable sequence number.
|
|
38
|
-
|
|
39
|
+
result = {
|
|
40
|
+
kind: "resolved",
|
|
41
|
+
sequenceNumber: referenceSequenceNumber,
|
|
42
|
+
timestamp: this.hooks.getCurrentTimestamp(),
|
|
43
|
+
};
|
|
39
44
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
45
|
+
else {
|
|
46
|
+
// A pending mark needs its batch tracked so resolve() can promote it from the live map.
|
|
47
|
+
this.tracking = true;
|
|
48
|
+
// The pending batch is sequenced after the reference point, so its first possible sequence
|
|
49
|
+
// number is `referenceSequenceNumber + 1`. Store that as an inclusive lower bound so resolve()
|
|
50
|
+
// scans directly from it.
|
|
51
|
+
result = {
|
|
52
|
+
kind: "pending",
|
|
53
|
+
batchId,
|
|
54
|
+
sequenceNumberLowerBound: referenceSequenceNumber + 1,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
this.hooks.logger.sendTelemetryEvent({ eventName: "Capture", kind: result.kind });
|
|
58
|
+
return result;
|
|
43
59
|
}
|
|
44
60
|
async resolve(batchId, sequenceNumberLowerBound) {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
61
|
+
const startTime = Date.now();
|
|
62
|
+
// Track from here so inbound batches are recorded even without a prior capture/subscribe; otherwise
|
|
63
|
+
// a batch sequencing during the scan (or live, in the no-reader case) is skipped and unrecoverable.
|
|
64
|
+
this.tracking = true;
|
|
65
|
+
// Defaults cover the throw path (only the history scan can throw — e.g. an unpacker
|
|
66
|
+
// DataCorruptionError or the 0xd1c reader-contract assert): the Resolve event still fires via
|
|
67
|
+
// `finally`, with outcome "error".
|
|
68
|
+
let path = "history";
|
|
69
|
+
let outcome = "error";
|
|
70
|
+
let resolvedSequenceNumber;
|
|
71
|
+
try {
|
|
72
|
+
// Fast path: batch sequenced live this session.
|
|
73
|
+
const resolvedBatch = this.sessionResolutionFor(batchId);
|
|
74
|
+
if (resolvedBatch === undefined) {
|
|
75
|
+
const reader = this.hooks.getHistoricalOpReader?.();
|
|
76
|
+
if (reader === undefined) {
|
|
77
|
+
// No reader: the batch may still sequence live, so report pending.
|
|
78
|
+
path = "noReader";
|
|
79
|
+
outcome = "pending";
|
|
80
|
+
return { kind: "pending" };
|
|
81
|
+
}
|
|
82
|
+
// Otherwise scan history from the mark's reference point.
|
|
83
|
+
path = "history";
|
|
84
|
+
let result = await this.resolveFromHistory(reader, batchId, sequenceNumberLowerBound);
|
|
85
|
+
if (result.kind !== "resolved") {
|
|
86
|
+
// The batch may have sequenced live while the history scan was in progress. Prefer that
|
|
87
|
+
// authoritative in-session result over a stale history miss.
|
|
88
|
+
const liveResult = this.sessionResolutionFor(batchId);
|
|
89
|
+
if (liveResult !== undefined) {
|
|
90
|
+
path = "session";
|
|
91
|
+
result = { kind: "resolved", ...liveResult };
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
outcome = result.kind;
|
|
95
|
+
if (result.kind === "resolved") {
|
|
96
|
+
resolvedSequenceNumber = result.sequenceNumber;
|
|
97
|
+
}
|
|
98
|
+
return result;
|
|
99
|
+
}
|
|
100
|
+
path = "session";
|
|
101
|
+
outcome = "resolved";
|
|
102
|
+
resolvedSequenceNumber = resolvedBatch.sequenceNumber;
|
|
103
|
+
return { kind: "resolved", ...resolvedBatch };
|
|
49
104
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
105
|
+
finally {
|
|
106
|
+
this.hooks.logger.sendTelemetryEvent({
|
|
107
|
+
eventName: "Resolve",
|
|
108
|
+
outcome,
|
|
109
|
+
path,
|
|
110
|
+
durationMs: Date.now() - startTime,
|
|
111
|
+
...(resolvedSequenceNumber === undefined
|
|
112
|
+
? {}
|
|
113
|
+
: { sequenceNumber: resolvedSequenceNumber }),
|
|
114
|
+
});
|
|
55
115
|
}
|
|
56
|
-
return this.resolveFromHistory(reader, batchId, sequenceNumberLowerBound);
|
|
57
116
|
}
|
|
58
117
|
/**
|
|
59
|
-
* Scans sequenced ops
|
|
118
|
+
* Scans sequenced ops from `sequenceNumberLowerBound` (inclusive) for `batchId`, returning its last op's
|
|
60
119
|
* sequence number when found and aborting the fetch once found or exhausted. Each op is routed through
|
|
61
120
|
* the same unpack pipeline the live inbound path uses (chunk reassembly, ungroup, decompress) and
|
|
62
|
-
* {@link inboundVersionMarkUpdate} derives its batch identity, so
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
* See DEV.md.
|
|
121
|
+
* {@link inboundVersionMarkUpdate} derives its batch identity, so virtualized batches resolve here too.
|
|
122
|
+
* The injected historical unpacker owns virtualization details, including tolerating a requested range
|
|
123
|
+
* that begins within an incomplete chunk stream. On a miss, {@link classifyMiss} distinguishes
|
|
124
|
+
* `pending` (not sequenced yet) from `unresolvable` (ops trimmed).
|
|
67
125
|
*/
|
|
68
126
|
async resolveFromHistory(reader, batchId, sequenceNumberLowerBound) {
|
|
69
|
-
const from = sequenceNumberLowerBound
|
|
127
|
+
const from = sequenceNumberLowerBound;
|
|
70
128
|
const unpack = this.hooks.createHistoricalOpUnpacker?.();
|
|
71
129
|
const abortController = new AbortController();
|
|
72
130
|
try {
|
|
73
131
|
const stream = await reader.fetchMessages(from, undefined, abortController.signal);
|
|
74
132
|
let firstScannedSequenceNumber;
|
|
75
133
|
let carriedBatchId;
|
|
76
|
-
// Mirrors the processor's batch-in-progress state so we can drop an orphan end marker (below).
|
|
77
|
-
let inBatch = false;
|
|
78
134
|
while (true) {
|
|
79
135
|
const result = await stream.read();
|
|
80
136
|
if (result.done) {
|
|
@@ -82,31 +138,21 @@ export class VersionMarkResolver {
|
|
|
82
138
|
}
|
|
83
139
|
for (const op of result.value) {
|
|
84
140
|
firstScannedSequenceNumber ??= op.sequenceNumber;
|
|
85
|
-
|
|
86
|
-
// The anchor (`sequenceNumberLowerBound + 1`) may land inside a batch whose start
|
|
87
|
-
// precedes the window; its orphan end marker would trip the processor's 0x9d5 assert.
|
|
88
|
-
// Drop the clipped tail. The target batch is fully in-window, so this can't skip it.
|
|
89
|
-
continue;
|
|
90
|
-
}
|
|
91
|
-
// undefined = a system op, or an incomplete chunk awaiting later fragments.
|
|
141
|
+
// undefined = a system op or virtualized input that has not produced a complete result.
|
|
92
142
|
const inboundResult = unpack?.(op);
|
|
93
143
|
if (inboundResult === undefined) {
|
|
94
144
|
continue;
|
|
95
145
|
}
|
|
96
|
-
// Track batch progress with the processor so the guard above skips only orphan ends.
|
|
97
|
-
if (inboundResult.type === "batchStartingMessage") {
|
|
98
|
-
inBatch = true;
|
|
99
|
-
}
|
|
100
|
-
else if (inboundResult.type === "nextBatchMessage" &&
|
|
101
|
-
inboundResult.batchEnd === true) {
|
|
102
|
-
inBatch = false;
|
|
103
|
-
}
|
|
104
146
|
// Derive batch identity exactly as the live inbound path does, carrying the id across a
|
|
105
147
|
// piecemeal batch's messages.
|
|
106
148
|
const update = inboundVersionMarkUpdate(inboundResult, carriedBatchId);
|
|
107
149
|
carriedBatchId = update.carriedBatchId;
|
|
108
150
|
if (update.sequenced?.batchId === batchId) {
|
|
109
|
-
return {
|
|
151
|
+
return {
|
|
152
|
+
kind: "resolved",
|
|
153
|
+
sequenceNumber: update.sequenced.sequenceNumber,
|
|
154
|
+
timestamp: update.sequenced.timestamp,
|
|
155
|
+
};
|
|
110
156
|
}
|
|
111
157
|
}
|
|
112
158
|
}
|
|
@@ -127,7 +173,7 @@ export class VersionMarkResolver {
|
|
|
127
173
|
assert(firstScannedSequenceNumber === undefined || firstScannedSequenceNumber >= from, 0xd1c /* historical op reader returned an op before the requested range start */);
|
|
128
174
|
const tip = this.hooks.getCurrentSequenceNumber();
|
|
129
175
|
if (from > tip) {
|
|
130
|
-
// Nothing is sequenced at/after the mark's
|
|
176
|
+
// Nothing is sequenced at/after the mark's lower bound yet, so the batch cannot have landed.
|
|
131
177
|
return { kind: "pending" };
|
|
132
178
|
}
|
|
133
179
|
if (firstScannedSequenceNumber === undefined) {
|
|
@@ -137,10 +183,10 @@ export class VersionMarkResolver {
|
|
|
137
183
|
return { kind: "unresolvable" };
|
|
138
184
|
}
|
|
139
185
|
if (firstScannedSequenceNumber > from) {
|
|
140
|
-
// A trim gap at the anchor: the mark's ops (
|
|
186
|
+
// A trim gap at the anchor: the mark's ops (at/after its lower bound) are gone.
|
|
141
187
|
return { kind: "unresolvable" };
|
|
142
188
|
}
|
|
143
|
-
// Ops are present from the
|
|
189
|
+
// Ops are present from the lower bound and the batch is not among them: not yet sequenced.
|
|
144
190
|
return { kind: "pending" };
|
|
145
191
|
}
|
|
146
192
|
onBatchSequenced(listener) {
|
|
@@ -151,21 +197,24 @@ export class VersionMarkResolver {
|
|
|
151
197
|
this.batchListeners.delete(listener);
|
|
152
198
|
};
|
|
153
199
|
}
|
|
154
|
-
/** The
|
|
155
|
-
|
|
156
|
-
return this.
|
|
200
|
+
/** The resolved point for a batch seen live this session, or undefined. */
|
|
201
|
+
sessionResolutionFor(batchId) {
|
|
202
|
+
return this.resolvedBatchById.get(batchId);
|
|
157
203
|
}
|
|
158
204
|
/** Records an inbound batch and notifies listeners so any client can promote matching pending marks. */
|
|
159
|
-
processInboundBatch(batchId, sequenceNumber) {
|
|
160
|
-
const
|
|
161
|
-
if (
|
|
205
|
+
processInboundBatch(batchId, sequenceNumber, timestamp) {
|
|
206
|
+
const existingResolution = this.resolvedBatchById.get(batchId);
|
|
207
|
+
if (existingResolution !== undefined) {
|
|
208
|
+
// NOTE: It's possible (but exceedingly rare) that this is a spurious duplicated batch,
|
|
209
|
+
// with a different sequence number/timestamp.
|
|
210
|
+
// Rather than asserting or overwriting, we let the earlier version mark stand, it is sufficient.
|
|
162
211
|
return;
|
|
163
212
|
}
|
|
164
|
-
this.
|
|
213
|
+
this.resolvedBatchById.set(batchId, { sequenceNumber, timestamp });
|
|
165
214
|
this.evictBelowMinimumSequenceNumber();
|
|
166
215
|
for (const listener of this.batchListeners) {
|
|
167
216
|
try {
|
|
168
|
-
listener(batchId, sequenceNumber);
|
|
217
|
+
listener(batchId, sequenceNumber, timestamp);
|
|
169
218
|
}
|
|
170
219
|
catch (error) {
|
|
171
220
|
// Isolate each app listener (like the container's EventEmitterWithErrorHandling): one throw
|
|
@@ -183,11 +232,11 @@ export class VersionMarkResolver {
|
|
|
183
232
|
*/
|
|
184
233
|
evictBelowMinimumSequenceNumber() {
|
|
185
234
|
const minimumSequenceNumber = this.hooks.getCurrentMinimumSequenceNumber();
|
|
186
|
-
for (const [id,
|
|
187
|
-
if (sequenceNumber >= minimumSequenceNumber) {
|
|
235
|
+
for (const [id, resolution] of this.resolvedBatchById) {
|
|
236
|
+
if (resolution.sequenceNumber >= minimumSequenceNumber) {
|
|
188
237
|
break;
|
|
189
238
|
}
|
|
190
|
-
this.
|
|
239
|
+
this.resolvedBatchById.delete(id);
|
|
191
240
|
}
|
|
192
241
|
}
|
|
193
242
|
}
|