@fluidframework/container-runtime 2.0.0-dev.2.2.0.111723 → 2.0.0-dev.2.3.0.115467
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/dist/blobManager.d.ts +20 -5
- package/dist/blobManager.d.ts.map +1 -1
- package/dist/blobManager.js +57 -15
- package/dist/blobManager.js.map +1 -1
- package/dist/containerRuntime.d.ts +16 -33
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +71 -219
- package/dist/containerRuntime.js.map +1 -1
- package/dist/dataStoreContext.js +2 -2
- package/dist/dataStoreContext.js.map +1 -1
- package/dist/dataStores.d.ts.map +1 -1
- package/dist/dataStores.js +2 -1
- package/dist/dataStores.js.map +1 -1
- package/dist/garbageCollection.d.ts +7 -16
- package/dist/garbageCollection.d.ts.map +1 -1
- package/dist/garbageCollection.js +41 -61
- package/dist/garbageCollection.js.map +1 -1
- package/dist/garbageCollectionConstants.d.ts +19 -0
- package/dist/garbageCollectionConstants.d.ts.map +1 -0
- package/dist/garbageCollectionConstants.js +34 -0
- package/dist/garbageCollectionConstants.js.map +1 -0
- package/dist/gcSweepReadyUsageDetection.js +2 -2
- package/dist/gcSweepReadyUsageDetection.js.map +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -6
- package/dist/index.js.map +1 -1
- package/dist/opLifecycle/batchManager.d.ts +30 -0
- package/dist/opLifecycle/batchManager.d.ts.map +1 -0
- package/dist/{batchManager.js → opLifecycle/batchManager.js} +17 -17
- package/dist/opLifecycle/batchManager.js.map +1 -0
- package/dist/opLifecycle/definitions.d.ts +40 -0
- package/dist/opLifecycle/definitions.d.ts.map +1 -0
- package/dist/opLifecycle/definitions.js +7 -0
- package/dist/opLifecycle/definitions.js.map +1 -0
- package/dist/opLifecycle/index.d.ts +12 -0
- package/dist/opLifecycle/index.d.ts.map +1 -0
- package/dist/opLifecycle/index.js +21 -0
- package/dist/opLifecycle/index.js.map +1 -0
- package/dist/{opCompressor.d.ts → opLifecycle/opCompressor.d.ts} +2 -2
- package/dist/opLifecycle/opCompressor.d.ts.map +1 -0
- package/dist/{opCompressor.js → opLifecycle/opCompressor.js} +16 -13
- package/dist/opLifecycle/opCompressor.js.map +1 -0
- package/dist/{opDecompressor.d.ts → opLifecycle/opDecompressor.d.ts} +0 -0
- package/dist/opLifecycle/opDecompressor.d.ts.map +1 -0
- package/dist/{opDecompressor.js → opLifecycle/opDecompressor.js} +5 -5
- package/dist/opLifecycle/opDecompressor.js.map +1 -0
- package/dist/opLifecycle/opSplitter.d.ts +17 -0
- package/dist/opLifecycle/opSplitter.d.ts.map +1 -0
- package/dist/opLifecycle/opSplitter.js +61 -0
- package/dist/opLifecycle/opSplitter.js.map +1 -0
- package/dist/opLifecycle/outbox.d.ts +47 -0
- package/dist/opLifecycle/outbox.d.ts.map +1 -0
- package/dist/opLifecycle/outbox.js +153 -0
- package/dist/opLifecycle/outbox.js.map +1 -0
- package/dist/opLifecycle/remoteMessageProcessor.d.ts +26 -0
- package/dist/opLifecycle/remoteMessageProcessor.d.ts.map +1 -0
- package/dist/opLifecycle/remoteMessageProcessor.js +81 -0
- package/dist/opLifecycle/remoteMessageProcessor.js.map +1 -0
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/summaryFormat.js +2 -2
- package/dist/summaryFormat.js.map +1 -1
- package/lib/blobManager.d.ts +20 -5
- package/lib/blobManager.d.ts.map +1 -1
- package/lib/blobManager.js +59 -17
- package/lib/blobManager.js.map +1 -1
- package/lib/containerRuntime.d.ts +16 -33
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +68 -215
- package/lib/containerRuntime.js.map +1 -1
- package/lib/dataStoreContext.js +1 -1
- package/lib/dataStoreContext.js.map +1 -1
- package/lib/dataStores.d.ts.map +1 -1
- package/lib/dataStores.js +2 -1
- package/lib/dataStores.js.map +1 -1
- package/lib/garbageCollection.d.ts +7 -16
- package/lib/garbageCollection.d.ts.map +1 -1
- package/lib/garbageCollection.js +19 -39
- package/lib/garbageCollection.js.map +1 -1
- package/lib/garbageCollectionConstants.d.ts +19 -0
- package/lib/garbageCollectionConstants.d.ts.map +1 -0
- package/lib/garbageCollectionConstants.js +31 -0
- package/lib/garbageCollectionConstants.js.map +1 -0
- package/lib/gcSweepReadyUsageDetection.js +1 -1
- package/lib/gcSweepReadyUsageDetection.js.map +1 -1
- package/lib/index.d.ts +4 -2
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +3 -2
- package/lib/index.js.map +1 -1
- package/lib/opLifecycle/batchManager.d.ts +30 -0
- package/lib/opLifecycle/batchManager.d.ts.map +1 -0
- package/lib/{batchManager.js → opLifecycle/batchManager.js} +17 -17
- package/lib/opLifecycle/batchManager.js.map +1 -0
- package/lib/opLifecycle/definitions.d.ts +40 -0
- package/lib/opLifecycle/definitions.d.ts.map +1 -0
- package/lib/opLifecycle/definitions.js +6 -0
- package/lib/opLifecycle/definitions.js.map +1 -0
- package/lib/opLifecycle/index.d.ts +12 -0
- package/lib/opLifecycle/index.d.ts.map +1 -0
- package/lib/opLifecycle/index.js +11 -0
- package/lib/opLifecycle/index.js.map +1 -0
- package/lib/{opCompressor.d.ts → opLifecycle/opCompressor.d.ts} +2 -2
- package/lib/opLifecycle/opCompressor.d.ts.map +1 -0
- package/lib/{opCompressor.js → opLifecycle/opCompressor.js} +16 -13
- package/lib/opLifecycle/opCompressor.js.map +1 -0
- package/lib/{opDecompressor.d.ts → opLifecycle/opDecompressor.d.ts} +0 -0
- package/lib/opLifecycle/opDecompressor.d.ts.map +1 -0
- package/lib/{opDecompressor.js → opLifecycle/opDecompressor.js} +4 -4
- package/lib/opLifecycle/opDecompressor.js.map +1 -0
- package/lib/opLifecycle/opSplitter.d.ts +17 -0
- package/lib/opLifecycle/opSplitter.d.ts.map +1 -0
- package/lib/opLifecycle/opSplitter.js +57 -0
- package/lib/opLifecycle/opSplitter.js.map +1 -0
- package/lib/opLifecycle/outbox.d.ts +47 -0
- package/lib/opLifecycle/outbox.d.ts.map +1 -0
- package/lib/opLifecycle/outbox.js +149 -0
- package/lib/opLifecycle/outbox.js.map +1 -0
- package/lib/opLifecycle/remoteMessageProcessor.d.ts +26 -0
- package/lib/opLifecycle/remoteMessageProcessor.d.ts.map +1 -0
- package/lib/opLifecycle/remoteMessageProcessor.js +76 -0
- package/lib/opLifecycle/remoteMessageProcessor.js.map +1 -0
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/summaryFormat.js +1 -1
- package/lib/summaryFormat.js.map +1 -1
- package/package.json +21 -34
- package/src/blobManager.ts +74 -19
- package/src/containerRuntime.ts +91 -278
- package/src/dataStoreContext.ts +1 -1
- package/src/dataStores.ts +2 -1
- package/src/garbageCollection.ts +33 -43
- package/src/garbageCollectionConstants.ts +35 -0
- package/src/gcSweepReadyUsageDetection.ts +1 -1
- package/src/index.ts +5 -4
- package/src/{batchManager.ts → opLifecycle/batchManager.ts} +30 -33
- package/src/opLifecycle/definitions.ts +44 -0
- package/src/opLifecycle/index.ts +17 -0
- package/src/{opCompressor.ts → opLifecycle/opCompressor.ts} +21 -16
- package/src/{opDecompressor.ts → opLifecycle/opDecompressor.ts} +8 -6
- package/src/opLifecycle/opSplitter.ts +78 -0
- package/src/opLifecycle/outbox.ts +204 -0
- package/src/opLifecycle/remoteMessageProcessor.ts +90 -0
- package/src/packageVersion.ts +1 -1
- package/src/summaryFormat.ts +1 -1
- package/dist/batchManager.d.ts +0 -42
- package/dist/batchManager.d.ts.map +0 -1
- package/dist/batchManager.js.map +0 -1
- package/dist/opCompressor.d.ts.map +0 -1
- package/dist/opCompressor.js.map +0 -1
- package/dist/opDecompressor.d.ts.map +0 -1
- package/dist/opDecompressor.js.map +0 -1
- package/lib/batchManager.d.ts +0 -42
- package/lib/batchManager.d.ts.map +0 -1
- package/lib/batchManager.js.map +0 -1
- package/lib/opCompressor.d.ts.map +0 -1
- package/lib/opCompressor.js.map +0 -1
- package/lib/opDecompressor.d.ts.map +0 -1
- package/lib/opDecompressor.js.map +0 -1
|
@@ -0,0 +1,204 @@
|
|
|
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 { IContainerContext } from "@fluidframework/container-definitions";
|
|
8
|
+
import { GenericError } from "@fluidframework/container-utils";
|
|
9
|
+
import { MessageType } from "@fluidframework/protocol-definitions";
|
|
10
|
+
import { ICompressionRuntimeOptions } from "../containerRuntime";
|
|
11
|
+
import { PendingStateManager } from "../pendingStateManager";
|
|
12
|
+
import { BatchManager } from "./batchManager";
|
|
13
|
+
import { BatchMessage, IBatch } from "./definitions";
|
|
14
|
+
import { OpCompressor } from "./opCompressor";
|
|
15
|
+
|
|
16
|
+
export interface IOutboxConfig {
|
|
17
|
+
readonly compressionOptions: ICompressionRuntimeOptions;
|
|
18
|
+
// The maximum size of a batch that we can send over the wire.
|
|
19
|
+
readonly maxBatchSizeInBytes: number;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export interface IOutboxParameters {
|
|
23
|
+
readonly shouldSend: () => boolean,
|
|
24
|
+
readonly pendingStateManager: PendingStateManager,
|
|
25
|
+
readonly containerContext: IContainerContext,
|
|
26
|
+
readonly config: IOutboxConfig,
|
|
27
|
+
readonly compressor: OpCompressor;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export class Outbox {
|
|
31
|
+
private readonly attachFlowBatch: BatchManager;
|
|
32
|
+
private readonly mainBatch: BatchManager;
|
|
33
|
+
private readonly defaultAttachFlowSoftLimitInBytes = 64 * 1024;
|
|
34
|
+
|
|
35
|
+
constructor(private readonly params: IOutboxParameters) {
|
|
36
|
+
const isCompressionEnabled = this.params.config.compressionOptions.minimumBatchSizeInBytes !== Number.POSITIVE_INFINITY;
|
|
37
|
+
// We need to allow infinite size batches if we enable compression
|
|
38
|
+
const hardLimit = isCompressionEnabled ? Infinity : this.params.config.maxBatchSizeInBytes;
|
|
39
|
+
const softLimit = isCompressionEnabled ? Infinity : this.defaultAttachFlowSoftLimitInBytes;
|
|
40
|
+
|
|
41
|
+
this.attachFlowBatch = new BatchManager({
|
|
42
|
+
hardLimit,
|
|
43
|
+
softLimit,
|
|
44
|
+
});
|
|
45
|
+
this.mainBatch = new BatchManager({
|
|
46
|
+
hardLimit
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
public get isEmpty(): boolean {
|
|
51
|
+
return this.attachFlowBatch.length === 0 && this.mainBatch.length === 0;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
public submit(message: BatchMessage) {
|
|
55
|
+
if (!this.mainBatch.push(message)) {
|
|
56
|
+
throw new GenericError(
|
|
57
|
+
"BatchTooLarge",
|
|
58
|
+
/* error */ undefined,
|
|
59
|
+
{
|
|
60
|
+
opSize: (message.contents?.length) ?? 0,
|
|
61
|
+
count: this.mainBatch.length,
|
|
62
|
+
limit: this.mainBatch.options.hardLimit,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
public submitAttach(message: BatchMessage) {
|
|
68
|
+
if (!this.attachFlowBatch.push(message)) {
|
|
69
|
+
// BatchManager has two limits - soft limit & hard limit. Soft limit is only engaged
|
|
70
|
+
// when queue is not empty.
|
|
71
|
+
// Flush queue & retry. Failure on retry would mean - single message is bigger than hard limit
|
|
72
|
+
this.flushInternal(this.attachFlowBatch.popBatch());
|
|
73
|
+
if (!this.attachFlowBatch.push(message)) {
|
|
74
|
+
throw new GenericError(
|
|
75
|
+
"BatchTooLarge",
|
|
76
|
+
/* error */ undefined,
|
|
77
|
+
{
|
|
78
|
+
opSize: (message.contents?.length) ?? 0,
|
|
79
|
+
count: this.attachFlowBatch.length,
|
|
80
|
+
limit: this.attachFlowBatch.options.hardLimit,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// If compression is enabled, we will always successfully receive
|
|
86
|
+
// attach ops and compress then send them at the next JS turn, regardless
|
|
87
|
+
// of the overall size of the accumulated ops in the batch.
|
|
88
|
+
// However, it is more efficient to flush these ops faster, preferably
|
|
89
|
+
// after they reach a size which would benefit from compression.
|
|
90
|
+
if (this.attachFlowBatch.contentSizeInBytes >= this.params.config.compressionOptions.minimumBatchSizeInBytes) {
|
|
91
|
+
this.flushInternal(this.attachFlowBatch.popBatch());
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
public flush() {
|
|
96
|
+
this.flushInternal(this.attachFlowBatch.popBatch());
|
|
97
|
+
this.flushInternal(this.mainBatch.popBatch());
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
private flushInternal(rawBatch: IBatch) {
|
|
101
|
+
const processedBatch = this.compressBatch(rawBatch);
|
|
102
|
+
const clientSequenceNumber = this.sendBatch(processedBatch);
|
|
103
|
+
|
|
104
|
+
this.persistBatch(clientSequenceNumber, rawBatch.content);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
private compressBatch(batch: IBatch): IBatch {
|
|
108
|
+
if (batch.content.length === 0
|
|
109
|
+
|| this.params.config.compressionOptions === undefined
|
|
110
|
+
|| this.params.config.compressionOptions.minimumBatchSizeInBytes > batch.contentSizeInBytes) {
|
|
111
|
+
// Nothing to do if the batch is empty or if compression is disabled or if we don't need to compress
|
|
112
|
+
return batch;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const compressedBatch = this.params.compressor.compressBatch(batch);
|
|
116
|
+
if (compressedBatch.contentSizeInBytes > this.params.config.maxBatchSizeInBytes) {
|
|
117
|
+
throw new GenericError(
|
|
118
|
+
"BatchTooLarge",
|
|
119
|
+
/* error */ undefined,
|
|
120
|
+
{
|
|
121
|
+
opSize: batch.contentSizeInBytes,
|
|
122
|
+
count: batch.content.length,
|
|
123
|
+
limit: this.params.config.maxBatchSizeInBytes,
|
|
124
|
+
compressed: true,
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// If we don't reach the maximum supported size of a batch, it safe to be sent as is
|
|
129
|
+
return compressedBatch;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Sends the batch object to the container context to be sent over the wire.
|
|
134
|
+
*
|
|
135
|
+
* @param batch - batch to be sent
|
|
136
|
+
* @returns the client sequence number of the last batched op which was sent and
|
|
137
|
+
* -1 if there are no ops or the container cannot send ops.
|
|
138
|
+
*/
|
|
139
|
+
private sendBatch(batch: IBatch): number {
|
|
140
|
+
let clientSequenceNumber: number = -1;
|
|
141
|
+
const length = batch.content.length;
|
|
142
|
+
|
|
143
|
+
// Did we disconnect in the middle of turn-based batch?
|
|
144
|
+
// If so, do nothing, as pending state manager will resubmit it correctly on reconnect.
|
|
145
|
+
if (length === 0 || !this.params.shouldSend()) {
|
|
146
|
+
return clientSequenceNumber;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (this.params.containerContext.submitBatchFn === undefined) {
|
|
150
|
+
// Legacy path - supporting old loader versions. Can be removed only when LTS moves above
|
|
151
|
+
// version that has support for batches (submitBatchFn)
|
|
152
|
+
for (const message of batch.content) {
|
|
153
|
+
// Legacy path doesn't support compressed payloads and will submit uncompressed payload anyways
|
|
154
|
+
if (message.metadata?.compressed) {
|
|
155
|
+
delete message.metadata.compressed;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
clientSequenceNumber = this.params.containerContext.submitFn(
|
|
159
|
+
MessageType.Operation,
|
|
160
|
+
message.deserializedContent,
|
|
161
|
+
true, // batch
|
|
162
|
+
message.metadata);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
this.params.containerContext.deltaManager.flush();
|
|
166
|
+
} else {
|
|
167
|
+
// returns clientSequenceNumber of last message in a batch
|
|
168
|
+
clientSequenceNumber = this.params.containerContext.submitBatchFn(
|
|
169
|
+
batch.content.map((message) => ({ contents: message.contents, metadata: message.metadata })));
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Convert from clientSequenceNumber of last message in the batch to clientSequenceNumber of first message.
|
|
173
|
+
clientSequenceNumber -= length - 1;
|
|
174
|
+
assert(clientSequenceNumber >= 0, 0x3d0 /* clientSequenceNumber can't be negative */);
|
|
175
|
+
return clientSequenceNumber;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
private persistBatch(initialClientSequenceNumber: number, batch: BatchMessage[]) {
|
|
179
|
+
let clientSequenceNumber = initialClientSequenceNumber;
|
|
180
|
+
// Let the PendingStateManager know that a message was submitted.
|
|
181
|
+
// In future, need to shift toward keeping batch as a whole!
|
|
182
|
+
for (const message of batch) {
|
|
183
|
+
this.params.pendingStateManager.onSubmitMessage(
|
|
184
|
+
message.deserializedContent.type,
|
|
185
|
+
clientSequenceNumber,
|
|
186
|
+
message.referenceSequenceNumber,
|
|
187
|
+
message.deserializedContent.contents,
|
|
188
|
+
message.localOpMetadata,
|
|
189
|
+
message.metadata,
|
|
190
|
+
);
|
|
191
|
+
|
|
192
|
+
clientSequenceNumber++;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
this.params.pendingStateManager.onFlush();
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
public checkpoint() {
|
|
199
|
+
return {
|
|
200
|
+
mainBatch: this.mainBatch.checkpoint(),
|
|
201
|
+
attachFlowBatch: this.attachFlowBatch.checkpoint(),
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
3
|
+
* Licensed under the MIT License.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { ISequencedDocumentMessage, MessageType } from "@fluidframework/protocol-definitions";
|
|
7
|
+
import { ContainerMessageType, ContainerRuntimeMessage } from "../containerRuntime";
|
|
8
|
+
import { OpDecompressor } from "./opDecompressor";
|
|
9
|
+
import { OpSplitter } from "./opSplitter";
|
|
10
|
+
|
|
11
|
+
export class RemoteMessageProcessor {
|
|
12
|
+
constructor(
|
|
13
|
+
private readonly opSplitter: OpSplitter,
|
|
14
|
+
private readonly opDecompressor: OpDecompressor,
|
|
15
|
+
) { }
|
|
16
|
+
|
|
17
|
+
public get partialMessages(): ReadonlyMap<string, string[]> {
|
|
18
|
+
return this.opSplitter.chunks;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
public clearPartialMessagesFor(clientId: string) {
|
|
22
|
+
this.opSplitter.clearPartialChunks(clientId);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
public process(remoteMessage: ISequencedDocumentMessage): ISequencedDocumentMessage {
|
|
26
|
+
let message = copy(remoteMessage);
|
|
27
|
+
|
|
28
|
+
message = this.opDecompressor.processMessage(message);
|
|
29
|
+
unpackRuntimeMessage(message);
|
|
30
|
+
message = this.opSplitter.processRemoteMessage(message);
|
|
31
|
+
|
|
32
|
+
return message;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const copy = (remoteMessage: ISequencedDocumentMessage): ISequencedDocumentMessage => {
|
|
37
|
+
// Do shallow copy of message, as the processing flow will modify it.
|
|
38
|
+
// There might be multiple container instances receiving same message
|
|
39
|
+
// We do not need to make deep copy, as each layer will just replace message.content itself,
|
|
40
|
+
// but would not modify contents details
|
|
41
|
+
const message = { ...remoteMessage };
|
|
42
|
+
|
|
43
|
+
// back-compat: ADO #1385: eventually should become unconditional, but only for runtime messages!
|
|
44
|
+
// System message may have no contents, or in some cases (mostly for back-compat) they may have actual objects.
|
|
45
|
+
// Old ops may contain empty string (I assume noops).
|
|
46
|
+
if (typeof message.contents === "string" && message.contents !== "") {
|
|
47
|
+
message.contents = JSON.parse(message.contents);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return message;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* For a given message, it moves the nested contents and type on level up.
|
|
55
|
+
*
|
|
56
|
+
*/
|
|
57
|
+
const unpack = (message: ISequencedDocumentMessage) => {
|
|
58
|
+
const innerContents = message.contents as ContainerRuntimeMessage;
|
|
59
|
+
message.type = innerContents.type;
|
|
60
|
+
message.contents = innerContents.contents;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Unpacks runtime messages.
|
|
65
|
+
*
|
|
66
|
+
* @remarks This API makes no promises regarding backward-compatibility. This is internal API.
|
|
67
|
+
* @param message - message (as it observed in storage / service)
|
|
68
|
+
* @returns unpacked runtime message
|
|
69
|
+
*
|
|
70
|
+
* @internal
|
|
71
|
+
*/
|
|
72
|
+
export function unpackRuntimeMessage(message: ISequencedDocumentMessage): boolean {
|
|
73
|
+
if (message.type !== MessageType.Operation) {
|
|
74
|
+
// Legacy format, but it's already "unpacked",
|
|
75
|
+
// i.e. message.type is actually ContainerMessageType.
|
|
76
|
+
// Or it's non-runtime message.
|
|
77
|
+
// Nothing to do in such case.
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// legacy op format?
|
|
82
|
+
if (message.contents.address !== undefined && message.contents.type === undefined) {
|
|
83
|
+
message.type = ContainerMessageType.FluidDataStoreOp;
|
|
84
|
+
} else {
|
|
85
|
+
// new format
|
|
86
|
+
unpack(message);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return true;
|
|
90
|
+
}
|
package/src/packageVersion.ts
CHANGED
package/src/summaryFormat.ts
CHANGED
|
@@ -8,7 +8,7 @@ import { IDocumentStorageService } from "@fluidframework/driver-definitions";
|
|
|
8
8
|
import { readAndParse } from "@fluidframework/driver-utils";
|
|
9
9
|
import { ISequencedDocumentMessage, ISnapshotTree, SummaryType } from "@fluidframework/protocol-definitions";
|
|
10
10
|
import { channelsTreeName, ISummaryTreeWithStats } from "@fluidframework/runtime-definitions";
|
|
11
|
-
import { gcTreeKey } from "./
|
|
11
|
+
import { gcTreeKey } from "./garbageCollectionConstants";
|
|
12
12
|
|
|
13
13
|
type OmitAttributesVersions<T> = Omit<T, "snapshotFormatVersion" | "summaryFormatVersion">;
|
|
14
14
|
interface IFluidDataStoreAttributes0 {
|
package/dist/batchManager.d.ts
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
/*!
|
|
2
|
-
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
3
|
-
* Licensed under the MIT License.
|
|
4
|
-
*/
|
|
5
|
-
import { ITelemetryLogger } from "@fluidframework/common-definitions";
|
|
6
|
-
import { IBatchMessage } from "@fluidframework/container-definitions";
|
|
7
|
-
import { ContainerRuntimeMessage, ICompressionRuntimeOptions } from "./containerRuntime";
|
|
8
|
-
/**
|
|
9
|
-
* Message type used by BatchManager
|
|
10
|
-
*/
|
|
11
|
-
export declare type BatchMessage = IBatchMessage & {
|
|
12
|
-
localOpMetadata: unknown;
|
|
13
|
-
deserializedContent: ContainerRuntimeMessage;
|
|
14
|
-
referenceSequenceNumber: number;
|
|
15
|
-
};
|
|
16
|
-
export interface IBatchManagerOptions {
|
|
17
|
-
readonly hardLimit: number;
|
|
18
|
-
readonly softLimit?: number;
|
|
19
|
-
readonly compressionOptions?: ICompressionRuntimeOptions;
|
|
20
|
-
}
|
|
21
|
-
/**
|
|
22
|
-
* Helper class that manages partial batch & rollback.
|
|
23
|
-
*/
|
|
24
|
-
export declare class BatchManager {
|
|
25
|
-
readonly logger: ITelemetryLogger;
|
|
26
|
-
readonly options: IBatchManagerOptions;
|
|
27
|
-
private readonly opCompressor;
|
|
28
|
-
private pendingBatch;
|
|
29
|
-
private batchContentSize;
|
|
30
|
-
get length(): number;
|
|
31
|
-
constructor(logger: ITelemetryLogger, options: IBatchManagerOptions);
|
|
32
|
-
push(message: BatchMessage): boolean;
|
|
33
|
-
get empty(): boolean;
|
|
34
|
-
popBatch(): BatchMessage[];
|
|
35
|
-
/**
|
|
36
|
-
* Capture the pending state at this point
|
|
37
|
-
*/
|
|
38
|
-
checkpoint(): {
|
|
39
|
-
rollback: (process: (message: BatchMessage) => void) => void;
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
|
-
//# sourceMappingURL=batchManager.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"batchManager.d.ts","sourceRoot":"","sources":["../src/batchManager.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AACtE,OAAO,EAAE,aAAa,EAAE,MAAM,uCAAuC,CAAC;AACtE,OAAO,EAAE,uBAAuB,EAAE,0BAA0B,EAAE,MAAM,oBAAoB,CAAC;AAGzF;;GAEG;AACH,oBAAY,YAAY,GAAG,aAAa,GAAG;IACvC,eAAe,EAAE,OAAO,CAAC;IACzB,mBAAmB,EAAE,uBAAuB,CAAC;IAC7C,uBAAuB,EAAE,MAAM,CAAC;CACnC,CAAC;AAEF,MAAM,WAAW,oBAAoB;IACjC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,kBAAkB,CAAC,EAAE,0BAA0B,CAAC;CAC5D;AAED;;GAEG;AACH,qBAAa,YAAY;aAOO,MAAM,EAAE,gBAAgB;aAAkB,OAAO,EAAE,oBAAoB;IANnG,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAe;IAC5C,OAAO,CAAC,YAAY,CAAuB;IAC3C,OAAO,CAAC,gBAAgB,CAAK;IAE7B,IAAW,MAAM,WAAuC;gBAE5B,MAAM,EAAE,gBAAgB,EAAkB,OAAO,EAAE,oBAAoB;IAI5F,IAAI,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO;IAiC3C,IAAW,KAAK,YAA6C;IAEtD,QAAQ;IAef;;OAEG;IACI,UAAU;sCAGqB,YAAY,KAAK,IAAI;;CAY9D"}
|
package/dist/batchManager.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"batchManager.js","sourceRoot":"","sources":["../src/batchManager.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { ITelemetryLogger } from \"@fluidframework/common-definitions\";\nimport { IBatchMessage } from \"@fluidframework/container-definitions\";\nimport { ContainerRuntimeMessage, ICompressionRuntimeOptions } from \"./containerRuntime\";\nimport { OpCompressor } from \"./opCompressor\";\n\n/**\n * Message type used by BatchManager\n */\nexport type BatchMessage = IBatchMessage & {\n localOpMetadata: unknown;\n deserializedContent: ContainerRuntimeMessage;\n referenceSequenceNumber: number;\n};\n\nexport interface IBatchManagerOptions {\n readonly hardLimit: number;\n readonly softLimit?: number;\n readonly compressionOptions?: ICompressionRuntimeOptions;\n}\n\n/**\n * Helper class that manages partial batch & rollback.\n */\nexport class BatchManager {\n private readonly opCompressor: OpCompressor;\n private pendingBatch: BatchMessage [] = [];\n private batchContentSize = 0;\n\n public get length() { return this.pendingBatch.length; }\n\n constructor(public readonly logger: ITelemetryLogger, public readonly options: IBatchManagerOptions) {\n this.opCompressor = new OpCompressor(logger);\n }\n\n public push(message: BatchMessage): boolean {\n const contentSize = this.batchContentSize + (message.contents?.length ?? 0);\n const opCount = this.pendingBatch.length;\n\n // Attempt to estimate batch size, aka socket message size.\n // Each op has pretty large envelope, estimating to be 200 bytes.\n // Also content will be strigified, and that adds a lot of overhead due to a lot of escape characters.\n // Not taking it into account, as compression work should help there - compressed payload will be\n // initially stored as base64, and that requires only 2 extra escape characters.\n const socketMessageSize = contentSize + 200 * opCount;\n\n // If we were provided soft limit, check for exceeding it.\n // But only if we have any ops, as the intention here is to flush existing ops (on exceeding this limit)\n // and start over. That's not an option if we have no ops.\n // If compression is enabled, the soft and hard limit are ignored and the message will be pushed anyways.\n // Cases where the message is still too large will be handled by the maxConsecutiveReconnects path.\n if (this.options.softLimit !== undefined\n && this.length > 0\n && socketMessageSize >= this.options.softLimit\n && Infinity === (this.options.compressionOptions?.minimumBatchSizeInBytes ?? Infinity)) {\n return false;\n }\n\n if (socketMessageSize >= this.options.hardLimit\n && Infinity === (this.options.compressionOptions?.minimumBatchSizeInBytes ?? Infinity)) {\n return false;\n }\n\n this.batchContentSize = contentSize;\n this.pendingBatch.push(message);\n return true;\n }\n\n public get empty() { return this.pendingBatch.length === 0; }\n\n public popBatch() {\n const batch = this.pendingBatch;\n const size = this.batchContentSize;\n this.pendingBatch = [];\n this.batchContentSize = 0;\n\n if (batch.length > 0\n && this.options.compressionOptions !== undefined\n && this.options.compressionOptions.minimumBatchSizeInBytes < size) {\n return this.opCompressor.compressBatch(batch, size);\n }\n\n return batch;\n }\n\n /**\n * Capture the pending state at this point\n */\n public checkpoint() {\n const startPoint = this.pendingBatch.length;\n return {\n rollback: (process: (message: BatchMessage) => void) => {\n for (let i = this.pendingBatch.length; i > startPoint;) {\n i--;\n const message = this.pendingBatch[i];\n this.batchContentSize -= message.contents?.length ?? 0;\n process(message);\n }\n\n this.pendingBatch.length = startPoint;\n },\n };\n }\n}\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"opCompressor.d.ts","sourceRoot":"","sources":["../src/opCompressor.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AAItE,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAG9C;;;;GAIG;AACH,qBAAa,YAAY;IACrB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC;IACxB,OAAO,CAAC,oBAAoB,CAAK;gBAErB,MAAM,EAAE,gBAAgB;IAI7B,aAAa,CAAC,KAAK,EAAE,YAAY,EAAE,EAAE,cAAc,EAAE,MAAM,GAAG,YAAY,EAAE;CAiCtF"}
|
package/dist/opCompressor.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"opCompressor.js","sourceRoot":"","sources":["../src/opCompressor.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAGH,+DAAyD;AACzD,qEAA8D;AAC9D,iCAAiC;AAEjC,yDAAoF;AAEpF;;;;GAIG;AACH,MAAa,YAAY;IAIrB,YAAY,MAAwB;QAF5B,yBAAoB,GAAG,CAAC,CAAC;QAG7B,IAAI,CAAC,MAAM,GAAG,6BAAW,CAAC,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IAC7D,CAAC;IAEM,aAAa,CAAC,KAAqB,EAAE,cAAsB;QAC9D,MAAM,WAAW,GAAmB,EAAE,CAAC;QACvC,IAAI,CAAC,oBAAoB,EAAE,CAAC;QAC5B,MAAM,eAAe,GAA8B,EAAE,CAAC;QACtD,KAAK,MAAM,OAAO,IAAI,KAAK,EAAE;YACzB,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC,mBAAmB,CAAC,CAAC;SACrD;QAED,MAAM,gBAAgB,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACpC,MAAM,gBAAgB,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC,CAAC;QACnF,MAAM,kBAAkB,GAAG,IAAA,gBAAQ,EAAC,gBAAgB,CAAC,CAAC;QACtD,MAAM,iBAAiB,GAAG,wBAAS,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QAChF,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,gBAAgB,CAAC;QAE/C,IAAI,cAAc,GAAG,MAAM,IAAI,IAAI,CAAC,oBAAoB,GAAG,GAAG,EAAE;YAC5D,IAAI,CAAC,MAAM,CAAC,oBAAoB,CAAC;gBAC7B,SAAS,EAAE,iBAAiB;gBAC5B,QAAQ;gBACR,qBAAqB,EAAE,cAAc;gBACrC,oBAAoB,EAAE,iBAAiB,CAAC,MAAM;aACjD,CAAC,CAAC;SACN;QAED,WAAW,CAAC,IAAI,iCAAM,KAAK,CAAC,CAAC,CAAC,KAAE,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,cAAc,EAAE,iBAAiB,EAAE,CAAC,EAC5E,QAAQ,kCAAO,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,KAAE,UAAU,EAAE,IAAI,KAClD,WAAW,EAAE,wCAAqB,CAAC,GAAG,IAAG,CAAC;QAE7D,KAAK,MAAM,OAAO,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE;YAClC,WAAW,CAAC,IAAI,iCAAM,OAAO,KAAE,QAAQ,EAAE,SAAS,IAAG,CAAC;SACzD;QAED,OAAO,WAAW,CAAC;IACvB,CAAC;CACJ;AAzCD,oCAyCC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { ITelemetryLogger } from \"@fluidframework/common-definitions\";\nimport { IsoBuffer } from \"@fluidframework/common-utils\";\nimport { ChildLogger } from \"@fluidframework/telemetry-utils\";\nimport { compress } from \"lz4js\";\nimport { BatchMessage } from \"./batchManager\";\nimport { CompressionAlgorithms, ContainerRuntimeMessage } from \"./containerRuntime\";\n\n/**\n * Compresses batches of ops. It generates a single compressed op that contains\n * the contents of each op in the batch. It then submits empty ops for each original\n * op to reserve sequence numbers.\n */\nexport class OpCompressor {\n private readonly logger;\n private compressedBatchCount = 0;\n\n constructor(logger: ITelemetryLogger) {\n this.logger = ChildLogger.create(logger, \"OpCompressor\");\n }\n\n public compressBatch(batch: BatchMessage[], originalLength: number): BatchMessage[] {\n const batchToSend: BatchMessage[] = [];\n this.compressedBatchCount++;\n const batchedContents: ContainerRuntimeMessage[] = [];\n for (const message of batch) {\n batchedContents.push(message.deserializedContent);\n }\n\n const compressionStart = Date.now();\n const contentsAsBuffer = new TextEncoder().encode(JSON.stringify(batchedContents));\n const compressedContents = compress(contentsAsBuffer);\n const compressedContent = IsoBuffer.from(compressedContents).toString(\"base64\");\n const duration = Date.now() - compressionStart;\n\n if (originalLength > 200000 || this.compressedBatchCount % 100) {\n this.logger.sendPerformanceEvent({\n eventName: \"CompressedBatch\",\n duration,\n sizeBeforeCompression: originalLength,\n sizeAfterCompression: compressedContent.length,\n });\n }\n\n batchToSend.push({ ...batch[0], contents: JSON.stringify({ packedContents: compressedContent }),\n metadata: { ...batch[0].metadata, compressed: true },\n compression: CompressionAlgorithms.lz4 });\n\n for (const message of batch.slice(1)) {\n batchToSend.push({ ...message, contents: undefined });\n }\n\n return batchToSend;\n }\n}\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"opDecompressor.d.ts","sourceRoot":"","sources":["../src/opDecompressor.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,yBAAyB,EAAE,MAAM,sCAAsC,CAAC;AAIjF;;;;;;;GAOG;AACH,qBAAa,cAAc;IACvB,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,mBAAmB,CAAkB;IAC7C,OAAO,CAAC,cAAc,CAAK;IAEpB,cAAc,CAAC,OAAO,EAAE,yBAAyB,GAAG,yBAAyB;CA0DvF"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"opDecompressor.js","sourceRoot":"","sources":["../src/opDecompressor.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { decompress } from \"lz4js\";\nimport { ISequencedDocumentMessage } from \"@fluidframework/protocol-definitions\";\nimport { assert, IsoBuffer, Uint8ArrayToString } from \"@fluidframework/common-utils\";\nimport { CompressionAlgorithms } from \".\";\n\n/**\n * State machine that \"unrolls\" contents of compressed batches of ops after decompressing them.\n * This class relies on some implicit contracts defined below:\n * 1. A compressed batch will have its first message with batch metadata set to true and compressed set to true\n * 2. Messages in the middle of a compressed batch will have neither batch metadata nor the compression property set\n * 3. The final message of a batch will have batch metadata set to false\n * 4. An individually compressed op will have undefined batch metadata and compression set to true\n */\nexport class OpDecompressor {\n private activeBatch = false;\n private rootMessageContents: any | undefined;\n private processedCount = 0;\n\n public processMessage(message: ISequencedDocumentMessage): ISequencedDocumentMessage {\n // We're checking for compression = true or top level compression property so\n // that we can enable compression without waiting on all ordering services\n // to pick up protocol change. Eventually only the top level property should\n // be used.\n if (message.metadata?.batch === true\n && (message.metadata?.compressed || message.compression !== undefined)) {\n // Beginning of a compressed batch\n assert(this.activeBatch === false, \"shouldn't have multiple active batches\");\n if (message.compression) {\n // lz4 is the only supported compression algorithm for now\n assert(message.compression === CompressionAlgorithms.lz4,\n \"lz4 is currently the only supported compression algorithm\");\n }\n\n this.activeBatch = true;\n\n const contents = IsoBuffer.from(message.contents.packedContents, \"base64\");\n const decompressedMessage = decompress(contents);\n const intoString = Uint8ArrayToString(decompressedMessage);\n const asObj = JSON.parse(intoString);\n this.rootMessageContents = asObj;\n\n return { ...message, contents: this.rootMessageContents[this.processedCount++] };\n }\n\n if (this.rootMessageContents !== undefined && message.metadata?.batch === undefined && this.activeBatch) {\n // Continuation of compressed batch\n return { ...message, contents: this.rootMessageContents[this.processedCount++] };\n }\n\n if (this.rootMessageContents !== undefined && message.metadata?.batch === false) {\n // End of compressed batch\n const returnMessage = { ...message,\n contents: this.rootMessageContents[this.processedCount++] };\n\n this.activeBatch = false;\n this.rootMessageContents = undefined;\n this.processedCount = 0;\n\n return returnMessage;\n }\n\n if (message.metadata?.batch === undefined &&\n (message.metadata?.compressed || message.compression === CompressionAlgorithms.lz4)) {\n // Single compressed message\n assert(this.activeBatch === false, \"shouldn't receive compressed message in middle of a batch\");\n\n const contents = IsoBuffer.from(message.contents.packedContents, \"base64\");\n const decompressedMessage = decompress(contents);\n const intoString = new TextDecoder().decode(decompressedMessage);\n const asObj = JSON.parse(intoString);\n\n return { ...message, contents: asObj[0] };\n }\n\n return message;\n }\n}\n"]}
|
package/lib/batchManager.d.ts
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
/*!
|
|
2
|
-
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
3
|
-
* Licensed under the MIT License.
|
|
4
|
-
*/
|
|
5
|
-
import { ITelemetryLogger } from "@fluidframework/common-definitions";
|
|
6
|
-
import { IBatchMessage } from "@fluidframework/container-definitions";
|
|
7
|
-
import { ContainerRuntimeMessage, ICompressionRuntimeOptions } from "./containerRuntime";
|
|
8
|
-
/**
|
|
9
|
-
* Message type used by BatchManager
|
|
10
|
-
*/
|
|
11
|
-
export declare type BatchMessage = IBatchMessage & {
|
|
12
|
-
localOpMetadata: unknown;
|
|
13
|
-
deserializedContent: ContainerRuntimeMessage;
|
|
14
|
-
referenceSequenceNumber: number;
|
|
15
|
-
};
|
|
16
|
-
export interface IBatchManagerOptions {
|
|
17
|
-
readonly hardLimit: number;
|
|
18
|
-
readonly softLimit?: number;
|
|
19
|
-
readonly compressionOptions?: ICompressionRuntimeOptions;
|
|
20
|
-
}
|
|
21
|
-
/**
|
|
22
|
-
* Helper class that manages partial batch & rollback.
|
|
23
|
-
*/
|
|
24
|
-
export declare class BatchManager {
|
|
25
|
-
readonly logger: ITelemetryLogger;
|
|
26
|
-
readonly options: IBatchManagerOptions;
|
|
27
|
-
private readonly opCompressor;
|
|
28
|
-
private pendingBatch;
|
|
29
|
-
private batchContentSize;
|
|
30
|
-
get length(): number;
|
|
31
|
-
constructor(logger: ITelemetryLogger, options: IBatchManagerOptions);
|
|
32
|
-
push(message: BatchMessage): boolean;
|
|
33
|
-
get empty(): boolean;
|
|
34
|
-
popBatch(): BatchMessage[];
|
|
35
|
-
/**
|
|
36
|
-
* Capture the pending state at this point
|
|
37
|
-
*/
|
|
38
|
-
checkpoint(): {
|
|
39
|
-
rollback: (process: (message: BatchMessage) => void) => void;
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
|
-
//# sourceMappingURL=batchManager.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"batchManager.d.ts","sourceRoot":"","sources":["../src/batchManager.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AACtE,OAAO,EAAE,aAAa,EAAE,MAAM,uCAAuC,CAAC;AACtE,OAAO,EAAE,uBAAuB,EAAE,0BAA0B,EAAE,MAAM,oBAAoB,CAAC;AAGzF;;GAEG;AACH,oBAAY,YAAY,GAAG,aAAa,GAAG;IACvC,eAAe,EAAE,OAAO,CAAC;IACzB,mBAAmB,EAAE,uBAAuB,CAAC;IAC7C,uBAAuB,EAAE,MAAM,CAAC;CACnC,CAAC;AAEF,MAAM,WAAW,oBAAoB;IACjC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,kBAAkB,CAAC,EAAE,0BAA0B,CAAC;CAC5D;AAED;;GAEG;AACH,qBAAa,YAAY;aAOO,MAAM,EAAE,gBAAgB;aAAkB,OAAO,EAAE,oBAAoB;IANnG,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAe;IAC5C,OAAO,CAAC,YAAY,CAAuB;IAC3C,OAAO,CAAC,gBAAgB,CAAK;IAE7B,IAAW,MAAM,WAAuC;gBAE5B,MAAM,EAAE,gBAAgB,EAAkB,OAAO,EAAE,oBAAoB;IAI5F,IAAI,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO;IAiC3C,IAAW,KAAK,YAA6C;IAEtD,QAAQ;IAef;;OAEG;IACI,UAAU;sCAGqB,YAAY,KAAK,IAAI;;CAY9D"}
|
package/lib/batchManager.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"batchManager.js","sourceRoot":"","sources":["../src/batchManager.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { ITelemetryLogger } from \"@fluidframework/common-definitions\";\nimport { IBatchMessage } from \"@fluidframework/container-definitions\";\nimport { ContainerRuntimeMessage, ICompressionRuntimeOptions } from \"./containerRuntime\";\nimport { OpCompressor } from \"./opCompressor\";\n\n/**\n * Message type used by BatchManager\n */\nexport type BatchMessage = IBatchMessage & {\n localOpMetadata: unknown;\n deserializedContent: ContainerRuntimeMessage;\n referenceSequenceNumber: number;\n};\n\nexport interface IBatchManagerOptions {\n readonly hardLimit: number;\n readonly softLimit?: number;\n readonly compressionOptions?: ICompressionRuntimeOptions;\n}\n\n/**\n * Helper class that manages partial batch & rollback.\n */\nexport class BatchManager {\n private readonly opCompressor: OpCompressor;\n private pendingBatch: BatchMessage [] = [];\n private batchContentSize = 0;\n\n public get length() { return this.pendingBatch.length; }\n\n constructor(public readonly logger: ITelemetryLogger, public readonly options: IBatchManagerOptions) {\n this.opCompressor = new OpCompressor(logger);\n }\n\n public push(message: BatchMessage): boolean {\n const contentSize = this.batchContentSize + (message.contents?.length ?? 0);\n const opCount = this.pendingBatch.length;\n\n // Attempt to estimate batch size, aka socket message size.\n // Each op has pretty large envelope, estimating to be 200 bytes.\n // Also content will be strigified, and that adds a lot of overhead due to a lot of escape characters.\n // Not taking it into account, as compression work should help there - compressed payload will be\n // initially stored as base64, and that requires only 2 extra escape characters.\n const socketMessageSize = contentSize + 200 * opCount;\n\n // If we were provided soft limit, check for exceeding it.\n // But only if we have any ops, as the intention here is to flush existing ops (on exceeding this limit)\n // and start over. That's not an option if we have no ops.\n // If compression is enabled, the soft and hard limit are ignored and the message will be pushed anyways.\n // Cases where the message is still too large will be handled by the maxConsecutiveReconnects path.\n if (this.options.softLimit !== undefined\n && this.length > 0\n && socketMessageSize >= this.options.softLimit\n && Infinity === (this.options.compressionOptions?.minimumBatchSizeInBytes ?? Infinity)) {\n return false;\n }\n\n if (socketMessageSize >= this.options.hardLimit\n && Infinity === (this.options.compressionOptions?.minimumBatchSizeInBytes ?? Infinity)) {\n return false;\n }\n\n this.batchContentSize = contentSize;\n this.pendingBatch.push(message);\n return true;\n }\n\n public get empty() { return this.pendingBatch.length === 0; }\n\n public popBatch() {\n const batch = this.pendingBatch;\n const size = this.batchContentSize;\n this.pendingBatch = [];\n this.batchContentSize = 0;\n\n if (batch.length > 0\n && this.options.compressionOptions !== undefined\n && this.options.compressionOptions.minimumBatchSizeInBytes < size) {\n return this.opCompressor.compressBatch(batch, size);\n }\n\n return batch;\n }\n\n /**\n * Capture the pending state at this point\n */\n public checkpoint() {\n const startPoint = this.pendingBatch.length;\n return {\n rollback: (process: (message: BatchMessage) => void) => {\n for (let i = this.pendingBatch.length; i > startPoint;) {\n i--;\n const message = this.pendingBatch[i];\n this.batchContentSize -= message.contents?.length ?? 0;\n process(message);\n }\n\n this.pendingBatch.length = startPoint;\n },\n };\n }\n}\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"opCompressor.d.ts","sourceRoot":"","sources":["../src/opCompressor.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AAItE,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAG9C;;;;GAIG;AACH,qBAAa,YAAY;IACrB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC;IACxB,OAAO,CAAC,oBAAoB,CAAK;gBAErB,MAAM,EAAE,gBAAgB;IAI7B,aAAa,CAAC,KAAK,EAAE,YAAY,EAAE,EAAE,cAAc,EAAE,MAAM,GAAG,YAAY,EAAE;CAiCtF"}
|
package/lib/opCompressor.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"opCompressor.js","sourceRoot":"","sources":["../src/opCompressor.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAC9D,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAEjC,OAAO,EAAE,qBAAqB,EAA2B,MAAM,oBAAoB,CAAC;AAEpF;;;;GAIG;AACH,MAAM,OAAO,YAAY;IAIrB,YAAY,MAAwB;QAF5B,yBAAoB,GAAG,CAAC,CAAC;QAG7B,IAAI,CAAC,MAAM,GAAG,WAAW,CAAC,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IAC7D,CAAC;IAEM,aAAa,CAAC,KAAqB,EAAE,cAAsB;QAC9D,MAAM,WAAW,GAAmB,EAAE,CAAC;QACvC,IAAI,CAAC,oBAAoB,EAAE,CAAC;QAC5B,MAAM,eAAe,GAA8B,EAAE,CAAC;QACtD,KAAK,MAAM,OAAO,IAAI,KAAK,EAAE;YACzB,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC,mBAAmB,CAAC,CAAC;SACrD;QAED,MAAM,gBAAgB,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACpC,MAAM,gBAAgB,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC,CAAC;QACnF,MAAM,kBAAkB,GAAG,QAAQ,CAAC,gBAAgB,CAAC,CAAC;QACtD,MAAM,iBAAiB,GAAG,SAAS,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QAChF,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,gBAAgB,CAAC;QAE/C,IAAI,cAAc,GAAG,MAAM,IAAI,IAAI,CAAC,oBAAoB,GAAG,GAAG,EAAE;YAC5D,IAAI,CAAC,MAAM,CAAC,oBAAoB,CAAC;gBAC7B,SAAS,EAAE,iBAAiB;gBAC5B,QAAQ;gBACR,qBAAqB,EAAE,cAAc;gBACrC,oBAAoB,EAAE,iBAAiB,CAAC,MAAM;aACjD,CAAC,CAAC;SACN;QAED,WAAW,CAAC,IAAI,iCAAM,KAAK,CAAC,CAAC,CAAC,KAAE,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,cAAc,EAAE,iBAAiB,EAAE,CAAC,EAC5E,QAAQ,kCAAO,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,KAAE,UAAU,EAAE,IAAI,KAClD,WAAW,EAAE,qBAAqB,CAAC,GAAG,IAAG,CAAC;QAE7D,KAAK,MAAM,OAAO,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE;YAClC,WAAW,CAAC,IAAI,iCAAM,OAAO,KAAE,QAAQ,EAAE,SAAS,IAAG,CAAC;SACzD;QAED,OAAO,WAAW,CAAC;IACvB,CAAC;CACJ","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { ITelemetryLogger } from \"@fluidframework/common-definitions\";\nimport { IsoBuffer } from \"@fluidframework/common-utils\";\nimport { ChildLogger } from \"@fluidframework/telemetry-utils\";\nimport { compress } from \"lz4js\";\nimport { BatchMessage } from \"./batchManager\";\nimport { CompressionAlgorithms, ContainerRuntimeMessage } from \"./containerRuntime\";\n\n/**\n * Compresses batches of ops. It generates a single compressed op that contains\n * the contents of each op in the batch. It then submits empty ops for each original\n * op to reserve sequence numbers.\n */\nexport class OpCompressor {\n private readonly logger;\n private compressedBatchCount = 0;\n\n constructor(logger: ITelemetryLogger) {\n this.logger = ChildLogger.create(logger, \"OpCompressor\");\n }\n\n public compressBatch(batch: BatchMessage[], originalLength: number): BatchMessage[] {\n const batchToSend: BatchMessage[] = [];\n this.compressedBatchCount++;\n const batchedContents: ContainerRuntimeMessage[] = [];\n for (const message of batch) {\n batchedContents.push(message.deserializedContent);\n }\n\n const compressionStart = Date.now();\n const contentsAsBuffer = new TextEncoder().encode(JSON.stringify(batchedContents));\n const compressedContents = compress(contentsAsBuffer);\n const compressedContent = IsoBuffer.from(compressedContents).toString(\"base64\");\n const duration = Date.now() - compressionStart;\n\n if (originalLength > 200000 || this.compressedBatchCount % 100) {\n this.logger.sendPerformanceEvent({\n eventName: \"CompressedBatch\",\n duration,\n sizeBeforeCompression: originalLength,\n sizeAfterCompression: compressedContent.length,\n });\n }\n\n batchToSend.push({ ...batch[0], contents: JSON.stringify({ packedContents: compressedContent }),\n metadata: { ...batch[0].metadata, compressed: true },\n compression: CompressionAlgorithms.lz4 });\n\n for (const message of batch.slice(1)) {\n batchToSend.push({ ...message, contents: undefined });\n }\n\n return batchToSend;\n }\n}\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"opDecompressor.d.ts","sourceRoot":"","sources":["../src/opDecompressor.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,yBAAyB,EAAE,MAAM,sCAAsC,CAAC;AAIjF;;;;;;;GAOG;AACH,qBAAa,cAAc;IACvB,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,mBAAmB,CAAkB;IAC7C,OAAO,CAAC,cAAc,CAAK;IAEpB,cAAc,CAAC,OAAO,EAAE,yBAAyB,GAAG,yBAAyB;CA0DvF"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"opDecompressor.js","sourceRoot":"","sources":["../src/opDecompressor.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { decompress } from \"lz4js\";\nimport { ISequencedDocumentMessage } from \"@fluidframework/protocol-definitions\";\nimport { assert, IsoBuffer, Uint8ArrayToString } from \"@fluidframework/common-utils\";\nimport { CompressionAlgorithms } from \".\";\n\n/**\n * State machine that \"unrolls\" contents of compressed batches of ops after decompressing them.\n * This class relies on some implicit contracts defined below:\n * 1. A compressed batch will have its first message with batch metadata set to true and compressed set to true\n * 2. Messages in the middle of a compressed batch will have neither batch metadata nor the compression property set\n * 3. The final message of a batch will have batch metadata set to false\n * 4. An individually compressed op will have undefined batch metadata and compression set to true\n */\nexport class OpDecompressor {\n private activeBatch = false;\n private rootMessageContents: any | undefined;\n private processedCount = 0;\n\n public processMessage(message: ISequencedDocumentMessage): ISequencedDocumentMessage {\n // We're checking for compression = true or top level compression property so\n // that we can enable compression without waiting on all ordering services\n // to pick up protocol change. Eventually only the top level property should\n // be used.\n if (message.metadata?.batch === true\n && (message.metadata?.compressed || message.compression !== undefined)) {\n // Beginning of a compressed batch\n assert(this.activeBatch === false, \"shouldn't have multiple active batches\");\n if (message.compression) {\n // lz4 is the only supported compression algorithm for now\n assert(message.compression === CompressionAlgorithms.lz4,\n \"lz4 is currently the only supported compression algorithm\");\n }\n\n this.activeBatch = true;\n\n const contents = IsoBuffer.from(message.contents.packedContents, \"base64\");\n const decompressedMessage = decompress(contents);\n const intoString = Uint8ArrayToString(decompressedMessage);\n const asObj = JSON.parse(intoString);\n this.rootMessageContents = asObj;\n\n return { ...message, contents: this.rootMessageContents[this.processedCount++] };\n }\n\n if (this.rootMessageContents !== undefined && message.metadata?.batch === undefined && this.activeBatch) {\n // Continuation of compressed batch\n return { ...message, contents: this.rootMessageContents[this.processedCount++] };\n }\n\n if (this.rootMessageContents !== undefined && message.metadata?.batch === false) {\n // End of compressed batch\n const returnMessage = { ...message,\n contents: this.rootMessageContents[this.processedCount++] };\n\n this.activeBatch = false;\n this.rootMessageContents = undefined;\n this.processedCount = 0;\n\n return returnMessage;\n }\n\n if (message.metadata?.batch === undefined &&\n (message.metadata?.compressed || message.compression === CompressionAlgorithms.lz4)) {\n // Single compressed message\n assert(this.activeBatch === false, \"shouldn't receive compressed message in middle of a batch\");\n\n const contents = IsoBuffer.from(message.contents.packedContents, \"base64\");\n const decompressedMessage = decompress(contents);\n const intoString = new TextDecoder().decode(decompressedMessage);\n const asObj = JSON.parse(intoString);\n\n return { ...message, contents: asObj[0] };\n }\n\n return message;\n }\n}\n"]}
|