@fluidframework/container-runtime 2.0.0-dev.4.1.0.148229 → 2.0.0-dev.4.3.0.157531
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 +58 -0
- package/README.md +69 -0
- package/dist/blobManager.d.ts +6 -14
- package/dist/blobManager.d.ts.map +1 -1
- package/dist/blobManager.js +50 -37
- package/dist/blobManager.js.map +1 -1
- package/dist/containerRuntime.d.ts +47 -4
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +203 -49
- package/dist/containerRuntime.js.map +1 -1
- package/dist/dataStoreContext.d.ts +2 -1
- package/dist/dataStoreContext.d.ts.map +1 -1
- package/dist/dataStoreContext.js +3 -0
- package/dist/dataStoreContext.js.map +1 -1
- package/dist/dataStores.d.ts +5 -5
- package/dist/dataStores.d.ts.map +1 -1
- package/dist/dataStores.js +3 -6
- package/dist/dataStores.js.map +1 -1
- package/dist/gc/garbageCollection.d.ts.map +1 -1
- package/dist/gc/garbageCollection.js +5 -5
- package/dist/gc/garbageCollection.js.map +1 -1
- package/dist/gc/gcConfigs.d.ts.map +1 -1
- package/dist/gc/gcConfigs.js +1 -3
- package/dist/gc/gcConfigs.js.map +1 -1
- package/dist/gc/gcDefinitions.js +1 -1
- package/dist/gc/gcDefinitions.js.map +1 -1
- package/dist/gc/gcHelpers.d.ts.map +1 -1
- package/dist/gc/gcHelpers.js +6 -6
- package/dist/gc/gcHelpers.js.map +1 -1
- package/dist/id-compressor/appendOnlySortedMap.d.ts +146 -0
- package/dist/id-compressor/appendOnlySortedMap.d.ts.map +1 -0
- package/dist/id-compressor/appendOnlySortedMap.js +360 -0
- package/dist/id-compressor/appendOnlySortedMap.js.map +1 -0
- package/dist/id-compressor/idCompressor.d.ts +279 -0
- package/dist/id-compressor/idCompressor.d.ts.map +1 -0
- package/dist/id-compressor/idCompressor.js +1258 -0
- package/dist/id-compressor/idCompressor.js.map +1 -0
- package/dist/id-compressor/idRange.d.ts +11 -0
- package/dist/id-compressor/idRange.d.ts.map +1 -0
- package/dist/id-compressor/idRange.js +29 -0
- package/dist/id-compressor/idRange.js.map +1 -0
- package/dist/id-compressor/index.d.ts +14 -0
- package/dist/id-compressor/index.d.ts.map +1 -0
- package/dist/id-compressor/index.js +38 -0
- package/dist/id-compressor/index.js.map +1 -0
- package/dist/id-compressor/numericUuid.d.ts +59 -0
- package/dist/id-compressor/numericUuid.d.ts.map +1 -0
- package/dist/id-compressor/numericUuid.js +325 -0
- package/dist/id-compressor/numericUuid.js.map +1 -0
- package/dist/id-compressor/sessionIdNormalizer.d.ts +138 -0
- package/dist/id-compressor/sessionIdNormalizer.d.ts.map +1 -0
- package/dist/id-compressor/sessionIdNormalizer.js +488 -0
- package/dist/id-compressor/sessionIdNormalizer.js.map +1 -0
- package/dist/id-compressor/utils.d.ts +57 -0
- package/dist/id-compressor/utils.d.ts.map +1 -0
- package/dist/id-compressor/utils.js +90 -0
- package/dist/id-compressor/utils.js.map +1 -0
- package/dist/id-compressor/uuidUtilities.d.ts +30 -0
- package/dist/id-compressor/uuidUtilities.d.ts.map +1 -0
- package/dist/id-compressor/uuidUtilities.js +106 -0
- package/dist/id-compressor/uuidUtilities.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/opLifecycle/batchManager.d.ts +9 -2
- package/dist/opLifecycle/batchManager.d.ts.map +1 -1
- package/dist/opLifecycle/batchManager.js +21 -2
- package/dist/opLifecycle/batchManager.js.map +1 -1
- package/dist/opLifecycle/index.d.ts +2 -1
- package/dist/opLifecycle/index.d.ts.map +1 -1
- package/dist/opLifecycle/index.js +3 -1
- package/dist/opLifecycle/index.js.map +1 -1
- package/dist/opLifecycle/opDecompressor.d.ts.map +1 -1
- package/dist/opLifecycle/opDecompressor.js +2 -1
- package/dist/opLifecycle/opDecompressor.js.map +1 -1
- package/dist/opLifecycle/opGroupingManager.d.ts +14 -0
- package/dist/opLifecycle/opGroupingManager.d.ts.map +1 -0
- package/dist/opLifecycle/opGroupingManager.js +61 -0
- package/dist/opLifecycle/opGroupingManager.js.map +1 -0
- package/dist/opLifecycle/opSplitter.d.ts +1 -1
- package/dist/opLifecycle/opSplitter.d.ts.map +1 -1
- package/dist/opLifecycle/opSplitter.js +5 -6
- package/dist/opLifecycle/opSplitter.js.map +1 -1
- package/dist/opLifecycle/outbox.d.ts +4 -2
- package/dist/opLifecycle/outbox.d.ts.map +1 -1
- package/dist/opLifecycle/outbox.js +37 -25
- package/dist/opLifecycle/outbox.js.map +1 -1
- package/dist/opLifecycle/remoteMessageProcessor.d.ts +4 -2
- package/dist/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
- package/dist/opLifecycle/remoteMessageProcessor.js +30 -20
- package/dist/opLifecycle/remoteMessageProcessor.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/pendingStateManager.d.ts +1 -1
- package/dist/pendingStateManager.d.ts.map +1 -1
- package/dist/pendingStateManager.js +11 -3
- package/dist/pendingStateManager.js.map +1 -1
- package/dist/summary/index.d.ts +2 -2
- package/dist/summary/index.d.ts.map +1 -1
- package/dist/summary/index.js +4 -1
- package/dist/summary/index.js.map +1 -1
- package/dist/summary/orderedClientElection.d.ts +1 -0
- package/dist/summary/orderedClientElection.d.ts.map +1 -1
- package/dist/summary/orderedClientElection.js +19 -0
- package/dist/summary/orderedClientElection.js.map +1 -1
- package/dist/summary/runningSummarizer.d.ts +4 -3
- package/dist/summary/runningSummarizer.d.ts.map +1 -1
- package/dist/summary/runningSummarizer.js +65 -66
- package/dist/summary/runningSummarizer.js.map +1 -1
- package/dist/summary/summarizer.d.ts.map +1 -1
- package/dist/summary/summarizer.js +1 -5
- package/dist/summary/summarizer.js.map +1 -1
- package/dist/summary/summarizerHeuristics.d.ts +1 -0
- package/dist/summary/summarizerHeuristics.d.ts.map +1 -1
- package/dist/summary/summarizerHeuristics.js +3 -0
- package/dist/summary/summarizerHeuristics.js.map +1 -1
- package/dist/summary/summarizerNode/summarizerNode.js +1 -1
- package/dist/summary/summarizerNode/summarizerNode.js.map +1 -1
- package/dist/summary/summarizerNode/summarizerNodeWithGc.d.ts +128 -2
- package/dist/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
- package/dist/summary/summarizerNode/summarizerNodeWithGc.js +4 -3
- package/dist/summary/summarizerNode/summarizerNodeWithGc.js.map +1 -1
- package/dist/summary/summarizerTypes.d.ts +14 -2
- package/dist/summary/summarizerTypes.d.ts.map +1 -1
- package/dist/summary/summarizerTypes.js.map +1 -1
- package/dist/summary/summaryFormat.d.ts +3 -0
- package/dist/summary/summaryFormat.d.ts.map +1 -1
- package/dist/summary/summaryFormat.js +3 -1
- package/dist/summary/summaryFormat.js.map +1 -1
- package/dist/summary/summaryGenerator.d.ts +28 -2
- package/dist/summary/summaryGenerator.d.ts.map +1 -1
- package/dist/summary/summaryGenerator.js +19 -16
- package/dist/summary/summaryGenerator.js.map +1 -1
- package/dist/summary/summaryManager.d.ts.map +1 -1
- package/dist/summary/summaryManager.js +2 -0
- package/dist/summary/summaryManager.js.map +1 -1
- package/lib/blobManager.d.ts +6 -14
- package/lib/blobManager.d.ts.map +1 -1
- package/lib/blobManager.js +50 -37
- package/lib/blobManager.js.map +1 -1
- package/lib/containerRuntime.d.ts +47 -4
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +187 -52
- package/lib/containerRuntime.js.map +1 -1
- package/lib/dataStoreContext.d.ts +2 -1
- package/lib/dataStoreContext.d.ts.map +1 -1
- package/lib/dataStoreContext.js +3 -0
- package/lib/dataStoreContext.js.map +1 -1
- package/lib/dataStores.d.ts +5 -5
- package/lib/dataStores.d.ts.map +1 -1
- package/lib/dataStores.js +3 -6
- package/lib/dataStores.js.map +1 -1
- package/lib/gc/garbageCollection.d.ts.map +1 -1
- package/lib/gc/garbageCollection.js +5 -5
- package/lib/gc/garbageCollection.js.map +1 -1
- package/lib/gc/gcConfigs.d.ts.map +1 -1
- package/lib/gc/gcConfigs.js +1 -3
- package/lib/gc/gcConfigs.js.map +1 -1
- package/lib/gc/gcDefinitions.js +1 -1
- package/lib/gc/gcDefinitions.js.map +1 -1
- package/lib/gc/gcHelpers.d.ts.map +1 -1
- package/lib/gc/gcHelpers.js +6 -6
- package/lib/gc/gcHelpers.js.map +1 -1
- package/lib/id-compressor/appendOnlySortedMap.d.ts +146 -0
- package/lib/id-compressor/appendOnlySortedMap.d.ts.map +1 -0
- package/lib/id-compressor/appendOnlySortedMap.js +355 -0
- package/lib/id-compressor/appendOnlySortedMap.js.map +1 -0
- package/lib/id-compressor/idCompressor.d.ts +279 -0
- package/lib/id-compressor/idCompressor.d.ts.map +1 -0
- package/lib/id-compressor/idCompressor.js +1248 -0
- package/lib/id-compressor/idCompressor.js.map +1 -0
- package/lib/id-compressor/idRange.d.ts +11 -0
- package/lib/id-compressor/idRange.d.ts.map +1 -0
- package/lib/id-compressor/idRange.js +25 -0
- package/lib/id-compressor/idRange.js.map +1 -0
- package/lib/id-compressor/index.d.ts +14 -0
- package/lib/id-compressor/index.d.ts.map +1 -0
- package/lib/id-compressor/index.js +14 -0
- package/lib/id-compressor/index.js.map +1 -0
- package/lib/id-compressor/numericUuid.d.ts +59 -0
- package/lib/id-compressor/numericUuid.d.ts.map +1 -0
- package/lib/id-compressor/numericUuid.js +315 -0
- package/lib/id-compressor/numericUuid.js.map +1 -0
- package/lib/id-compressor/sessionIdNormalizer.d.ts +138 -0
- package/lib/id-compressor/sessionIdNormalizer.d.ts.map +1 -0
- package/lib/id-compressor/sessionIdNormalizer.js +484 -0
- package/lib/id-compressor/sessionIdNormalizer.js.map +1 -0
- package/lib/id-compressor/utils.d.ts +57 -0
- package/lib/id-compressor/utils.d.ts.map +1 -0
- package/lib/id-compressor/utils.js +79 -0
- package/lib/id-compressor/utils.js.map +1 -0
- package/lib/id-compressor/uuidUtilities.d.ts +30 -0
- package/lib/id-compressor/uuidUtilities.d.ts.map +1 -0
- package/lib/id-compressor/uuidUtilities.js +98 -0
- package/lib/id-compressor/uuidUtilities.js.map +1 -0
- package/lib/index.d.ts +1 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -0
- package/lib/index.js.map +1 -1
- package/lib/opLifecycle/batchManager.d.ts +9 -2
- package/lib/opLifecycle/batchManager.d.ts.map +1 -1
- package/lib/opLifecycle/batchManager.js +19 -1
- package/lib/opLifecycle/batchManager.js.map +1 -1
- package/lib/opLifecycle/index.d.ts +2 -1
- package/lib/opLifecycle/index.d.ts.map +1 -1
- package/lib/opLifecycle/index.js +1 -0
- package/lib/opLifecycle/index.js.map +1 -1
- package/lib/opLifecycle/opDecompressor.d.ts.map +1 -1
- package/lib/opLifecycle/opDecompressor.js +2 -1
- package/lib/opLifecycle/opDecompressor.js.map +1 -1
- package/lib/opLifecycle/opGroupingManager.d.ts +14 -0
- package/lib/opLifecycle/opGroupingManager.d.ts.map +1 -0
- package/lib/opLifecycle/opGroupingManager.js +57 -0
- package/lib/opLifecycle/opGroupingManager.js.map +1 -0
- package/lib/opLifecycle/opSplitter.d.ts +1 -1
- package/lib/opLifecycle/opSplitter.d.ts.map +1 -1
- package/lib/opLifecycle/opSplitter.js +5 -6
- package/lib/opLifecycle/opSplitter.js.map +1 -1
- package/lib/opLifecycle/outbox.d.ts +4 -2
- package/lib/opLifecycle/outbox.d.ts.map +1 -1
- package/lib/opLifecycle/outbox.js +38 -26
- package/lib/opLifecycle/outbox.js.map +1 -1
- package/lib/opLifecycle/remoteMessageProcessor.d.ts +4 -2
- package/lib/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
- package/lib/opLifecycle/remoteMessageProcessor.js +30 -20
- package/lib/opLifecycle/remoteMessageProcessor.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/pendingStateManager.d.ts +1 -1
- package/lib/pendingStateManager.d.ts.map +1 -1
- package/lib/pendingStateManager.js +11 -3
- package/lib/pendingStateManager.js.map +1 -1
- package/lib/summary/index.d.ts +2 -2
- package/lib/summary/index.d.ts.map +1 -1
- package/lib/summary/index.js +2 -1
- package/lib/summary/index.js.map +1 -1
- package/lib/summary/orderedClientElection.d.ts +1 -0
- package/lib/summary/orderedClientElection.d.ts.map +1 -1
- package/lib/summary/orderedClientElection.js +19 -0
- package/lib/summary/orderedClientElection.js.map +1 -1
- package/lib/summary/runningSummarizer.d.ts +4 -3
- package/lib/summary/runningSummarizer.d.ts.map +1 -1
- package/lib/summary/runningSummarizer.js +65 -66
- package/lib/summary/runningSummarizer.js.map +1 -1
- package/lib/summary/summarizer.d.ts.map +1 -1
- package/lib/summary/summarizer.js +1 -5
- package/lib/summary/summarizer.js.map +1 -1
- package/lib/summary/summarizerHeuristics.d.ts +1 -0
- package/lib/summary/summarizerHeuristics.d.ts.map +1 -1
- package/lib/summary/summarizerHeuristics.js +3 -0
- package/lib/summary/summarizerHeuristics.js.map +1 -1
- package/lib/summary/summarizerNode/summarizerNode.js +1 -1
- package/lib/summary/summarizerNode/summarizerNode.js.map +1 -1
- package/lib/summary/summarizerNode/summarizerNodeWithGc.d.ts +128 -2
- package/lib/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
- package/lib/summary/summarizerNode/summarizerNodeWithGc.js +3 -3
- package/lib/summary/summarizerNode/summarizerNodeWithGc.js.map +1 -1
- package/lib/summary/summarizerTypes.d.ts +14 -2
- package/lib/summary/summarizerTypes.d.ts.map +1 -1
- package/lib/summary/summarizerTypes.js.map +1 -1
- package/lib/summary/summaryFormat.d.ts +3 -0
- package/lib/summary/summaryFormat.d.ts.map +1 -1
- package/lib/summary/summaryFormat.js +2 -0
- package/lib/summary/summaryFormat.js.map +1 -1
- package/lib/summary/summaryGenerator.d.ts +28 -2
- package/lib/summary/summaryGenerator.d.ts.map +1 -1
- package/lib/summary/summaryGenerator.js +17 -15
- package/lib/summary/summaryGenerator.js.map +1 -1
- package/lib/summary/summaryManager.d.ts.map +1 -1
- package/lib/summary/summaryManager.js +2 -0
- package/lib/summary/summaryManager.js.map +1 -1
- package/package.json +29 -17
- package/src/blobManager.ts +64 -41
- package/src/containerRuntime.ts +294 -65
- package/src/dataStoreContext.ts +6 -0
- package/src/dataStores.ts +4 -7
- package/src/gc/garbageCollection.ts +7 -6
- package/src/gc/gcConfigs.ts +1 -3
- package/src/gc/gcDefinitions.ts +1 -1
- package/src/gc/gcHelpers.ts +9 -6
- package/src/id-compressor/README.md +3 -0
- package/src/id-compressor/appendOnlySortedMap.ts +427 -0
- package/src/id-compressor/idCompressor.ts +1854 -0
- package/src/id-compressor/idRange.ts +35 -0
- package/src/id-compressor/index.ts +35 -0
- package/src/id-compressor/numericUuid.ts +383 -0
- package/src/id-compressor/sessionIdNormalizer.ts +609 -0
- package/src/id-compressor/utils.ts +114 -0
- package/src/id-compressor/uuidUtilities.ts +123 -0
- package/src/index.ts +1 -0
- package/src/opLifecycle/README.md +119 -0
- package/src/opLifecycle/batchManager.ts +35 -2
- package/src/opLifecycle/index.ts +2 -1
- package/src/opLifecycle/opDecompressor.ts +1 -0
- package/src/opLifecycle/opGroupingManager.ts +82 -0
- package/src/opLifecycle/opSplitter.ts +1 -5
- package/src/opLifecycle/outbox.ts +64 -26
- package/src/opLifecycle/remoteMessageProcessor.ts +38 -22
- package/src/packageVersion.ts +1 -1
- package/src/pendingStateManager.ts +21 -7
- package/src/summary/index.ts +2 -1
- package/src/summary/orderedClientElection.ts +17 -1
- package/src/summary/runningSummarizer.ts +78 -77
- package/src/summary/summarizer.ts +0 -8
- package/src/summary/summarizerHeuristics.ts +4 -0
- package/src/summary/summarizerNode/summarizerNode.ts +1 -1
- package/src/summary/summarizerNode/summarizerNodeWithGc.ts +3 -3
- package/src/summary/summarizerTypes.ts +20 -3
- package/src/summary/summaryFormat.ts +4 -0
- package/src/summary/summaryGenerator.ts +22 -16
- package/src/summary/summaryManager.ts +2 -0
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
3
|
+
* Licensed under the MIT License.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Remove `readonly` from all fields.
|
|
8
|
+
*/
|
|
9
|
+
export type Mutable<T> = { -readonly [P in keyof T]: T[P] };
|
|
10
|
+
|
|
11
|
+
/** A union type of the first `N` positive integers */
|
|
12
|
+
export type TakeWholeNumbers<N extends number, A extends never[] = []> = N extends A["length"]
|
|
13
|
+
? never
|
|
14
|
+
: A["length"] | TakeWholeNumbers<N, [never, ...A]>;
|
|
15
|
+
|
|
16
|
+
/** Returns a tuple type with exactly `Length` elements of type `T` */
|
|
17
|
+
export type ArrayOfLength<T, Length extends number, A extends T[] = []> = Length extends A["length"]
|
|
18
|
+
? A
|
|
19
|
+
: ArrayOfLength<T, Length, [T, ...A]>;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Fails true iff `array` has at least `length` elements
|
|
23
|
+
*/
|
|
24
|
+
export function hasAtLeastLength<T, Len extends TakeWholeNumbers<16>>(
|
|
25
|
+
array: readonly T[],
|
|
26
|
+
length: Len,
|
|
27
|
+
): array is [...ArrayOfLength<T, Len>, ...T[]] {
|
|
28
|
+
return array.length >= length;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* A numeric comparator used for sorting in ascending order.
|
|
33
|
+
*
|
|
34
|
+
* Handles +/-0 like Map: -0 is equal to +0.
|
|
35
|
+
*/
|
|
36
|
+
export function compareFiniteNumbers<T extends number>(a: T, b: T): number {
|
|
37
|
+
return a - b;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* A numeric comparator used for sorting in descending order.
|
|
42
|
+
*
|
|
43
|
+
* Handles +/-0 like Map: -0 is equal to +0.
|
|
44
|
+
*/
|
|
45
|
+
export function compareFiniteNumbersReversed<T extends number>(a: T, b: T): number {
|
|
46
|
+
return b - a;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Compare two maps and return true if their contents are equivalent.
|
|
51
|
+
* @param mapA - The first array to compare
|
|
52
|
+
* @param mapB - The second array to compare
|
|
53
|
+
* @param elementComparator - The function used to check if two `T`s are equivalent.
|
|
54
|
+
* Defaults to `Object.is()` equality (a shallow compare)
|
|
55
|
+
*/
|
|
56
|
+
export function compareMaps<K, V>(
|
|
57
|
+
mapA: ReadonlyMap<K, V>,
|
|
58
|
+
mapB: ReadonlyMap<K, V>,
|
|
59
|
+
elementComparator: (a: V, b: V) => boolean = Object.is,
|
|
60
|
+
): boolean {
|
|
61
|
+
if (mapA.size !== mapB.size) {
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
for (const [keyA, valueA] of mapA) {
|
|
66
|
+
const valueB = mapB.get(keyA);
|
|
67
|
+
if (valueB === undefined || !elementComparator(valueA, valueB)) {
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Retrieve a value from a map with the given key, or create a new entry if the key is not in the map.
|
|
77
|
+
* @param map - The map to query/update
|
|
78
|
+
* @param key - The key to lookup in the map
|
|
79
|
+
* @param defaultValue - a function which returns a default value. This is called and used to set an initial value for the given key in the map if none exists
|
|
80
|
+
* @returns either the existing value for the given key, or the newly-created value (the result of `defaultValue`)
|
|
81
|
+
*/
|
|
82
|
+
export function getOrCreate<K, V>(map: Map<K, V>, key: K, defaultValue: (key: K) => V): V {
|
|
83
|
+
let value = map.get(key);
|
|
84
|
+
if (value === undefined) {
|
|
85
|
+
value = defaultValue(key);
|
|
86
|
+
map.set(key, value);
|
|
87
|
+
}
|
|
88
|
+
return value;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Compares strings lexically to form a strict partial ordering.
|
|
93
|
+
*/
|
|
94
|
+
export function compareStrings<T extends string>(a: T, b: T): number {
|
|
95
|
+
return a > b ? 1 : a === b ? 0 : -1;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function fail(message: string): never {
|
|
99
|
+
throw new Error(message);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Sets a property in such a way that it is only set on `destination` if the provided value is not undefined.
|
|
104
|
+
* This avoids having explicit undefined values under properties that would cause `Object.hasOwnProperty` to return true.
|
|
105
|
+
*/
|
|
106
|
+
export function setPropertyIfDefined<TDst, P extends keyof TDst>(
|
|
107
|
+
value: TDst[P] | undefined,
|
|
108
|
+
destination: TDst,
|
|
109
|
+
property: P,
|
|
110
|
+
): void {
|
|
111
|
+
if (value !== undefined) {
|
|
112
|
+
destination[property] = value;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
3
|
+
* Licensed under the MIT License.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { assert } from "@fluidframework/common-utils";
|
|
7
|
+
import { StableId, UuidString } from "@fluidframework/runtime-definitions";
|
|
8
|
+
import { v4, NIL } from "uuid";
|
|
9
|
+
|
|
10
|
+
const hexadecimalCharCodes = Array.from("09afAF").map((c) => c.charCodeAt(0)) as [
|
|
11
|
+
zero: number,
|
|
12
|
+
nine: number,
|
|
13
|
+
a: number,
|
|
14
|
+
f: number,
|
|
15
|
+
A: number,
|
|
16
|
+
F: number,
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
function isHexadecimalCharacter(charCode: number): boolean {
|
|
20
|
+
return (
|
|
21
|
+
(charCode >= hexadecimalCharCodes[0] && charCode <= hexadecimalCharCodes[1]) ||
|
|
22
|
+
(charCode >= hexadecimalCharCodes[2] && charCode <= hexadecimalCharCodes[3]) ||
|
|
23
|
+
(charCode >= hexadecimalCharCodes[4] && charCode <= hexadecimalCharCodes[5])
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** The null (lowest/all-zeros) UUID */
|
|
28
|
+
export const nilUuid = assertIsUuidString(NIL);
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Asserts that the given string is a UUID
|
|
32
|
+
*/
|
|
33
|
+
export function assertIsUuidString(uuidString: string): UuidString {
|
|
34
|
+
assert(isUuidString(uuidString), 0x4a2 /* Expected an UuidString */);
|
|
35
|
+
return uuidString;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Returns true iff the given string is a valid UUID-like string of hexadecimal characters
|
|
40
|
+
* 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'
|
|
41
|
+
*/
|
|
42
|
+
export function isUuidString(str: string): str is UuidString {
|
|
43
|
+
for (let i = 0; i < str.length; i++) {
|
|
44
|
+
switch (i) {
|
|
45
|
+
case 8:
|
|
46
|
+
case 13:
|
|
47
|
+
case 18:
|
|
48
|
+
case 23:
|
|
49
|
+
if (str.charAt(i) !== "-") {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
break;
|
|
53
|
+
|
|
54
|
+
default:
|
|
55
|
+
if (!isHexadecimalCharacter(str.charCodeAt(i))) {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
break;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Generate a random stable ID
|
|
67
|
+
*/
|
|
68
|
+
export function generateStableId(): StableId {
|
|
69
|
+
return assertIsStableId(v4());
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Asserts that the given string is a stable ID.
|
|
74
|
+
*/
|
|
75
|
+
export function assertIsStableId(stableId: string): StableId {
|
|
76
|
+
assert(isStableId(stableId), 0x4a3 /* Expected a StableId */);
|
|
77
|
+
return stableId;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Returns true iff the given string is a valid Version 4, variant 2 UUID
|
|
82
|
+
* 'xxxxxxxx-xxxx-4xxx-vxxx-xxxxxxxxxxxx'
|
|
83
|
+
*/
|
|
84
|
+
export function isStableId(str: string): str is StableId {
|
|
85
|
+
if (str.length !== 36) {
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
for (let i = 0; i < str.length; i++) {
|
|
90
|
+
switch (i) {
|
|
91
|
+
case 8:
|
|
92
|
+
case 13:
|
|
93
|
+
case 18:
|
|
94
|
+
case 23:
|
|
95
|
+
if (str.charAt(i) !== "-") {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
break;
|
|
99
|
+
|
|
100
|
+
case 14:
|
|
101
|
+
if (str.charAt(i) !== "4") {
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
break;
|
|
105
|
+
|
|
106
|
+
case 19: {
|
|
107
|
+
const char = str.charAt(i);
|
|
108
|
+
if (char !== "8" && char !== "9" && char !== "a" && char !== "b") {
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
break;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
default:
|
|
115
|
+
if (!isHexadecimalCharacter(str.charCodeAt(i))) {
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
break;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return true;
|
|
123
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -12,10 +12,13 @@ By default, the runtime is configured with a max batch size of `716800` bytes, w
|
|
|
12
12
|
|
|
13
13
|
- [Introduction](#introduction)
|
|
14
14
|
- [Compression](#compression)
|
|
15
|
+
- [Grouped batching](#grouped-batching)
|
|
16
|
+
- [Risks](#risks)
|
|
15
17
|
- [Chunking for compression](#chunking-for-compression)
|
|
16
18
|
- [Disabling in case of emergency](#disabling-in-case-of-emergency)
|
|
17
19
|
- [Example configs](#example-configs)
|
|
18
20
|
- [How it works](#how-it-works)
|
|
21
|
+
- [How grouped batching works](#how-grouped-batching-works)
|
|
19
22
|
|
|
20
23
|
## Compression
|
|
21
24
|
|
|
@@ -26,6 +29,28 @@ By default, the runtime is configured with a max batch size of `716800` bytes, w
|
|
|
26
29
|
- `minimumBatchSizeInBytes` – the minimum size of the batch for which compression should kick in. If the payload is too small, compression may not yield too many benefits. To target the original 1MB issue, a good value here would be to match the default maxBatchSizeInBytes (972800), however, experimentally, a good lower value could be at around 614400 bytes. Setting this value to `Number.POSITIVE_INFINITY` will disable compression.
|
|
27
30
|
- `compressionAlgorithm` – currently, only `lz4` is supported.
|
|
28
31
|
|
|
32
|
+
## Grouped batching
|
|
33
|
+
|
|
34
|
+
**Note: This feature is currently considered experimental and is not ready for production usage.**
|
|
35
|
+
|
|
36
|
+
The `IContainerRuntimeOptions.enableGroupedBatching` option has been added to the container runtime layer and is **off by default**. This option will group all batch messages under a new "grouped" message to be sent to the service. Upon receiving this new "grouped" message, the batch messages will be extracted and given the sequence number of the parent "grouped" message.
|
|
37
|
+
|
|
38
|
+
The purpose for enabling grouped batching on top of compression is that regular compression won't include the empty messages in the chunks. Thus, if we have batches with many messages (i.e. more than 4k), we will go over the batch size limit just on empty op envelopes alone.
|
|
39
|
+
|
|
40
|
+
See [below](#how-grouped-batching-works) for an example.
|
|
41
|
+
|
|
42
|
+
### Risks
|
|
43
|
+
|
|
44
|
+
This option is experimental and should not be enabled yet in production. This option should **ONLY** be enabled after observing that 99.9% of your application sessions contains these changes (runtime version "2.0.0-internal.4.1.0" or later). Containers created with this option may not open in future versions of the framework.
|
|
45
|
+
|
|
46
|
+
This option will change a couple of expectations around message structure and runtime layer expectations. Only enable this option after testing
|
|
47
|
+
and verifying that the following expectation changes won't have any effects:
|
|
48
|
+
|
|
49
|
+
- batch messages observed at the runtime layer will not match messages seen at the loader layer (i.e. grouped form at loader layer, ungrouped form at runtime layer)
|
|
50
|
+
- messages within the same batch will have the same sequence number
|
|
51
|
+
- client sequence numbers on batch messages can only be used to order messages with the same sequenceNumber
|
|
52
|
+
- requires all ops to be processed by runtime layer (version "2.0.0-internal.1.2.0" or later https://github.com/microsoft/FluidFramework/pull/11832)
|
|
53
|
+
|
|
29
54
|
## Chunking for compression
|
|
30
55
|
|
|
31
56
|
**Op chunking for compression targets payloads which exceed the max batch size after compression.** So, only payloads which are already compressed. By default, the feature is enabled.
|
|
@@ -39,6 +64,7 @@ This config would govern chunking compressed batches only. We will not be enabli
|
|
|
39
64
|
If the features are enabled using the configs, they can be disabled at runtime via feature gates as following:
|
|
40
65
|
|
|
41
66
|
- `Fluid.ContainerRuntime.CompressionDisabled` - if set to true, will disable compression (this has a side effect of also disabling chunking, as chunking is invoked only for compressed payloads).
|
|
67
|
+
- `Fluid.ContainerRuntime.DisableGroupedBatching` - if set to true, will disable grouped batching.
|
|
42
68
|
- `Fluid.ContainerRuntime.CompressionChunkingDisabled` - if set to true, will disable chunking for compression.
|
|
43
69
|
|
|
44
70
|
## Example configs
|
|
@@ -75,6 +101,14 @@ To disable compression (will also disable chunking, as chunking works only for c
|
|
|
75
101
|
}
|
|
76
102
|
```
|
|
77
103
|
|
|
104
|
+
To enable grouped batching:
|
|
105
|
+
|
|
106
|
+
```
|
|
107
|
+
const runtimeOptions: IContainerRuntimeOptions = {
|
|
108
|
+
enableGroupedBatching: true,
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
78
112
|
## How it works
|
|
79
113
|
|
|
80
114
|
Compression currently works as a runtime layer over the regular op sending/receiving pipeline.
|
|
@@ -155,3 +189,88 @@ Notice that the sequence numbers don’t matter here, as all ops will be based o
|
|
|
155
189
|
Additionally, as compression preserves the original uncompressed batch layout in terms of the number of ops by using empty ops to reserve the sequence numbers, this ensures that the clients will always receive the exact count of ops to rebuild the uncompressed batch sequentially.
|
|
156
190
|
|
|
157
191
|
On the receiving end, the client will accumulate chunks 1 and 2 and keep them in memory. When chunk 3 is received, the original large, decompressed op will be rebuilt, and the runtime will then process the batch as if it is a compressed batch.
|
|
192
|
+
|
|
193
|
+
## How grouped batching works
|
|
194
|
+
|
|
195
|
+
**Note: There are plans to replace empty ops with something more efficient when doing grouped batching AB#4092**
|
|
196
|
+
|
|
197
|
+
Given the following baseline batch:
|
|
198
|
+
|
|
199
|
+
```
|
|
200
|
+
+---------------+---------------+---------------+---------------+---------------+
|
|
201
|
+
| Op 1 | Op 2 | Op 3 | Op 4 | Op 5 |
|
|
202
|
+
| Contents: "a" | Contents: "b" | Contents: "c" | Contents: "d" | Contents: "e" |
|
|
203
|
+
+---------------+---------------+---------------+---------------+---------------+
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
Compressed batch:
|
|
207
|
+
|
|
208
|
+
```
|
|
209
|
+
+--------------------+-----------------+-----------------+-----------------+-----------------+
|
|
210
|
+
| Op 1 | Op 2 | Op 3 | Op 4 | Op 5 |
|
|
211
|
+
| Contents: "abcde" | Contents: empty | Contents: empty | Contents: empty | Contents: empty |
|
|
212
|
+
| Compression: 'lz4' | | | | |
|
|
213
|
+
+--------------------+-----------------+-----------------+-----------------+-----------------+
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
Grouped batch:
|
|
217
|
+
|
|
218
|
+
```
|
|
219
|
+
+---------------------------------------------------------------------------------------------------------------------------------+
|
|
220
|
+
| Op 1 Contents: +--------------------+-----------------+-----------------+-----------------+-----------------+ |
|
|
221
|
+
| SeqNum: 1 | Op 1 | Op 2 | Op 3 | Op 4 | Op 5 | |
|
|
222
|
+
| Type: "groupedBatch" | Contents: "abcde" | Contents: empty | Contents: empty | Contents: empty | Contents: empty | |
|
|
223
|
+
| | Compression: 'lz4' | | | | | |
|
|
224
|
+
| +--------------------+-----------------+-----------------+-----------------+-----------------+ |
|
|
225
|
+
+---------------------------------------------------------------------------------------------------------------------------------+
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
Can produce the following chunks:
|
|
229
|
+
|
|
230
|
+
```
|
|
231
|
+
+-------------------------------------------------+
|
|
232
|
+
| Chunk 1/2 Contents: +----------------------+ |
|
|
233
|
+
| SeqNum: 1 | +-----------------+ | |
|
|
234
|
+
| | | Contents: "abc" | | |
|
|
235
|
+
| | +-----------------+ | |
|
|
236
|
+
| +----------------------+ |
|
|
237
|
+
+-------------------------------------------------+
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
```
|
|
241
|
+
+--------------------------------------------------------------------------------------------------------------------------+
|
|
242
|
+
| Chunk 2/2 Contents: +---------------------------------------------------------------------------------------------+ | |
|
|
243
|
+
| SeqNum: 2 | +----------------+-----------------+-----------------+-----------------+-----------------+ | | |
|
|
244
|
+
| | | Contents: "de" | Contents: empty | Contents: empty | Contents: empty | Contents: empty | | | |
|
|
245
|
+
| | +----------------+-----------------+-----------------+-----------------+-----------------+ | | |
|
|
246
|
+
| +---------------------------------------------------------------------------------------------+ | |
|
|
247
|
+
+--------------------------------------------------------------------------------------------------------------------------+
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
- Send to service
|
|
251
|
+
- Service acks ops sent
|
|
252
|
+
- Receive chunks from service
|
|
253
|
+
- Recompile to the grouped batch step
|
|
254
|
+
|
|
255
|
+
Ungrouped batch:
|
|
256
|
+
|
|
257
|
+
```
|
|
258
|
+
+--------------------+-----------------+-----------------+-----------------+-----------------+
|
|
259
|
+
| Op 1 | Op 2 | Op 3 | Op 4 | Op 5 |
|
|
260
|
+
| Contents: "abcde" | Contents: empty | Contents: empty | Contents: empty | Contents: empty |
|
|
261
|
+
| SeqNum: 2 | SeqNum: 2 | SeqNum: 2 | SeqNum: 2 | SeqNum: 2 |
|
|
262
|
+
| ClientSeqNum: 1 | ClientSeqNum: 2 | ClientSeqNum: 3 | ClientSeqNum: 4 | ClientSeqNum: 5 |
|
|
263
|
+
| Compression: 'lz4' | | | | |
|
|
264
|
+
+--------------------+-----------------+-----------------+-----------------+-----------------+
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
Uncompressed batch:
|
|
268
|
+
|
|
269
|
+
```
|
|
270
|
+
+-----------------+-----------------+-----------------+-----------------+-----------------+
|
|
271
|
+
| Op 1 | Op 2 | Op 3 | Op 4 | Op 5 |
|
|
272
|
+
| Contents: "a" | Contents: "b" | Contents: "c" | Contents: "d" | Contents: "e" |
|
|
273
|
+
| SeqNum: 2 | SeqNum: 2 | SeqNum: 2 | SeqNum: 2 | SeqNum: 2 |
|
|
274
|
+
| ClientSeqNum: 1 | ClientSeqNum: 2 | ClientSeqNum: 3 | ClientSeqNum: 4 | ClientSeqNum: 5 |
|
|
275
|
+
+-----------------+-----------------+-----------------+-----------------+-----------------+
|
|
276
|
+
```
|
|
@@ -12,6 +12,11 @@ export interface IBatchManagerOptions {
|
|
|
12
12
|
readonly compressionOptions?: ICompressionRuntimeOptions;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
export interface BatchSequenceNumbers {
|
|
16
|
+
referenceSequenceNumber?: number;
|
|
17
|
+
clientSequenceNumber?: number;
|
|
18
|
+
}
|
|
19
|
+
|
|
15
20
|
/**
|
|
16
21
|
* Estimated size of the stringification overhead for an op accumulated
|
|
17
22
|
* from runtime to loader to the service.
|
|
@@ -32,15 +37,24 @@ export class BatchManager {
|
|
|
32
37
|
return this.batchContentSize;
|
|
33
38
|
}
|
|
34
39
|
|
|
35
|
-
public get
|
|
40
|
+
public get sequenceNumbers(): BatchSequenceNumbers {
|
|
41
|
+
return {
|
|
42
|
+
referenceSequenceNumber: this.referenceSequenceNumber,
|
|
43
|
+
clientSequenceNumber: this.clientSequenceNumber,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
private get referenceSequenceNumber(): number | undefined {
|
|
36
48
|
return this.pendingBatch.length === 0
|
|
37
49
|
? undefined
|
|
38
50
|
: this.pendingBatch[this.pendingBatch.length - 1].referenceSequenceNumber;
|
|
39
51
|
}
|
|
40
52
|
|
|
53
|
+
private clientSequenceNumber: number | undefined;
|
|
54
|
+
|
|
41
55
|
constructor(public readonly options: IBatchManagerOptions) {}
|
|
42
56
|
|
|
43
|
-
public push(message: BatchMessage): boolean {
|
|
57
|
+
public push(message: BatchMessage, currentClientSequenceNumber?: number): boolean {
|
|
44
58
|
const contentSize = this.batchContentSize + (message.contents?.length ?? 0);
|
|
45
59
|
const opCount = this.pendingBatch.length;
|
|
46
60
|
|
|
@@ -68,6 +82,10 @@ export class BatchManager {
|
|
|
68
82
|
return false;
|
|
69
83
|
}
|
|
70
84
|
|
|
85
|
+
if (this.pendingBatch.length === 0) {
|
|
86
|
+
this.clientSequenceNumber = currentClientSequenceNumber;
|
|
87
|
+
}
|
|
88
|
+
|
|
71
89
|
this.batchContentSize = contentSize;
|
|
72
90
|
this.pendingBatch.push(message);
|
|
73
91
|
return true;
|
|
@@ -86,6 +104,7 @@ export class BatchManager {
|
|
|
86
104
|
|
|
87
105
|
this.pendingBatch = [];
|
|
88
106
|
this.batchContentSize = 0;
|
|
107
|
+
this.clientSequenceNumber = undefined;
|
|
89
108
|
|
|
90
109
|
return addBatchMetadata(batch);
|
|
91
110
|
}
|
|
@@ -136,3 +155,17 @@ const addBatchMetadata = (batch: IBatch): IBatch => {
|
|
|
136
155
|
export const estimateSocketSize = (batch: IBatch): number => {
|
|
137
156
|
return batch.contentSizeInBytes + opOverhead * batch.content.length;
|
|
138
157
|
};
|
|
158
|
+
|
|
159
|
+
export const sequenceNumbersMatch = (
|
|
160
|
+
seqNums: BatchSequenceNumbers,
|
|
161
|
+
otherSeqNums: BatchSequenceNumbers,
|
|
162
|
+
): boolean => {
|
|
163
|
+
return (
|
|
164
|
+
(seqNums.referenceSequenceNumber === undefined ||
|
|
165
|
+
otherSeqNums.referenceSequenceNumber === undefined ||
|
|
166
|
+
seqNums.referenceSequenceNumber === otherSeqNums.referenceSequenceNumber) &&
|
|
167
|
+
(seqNums.clientSequenceNumber === undefined ||
|
|
168
|
+
otherSeqNums.clientSequenceNumber === undefined ||
|
|
169
|
+
seqNums.clientSequenceNumber === otherSeqNums.clientSequenceNumber)
|
|
170
|
+
);
|
|
171
|
+
};
|
package/src/opLifecycle/index.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
export { BatchManager, estimateSocketSize } from "./batchManager";
|
|
6
|
+
export { BatchManager, estimateSocketSize, BatchSequenceNumbers } from "./batchManager";
|
|
7
7
|
export {
|
|
8
8
|
BatchMessage,
|
|
9
9
|
IBatch,
|
|
@@ -16,3 +16,4 @@ export { OpCompressor } from "./opCompressor";
|
|
|
16
16
|
export { OpDecompressor } from "./opDecompressor";
|
|
17
17
|
export { OpSplitter, splitOp } from "./opSplitter";
|
|
18
18
|
export { RemoteMessageProcessor, unpackRuntimeMessage } from "./remoteMessageProcessor";
|
|
19
|
+
export { OpGroupingManager } from "./opGroupingManager";
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
3
|
+
* Licensed under the MIT License.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { assert } from "@fluidframework/common-utils";
|
|
7
|
+
import { ISequencedDocumentMessage } from "@fluidframework/protocol-definitions";
|
|
8
|
+
import { ContainerMessageType, ContainerRuntimeMessage } from "..";
|
|
9
|
+
import { IBatch } from "./definitions";
|
|
10
|
+
|
|
11
|
+
interface IGroupedMessage {
|
|
12
|
+
contents?: unknown;
|
|
13
|
+
metadata?: Record<string, unknown>;
|
|
14
|
+
compression?: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export class OpGroupingManager {
|
|
18
|
+
static groupedBatchOp = "groupedBatch";
|
|
19
|
+
|
|
20
|
+
constructor(private readonly groupedBatchingEnabled: boolean) {}
|
|
21
|
+
|
|
22
|
+
public groupBatch(batch: IBatch): IBatch {
|
|
23
|
+
if (batch.content.length < 2 || !this.groupedBatchingEnabled) {
|
|
24
|
+
return batch;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
for (const message of batch.content) {
|
|
28
|
+
// Blob attaches cannot be grouped (grouped batching would hide metadata)
|
|
29
|
+
if (message.deserializedContent.type === ContainerMessageType.BlobAttach) {
|
|
30
|
+
return batch;
|
|
31
|
+
}
|
|
32
|
+
if (message.metadata) {
|
|
33
|
+
const keys = Object.keys(message.metadata);
|
|
34
|
+
assert(keys.length < 2, 0x5dd /* cannot group ops with metadata */);
|
|
35
|
+
assert(
|
|
36
|
+
keys.length === 0 || keys[0] === "batch",
|
|
37
|
+
0x5de /* unexpected op metadata */,
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Need deserializedContent for back-compat
|
|
43
|
+
const deserializedContent = {
|
|
44
|
+
type: OpGroupingManager.groupedBatchOp,
|
|
45
|
+
contents: batch.content.map<IGroupedMessage>((message) => ({
|
|
46
|
+
contents: message.contents === undefined ? undefined : JSON.parse(message.contents),
|
|
47
|
+
metadata: message.metadata,
|
|
48
|
+
compression: message.compression,
|
|
49
|
+
})),
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const groupedBatch: IBatch = {
|
|
53
|
+
...batch,
|
|
54
|
+
content: [
|
|
55
|
+
{
|
|
56
|
+
localOpMetadata: undefined,
|
|
57
|
+
metadata: undefined,
|
|
58
|
+
referenceSequenceNumber: batch.content[0].referenceSequenceNumber,
|
|
59
|
+
deserializedContent: deserializedContent as ContainerRuntimeMessage,
|
|
60
|
+
contents: JSON.stringify(deserializedContent),
|
|
61
|
+
},
|
|
62
|
+
],
|
|
63
|
+
};
|
|
64
|
+
return groupedBatch;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
public ungroupOp(op: ISequencedDocumentMessage): ISequencedDocumentMessage[] {
|
|
68
|
+
if (op.contents?.type !== OpGroupingManager.groupedBatchOp) {
|
|
69
|
+
return [op];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const messages = op.contents.contents as IGroupedMessage[];
|
|
73
|
+
let fakeCsn = 1;
|
|
74
|
+
return messages.map((subMessage) => ({
|
|
75
|
+
...op,
|
|
76
|
+
clientSequenceNumber: fakeCsn++,
|
|
77
|
+
contents: subMessage.contents,
|
|
78
|
+
metadata: subMessage.metadata,
|
|
79
|
+
compression: subMessage.compression,
|
|
80
|
+
}));
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -135,7 +135,7 @@ export class OpSplitter {
|
|
|
135
135
|
* @param batch - the compressed batch which needs to be processed
|
|
136
136
|
* @returns A new adjusted batch which can be sent over the wire
|
|
137
137
|
*/
|
|
138
|
-
public
|
|
138
|
+
public splitFirstBatchMessage(batch: IBatch): IBatch {
|
|
139
139
|
assert(this.isBatchChunkingEnabled, 0x513 /* Chunking needs to be enabled */);
|
|
140
140
|
assert(
|
|
141
141
|
batch.contentSizeInBytes > 0 && batch.content.length > 0,
|
|
@@ -152,10 +152,6 @@ export class OpSplitter {
|
|
|
152
152
|
);
|
|
153
153
|
|
|
154
154
|
const firstMessage = batch.content[0]; // we expect this to be the large compressed op, which needs to be split
|
|
155
|
-
assert(
|
|
156
|
-
firstMessage.metadata?.compressed === true || firstMessage.compression !== undefined,
|
|
157
|
-
0x517 /* Batch needs to be compressed */,
|
|
158
|
-
);
|
|
159
155
|
assert(
|
|
160
156
|
(firstMessage.contents?.length ?? 0) >= this.chunkSizeInBytes,
|
|
161
157
|
0x518 /* First message in the batch needs to be chunkable */,
|