@fluidframework/container-runtime 2.32.0 → 2.33.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 +4 -0
- package/api-report/container-runtime.legacy.alpha.api.md +71 -67
- package/container-runtime.test-files.tar +0 -0
- package/dist/blobManager/blobManager.d.ts +7 -4
- package/dist/blobManager/blobManager.d.ts.map +1 -1
- package/dist/blobManager/blobManager.js +38 -12
- package/dist/blobManager/blobManager.js.map +1 -1
- package/dist/channelCollection.d.ts +4 -0
- package/dist/channelCollection.d.ts.map +1 -1
- package/dist/channelCollection.js +24 -0
- package/dist/channelCollection.js.map +1 -1
- package/dist/compatUtils.d.ts +74 -0
- package/dist/compatUtils.d.ts.map +1 -0
- package/dist/compatUtils.js +151 -0
- package/dist/compatUtils.js.map +1 -0
- package/dist/compressionDefinitions.d.ts +39 -0
- package/dist/compressionDefinitions.d.ts.map +1 -0
- package/dist/compressionDefinitions.js +30 -0
- package/dist/compressionDefinitions.js.map +1 -0
- package/dist/containerRuntime.d.ts +78 -52
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +141 -54
- package/dist/containerRuntime.js.map +1 -1
- package/dist/dataStoreContext.d.ts +3 -0
- package/dist/dataStoreContext.d.ts.map +1 -1
- package/dist/dataStoreContext.js +122 -66
- package/dist/dataStoreContext.js.map +1 -1
- package/dist/deltaManagerProxies.d.ts +55 -12
- package/dist/deltaManagerProxies.d.ts.map +1 -1
- package/dist/deltaManagerProxies.js +63 -55
- package/dist/deltaManagerProxies.js.map +1 -1
- package/dist/gc/gcDefinitions.d.ts +2 -0
- package/dist/gc/gcDefinitions.d.ts.map +1 -1
- package/dist/gc/gcDefinitions.js.map +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/legacy.d.ts +1 -0
- package/dist/opLifecycle/batchManager.d.ts +3 -15
- package/dist/opLifecycle/batchManager.d.ts.map +1 -1
- package/dist/opLifecycle/batchManager.js +5 -39
- package/dist/opLifecycle/batchManager.js.map +1 -1
- package/dist/opLifecycle/definitions.d.ts +44 -11
- package/dist/opLifecycle/definitions.d.ts.map +1 -1
- package/dist/opLifecycle/definitions.js.map +1 -1
- package/dist/opLifecycle/index.d.ts +3 -3
- package/dist/opLifecycle/index.d.ts.map +1 -1
- package/dist/opLifecycle/index.js +3 -2
- package/dist/opLifecycle/index.js.map +1 -1
- package/dist/opLifecycle/opCompressor.d.ts.map +1 -1
- package/dist/opLifecycle/opCompressor.js +4 -4
- package/dist/opLifecycle/opCompressor.js.map +1 -1
- package/dist/opLifecycle/opDecompressor.js +3 -3
- package/dist/opLifecycle/opDecompressor.js.map +1 -1
- package/dist/opLifecycle/opGroupingManager.d.ts +2 -2
- package/dist/opLifecycle/opGroupingManager.d.ts.map +1 -1
- package/dist/opLifecycle/opGroupingManager.js +1 -2
- package/dist/opLifecycle/opGroupingManager.js.map +1 -1
- package/dist/opLifecycle/opSerialization.d.ts +3 -1
- package/dist/opLifecycle/opSerialization.d.ts.map +1 -1
- package/dist/opLifecycle/opSerialization.js +4 -2
- package/dist/opLifecycle/opSerialization.js.map +1 -1
- package/dist/opLifecycle/opSplitter.d.ts.map +1 -1
- package/dist/opLifecycle/opSplitter.js +2 -2
- package/dist/opLifecycle/opSplitter.js.map +1 -1
- package/dist/opLifecycle/outbox.d.ts +25 -3
- package/dist/opLifecycle/outbox.d.ts.map +1 -1
- package/dist/opLifecycle/outbox.js +112 -61
- package/dist/opLifecycle/outbox.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 +36 -7
- package/dist/pendingStateManager.d.ts.map +1 -1
- package/dist/pendingStateManager.js +83 -16
- package/dist/pendingStateManager.js.map +1 -1
- package/dist/runtimeLayerCompatState.d.ts.map +1 -1
- package/dist/runtimeLayerCompatState.js +1 -1
- package/dist/runtimeLayerCompatState.js.map +1 -1
- package/dist/summary/documentSchema.d.ts +1 -0
- package/dist/summary/documentSchema.d.ts.map +1 -1
- package/dist/summary/documentSchema.js +2 -0
- package/dist/summary/documentSchema.js.map +1 -1
- package/lib/blobManager/blobManager.d.ts +7 -4
- package/lib/blobManager/blobManager.d.ts.map +1 -1
- package/lib/blobManager/blobManager.js +38 -12
- package/lib/blobManager/blobManager.js.map +1 -1
- package/lib/channelCollection.d.ts +4 -0
- package/lib/channelCollection.d.ts.map +1 -1
- package/lib/channelCollection.js +24 -0
- package/lib/channelCollection.js.map +1 -1
- package/lib/compatUtils.d.ts +74 -0
- package/lib/compatUtils.d.ts.map +1 -0
- package/lib/compatUtils.js +142 -0
- package/lib/compatUtils.js.map +1 -0
- package/lib/compressionDefinitions.d.ts +39 -0
- package/lib/compressionDefinitions.d.ts.map +1 -0
- package/lib/compressionDefinitions.js +27 -0
- package/lib/compressionDefinitions.js.map +1 -0
- package/lib/containerRuntime.d.ts +78 -52
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +143 -56
- package/lib/containerRuntime.js.map +1 -1
- package/lib/dataStoreContext.d.ts +3 -0
- package/lib/dataStoreContext.d.ts.map +1 -1
- package/lib/dataStoreContext.js +57 -1
- package/lib/dataStoreContext.js.map +1 -1
- package/lib/deltaManagerProxies.d.ts +55 -12
- package/lib/deltaManagerProxies.d.ts.map +1 -1
- package/lib/deltaManagerProxies.js +63 -55
- package/lib/deltaManagerProxies.js.map +1 -1
- package/lib/gc/gcDefinitions.d.ts +2 -0
- package/lib/gc/gcDefinitions.d.ts.map +1 -1
- package/lib/gc/gcDefinitions.js.map +1 -1
- package/lib/index.d.ts +4 -2
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +2 -1
- package/lib/index.js.map +1 -1
- package/lib/legacy.d.ts +1 -0
- package/lib/opLifecycle/batchManager.d.ts +3 -15
- package/lib/opLifecycle/batchManager.d.ts.map +1 -1
- package/lib/opLifecycle/batchManager.js +4 -37
- package/lib/opLifecycle/batchManager.js.map +1 -1
- package/lib/opLifecycle/definitions.d.ts +44 -11
- package/lib/opLifecycle/definitions.d.ts.map +1 -1
- package/lib/opLifecycle/definitions.js.map +1 -1
- package/lib/opLifecycle/index.d.ts +3 -3
- 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.d.ts.map +1 -1
- package/lib/opLifecycle/opCompressor.js +2 -2
- package/lib/opLifecycle/opCompressor.js.map +1 -1
- package/lib/opLifecycle/opDecompressor.js +1 -1
- package/lib/opLifecycle/opDecompressor.js.map +1 -1
- package/lib/opLifecycle/opGroupingManager.d.ts +2 -2
- package/lib/opLifecycle/opGroupingManager.d.ts.map +1 -1
- package/lib/opLifecycle/opGroupingManager.js +1 -2
- package/lib/opLifecycle/opGroupingManager.js.map +1 -1
- package/lib/opLifecycle/opSerialization.d.ts +3 -1
- package/lib/opLifecycle/opSerialization.d.ts.map +1 -1
- package/lib/opLifecycle/opSerialization.js +4 -2
- package/lib/opLifecycle/opSerialization.js.map +1 -1
- package/lib/opLifecycle/opSplitter.d.ts.map +1 -1
- package/lib/opLifecycle/opSplitter.js +1 -1
- package/lib/opLifecycle/opSplitter.js.map +1 -1
- package/lib/opLifecycle/outbox.d.ts +25 -3
- package/lib/opLifecycle/outbox.d.ts.map +1 -1
- package/lib/opLifecycle/outbox.js +110 -61
- package/lib/opLifecycle/outbox.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 +36 -7
- package/lib/pendingStateManager.d.ts.map +1 -1
- package/lib/pendingStateManager.js +84 -17
- package/lib/pendingStateManager.js.map +1 -1
- package/lib/runtimeLayerCompatState.d.ts.map +1 -1
- package/lib/runtimeLayerCompatState.js +2 -2
- package/lib/runtimeLayerCompatState.js.map +1 -1
- package/lib/summary/documentSchema.d.ts +1 -0
- package/lib/summary/documentSchema.d.ts.map +1 -1
- package/lib/summary/documentSchema.js +2 -0
- package/lib/summary/documentSchema.js.map +1 -1
- package/lib/tsdoc-metadata.json +1 -1
- package/package.json +21 -20
- package/src/blobManager/blobManager.ts +48 -15
- package/src/channelCollection.ts +27 -0
- package/src/compatUtils.ts +211 -0
- package/src/compressionDefinitions.ts +47 -0
- package/src/containerRuntime.ts +259 -108
- package/src/dataStoreContext.ts +82 -2
- package/src/deltaManagerProxies.ts +132 -70
- package/src/gc/gcDefinitions.ts +2 -0
- package/src/index.ts +5 -3
- package/src/opLifecycle/batchManager.ts +7 -52
- package/src/opLifecycle/definitions.ts +45 -11
- package/src/opLifecycle/index.ts +7 -2
- package/src/opLifecycle/opCompressor.ts +2 -2
- package/src/opLifecycle/opDecompressor.ts +1 -1
- package/src/opLifecycle/opGroupingManager.ts +7 -5
- package/src/opLifecycle/opSerialization.ts +6 -2
- package/src/opLifecycle/opSplitter.ts +1 -1
- package/src/opLifecycle/outbox.ts +154 -85
- package/src/packageVersion.ts +1 -1
- package/src/pendingStateManager.ts +135 -21
- package/src/runtimeLayerCompatState.ts +5 -2
- package/src/summary/documentSchema.ts +3 -0
package/src/opLifecycle/index.ts
CHANGED
|
@@ -7,7 +7,6 @@ export {
|
|
|
7
7
|
BatchId,
|
|
8
8
|
BatchManager,
|
|
9
9
|
BatchSequenceNumbers,
|
|
10
|
-
estimateSocketSize,
|
|
11
10
|
getEffectiveBatchId,
|
|
12
11
|
generateBatchId,
|
|
13
12
|
IBatchManagerOptions,
|
|
@@ -15,6 +14,7 @@ export {
|
|
|
15
14
|
export {
|
|
16
15
|
LocalBatch,
|
|
17
16
|
LocalBatchMessage,
|
|
17
|
+
LocalEmptyBatchPlaceholder,
|
|
18
18
|
OutboundBatch,
|
|
19
19
|
OutboundBatchMessage,
|
|
20
20
|
OutboundSingletonBatch,
|
|
@@ -26,7 +26,12 @@ export {
|
|
|
26
26
|
serializeOp,
|
|
27
27
|
ensureContentsDeserialized,
|
|
28
28
|
} from "./opSerialization.js";
|
|
29
|
-
export {
|
|
29
|
+
export {
|
|
30
|
+
estimateSocketSize,
|
|
31
|
+
localBatchToOutboundBatch,
|
|
32
|
+
Outbox,
|
|
33
|
+
getLongStack,
|
|
34
|
+
} from "./outbox.js";
|
|
30
35
|
export { OpCompressor } from "./opCompressor.js";
|
|
31
36
|
export { OpDecompressor } from "./opDecompressor.js";
|
|
32
37
|
export { OpSplitter, splitOp, isChunkedMessage } from "./opSplitter.js";
|
|
@@ -13,10 +13,10 @@ import {
|
|
|
13
13
|
} from "@fluidframework/telemetry-utils/internal";
|
|
14
14
|
import { compress } from "lz4js";
|
|
15
15
|
|
|
16
|
-
import { CompressionAlgorithms } from "../
|
|
16
|
+
import { CompressionAlgorithms } from "../compressionDefinitions.js";
|
|
17
17
|
|
|
18
|
-
import { estimateSocketSize } from "./batchManager.js";
|
|
19
18
|
import { type OutboundBatchMessage, type OutboundSingletonBatch } from "./definitions.js";
|
|
19
|
+
import { estimateSocketSize } from "./outbox.js";
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
22
|
* Compresses batches of ops.
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
} from "@fluidframework/telemetry-utils/internal";
|
|
14
14
|
import { decompress } from "lz4js";
|
|
15
15
|
|
|
16
|
-
import { CompressionAlgorithms } from "../
|
|
16
|
+
import { CompressionAlgorithms } from "../compressionDefinitions.js";
|
|
17
17
|
import { IBatchMetadata } from "../metadata.js";
|
|
18
18
|
|
|
19
19
|
/**
|
|
@@ -12,8 +12,8 @@ import {
|
|
|
12
12
|
} from "@fluidframework/telemetry-utils/internal";
|
|
13
13
|
|
|
14
14
|
import {
|
|
15
|
+
type LocalEmptyBatchPlaceholder,
|
|
15
16
|
type OutboundBatch,
|
|
16
|
-
type OutboundBatchMessage,
|
|
17
17
|
type OutboundSingletonBatch,
|
|
18
18
|
} from "./definitions.js";
|
|
19
19
|
|
|
@@ -67,7 +67,10 @@ export class OpGroupingManager {
|
|
|
67
67
|
public createEmptyGroupedBatch(
|
|
68
68
|
resubmittingBatchId: string,
|
|
69
69
|
referenceSequenceNumber: number,
|
|
70
|
-
): {
|
|
70
|
+
): {
|
|
71
|
+
outboundBatch: OutboundSingletonBatch;
|
|
72
|
+
placeholderMessage: LocalEmptyBatchPlaceholder;
|
|
73
|
+
} {
|
|
71
74
|
assert(
|
|
72
75
|
this.config.groupedBatchingEnabled,
|
|
73
76
|
0xa00 /* cannot create empty grouped batch when grouped batching is disabled */,
|
|
@@ -77,15 +80,14 @@ export class OpGroupingManager {
|
|
|
77
80
|
contents: [],
|
|
78
81
|
});
|
|
79
82
|
|
|
80
|
-
const placeholderMessage:
|
|
83
|
+
const placeholderMessage: LocalEmptyBatchPlaceholder = {
|
|
81
84
|
metadata: { batchId: resubmittingBatchId },
|
|
82
85
|
localOpMetadata: { emptyBatch: true },
|
|
83
86
|
referenceSequenceNumber,
|
|
84
|
-
contents: serializedOp,
|
|
85
87
|
};
|
|
86
88
|
const outboundBatch: OutboundSingletonBatch = {
|
|
87
89
|
contentSizeInBytes: 0,
|
|
88
|
-
messages: [placeholderMessage],
|
|
90
|
+
messages: [{ ...placeholderMessage, contents: serializedOp }],
|
|
89
91
|
referenceSequenceNumber,
|
|
90
92
|
};
|
|
91
93
|
return { outboundBatch, placeholderMessage };
|
|
@@ -30,10 +30,14 @@ export function ensureContentsDeserialized(mutableMessage: ISequencedDocumentMes
|
|
|
30
30
|
/**
|
|
31
31
|
* Before submitting an op to the Outbox, its contents must be serialized using this function.
|
|
32
32
|
* @remarks - The deserialization on process happens via the function {@link ensureContentsDeserialized}.
|
|
33
|
+
*
|
|
34
|
+
* @param toSerialize - op message to serialize. Also supports an array of ops.
|
|
33
35
|
*/
|
|
34
|
-
export function serializeOp(
|
|
36
|
+
export function serializeOp(
|
|
37
|
+
toSerialize: LocalContainerRuntimeMessage | LocalContainerRuntimeMessage[],
|
|
38
|
+
): string {
|
|
35
39
|
return JSON.stringify(
|
|
36
|
-
|
|
40
|
+
toSerialize,
|
|
37
41
|
// replacer:
|
|
38
42
|
(key, value: unknown) => {
|
|
39
43
|
// If 'value' is an IFluidHandle return its encoded form.
|
|
@@ -16,12 +16,12 @@ import {
|
|
|
16
16
|
|
|
17
17
|
import { ContainerMessageType, ContainerRuntimeChunkedOpMessage } from "../messageTypes.js";
|
|
18
18
|
|
|
19
|
-
import { estimateSocketSize } from "./batchManager.js";
|
|
20
19
|
import {
|
|
21
20
|
IChunkedOp,
|
|
22
21
|
type OutboundBatchMessage,
|
|
23
22
|
type OutboundSingletonBatch,
|
|
24
23
|
} from "./definitions.js";
|
|
24
|
+
import { estimateSocketSize } from "./outbox.js";
|
|
25
25
|
|
|
26
26
|
export function isChunkedMessage(message: ISequencedDocumentMessage): boolean {
|
|
27
27
|
return isChunkedContents(message.contents);
|
|
@@ -4,23 +4,25 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { IBatchMessage } from "@fluidframework/container-definitions/internal";
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
ITelemetryBaseLogger,
|
|
9
|
+
type ITelemetryBaseProperties,
|
|
10
|
+
} from "@fluidframework/core-interfaces";
|
|
8
11
|
import { assert, Lazy } from "@fluidframework/core-utils/internal";
|
|
9
12
|
import {
|
|
10
13
|
DataProcessingError,
|
|
11
|
-
GenericError,
|
|
12
14
|
UsageError,
|
|
13
15
|
createChildLogger,
|
|
16
|
+
type IFluidErrorBase,
|
|
14
17
|
type ITelemetryLoggerExt,
|
|
15
18
|
} from "@fluidframework/telemetry-utils/internal";
|
|
16
19
|
|
|
17
|
-
import { ICompressionRuntimeOptions } from "../
|
|
20
|
+
import { ICompressionRuntimeOptions } from "../compressionDefinitions.js";
|
|
18
21
|
import { PendingMessageResubmitData, PendingStateManager } from "../pendingStateManager.js";
|
|
19
22
|
|
|
20
23
|
import {
|
|
21
24
|
BatchManager,
|
|
22
25
|
BatchSequenceNumbers,
|
|
23
|
-
estimateSocketSize,
|
|
24
26
|
sequenceNumbersMatch,
|
|
25
27
|
type BatchId,
|
|
26
28
|
} from "./batchManager.js";
|
|
@@ -34,6 +36,7 @@ import {
|
|
|
34
36
|
} from "./definitions.js";
|
|
35
37
|
import { OpCompressor } from "./opCompressor.js";
|
|
36
38
|
import { OpGroupingManager } from "./opGroupingManager.js";
|
|
39
|
+
import { serializeOp } from "./opSerialization.js";
|
|
37
40
|
import { OpSplitter } from "./opSplitter.js";
|
|
38
41
|
|
|
39
42
|
export interface IOutboxConfig {
|
|
@@ -106,6 +109,61 @@ export function getLongStack<T>(action: () => T, length: number = 50): T {
|
|
|
106
109
|
}
|
|
107
110
|
}
|
|
108
111
|
|
|
112
|
+
/**
|
|
113
|
+
* Convert from local batch to outbound batch, including computing contentSizeInBytes.
|
|
114
|
+
*/
|
|
115
|
+
export function localBatchToOutboundBatch({
|
|
116
|
+
staged: _staged, // Peel this off the incoming batch, it's irrelevant (see Note below)
|
|
117
|
+
...localBatch
|
|
118
|
+
}: LocalBatch): OutboundBatch {
|
|
119
|
+
// Note: the staged property might be misleading here, in case a pre-staging batch is resubmitted during staging mode.
|
|
120
|
+
// It will be set to true, but the batch was not actually staged.
|
|
121
|
+
|
|
122
|
+
// Shallow copy each message as we switch types
|
|
123
|
+
const outboundMessages = localBatch.messages.map<OutboundBatchMessage>(
|
|
124
|
+
({ runtimeOp, ...message }) => ({
|
|
125
|
+
contents: serializeOp(runtimeOp),
|
|
126
|
+
...message,
|
|
127
|
+
}),
|
|
128
|
+
);
|
|
129
|
+
const contentSizeInBytes = outboundMessages.reduce(
|
|
130
|
+
(acc, message) => acc + (message.contents?.length ?? 0),
|
|
131
|
+
0,
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
// Shallow copy the local batch, updating the messages to be outbound messages and adding contentSizeInBytes
|
|
135
|
+
const outboundBatch: OutboundBatch = {
|
|
136
|
+
...localBatch,
|
|
137
|
+
messages: outboundMessages,
|
|
138
|
+
contentSizeInBytes,
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
return outboundBatch;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Estimated size of the stringification overhead for an op accumulated
|
|
146
|
+
* from runtime to loader to the service.
|
|
147
|
+
*/
|
|
148
|
+
const opOverhead = 200;
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Estimates the real size in bytes on the socket for a given batch. It assumes that
|
|
152
|
+
* the envelope size (and the size of an empty op) is 200 bytes, taking into account
|
|
153
|
+
* extra overhead from stringification.
|
|
154
|
+
*
|
|
155
|
+
* @remarks
|
|
156
|
+
* Also content will be stringified, and that adds a lot of overhead due to a lot of escape characters.
|
|
157
|
+
* Not taking it into account, as compression work should help there - compressed payload will be
|
|
158
|
+
* initially stored as base64, and that requires only 2 extra escape characters.
|
|
159
|
+
*
|
|
160
|
+
* @param batch - the batch to inspect
|
|
161
|
+
* @returns An estimate of the payload size in bytes which will be produced when the batch is sent over the wire
|
|
162
|
+
*/
|
|
163
|
+
export const estimateSocketSize = (batch: OutboundBatch): number => {
|
|
164
|
+
return batch.contentSizeInBytes + opOverhead * batch.messages.length;
|
|
165
|
+
};
|
|
166
|
+
|
|
109
167
|
/**
|
|
110
168
|
* The Outbox collects messages submitted by the ContainerRuntime into a batch,
|
|
111
169
|
* and then flushes the batch when requested.
|
|
@@ -133,18 +191,9 @@ export class Outbox {
|
|
|
133
191
|
constructor(private readonly params: IOutboxParameters) {
|
|
134
192
|
this.logger = createChildLogger({ logger: params.logger, namespace: "Outbox" });
|
|
135
193
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
Number.POSITIVE_INFINITY;
|
|
139
|
-
// We need to allow infinite size batches if we enable compression
|
|
140
|
-
const hardLimit = isCompressionEnabled
|
|
141
|
-
? Number.POSITIVE_INFINITY
|
|
142
|
-
: this.params.config.maxBatchSizeInBytes;
|
|
143
|
-
|
|
144
|
-
this.mainBatch = new BatchManager({ hardLimit, canRebase: true });
|
|
145
|
-
this.blobAttachBatch = new BatchManager({ hardLimit, canRebase: true });
|
|
194
|
+
this.mainBatch = new BatchManager({ canRebase: true });
|
|
195
|
+
this.blobAttachBatch = new BatchManager({ canRebase: true });
|
|
146
196
|
this.idAllocationBatch = new BatchManager({
|
|
147
|
-
hardLimit,
|
|
148
197
|
canRebase: false,
|
|
149
198
|
ignoreBatchId: true,
|
|
150
199
|
});
|
|
@@ -224,7 +273,7 @@ export class Outbox {
|
|
|
224
273
|
? "generic"
|
|
225
274
|
: "error",
|
|
226
275
|
eventName: "ReferenceSequenceNumberMismatch",
|
|
227
|
-
|
|
276
|
+
details: {
|
|
228
277
|
expectedDueToReentrancy,
|
|
229
278
|
mainReferenceSequenceNumber: mainBatchSeqNums.referenceSequenceNumber,
|
|
230
279
|
mainClientSequenceNumber: mainBatchSeqNums.clientSequenceNumber,
|
|
@@ -274,20 +323,11 @@ export class Outbox {
|
|
|
274
323
|
batchManager: BatchManager,
|
|
275
324
|
message: LocalBatchMessage,
|
|
276
325
|
): void {
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
)
|
|
283
|
-
) {
|
|
284
|
-
throw new GenericError("BatchTooLarge", /* error */ undefined, {
|
|
285
|
-
opSize: message.serializedOp?.length ?? 0,
|
|
286
|
-
batchSize: batchManager.contentSizeInBytes,
|
|
287
|
-
count: batchManager.length,
|
|
288
|
-
limit: batchManager.options.hardLimit,
|
|
289
|
-
});
|
|
290
|
-
}
|
|
326
|
+
batchManager.push(
|
|
327
|
+
message,
|
|
328
|
+
this.isContextReentrant(),
|
|
329
|
+
this.params.getCurrentSequenceNumbers().clientSequenceNumber,
|
|
330
|
+
);
|
|
291
331
|
}
|
|
292
332
|
|
|
293
333
|
/**
|
|
@@ -297,17 +337,19 @@ export class Outbox {
|
|
|
297
337
|
* @throws If called from a reentrant context, or if the batch being flushed is too large.
|
|
298
338
|
* @param resubmittingBatchId - If defined, indicates this is a resubmission of a batch
|
|
299
339
|
* with the given Batch ID, which must be preserved
|
|
340
|
+
* @param resubmittingStagedBatch - If defined, indicates this is a resubmission of a batch that is staged,
|
|
341
|
+
* meaning it should not be sent to the ordering service yet.
|
|
300
342
|
*/
|
|
301
|
-
public flush(resubmittingBatchId?: BatchId): void {
|
|
343
|
+
public flush(resubmittingBatchId?: BatchId, resubmittingStagedBatch?: boolean): void {
|
|
302
344
|
assert(
|
|
303
345
|
!this.isContextReentrant(),
|
|
304
346
|
0xb7b /* Flushing must not happen while incoming changes are being processed */,
|
|
305
347
|
);
|
|
306
348
|
|
|
307
|
-
this.flushAll(resubmittingBatchId);
|
|
349
|
+
this.flushAll(resubmittingBatchId, resubmittingStagedBatch);
|
|
308
350
|
}
|
|
309
351
|
|
|
310
|
-
private flushAll(resubmittingBatchId?: BatchId): void {
|
|
352
|
+
private flushAll(resubmittingBatchId?: BatchId, resubmittingStagedBatch?: boolean): void {
|
|
311
353
|
const allBatchesEmpty =
|
|
312
354
|
this.idAllocationBatch.empty && this.blobAttachBatch.empty && this.mainBatch.empty;
|
|
313
355
|
if (allBatchesEmpty) {
|
|
@@ -317,27 +359,35 @@ export class Outbox {
|
|
|
317
359
|
// by the rest of the system, including remote clients.
|
|
318
360
|
// In some cases we *must* resubmit the empty batch (to match up with a non-empty version tracked locally by a container fork), so we do it always.
|
|
319
361
|
if (resubmittingBatchId) {
|
|
320
|
-
this.flushEmptyBatch(resubmittingBatchId);
|
|
362
|
+
this.flushEmptyBatch(resubmittingBatchId, resubmittingStagedBatch === true);
|
|
321
363
|
}
|
|
322
364
|
return;
|
|
323
365
|
}
|
|
324
366
|
|
|
325
367
|
// Don't use resubmittingBatchId for idAllocationBatch.
|
|
326
368
|
// ID Allocation messages are not directly resubmitted so we don't want to reuse the batch ID.
|
|
327
|
-
this.flushInternal(
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
369
|
+
this.flushInternal({
|
|
370
|
+
batchManager: this.idAllocationBatch,
|
|
371
|
+
// Note: For now, we will never stage ID Allocation messages.
|
|
372
|
+
// They won't contain personal info and no harm in extra allocations in case of discarding the staged changes
|
|
373
|
+
});
|
|
374
|
+
this.flushInternal({
|
|
375
|
+
batchManager: this.blobAttachBatch,
|
|
376
|
+
disableGroupedBatching: true,
|
|
331
377
|
resubmittingBatchId,
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
378
|
+
resubmittingStagedBatch,
|
|
379
|
+
});
|
|
380
|
+
this.flushInternal({
|
|
381
|
+
batchManager: this.mainBatch,
|
|
336
382
|
resubmittingBatchId,
|
|
337
|
-
|
|
383
|
+
resubmittingStagedBatch,
|
|
384
|
+
});
|
|
338
385
|
}
|
|
339
386
|
|
|
340
|
-
private flushEmptyBatch(
|
|
387
|
+
private flushEmptyBatch(
|
|
388
|
+
resubmittingBatchId: BatchId,
|
|
389
|
+
resubmittingStagedBatch: boolean,
|
|
390
|
+
): void {
|
|
341
391
|
const referenceSequenceNumber =
|
|
342
392
|
this.params.getCurrentSequenceNumbers().referenceSequenceNumber;
|
|
343
393
|
assert(
|
|
@@ -350,28 +400,42 @@ export class Outbox {
|
|
|
350
400
|
referenceSequenceNumber,
|
|
351
401
|
);
|
|
352
402
|
let clientSequenceNumber: number | undefined;
|
|
353
|
-
if (this.params.shouldSend()) {
|
|
403
|
+
if (this.params.shouldSend() && !resubmittingStagedBatch) {
|
|
354
404
|
clientSequenceNumber = this.sendBatch(outboundBatch);
|
|
355
405
|
}
|
|
356
406
|
|
|
357
407
|
// Push the empty batch placeholder to the PendingStateManager
|
|
358
|
-
this.params.pendingStateManager.
|
|
359
|
-
|
|
408
|
+
this.params.pendingStateManager.onFlushEmptyBatch(
|
|
409
|
+
placeholderMessage,
|
|
360
410
|
clientSequenceNumber,
|
|
411
|
+
resubmittingStagedBatch,
|
|
361
412
|
);
|
|
362
413
|
return;
|
|
363
414
|
}
|
|
364
415
|
|
|
365
|
-
private flushInternal(
|
|
366
|
-
batchManager: BatchManager
|
|
367
|
-
disableGroupedBatching
|
|
368
|
-
resubmittingBatchId?: BatchId
|
|
369
|
-
|
|
416
|
+
private flushInternal(params: {
|
|
417
|
+
batchManager: BatchManager;
|
|
418
|
+
disableGroupedBatching?: boolean;
|
|
419
|
+
resubmittingBatchId?: BatchId; // undefined if not resubmitting
|
|
420
|
+
resubmittingStagedBatch?: boolean; // undefined if not resubmitting
|
|
421
|
+
}): void {
|
|
422
|
+
const {
|
|
423
|
+
batchManager,
|
|
424
|
+
disableGroupedBatching = false,
|
|
425
|
+
resubmittingBatchId,
|
|
426
|
+
resubmittingStagedBatch,
|
|
427
|
+
} = params;
|
|
370
428
|
if (batchManager.empty) {
|
|
371
429
|
return;
|
|
372
430
|
}
|
|
373
431
|
|
|
374
432
|
const rawBatch = batchManager.popBatch(resubmittingBatchId);
|
|
433
|
+
|
|
434
|
+
// When resubmitting, we respect the staged state of the original batch.
|
|
435
|
+
// In this case rawBatch.staged will match the state of inStagingMode when
|
|
436
|
+
// the resubmit occurred, which is not relevant.
|
|
437
|
+
const staged = resubmittingStagedBatch ?? rawBatch.staged === true;
|
|
438
|
+
|
|
375
439
|
const groupingEnabled =
|
|
376
440
|
!disableGroupedBatching && this.params.groupingManager.groupedBatchingEnabled();
|
|
377
441
|
if (
|
|
@@ -386,6 +450,9 @@ export class Outbox {
|
|
|
386
450
|
// If a batch contains reentrant ops (ops created as a result from processing another op)
|
|
387
451
|
// it needs to be rebased so that we can ensure consistent reference sequence numbers
|
|
388
452
|
// and eventual consistency at the DDS level.
|
|
453
|
+
// Note: Since this is happening in the same turn the ops were originally created with,
|
|
454
|
+
// and they haven't gone to PendingStateManager yet, we can just let them respect
|
|
455
|
+
// ContainerRuntime.inStagingMode
|
|
389
456
|
this.rebase(rawBatch, batchManager);
|
|
390
457
|
return;
|
|
391
458
|
}
|
|
@@ -394,7 +461,7 @@ export class Outbox {
|
|
|
394
461
|
// Did we disconnect? (i.e. is shouldSend false?)
|
|
395
462
|
// If so, do nothing, as pending state manager will resubmit it correctly on reconnect.
|
|
396
463
|
// Because flush() is a task that executes async (on clean stack), we can get here in disconnected state.
|
|
397
|
-
if (this.params.shouldSend()) {
|
|
464
|
+
if (this.params.shouldSend() && !staged) {
|
|
398
465
|
const virtualizedBatch = this.virtualizeBatch(rawBatch, groupingEnabled);
|
|
399
466
|
|
|
400
467
|
clientSequenceNumber = this.sendBatch(virtualizedBatch);
|
|
@@ -407,6 +474,7 @@ export class Outbox {
|
|
|
407
474
|
this.params.pendingStateManager.onFlushBatch(
|
|
408
475
|
rawBatch.messages,
|
|
409
476
|
clientSequenceNumber,
|
|
477
|
+
staged,
|
|
410
478
|
batchManager.options.ignoreBatchId,
|
|
411
479
|
);
|
|
412
480
|
}
|
|
@@ -424,7 +492,7 @@ export class Outbox {
|
|
|
424
492
|
this.rebasing = true;
|
|
425
493
|
for (const message of rawBatch.messages) {
|
|
426
494
|
this.params.reSubmit({
|
|
427
|
-
|
|
495
|
+
runtimeOp: message.runtimeOp,
|
|
428
496
|
localOpMetadata: message.localOpMetadata,
|
|
429
497
|
opMetadata: message.metadata,
|
|
430
498
|
});
|
|
@@ -442,7 +510,7 @@ export class Outbox {
|
|
|
442
510
|
this.batchRebasesToReport--;
|
|
443
511
|
}
|
|
444
512
|
|
|
445
|
-
this.flushInternal(batchManager);
|
|
513
|
+
this.flushInternal({ batchManager });
|
|
446
514
|
this.rebasing = false;
|
|
447
515
|
}
|
|
448
516
|
|
|
@@ -465,15 +533,7 @@ export class Outbox {
|
|
|
465
533
|
*/
|
|
466
534
|
private virtualizeBatch(localBatch: LocalBatch, groupingEnabled: boolean): OutboundBatch {
|
|
467
535
|
// Shallow copy the local batch, updating the messages to be outbound messages
|
|
468
|
-
const originalBatch
|
|
469
|
-
...localBatch,
|
|
470
|
-
messages: localBatch.messages.map<OutboundBatchMessage>(
|
|
471
|
-
({ serializedOp, ...message }) => ({
|
|
472
|
-
contents: serializedOp,
|
|
473
|
-
...message,
|
|
474
|
-
}),
|
|
475
|
-
),
|
|
476
|
-
};
|
|
536
|
+
const originalBatch = localBatchToOutboundBatch(localBatch);
|
|
477
537
|
|
|
478
538
|
const originalOrGroupedBatch = groupingEnabled
|
|
479
539
|
? this.params.groupingManager.groupBatch(originalBatch)
|
|
@@ -489,7 +549,6 @@ export class Outbox {
|
|
|
489
549
|
const singletonBatch = originalOrGroupedBatch as OutboundSingletonBatch;
|
|
490
550
|
|
|
491
551
|
if (
|
|
492
|
-
this.params.config.compressionOptions === undefined ||
|
|
493
552
|
this.params.config.compressionOptions.minimumBatchSizeInBytes >
|
|
494
553
|
singletonBatch.contentSizeInBytes ||
|
|
495
554
|
this.params.submitBatchFn === undefined
|
|
@@ -506,21 +565,11 @@ export class Outbox {
|
|
|
506
565
|
: this.params.splitter.splitSingletonBatchMessage(compressedBatch);
|
|
507
566
|
}
|
|
508
567
|
|
|
568
|
+
// We want to distinguish this "BatchTooLarge" case from the generic "BatchTooLarge" case in sendBatch
|
|
509
569
|
if (compressedBatch.contentSizeInBytes >= this.params.config.maxBatchSizeInBytes) {
|
|
510
|
-
throw
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
/* sequencedMessage */ undefined,
|
|
514
|
-
{
|
|
515
|
-
batchSize: singletonBatch.contentSizeInBytes,
|
|
516
|
-
compressedBatchSize: compressedBatch.contentSizeInBytes,
|
|
517
|
-
count: compressedBatch.messages.length,
|
|
518
|
-
limit: this.params.config.maxBatchSizeInBytes,
|
|
519
|
-
chunkingEnabled: this.params.splitter.isBatchChunkingEnabled,
|
|
520
|
-
compressionOptions: JSON.stringify(this.params.config.compressionOptions),
|
|
521
|
-
socketSize: estimateSocketSize(singletonBatch),
|
|
522
|
-
},
|
|
523
|
-
);
|
|
570
|
+
throw this.makeBatchTooLargeError(compressedBatch, "CompressionInsufficient", {
|
|
571
|
+
uncompressedSizeInBytes: singletonBatch.contentSizeInBytes,
|
|
572
|
+
});
|
|
524
573
|
}
|
|
525
574
|
|
|
526
575
|
return compressedBatch;
|
|
@@ -540,12 +589,7 @@ export class Outbox {
|
|
|
540
589
|
|
|
541
590
|
const socketSize = estimateSocketSize(batch);
|
|
542
591
|
if (socketSize >= this.params.config.maxBatchSizeInBytes) {
|
|
543
|
-
this.
|
|
544
|
-
eventName: "LargeBatch",
|
|
545
|
-
length: batch.messages.length,
|
|
546
|
-
sizeInBytes: batch.contentSizeInBytes,
|
|
547
|
-
socketSize,
|
|
548
|
-
});
|
|
592
|
+
throw this.makeBatchTooLargeError(batch, "CannotSend");
|
|
549
593
|
}
|
|
550
594
|
|
|
551
595
|
let clientSequenceNumber: number;
|
|
@@ -577,6 +621,31 @@ export class Outbox {
|
|
|
577
621
|
return clientSequenceNumber;
|
|
578
622
|
}
|
|
579
623
|
|
|
624
|
+
private makeBatchTooLargeError(
|
|
625
|
+
batch: OutboundBatch,
|
|
626
|
+
codepath: string,
|
|
627
|
+
moreDetails?: ITelemetryBaseProperties,
|
|
628
|
+
): IFluidErrorBase {
|
|
629
|
+
return DataProcessingError.create(
|
|
630
|
+
"BatchTooLarge",
|
|
631
|
+
codepath,
|
|
632
|
+
/* sequencedMessage */ undefined,
|
|
633
|
+
{
|
|
634
|
+
errorDetails: {
|
|
635
|
+
opCount: batch.messages.length,
|
|
636
|
+
contentSizeInBytes: batch.contentSizeInBytes,
|
|
637
|
+
socketSize: estimateSocketSize(batch),
|
|
638
|
+
maxBatchSizeInBytes: this.params.config.maxBatchSizeInBytes,
|
|
639
|
+
groupedBatchingEnabled: this.params.groupingManager.groupedBatchingEnabled(),
|
|
640
|
+
compressionOptions: JSON.stringify(this.params.config.compressionOptions),
|
|
641
|
+
chunkingEnabled: this.params.splitter.isBatchChunkingEnabled,
|
|
642
|
+
chunkSizeInBytes: this.params.splitter.chunkSizeInBytes,
|
|
643
|
+
...moreDetails,
|
|
644
|
+
},
|
|
645
|
+
},
|
|
646
|
+
);
|
|
647
|
+
}
|
|
648
|
+
|
|
580
649
|
/**
|
|
581
650
|
* Gets a checkpoint object per batch that facilitates iterating over the batch messages when rolling back.
|
|
582
651
|
*/
|
package/src/packageVersion.ts
CHANGED