@fluidframework/container-runtime 2.92.0 → 2.100.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 +18 -0
- package/README.md +1 -1
- package/container-runtime.test-files.tar +0 -0
- package/dist/containerRuntime.d.ts +16 -4
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +80 -60
- package/dist/containerRuntime.js.map +1 -1
- package/dist/dataStore.d.ts +1 -1
- package/dist/dataStore.d.ts.map +1 -1
- package/dist/dataStore.js +6 -7
- package/dist/dataStore.js.map +1 -1
- package/dist/opLifecycle/batchManager.d.ts +3 -9
- package/dist/opLifecycle/batchManager.d.ts.map +1 -1
- package/dist/opLifecycle/batchManager.js +3 -2
- package/dist/opLifecycle/batchManager.js.map +1 -1
- package/dist/opLifecycle/outbox.d.ts +8 -5
- package/dist/opLifecycle/outbox.d.ts.map +1 -1
- package/dist/opLifecycle/outbox.js +40 -57
- package/dist/opLifecycle/outbox.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.d.ts.map +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/pendingStateManager.d.ts +1 -6
- package/dist/pendingStateManager.d.ts.map +1 -1
- package/dist/pendingStateManager.js +6 -16
- package/dist/pendingStateManager.js.map +1 -1
- package/dist/runtimeLayerCompatState.d.ts +2 -2
- package/eslint.config.mts +1 -1
- package/lib/containerRuntime.d.ts +16 -4
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +82 -62
- package/lib/containerRuntime.js.map +1 -1
- package/lib/dataStore.d.ts +1 -1
- package/lib/dataStore.d.ts.map +1 -1
- package/lib/dataStore.js +1 -2
- package/lib/dataStore.js.map +1 -1
- package/lib/opLifecycle/batchManager.d.ts +3 -9
- package/lib/opLifecycle/batchManager.d.ts.map +1 -1
- package/lib/opLifecycle/batchManager.js +3 -2
- package/lib/opLifecycle/batchManager.js.map +1 -1
- package/lib/opLifecycle/outbox.d.ts +8 -5
- package/lib/opLifecycle/outbox.d.ts.map +1 -1
- package/lib/opLifecycle/outbox.js +41 -58
- package/lib/opLifecycle/outbox.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.d.ts.map +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/pendingStateManager.d.ts +1 -6
- package/lib/pendingStateManager.d.ts.map +1 -1
- package/lib/pendingStateManager.js +6 -16
- package/lib/pendingStateManager.js.map +1 -1
- package/lib/runtimeLayerCompatState.d.ts +2 -2
- package/lib/tsdoc-metadata.json +1 -1
- package/package.json +26 -31
- package/src/containerRuntime.ts +91 -76
- package/src/dataStore.ts +5 -6
- package/src/opLifecycle/batchManager.ts +4 -12
- package/src/opLifecycle/outbox.ts +51 -69
- package/src/packageVersion.ts +1 -1
- package/src/pendingStateManager.ts +5 -23
package/lib/tsdoc-metadata.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fluidframework/container-runtime",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.100.0",
|
|
4
4
|
"description": "Fluid container runtime",
|
|
5
5
|
"homepage": "https://fluidframework.com",
|
|
6
6
|
"repository": {
|
|
@@ -119,18 +119,18 @@
|
|
|
119
119
|
"temp-directory": "nyc/.nyc_output"
|
|
120
120
|
},
|
|
121
121
|
"dependencies": {
|
|
122
|
-
"@fluid-internal/client-utils": "~2.
|
|
123
|
-
"@fluidframework/container-definitions": "~2.
|
|
124
|
-
"@fluidframework/container-runtime-definitions": "~2.
|
|
125
|
-
"@fluidframework/core-interfaces": "~2.
|
|
126
|
-
"@fluidframework/core-utils": "~2.
|
|
127
|
-
"@fluidframework/datastore": "~2.
|
|
128
|
-
"@fluidframework/driver-definitions": "~2.
|
|
129
|
-
"@fluidframework/driver-utils": "~2.
|
|
130
|
-
"@fluidframework/id-compressor": "~2.
|
|
131
|
-
"@fluidframework/runtime-definitions": "~2.
|
|
132
|
-
"@fluidframework/runtime-utils": "~2.
|
|
133
|
-
"@fluidframework/telemetry-utils": "~2.
|
|
122
|
+
"@fluid-internal/client-utils": "~2.100.0",
|
|
123
|
+
"@fluidframework/container-definitions": "~2.100.0",
|
|
124
|
+
"@fluidframework/container-runtime-definitions": "~2.100.0",
|
|
125
|
+
"@fluidframework/core-interfaces": "~2.100.0",
|
|
126
|
+
"@fluidframework/core-utils": "~2.100.0",
|
|
127
|
+
"@fluidframework/datastore": "~2.100.0",
|
|
128
|
+
"@fluidframework/driver-definitions": "~2.100.0",
|
|
129
|
+
"@fluidframework/driver-utils": "~2.100.0",
|
|
130
|
+
"@fluidframework/id-compressor": "~2.100.0",
|
|
131
|
+
"@fluidframework/runtime-definitions": "~2.100.0",
|
|
132
|
+
"@fluidframework/runtime-utils": "~2.100.0",
|
|
133
|
+
"@fluidframework/telemetry-utils": "~2.100.0",
|
|
134
134
|
"@tylerbu/sorted-btree-es6": "^2.1.1",
|
|
135
135
|
"double-ended-queue": "^2.1.0-0",
|
|
136
136
|
"lz4js": "^0.2.0",
|
|
@@ -140,21 +140,21 @@
|
|
|
140
140
|
"devDependencies": {
|
|
141
141
|
"@arethetypeswrong/cli": "^0.18.2",
|
|
142
142
|
"@biomejs/biome": "~2.4.5",
|
|
143
|
-
"@fluid-internal/mocha-test-setup": "~2.
|
|
144
|
-
"@fluid-private/stochastic-test-utils": "~2.
|
|
145
|
-
"@fluid-private/test-pairwise-generator": "~2.
|
|
146
|
-
"@fluid-tools/benchmark": "^0.
|
|
147
|
-
"@fluid-tools/build-cli": "^0.
|
|
143
|
+
"@fluid-internal/mocha-test-setup": "~2.100.0",
|
|
144
|
+
"@fluid-private/stochastic-test-utils": "~2.100.0",
|
|
145
|
+
"@fluid-private/test-pairwise-generator": "~2.100.0",
|
|
146
|
+
"@fluid-tools/benchmark": "^0.58.0",
|
|
147
|
+
"@fluid-tools/build-cli": "^0.65.0",
|
|
148
148
|
"@fluidframework/build-common": "^2.0.3",
|
|
149
|
-
"@fluidframework/build-tools": "^0.
|
|
150
|
-
"@fluidframework/container-runtime-previous": "npm:@fluidframework/container-runtime@2.
|
|
149
|
+
"@fluidframework/build-tools": "^0.65.0",
|
|
150
|
+
"@fluidframework/container-runtime-previous": "npm:@fluidframework/container-runtime@2.92.0",
|
|
151
151
|
"@fluidframework/eslint-config-fluid": "^9.0.0",
|
|
152
|
-
"@fluidframework/test-runtime-utils": "~2.
|
|
153
|
-
"@microsoft/api-extractor": "7.
|
|
152
|
+
"@fluidframework/test-runtime-utils": "~2.100.0",
|
|
153
|
+
"@microsoft/api-extractor": "7.58.1",
|
|
154
154
|
"@types/double-ended-queue": "^2.1.0",
|
|
155
155
|
"@types/lz4js": "^0.2.0",
|
|
156
156
|
"@types/mocha": "^10.0.10",
|
|
157
|
-
"@types/node": "~
|
|
157
|
+
"@types/node": "~22.19.17",
|
|
158
158
|
"@types/sinon": "^17.0.3",
|
|
159
159
|
"c8": "^10.1.3",
|
|
160
160
|
"concurrently": "^9.2.1",
|
|
@@ -169,17 +169,14 @@
|
|
|
169
169
|
"typescript": "~5.4.5"
|
|
170
170
|
},
|
|
171
171
|
"typeValidation": {
|
|
172
|
-
"broken": {
|
|
173
|
-
"Interface_ContainerRuntimeOptions": {
|
|
174
|
-
"forwardCompat": false
|
|
175
|
-
}
|
|
176
|
-
},
|
|
172
|
+
"broken": {},
|
|
177
173
|
"entrypoint": "legacy"
|
|
178
174
|
},
|
|
179
175
|
"scripts": {
|
|
180
176
|
"api": "fluid-build . --task api",
|
|
181
177
|
"api-extractor:commonjs": "flub generate entrypoints --resolutionConditions require --outFileLegacyBeta legacy --outDir ./dist",
|
|
182
178
|
"api-extractor:esnext": "flub generate entrypoints --outFileLegacyBeta legacy --outDir ./lib --node10TypeCompat",
|
|
179
|
+
"bench": "cross-env FLUID_TEST_PERF_MODE=1 mocha --parentProcess",
|
|
183
180
|
"build": "fluid-build . --task build",
|
|
184
181
|
"build:api-reports": "concurrently \"npm:build:api-reports:*\"",
|
|
185
182
|
"build:api-reports:current": "api-extractor run --local --config api-extractor/api-extractor.current.json",
|
|
@@ -215,7 +212,6 @@
|
|
|
215
212
|
"pack:tests": "tar -cf ./container-runtime.test-files.tar ./src/test ./dist/test ./lib/test",
|
|
216
213
|
"place:cjs:package-stub": "copyfiles -f ../../../common/build/build-common/src/cjs/package.json ./dist",
|
|
217
214
|
"test": "npm run test:mocha",
|
|
218
|
-
"test:benchmark:report": "cross-env \"MOCHA_SPEC=dist/**/*.perf.spec.*js\" mocha --timeout 10s --perfMode --parentProcess --fgrep @Benchmark --fgrep @ExecutionTime --reporter @fluid-tools/benchmark/dist/MochaReporter.js",
|
|
219
215
|
"test:coverage": "c8 npm test",
|
|
220
216
|
"test:mocha": "npm run test:mocha:esm && echo skipping cjs to avoid overhead - npm run test:mocha:cjs",
|
|
221
217
|
"test:mocha:cjs": "cross-env FLUID_TEST_MODULE_SYSTEM=CJS mocha",
|
|
@@ -223,7 +219,6 @@
|
|
|
223
219
|
"test:mocha:verbose": "cross-env FLUID_TEST_VERBOSE=1 npm run test:mocha",
|
|
224
220
|
"tsc": "fluid-tsc commonjs --project ./tsconfig.cjs.json && npm run place:cjs:package-stub",
|
|
225
221
|
"tsc:watch": "npm run place:cjs:package-stub && fluid-tsc commonjs --project ./tsconfig.cjs.json --watch",
|
|
226
|
-
"typetests:gen": "flub generate typetests --dir . -v"
|
|
227
|
-
"typetests:prepare": "flub typetests --dir . --reset --previous --normalize"
|
|
222
|
+
"typetests:gen": "flub generate typetests --dir . -v"
|
|
228
223
|
}
|
|
229
224
|
}
|
package/src/containerRuntime.ts
CHANGED
|
@@ -95,7 +95,6 @@ import { FetchSource, MessageType } from "@fluidframework/driver-definitions/int
|
|
|
95
95
|
import { readAndParse } from "@fluidframework/driver-utils/internal";
|
|
96
96
|
import type { IIdCompressor } from "@fluidframework/id-compressor";
|
|
97
97
|
import type {
|
|
98
|
-
// eslint-disable-next-line import-x/no-deprecated -- Will be undeprecated in 2.100.0 when it becomes an internal API
|
|
99
98
|
IIdCompressorCore,
|
|
100
99
|
IdCreationRange,
|
|
101
100
|
SerializedIdCompressorWithNoSession,
|
|
@@ -105,6 +104,7 @@ import {
|
|
|
105
104
|
createIdCompressor,
|
|
106
105
|
createSessionId,
|
|
107
106
|
deserializeIdCompressor,
|
|
107
|
+
toIdCompressorWithCore,
|
|
108
108
|
} from "@fluidframework/id-compressor/internal";
|
|
109
109
|
import {
|
|
110
110
|
FlushMode,
|
|
@@ -131,7 +131,6 @@ import type {
|
|
|
131
131
|
IContainerRuntimeBaseInternal,
|
|
132
132
|
MinimumVersionForCollab,
|
|
133
133
|
ContainerExtensionExpectations,
|
|
134
|
-
ContainerRuntimeBaseAlpha,
|
|
135
134
|
} from "@fluidframework/runtime-definitions/internal";
|
|
136
135
|
import {
|
|
137
136
|
addBlobToSummary,
|
|
@@ -173,6 +172,7 @@ import {
|
|
|
173
172
|
wrapError,
|
|
174
173
|
tagCodeArtifacts,
|
|
175
174
|
normalizeError,
|
|
175
|
+
toITelemetryLoggerExt,
|
|
176
176
|
} from "@fluidframework/telemetry-utils/internal";
|
|
177
177
|
import { gt } from "semver-ts";
|
|
178
178
|
import { v4 as uuid } from "uuid";
|
|
@@ -675,18 +675,11 @@ export function getDeviceSpec(): {
|
|
|
675
675
|
deviceMemory?: number | undefined;
|
|
676
676
|
hardwareConcurrency?: number | undefined;
|
|
677
677
|
} {
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
hardwareConcurrency: navigator.hardwareConcurrency,
|
|
684
|
-
};
|
|
685
|
-
}
|
|
686
|
-
} catch {
|
|
687
|
-
// Eat the error
|
|
688
|
-
}
|
|
689
|
-
return {};
|
|
678
|
+
return {
|
|
679
|
+
// deviceMemory is only available in browsers and is not part of the Navigator type definition. In Node 22 it is undefined.
|
|
680
|
+
deviceMemory: (navigator as Navigator & { deviceMemory?: number }).deviceMemory,
|
|
681
|
+
hardwareConcurrency: navigator.hardwareConcurrency,
|
|
682
|
+
};
|
|
690
683
|
}
|
|
691
684
|
|
|
692
685
|
/**
|
|
@@ -846,7 +839,7 @@ export async function loadContainerRuntime(
|
|
|
846
839
|
* @legacy @alpha
|
|
847
840
|
*/
|
|
848
841
|
export async function loadContainerRuntimeAlpha(params: LoadContainerRuntimeParams): Promise<{
|
|
849
|
-
runtime: IContainerRuntime &
|
|
842
|
+
runtime: IContainerRuntime & IRuntime;
|
|
850
843
|
}> {
|
|
851
844
|
return ContainerRuntime.loadRuntime2({
|
|
852
845
|
...params,
|
|
@@ -1185,7 +1178,6 @@ export class ContainerRuntime
|
|
|
1185
1178
|
idCompressorMode = desiredIdCompressorMode;
|
|
1186
1179
|
}
|
|
1187
1180
|
|
|
1188
|
-
// eslint-disable-next-line import-x/no-deprecated -- Will be undeprecated in 2.100.0 when it becomes an internal API
|
|
1189
1181
|
const createIdCompressorFn = (): IIdCompressor & IIdCompressorCore => {
|
|
1190
1182
|
/**
|
|
1191
1183
|
* Because the IdCompressor emits so much telemetry, this function is used to sample
|
|
@@ -1204,17 +1196,21 @@ export class ContainerRuntime
|
|
|
1204
1196
|
const pendingLocalState = context.pendingLocalState as IPendingRuntimeState;
|
|
1205
1197
|
|
|
1206
1198
|
if (pendingLocalState?.pendingIdCompressorState !== undefined) {
|
|
1207
|
-
return
|
|
1208
|
-
|
|
1209
|
-
|
|
1199
|
+
return toIdCompressorWithCore(
|
|
1200
|
+
deserializeIdCompressor(
|
|
1201
|
+
pendingLocalState.pendingIdCompressorState,
|
|
1202
|
+
toITelemetryLoggerExt(compressorLogger),
|
|
1203
|
+
),
|
|
1210
1204
|
);
|
|
1211
1205
|
} else if (serializedIdCompressor === undefined) {
|
|
1212
|
-
return createIdCompressor(compressorLogger);
|
|
1206
|
+
return toIdCompressorWithCore(createIdCompressor(compressorLogger));
|
|
1213
1207
|
} else {
|
|
1214
|
-
return
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1208
|
+
return toIdCompressorWithCore(
|
|
1209
|
+
deserializeIdCompressor(
|
|
1210
|
+
serializedIdCompressor,
|
|
1211
|
+
createSessionId(),
|
|
1212
|
+
toITelemetryLoggerExt(compressorLogger),
|
|
1213
|
+
),
|
|
1218
1214
|
);
|
|
1219
1215
|
}
|
|
1220
1216
|
};
|
|
@@ -1378,7 +1374,6 @@ export class ContainerRuntime
|
|
|
1378
1374
|
return this.documentsSchemaController.sessionSchema.runtime;
|
|
1379
1375
|
}
|
|
1380
1376
|
|
|
1381
|
-
// eslint-disable-next-line import-x/no-deprecated -- Will be undeprecated in 2.100.0 when it becomes an internal API
|
|
1382
1377
|
private _idCompressor: (IIdCompressor & IIdCompressorCore) | undefined;
|
|
1383
1378
|
|
|
1384
1379
|
// We accumulate Id compressor Ops while Id compressor is not loaded yet (only for "delayed" mode)
|
|
@@ -1394,7 +1389,6 @@ export class ContainerRuntime
|
|
|
1394
1389
|
/**
|
|
1395
1390
|
* {@inheritDoc @fluidframework/runtime-definitions#IContainerRuntimeBase.idCompressor}
|
|
1396
1391
|
*/
|
|
1397
|
-
// eslint-disable-next-line import-x/no-deprecated -- Will be undeprecated in 2.100.0 when it becomes an internal API
|
|
1398
1392
|
public get idCompressor(): (IIdCompressor & IIdCompressorCore) | undefined {
|
|
1399
1393
|
// Expose ID Compressor only if it's On from the start.
|
|
1400
1394
|
// If container uses delayed mode, then we can only expose generateDocumentUniqueId() and nothing else.
|
|
@@ -1615,7 +1609,6 @@ export class ContainerRuntime
|
|
|
1615
1609
|
|
|
1616
1610
|
blobManagerLoadInfo: IBlobManagerLoadInfo,
|
|
1617
1611
|
private readonly _storage: IContainerStorageService,
|
|
1618
|
-
// eslint-disable-next-line import-x/no-deprecated -- Will be undeprecated in 2.100.0 when it becomes an internal API
|
|
1619
1612
|
private readonly createIdCompressorFn: () => IIdCompressor & IIdCompressorCore,
|
|
1620
1613
|
|
|
1621
1614
|
private readonly documentsSchemaController: DocumentsSchemaController,
|
|
@@ -2069,6 +2062,24 @@ export class ContainerRuntime
|
|
|
2069
2062
|
}),
|
|
2070
2063
|
reSubmit: this.reSubmit.bind(this),
|
|
2071
2064
|
opReentrancy: () => this.dataModelChangeRunner.running,
|
|
2065
|
+
generateIdAllocationOp: (): LocalBatchMessage | undefined => {
|
|
2066
|
+
if (this._idCompressor === undefined) {
|
|
2067
|
+
return undefined;
|
|
2068
|
+
}
|
|
2069
|
+
const idRange = this._idCompressor.takeNextCreationRange();
|
|
2070
|
+
if (idRange.ids === undefined) {
|
|
2071
|
+
return undefined;
|
|
2072
|
+
}
|
|
2073
|
+
const idAllocationMessage: ContainerRuntimeIdAllocationMessage = {
|
|
2074
|
+
type: ContainerMessageType.IdAllocation,
|
|
2075
|
+
contents: idRange,
|
|
2076
|
+
};
|
|
2077
|
+
return {
|
|
2078
|
+
runtimeOp: idAllocationMessage,
|
|
2079
|
+
referenceSequenceNumber: this.deltaManager.lastSequenceNumber,
|
|
2080
|
+
staged: false,
|
|
2081
|
+
};
|
|
2082
|
+
},
|
|
2072
2083
|
});
|
|
2073
2084
|
|
|
2074
2085
|
this._quorum = quorum;
|
|
@@ -3580,7 +3591,18 @@ export class ContainerRuntime
|
|
|
3580
3591
|
let stageControls: StageControlsInternal | undefined;
|
|
3581
3592
|
if (this.mc.config.getBoolean("Fluid.ContainerRuntime.EnableRollback") === true) {
|
|
3582
3593
|
if (!this.batchRunner.running && !this.inStagingMode) {
|
|
3583
|
-
|
|
3594
|
+
// Use silent=true to suppress stagingModeChanged events for orderSequentially.
|
|
3595
|
+
// orderSequentially uses staging mode as a rollback mechanism.
|
|
3596
|
+
// Emitting stagingModeChanged here would:
|
|
3597
|
+
// - Cause UI flicker — consumers rendering staging mode event would see
|
|
3598
|
+
// unexpected enter/exit flashes on every orderSequentially call.
|
|
3599
|
+
// - consumers cannot distinguish this internal usage
|
|
3600
|
+
// from a user explicitly entering staging mode, as there is no source field
|
|
3601
|
+
// on the event to filter by.
|
|
3602
|
+
// - if orderSequentially is
|
|
3603
|
+
// later reimplemented without staging mode, consumers calibrated
|
|
3604
|
+
// to these events would break silently.
|
|
3605
|
+
stageControls = this.enterStagingModeCore(true);
|
|
3584
3606
|
}
|
|
3585
3607
|
// Note: we are not touching any batches other than mainBatch here, for two reasons:
|
|
3586
3608
|
// 1. It would not help, as other batches are flushed independently from main batch.
|
|
@@ -3656,9 +3678,22 @@ export class ContainerRuntime
|
|
|
3656
3678
|
* Enter Staging Mode, such that ops submitted to the ContainerRuntime will not be sent to the ordering service.
|
|
3657
3679
|
* To exit Staging Mode, call either discardChanges or commitChanges on the Stage Controls returned from this method.
|
|
3658
3680
|
*
|
|
3681
|
+
* @remarks
|
|
3682
|
+
* The `stagingModeChanged` event is emitted when staging mode is entered or exited via this method.
|
|
3683
|
+
* It is NOT emitted when staging mode is used internally (e.g. by `orderSequentially` for rollback support).
|
|
3684
|
+
*
|
|
3659
3685
|
* @returns Controls for exiting Staging Mode.
|
|
3660
3686
|
*/
|
|
3661
|
-
public enterStagingMode = (): StageControlsInternal =>
|
|
3687
|
+
public enterStagingMode = (): StageControlsInternal => this.enterStagingModeCore(false);
|
|
3688
|
+
|
|
3689
|
+
/**
|
|
3690
|
+
* Internal implementation of enterStagingMode.
|
|
3691
|
+
* @param silent - When true, suppresses `stagingModeChanged` event emission.
|
|
3692
|
+
* Pass `true` when staging mode is used as an internal implementation detail (e.g. by
|
|
3693
|
+
* `orderSequentially` for rollback support) so that external listeners only observe
|
|
3694
|
+
* user-initiated staging mode transitions. Pass `false` for all public entry points.
|
|
3695
|
+
*/
|
|
3696
|
+
private readonly enterStagingModeCore = (silent: boolean): StageControlsInternal => {
|
|
3662
3697
|
if (this.stageControls !== undefined) {
|
|
3663
3698
|
throw new UsageError("Already in staging mode");
|
|
3664
3699
|
}
|
|
@@ -3670,10 +3705,15 @@ export class ContainerRuntime
|
|
|
3670
3705
|
// since we mark whole batches as "staged" or not to indicate whether to submit them.
|
|
3671
3706
|
this.flush();
|
|
3672
3707
|
|
|
3708
|
+
// Note: `silent` is captured from the enclosing `enterStagingModeCore` call.
|
|
3709
|
+
// When `true`, both enter and exit events are suppressed (see orderSequentially).
|
|
3673
3710
|
const exitStagingMode = (
|
|
3674
3711
|
discardOrCommit: () => IPendingMessage["batchInfo"][],
|
|
3675
3712
|
exitMethod: "commit" | "discard",
|
|
3676
3713
|
): void => {
|
|
3714
|
+
if (this.stageControls !== stageControls) {
|
|
3715
|
+
throw new UsageError("Not in staging mode");
|
|
3716
|
+
}
|
|
3677
3717
|
try {
|
|
3678
3718
|
PerformanceEvent.timedExec(
|
|
3679
3719
|
this.mc.logger,
|
|
@@ -3687,9 +3727,6 @@ export class ContainerRuntime
|
|
|
3687
3727
|
|
|
3688
3728
|
this.stageControls = undefined;
|
|
3689
3729
|
|
|
3690
|
-
// During Staging Mode, we avoid submitting any ID Allocation ops (apart from resubmitting pre-staging ops).
|
|
3691
|
-
// Now that we've exited, we need to submit an ID Allocation op for any IDs that were generated while in Staging Mode.
|
|
3692
|
-
this.submitIdAllocationOpIfNeeded({ staged: false });
|
|
3693
3730
|
const batchInfos = discardOrCommit();
|
|
3694
3731
|
event.reportProgress({
|
|
3695
3732
|
details: {
|
|
@@ -3708,6 +3745,12 @@ export class ContainerRuntime
|
|
|
3708
3745
|
this.closeFn(normalizedError);
|
|
3709
3746
|
throw normalizedError;
|
|
3710
3747
|
}
|
|
3748
|
+
if (!silent) {
|
|
3749
|
+
this.emit("stagingModeChanged", {
|
|
3750
|
+
inStagingMode: false,
|
|
3751
|
+
commit: exitMethod === "commit",
|
|
3752
|
+
});
|
|
3753
|
+
}
|
|
3711
3754
|
};
|
|
3712
3755
|
|
|
3713
3756
|
const stageControls: StageControlsInternal = {
|
|
@@ -3737,8 +3780,16 @@ export class ContainerRuntime
|
|
|
3737
3780
|
|
|
3738
3781
|
this.stageControls = stageControls;
|
|
3739
3782
|
this.channelCollection.notifyStagingMode(true);
|
|
3783
|
+
if (!silent) {
|
|
3784
|
+
try {
|
|
3785
|
+
this.emit("stagingModeChanged", { inStagingMode: true });
|
|
3786
|
+
} catch (error) {
|
|
3787
|
+
// Don't let a listener error prevent the caller from receiving stage controls.
|
|
3788
|
+
this.mc.logger.sendErrorEvent({ eventName: "StagingModeChangedError" }, error);
|
|
3789
|
+
}
|
|
3790
|
+
}
|
|
3740
3791
|
|
|
3741
|
-
return
|
|
3792
|
+
return stageControls;
|
|
3742
3793
|
};
|
|
3743
3794
|
|
|
3744
3795
|
/**
|
|
@@ -4272,7 +4323,6 @@ export class ContainerRuntime
|
|
|
4272
4323
|
outboxLength: this.outbox.messageCount,
|
|
4273
4324
|
mainBatchLength: this.outbox.mainBatchMessageCount,
|
|
4274
4325
|
blobAttachBatchLength: this.outbox.blobAttachBatchMessageCount,
|
|
4275
|
-
idAllocationBatchLength: this.outbox.idAllocationBatchMessageCount,
|
|
4276
4326
|
},
|
|
4277
4327
|
);
|
|
4278
4328
|
}
|
|
@@ -4605,7 +4655,8 @@ export class ContainerRuntime
|
|
|
4605
4655
|
|
|
4606
4656
|
/**
|
|
4607
4657
|
* This helper is called during summarization. If the container is dirty, it will return a failed summarize result
|
|
4608
|
-
* (IBaseSummarizeResult) unless this is the final summarize attempt
|
|
4658
|
+
* (IBaseSummarizeResult) unless this is the final summarize attempt, in which case the summary is allowed to
|
|
4659
|
+
* proceed to make progress in documents where there are consistently pending ops in the summarizer.
|
|
4609
4660
|
* @param logger - The logger to be used for sending telemetry.
|
|
4610
4661
|
* @param referenceSequenceNumber - The reference sequence number of the summary attempt.
|
|
4611
4662
|
* @param minimumSequenceNumber - The minimum sequence number of the summary attempt.
|
|
@@ -4624,13 +4675,9 @@ export class ContainerRuntime
|
|
|
4624
4675
|
return;
|
|
4625
4676
|
}
|
|
4626
4677
|
|
|
4627
|
-
//
|
|
4628
|
-
//
|
|
4629
|
-
|
|
4630
|
-
if (
|
|
4631
|
-
finalAttempt &&
|
|
4632
|
-
this.mc.config.getBoolean("Fluid.Summarizer.SkipFailingIncorrectSummary") === true
|
|
4633
|
-
) {
|
|
4678
|
+
// Don't fail the summary in the last attempt. This is a fallback to make progress in
|
|
4679
|
+
// documents where there are consistently pending ops in the summarizer.
|
|
4680
|
+
if (finalAttempt) {
|
|
4634
4681
|
const error = DataProcessingError.create(
|
|
4635
4682
|
"Pending ops during summarization",
|
|
4636
4683
|
"submitSummary",
|
|
@@ -4639,7 +4686,7 @@ export class ContainerRuntime
|
|
|
4639
4686
|
);
|
|
4640
4687
|
logger.sendErrorEvent(
|
|
4641
4688
|
{
|
|
4642
|
-
eventName: "
|
|
4689
|
+
eventName: "PendingOpsDuringSummaryFinalAttempt",
|
|
4643
4690
|
referenceSequenceNumber,
|
|
4644
4691
|
minimumSequenceNumber,
|
|
4645
4692
|
beforeGenerate: beforeSummaryGeneration,
|
|
@@ -4733,33 +4780,6 @@ export class ContainerRuntime
|
|
|
4733
4780
|
return this.blobManager.lookupTemporaryBlobStorageId(localId);
|
|
4734
4781
|
}
|
|
4735
4782
|
|
|
4736
|
-
private submitIdAllocationOpIfNeeded({
|
|
4737
|
-
resubmitOutstandingRanges = false,
|
|
4738
|
-
staged,
|
|
4739
|
-
}: {
|
|
4740
|
-
resubmitOutstandingRanges?: boolean;
|
|
4741
|
-
staged: boolean;
|
|
4742
|
-
}): void {
|
|
4743
|
-
if (this._idCompressor) {
|
|
4744
|
-
const idRange = resubmitOutstandingRanges
|
|
4745
|
-
? this._idCompressor.takeUnfinalizedCreationRange()
|
|
4746
|
-
: this._idCompressor.takeNextCreationRange();
|
|
4747
|
-
// Don't include the idRange if there weren't any Ids allocated
|
|
4748
|
-
if (idRange.ids !== undefined) {
|
|
4749
|
-
const idAllocationMessage: ContainerRuntimeIdAllocationMessage = {
|
|
4750
|
-
type: ContainerMessageType.IdAllocation,
|
|
4751
|
-
contents: idRange,
|
|
4752
|
-
};
|
|
4753
|
-
const idAllocationBatchMessage: LocalBatchMessage = {
|
|
4754
|
-
runtimeOp: idAllocationMessage,
|
|
4755
|
-
referenceSequenceNumber: this.deltaManager.lastSequenceNumber,
|
|
4756
|
-
staged,
|
|
4757
|
-
};
|
|
4758
|
-
this.outbox.submitIdAllocation(idAllocationBatchMessage);
|
|
4759
|
-
}
|
|
4760
|
-
}
|
|
4761
|
-
}
|
|
4762
|
-
|
|
4763
4783
|
private submit(
|
|
4764
4784
|
containerRuntimeMessage: LocalContainerRuntimeMessage,
|
|
4765
4785
|
localOpMetadata: unknown = undefined,
|
|
@@ -4803,11 +4823,6 @@ export class ContainerRuntime
|
|
|
4803
4823
|
0xbba /* Unexpected message type submitted in Staging Mode */,
|
|
4804
4824
|
);
|
|
4805
4825
|
|
|
4806
|
-
// Before submitting any non-staged change, submit the ID Allocation op to cover any compressed IDs included in the op.
|
|
4807
|
-
if (!staged) {
|
|
4808
|
-
this.submitIdAllocationOpIfNeeded({ staged: false });
|
|
4809
|
-
}
|
|
4810
|
-
|
|
4811
4826
|
// Allow document schema controller to send a message if it needs to propose change in document schema.
|
|
4812
4827
|
// If it needs to send a message, it will call provided callback with payload of such message and rely
|
|
4813
4828
|
// on this callback to do actual sending.
|
|
@@ -4867,7 +4882,7 @@ export class ContainerRuntime
|
|
|
4867
4882
|
// Incoming ops still break the batch via direct this.flush() calls elsewhere
|
|
4868
4883
|
// (deltaManager "op" handler, process(), connection changes, getPendingLocalState,
|
|
4869
4884
|
// exitStagingMode). Those all bypass scheduleFlush(), so they're unaffected by this check.
|
|
4870
|
-
// Additionally, outbox.
|
|
4885
|
+
// Additionally, outbox.outboxSequenceNumberCoherencyCheck() (called on every submit) detects
|
|
4871
4886
|
// sequence number changes and throws if unexpected changes are detected.
|
|
4872
4887
|
if (
|
|
4873
4888
|
this.inStagingMode &&
|
package/src/dataStore.ts
CHANGED
|
@@ -7,11 +7,10 @@ import { AttachState } from "@fluidframework/container-definitions";
|
|
|
7
7
|
import type { FluidObject } from "@fluidframework/core-interfaces";
|
|
8
8
|
import type { IFluidHandleInternal } from "@fluidframework/core-interfaces/internal";
|
|
9
9
|
import { assert, unreachableCase } from "@fluidframework/core-utils/internal";
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
asLegacyAlpha,
|
|
10
|
+
import type {
|
|
11
|
+
AliasResult,
|
|
12
|
+
IDataStore,
|
|
13
|
+
IFluidDataStoreChannel,
|
|
15
14
|
} from "@fluidframework/runtime-definitions/internal";
|
|
16
15
|
import {
|
|
17
16
|
type ITelemetryLoggerExt,
|
|
@@ -81,7 +80,7 @@ class DataStore implements IDataStore {
|
|
|
81
80
|
if (alias.includes("/")) {
|
|
82
81
|
throw new UsageError(`The alias cannot contain slashes: '${alias}'`);
|
|
83
82
|
}
|
|
84
|
-
if (
|
|
83
|
+
if (this.parentContext.containerRuntime.inStagingMode === true) {
|
|
85
84
|
throw new UsageError("Cannot set aliases while in staging mode");
|
|
86
85
|
}
|
|
87
86
|
|
|
@@ -20,17 +20,8 @@ import { serializeOp } from "./opSerialization.js";
|
|
|
20
20
|
import type { BatchStartInfo } from "./remoteMessageProcessor.js";
|
|
21
21
|
|
|
22
22
|
export interface IBatchManagerOptions {
|
|
23
|
+
readonly disableGroupedBatching: boolean;
|
|
23
24
|
readonly compressionOptions?: ICompressionRuntimeOptions;
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* If true, the outbox is allowed to rebase the batch during flushing.
|
|
27
|
-
*/
|
|
28
|
-
readonly canRebase: boolean;
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* If true, don't compare batchID of incoming batches to this. e.g. ID Allocation Batch IDs should be ignored
|
|
32
|
-
*/
|
|
33
|
-
readonly ignoreBatchId?: boolean;
|
|
34
25
|
}
|
|
35
26
|
|
|
36
27
|
export interface BatchSequenceNumbers {
|
|
@@ -127,8 +118,9 @@ export class BatchManager {
|
|
|
127
118
|
|
|
128
119
|
/**
|
|
129
120
|
* Gets the pending batch and clears state for the next batch.
|
|
121
|
+
* The caller is responsible for calling {@link addBatchMetadata} after any modifications (e.g. prepending messages).
|
|
130
122
|
*/
|
|
131
|
-
public popBatch(
|
|
123
|
+
public popBatch(): LocalBatch {
|
|
132
124
|
assert(this.pendingBatch[0] !== undefined, 0xb8a /* expected non-empty batch */);
|
|
133
125
|
const batch: LocalBatch = {
|
|
134
126
|
messages: this.pendingBatch,
|
|
@@ -141,7 +133,7 @@ export class BatchManager {
|
|
|
141
133
|
this.clientSequenceNumber = undefined;
|
|
142
134
|
this.hasReentrantOps = false;
|
|
143
135
|
|
|
144
|
-
return
|
|
136
|
+
return batch;
|
|
145
137
|
}
|
|
146
138
|
|
|
147
139
|
/**
|