forked from microsoft/FluidFramework
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Introduce a new FlushMode in the interest of not creating too many ba…
…tches (microsoft#14106) ## Description When the client uses async code, there is a risk of creating too many batches (enough batches to trigger server-side throttling) as with FlushMode.TurnBased as flush is scheduled as a microtask. This change allows for flushing to happen in a scheduled macrotask, capturing all the ops from all microtask and bundling them as a single batch. **Note this ability is not part of the public API. The runtime does not accept this new flush mode without explicit casting** This change is microsoft#14060 but for `main`
- Loading branch information
Showing
5 changed files
with
183 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
122 changes: 122 additions & 0 deletions
122
packages/test/test-end-to-end-tests/src/test/fewerBatches.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
/*! | ||
* Copyright (c) Microsoft Corporation and contributors. All rights reserved. | ||
* Licensed under the MIT License. | ||
*/ | ||
|
||
import { strict as assert } from "assert"; | ||
import { IContainer } from "@fluidframework/container-definitions"; | ||
import { SharedMap } from "@fluidframework/map"; | ||
import { IDocumentMessage } from "@fluidframework/protocol-definitions"; | ||
import { requestFluidObject } from "@fluidframework/runtime-utils"; | ||
import { | ||
ChannelFactoryRegistry, | ||
DataObjectFactoryType, | ||
ITestContainerConfig, | ||
ITestFluidObject, | ||
ITestObjectProvider, | ||
waitForContainerConnection, | ||
} from "@fluidframework/test-utils"; | ||
import { describeNoCompat } from "@fluidframework/test-version-utils"; | ||
import { FlushMode, FlushModeExperimental } from "@fluidframework/runtime-definitions"; | ||
|
||
describeNoCompat("Less batches", (getTestObjectProvider) => { | ||
const mapId = "mapId"; | ||
const registry: ChannelFactoryRegistry = [[mapId, SharedMap.getFactory()]]; | ||
const testContainerConfig: ITestContainerConfig = { | ||
fluidDataObjectType: DataObjectFactoryType.Test, | ||
registry, | ||
}; | ||
|
||
let provider: ITestObjectProvider; | ||
const capturedBatches: IDocumentMessage[][] = []; | ||
|
||
beforeEach(() => { | ||
provider = getTestObjectProvider(); | ||
capturedBatches.splice(0); | ||
}); | ||
afterEach(async () => provider.reset()); | ||
|
||
let localContainer: IContainer; | ||
let remoteContainer: IContainer; | ||
let dataObject1: ITestFluidObject; | ||
let dataObject2: ITestFluidObject; | ||
let dataObject1map: SharedMap; | ||
let dataObject2map: SharedMap; | ||
|
||
const setupContainers = async (containerConfig: ITestContainerConfig) => { | ||
// Create a Container for the first client. | ||
localContainer = await provider.makeTestContainer(containerConfig); | ||
dataObject1 = await requestFluidObject<ITestFluidObject>(localContainer, "default"); | ||
dataObject1map = await dataObject1.getSharedObject<SharedMap>(mapId); | ||
|
||
// Load the Container that was created by the first client. | ||
remoteContainer = await provider.loadTestContainer(containerConfig); | ||
dataObject2 = await requestFluidObject<ITestFluidObject>(remoteContainer, "default"); | ||
dataObject2map = await dataObject2.getSharedObject<SharedMap>(mapId); | ||
await waitForContainerConnection(localContainer, true); | ||
await waitForContainerConnection(remoteContainer, true); | ||
|
||
localContainer.deltaManager.outbound.on("op", (batch: IDocumentMessage[]) => { | ||
capturedBatches.push(batch); | ||
}); | ||
await provider.ensureSynchronized(); | ||
}; | ||
|
||
[ | ||
{ | ||
flushMode: FlushMode.TurnBased, | ||
batchCount: 5, | ||
}, | ||
{ | ||
flushMode: FlushMode.Immediate, | ||
batchCount: 5, | ||
}, | ||
{ | ||
flushMode: FlushModeExperimental.Async as unknown as FlushMode, | ||
batchCount: 1, | ||
}, | ||
].forEach((test) => { | ||
it(`With runtime flushMode=FlushMode.${ | ||
FlushMode[test.flushMode] | ||
}, ops across JS turns produce ${test.batchCount} batches`, async () => { | ||
await setupContainers({ | ||
...testContainerConfig, | ||
runtimeOptions: { | ||
flushMode: test.flushMode, | ||
}, | ||
}); | ||
|
||
// Force the container into write-mode | ||
dataObject1map.set("key0", "0"); | ||
await provider.ensureSynchronized(); | ||
|
||
// Ignore the batch we just sent | ||
capturedBatches.splice(0); | ||
|
||
const opCount = 5; | ||
dataObject1map.set("key1", "1"); | ||
|
||
await Promise.resolve().then(async () => { | ||
dataObject1map.set("key2", "2"); | ||
}); | ||
await Promise.resolve().then(async () => { | ||
dataObject1map.set("key3", "3"); | ||
}); | ||
await Promise.resolve().then(async () => { | ||
dataObject1map.set("key4", "4"); | ||
await Promise.resolve().then(async () => { | ||
dataObject1map.set("key5", "5"); | ||
}); | ||
}); | ||
|
||
await provider.ensureSynchronized(); | ||
|
||
assert.strictEqual(capturedBatches.length, test.batchCount); | ||
|
||
for (let i = 1; i <= opCount; i++) { | ||
const value = dataObject2map.get(`key${i}`); | ||
assert.strictEqual(value, `${i}`, `Wrong value for key${i}`); | ||
} | ||
}); | ||
}); | ||
}); |