@fluidframework/test-utils 2.0.0-dev.4.1.0.148229 → 2.0.0-dev.4.3.0.157531

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ # @fluidframework/test-utils
2
+
3
+ ## 2.0.0-internal.4.1.0
4
+
5
+ Dependency updates only.
@@ -8,7 +8,7 @@ import { IGCRuntimeOptions, ISummarizer } from "@fluidframework/container-runtim
8
8
  import { IFluidDataStoreFactory, NamedFluidDataStoreRegistryEntries } from "@fluidframework/runtime-definitions";
9
9
  import { IConfigProviderBase } from "@fluidframework/telemetry-utils";
10
10
  import { ITelemetryBaseLogger } from "@fluidframework/common-definitions";
11
- import { ITestObjectProvider } from "./testObjectProvider";
11
+ import { ITestContainerConfig, ITestObjectProvider } from "./testObjectProvider";
12
12
  /**
13
13
  * Creates a summarizer client from the given container and data store factory, and returns the summarizer client's
14
14
  * IContainer and ISummarizer.
@@ -26,6 +26,16 @@ export declare function createSummarizer(provider: ITestObjectProvider, containe
26
26
  container: IContainer;
27
27
  summarizer: ISummarizer;
28
28
  }>;
29
+ /**
30
+ * Creates a summarizer client from the given container and returns the summarizer client's IContainer and ISummarizer.
31
+ * The ISummarizer can be used to generate on-demand summaries. The IContainer can be used to fetch data stores, etc.
32
+ *
33
+ * Can pass in a test config provider to enable/disable features.
34
+ */
35
+ export declare function createSummarizerWithTestConfig(provider: ITestObjectProvider, container: IContainer, config: ITestContainerConfig, summaryVersion?: string, logger?: ITelemetryBaseLogger): Promise<{
36
+ container: IContainer;
37
+ summarizer: ISummarizer;
38
+ }>;
29
39
  /**
30
40
  * Summarizes on demand and returns the summary tree, the version number and the reference sequence number of the
31
41
  * submitted summary.
@@ -1 +1 @@
1
- {"version":3,"file":"TestSummaryUtils.d.ts","sourceRoot":"","sources":["../src/TestSummaryUtils.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,2CAA2C,EAAE,MAAM,0BAA0B,CAAC;AAEvF,OAAO,EAAE,UAAU,EAA6B,MAAM,uCAAuC,CAAC;AAC9F,OAAO,EACN,iBAAiB,EACjB,WAAW,EAEX,MAAM,mCAAmC,CAAC;AAG3C,OAAO,EAEN,sBAAsB,EACtB,kCAAkC,EAClC,MAAM,qCAAqC,CAAC;AAE7C,OAAO,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AACtE,OAAO,EAAE,oBAAoB,EAAE,MAAM,oCAAoC,CAAC;AAC1E,OAAO,EAAwB,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAwDjF;;;;GAIG;AACH,wBAAsB,2BAA2B,CAChD,QAAQ,EAAE,mBAAmB,EAC7B,SAAS,EAAE,UAAU,EACrB,gBAAgB,EAAE,sBAAsB,EACxC,cAAc,CAAC,EAAE,MAAM,EACvB,2BAA2B,qDAA8C,EACzE,eAAe,CAAC,EAAE,kCAAkC,EACpD,MAAM,CAAC,EAAE,oBAAoB,GAC3B,OAAO,CAAC;IAAE,SAAS,EAAE,UAAU,CAAC;IAAC,UAAU,EAAE,WAAW,CAAA;CAAE,CAAC,CAgB7D;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,CACrC,QAAQ,EAAE,mBAAmB,EAC7B,SAAS,EAAE,UAAU,EACrB,cAAc,CAAC,EAAE,MAAM,EACvB,SAAS,CAAC,EAAE,iBAAiB,EAC7B,cAAc,GAAE,mBAA0C,EAC1D,MAAM,CAAC,EAAE,oBAAoB,GAC3B,OAAO,CAAC;IAAE,SAAS,EAAE,UAAU,CAAC;IAAC,UAAU,EAAE,WAAW,CAAA;CAAE,CAAC,CAU7D;AAED;;;GAGG;AACH,wBAAsB,YAAY,CAAC,UAAU,EAAE,WAAW,EAAE,MAAM,GAAE,MAA0B;;;;GAwB7F"}
1
+ {"version":3,"file":"TestSummaryUtils.d.ts","sourceRoot":"","sources":["../src/TestSummaryUtils.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,2CAA2C,EAAE,MAAM,0BAA0B,CAAC;AAEvF,OAAO,EAAE,UAAU,EAA6B,MAAM,uCAAuC,CAAC;AAC9F,OAAO,EACN,iBAAiB,EACjB,WAAW,EAEX,MAAM,mCAAmC,CAAC;AAG3C,OAAO,EAEN,sBAAsB,EACtB,kCAAkC,EAClC,MAAM,qCAAqC,CAAC;AAE7C,OAAO,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AACtE,OAAO,EAAE,oBAAoB,EAAE,MAAM,oCAAoC,CAAC;AAC1E,OAAO,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAwDjF;;;;GAIG;AACH,wBAAsB,2BAA2B,CAChD,QAAQ,EAAE,mBAAmB,EAC7B,SAAS,EAAE,UAAU,EACrB,gBAAgB,EAAE,sBAAsB,EACxC,cAAc,CAAC,EAAE,MAAM,EACvB,2BAA2B,qDAA8C,EACzE,eAAe,CAAC,EAAE,kCAAkC,EACpD,MAAM,CAAC,EAAE,oBAAoB,GAC3B,OAAO,CAAC;IAAE,SAAS,EAAE,UAAU,CAAC;IAAC,UAAU,EAAE,WAAW,CAAA;CAAE,CAAC,CAgB7D;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,CACrC,QAAQ,EAAE,mBAAmB,EAC7B,SAAS,EAAE,UAAU,EACrB,cAAc,CAAC,EAAE,MAAM,EACvB,SAAS,CAAC,EAAE,iBAAiB,EAC7B,cAAc,GAAE,mBAA0C,EAC1D,MAAM,CAAC,EAAE,oBAAoB,GAC3B,OAAO,CAAC;IAAE,SAAS,EAAE,UAAU,CAAC;IAAC,UAAU,EAAE,WAAW,CAAA;CAAE,CAAC,CAU7D;AAED;;;;;GAKG;AACH,wBAAsB,8BAA8B,CACnD,QAAQ,EAAE,mBAAmB,EAC7B,SAAS,EAAE,UAAU,EACrB,MAAM,EAAE,oBAAoB,EAC5B,cAAc,CAAC,EAAE,MAAM,EACvB,MAAM,CAAC,EAAE,oBAAoB,GAC3B,OAAO,CAAC;IAAE,SAAS,EAAE,UAAU,CAAC;IAAC,UAAU,EAAE,WAAW,CAAA;CAAE,CAAC,CAe7D;AAED;;;GAGG;AACH,wBAAsB,YAAY,CAAC,UAAU,EAAE,WAAW,EAAE,MAAM,GAAE,MAA0B;;;;GAwB7F"}
@@ -4,7 +4,7 @@
4
4
  * Licensed under the MIT License.
5
5
  */
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.summarizeNow = exports.createSummarizer = exports.createSummarizerFromFactory = void 0;
7
+ exports.summarizeNow = exports.createSummarizerWithTestConfig = exports.createSummarizer = exports.createSummarizerFromFactory = void 0;
8
8
  const aqueduct_1 = require("@fluidframework/aqueduct");
9
9
  const common_utils_1 = require("@fluidframework/common-utils");
10
10
  const container_definitions_1 = require("@fluidframework/container-definitions");
@@ -86,6 +86,19 @@ async function createSummarizer(provider, container, summaryVersion, gcOptions,
86
86
  return createSummarizerCore(container, loader, summaryVersion);
87
87
  }
88
88
  exports.createSummarizer = createSummarizer;
89
+ /**
90
+ * Creates a summarizer client from the given container and returns the summarizer client's IContainer and ISummarizer.
91
+ * The ISummarizer can be used to generate on-demand summaries. The IContainer can be used to fetch data stores, etc.
92
+ *
93
+ * Can pass in a test config provider to enable/disable features.
94
+ */
95
+ async function createSummarizerWithTestConfig(provider, container, config, summaryVersion, logger) {
96
+ var _a, _b;
97
+ const testContainerConfig = Object.assign(Object.assign({}, config), { runtimeOptions: Object.assign(Object.assign({}, config.runtimeOptions), { summaryOptions: defaultSummaryOptions }), loaderProps: Object.assign(Object.assign({}, config.loaderProps), { configProvider: (_b = (_a = config.loaderProps) === null || _a === void 0 ? void 0 : _a.configProvider) !== null && _b !== void 0 ? _b : (0, TestConfigs_1.mockConfigProvider)(), logger }) });
98
+ const loader = provider.makeTestLoader(testContainerConfig);
99
+ return createSummarizerCore(container, loader, summaryVersion);
100
+ }
101
+ exports.createSummarizerWithTestConfig = createSummarizerWithTestConfig;
89
102
  /**
90
103
  * Summarizes on demand and returns the summary tree, the version number and the reference sequence number of the
91
104
  * submitted summary.
@@ -1 +1 @@
1
- {"version":3,"file":"TestSummaryUtils.js","sourceRoot":"","sources":["../src/TestSummaryUtils.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 { ContainerRuntimeFactoryWithDefaultDataStore } from \"@fluidframework/aqueduct\";\nimport { assert } from \"@fluidframework/common-utils\";\nimport { IContainer, IHostLoader, LoaderHeader } from \"@fluidframework/container-definitions\";\nimport {\n\tIGCRuntimeOptions,\n\tISummarizer,\n\tISummaryRuntimeOptions,\n} from \"@fluidframework/container-runtime\";\nimport { FluidObject, IRequest } from \"@fluidframework/core-interfaces\";\nimport { DriverHeader } from \"@fluidframework/driver-definitions\";\nimport {\n\tIContainerRuntimeBase,\n\tIFluidDataStoreFactory,\n\tNamedFluidDataStoreRegistryEntries,\n} from \"@fluidframework/runtime-definitions\";\nimport { requestFluidObject } from \"@fluidframework/runtime-utils\";\nimport { IConfigProviderBase } from \"@fluidframework/telemetry-utils\";\nimport { ITelemetryBaseLogger } from \"@fluidframework/common-definitions\";\nimport { ITestContainerConfig, ITestObjectProvider } from \"./testObjectProvider\";\nimport { mockConfigProvider } from \"./TestConfigs\";\nimport { waitForContainerConnection } from \"./containerUtils\";\nimport { timeoutAwait } from \"./timeoutUtils\";\n\nconst summarizerClientType = \"summarizer\";\n\nasync function createSummarizerCore(\n\tcontainer: IContainer,\n\tloader: IHostLoader,\n\tsummaryVersion?: string,\n) {\n\tconst absoluteUrl = await container.getAbsoluteUrl(\"\");\n\tif (absoluteUrl === undefined) {\n\t\tthrow new Error(\"URL could not be resolved\");\n\t}\n\n\tconst request: IRequest = {\n\t\theaders: {\n\t\t\t[LoaderHeader.cache]: false,\n\t\t\t[LoaderHeader.clientDetails]: {\n\t\t\t\tcapabilities: { interactive: false },\n\t\t\t\ttype: summarizerClientType,\n\t\t\t},\n\t\t\t[DriverHeader.summarizingClient]: true,\n\t\t\t[LoaderHeader.version]: summaryVersion,\n\t\t},\n\t\turl: absoluteUrl,\n\t};\n\tconst summarizerContainer = await loader.resolve(request);\n\tawait waitForContainerConnection(summarizerContainer);\n\n\tconst fluidObject: FluidObject<ISummarizer> | undefined = summarizerContainer.getEntryPoint\n\t\t? await summarizerContainer.getEntryPoint?.()\n\t\t: await requestFluidObject<FluidObject<ISummarizer>>(summarizerContainer, {\n\t\t\t\turl: \"_summarizer\",\n\t\t });\n\tif (fluidObject?.ISummarizer === undefined) {\n\t\tthrow new Error(\"Fluid object does not implement ISummarizer\");\n\t}\n\n\treturn {\n\t\tcontainer: summarizerContainer,\n\t\tsummarizer: fluidObject.ISummarizer,\n\t};\n}\n\nconst defaultSummaryOptions: ISummaryRuntimeOptions = {\n\tsummaryConfigOverrides: {\n\t\tstate: \"disableHeuristics\",\n\t\tmaxAckWaitTime: 10000,\n\t\tmaxOpsSinceLastSummary: 7000,\n\t\tinitialSummarizerDelayMs: 0,\n\t},\n};\n\n/**\n * Creates a summarizer client from the given container and data store factory, and returns the summarizer client's\n * IContainer and ISummarizer.\n * The ISummarizer can be used to generate on-demand summaries. The IContainer can be used to fetch data stores, etc.\n */\nexport async function createSummarizerFromFactory(\n\tprovider: ITestObjectProvider,\n\tcontainer: IContainer,\n\tdataStoreFactory: IFluidDataStoreFactory,\n\tsummaryVersion?: string,\n\tcontainerRuntimeFactoryType = ContainerRuntimeFactoryWithDefaultDataStore,\n\tregistryEntries?: NamedFluidDataStoreRegistryEntries,\n\tlogger?: ITelemetryBaseLogger,\n): Promise<{ container: IContainer; summarizer: ISummarizer }> {\n\tconst innerRequestHandler = async (request: IRequest, runtime: IContainerRuntimeBase) =>\n\t\truntime.IFluidHandleContext.resolveHandle(request);\n\tconst runtimeFactory = new containerRuntimeFactoryType(\n\t\tdataStoreFactory,\n\t\tregistryEntries ?? [[dataStoreFactory.type, Promise.resolve(dataStoreFactory)]],\n\t\tundefined,\n\t\t[innerRequestHandler],\n\t\t{ summaryOptions: defaultSummaryOptions },\n\t);\n\n\tconst loader = provider.createLoader([[provider.defaultCodeDetails, runtimeFactory]], {\n\t\tconfigProvider: mockConfigProvider(),\n\t\tlogger,\n\t});\n\treturn createSummarizerCore(container, loader, summaryVersion);\n}\n\n/**\n * Creates a summarizer client from the given container and returns the summarizer client's IContainer and ISummarizer.\n * The ISummarizer can be used to generate on-demand summaries. The IContainer can be used to fetch data stores, etc.\n */\nexport async function createSummarizer(\n\tprovider: ITestObjectProvider,\n\tcontainer: IContainer,\n\tsummaryVersion?: string,\n\tgcOptions?: IGCRuntimeOptions,\n\tconfigProvider: IConfigProviderBase = mockConfigProvider(),\n\tlogger?: ITelemetryBaseLogger,\n): Promise<{ container: IContainer; summarizer: ISummarizer }> {\n\tconst testContainerConfig: ITestContainerConfig = {\n\t\truntimeOptions: {\n\t\t\tsummaryOptions: defaultSummaryOptions,\n\t\t\tgcOptions,\n\t\t},\n\t\tloaderProps: { configProvider, logger },\n\t};\n\tconst loader = provider.makeTestLoader(testContainerConfig);\n\treturn createSummarizerCore(container, loader, summaryVersion);\n}\n\n/**\n * Summarizes on demand and returns the summary tree, the version number and the reference sequence number of the\n * submitted summary.\n */\nexport async function summarizeNow(summarizer: ISummarizer, reason: string = \"end-to-end test\") {\n\tconst result = summarizer.summarizeOnDemand({ reason });\n\n\tconst submitResult = await timeoutAwait(result.summarySubmitted);\n\tassert(submitResult.success, \"on-demand summary should submit\");\n\tassert(\n\t\tsubmitResult.data.stage === \"submit\",\n\t\t\"on-demand summary submitted data stage should be submit\",\n\t);\n\tassert(submitResult.data.summaryTree !== undefined, \"summary tree should exist\");\n\n\tconst broadcastResult = await timeoutAwait(result.summaryOpBroadcasted);\n\tassert(broadcastResult.success, \"summary op should be broadcast\");\n\n\tconst ackNackResult = await timeoutAwait(result.receivedSummaryAckOrNack);\n\tassert(ackNackResult.success, \"summary op should be acked\");\n\n\tawait new Promise((resolve) => process.nextTick(resolve));\n\n\treturn {\n\t\tsummaryTree: submitResult.data.summaryTree,\n\t\tsummaryVersion: ackNackResult.data.summaryAckOp.contents.handle,\n\t\tsummaryRefSeq: submitResult.data.referenceSequenceNumber,\n\t};\n}\n"]}
1
+ {"version":3,"file":"TestSummaryUtils.js","sourceRoot":"","sources":["../src/TestSummaryUtils.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 { ContainerRuntimeFactoryWithDefaultDataStore } from \"@fluidframework/aqueduct\";\nimport { assert } from \"@fluidframework/common-utils\";\nimport { IContainer, IHostLoader, LoaderHeader } from \"@fluidframework/container-definitions\";\nimport {\n\tIGCRuntimeOptions,\n\tISummarizer,\n\tISummaryRuntimeOptions,\n} from \"@fluidframework/container-runtime\";\nimport { FluidObject, IRequest } from \"@fluidframework/core-interfaces\";\nimport { DriverHeader } from \"@fluidframework/driver-definitions\";\nimport {\n\tIContainerRuntimeBase,\n\tIFluidDataStoreFactory,\n\tNamedFluidDataStoreRegistryEntries,\n} from \"@fluidframework/runtime-definitions\";\nimport { requestFluidObject } from \"@fluidframework/runtime-utils\";\nimport { IConfigProviderBase } from \"@fluidframework/telemetry-utils\";\nimport { ITelemetryBaseLogger } from \"@fluidframework/common-definitions\";\nimport { ITestContainerConfig, ITestObjectProvider } from \"./testObjectProvider\";\nimport { mockConfigProvider } from \"./TestConfigs\";\nimport { waitForContainerConnection } from \"./containerUtils\";\nimport { timeoutAwait } from \"./timeoutUtils\";\n\nconst summarizerClientType = \"summarizer\";\n\nasync function createSummarizerCore(\n\tcontainer: IContainer,\n\tloader: IHostLoader,\n\tsummaryVersion?: string,\n) {\n\tconst absoluteUrl = await container.getAbsoluteUrl(\"\");\n\tif (absoluteUrl === undefined) {\n\t\tthrow new Error(\"URL could not be resolved\");\n\t}\n\n\tconst request: IRequest = {\n\t\theaders: {\n\t\t\t[LoaderHeader.cache]: false,\n\t\t\t[LoaderHeader.clientDetails]: {\n\t\t\t\tcapabilities: { interactive: false },\n\t\t\t\ttype: summarizerClientType,\n\t\t\t},\n\t\t\t[DriverHeader.summarizingClient]: true,\n\t\t\t[LoaderHeader.version]: summaryVersion,\n\t\t},\n\t\turl: absoluteUrl,\n\t};\n\tconst summarizerContainer = await loader.resolve(request);\n\tawait waitForContainerConnection(summarizerContainer);\n\n\tconst fluidObject: FluidObject<ISummarizer> | undefined = summarizerContainer.getEntryPoint\n\t\t? await summarizerContainer.getEntryPoint?.()\n\t\t: await requestFluidObject<FluidObject<ISummarizer>>(summarizerContainer, {\n\t\t\t\turl: \"_summarizer\",\n\t\t });\n\tif (fluidObject?.ISummarizer === undefined) {\n\t\tthrow new Error(\"Fluid object does not implement ISummarizer\");\n\t}\n\n\treturn {\n\t\tcontainer: summarizerContainer,\n\t\tsummarizer: fluidObject.ISummarizer,\n\t};\n}\n\nconst defaultSummaryOptions: ISummaryRuntimeOptions = {\n\tsummaryConfigOverrides: {\n\t\tstate: \"disableHeuristics\",\n\t\tmaxAckWaitTime: 10000,\n\t\tmaxOpsSinceLastSummary: 7000,\n\t\tinitialSummarizerDelayMs: 0,\n\t},\n};\n\n/**\n * Creates a summarizer client from the given container and data store factory, and returns the summarizer client's\n * IContainer and ISummarizer.\n * The ISummarizer can be used to generate on-demand summaries. The IContainer can be used to fetch data stores, etc.\n */\nexport async function createSummarizerFromFactory(\n\tprovider: ITestObjectProvider,\n\tcontainer: IContainer,\n\tdataStoreFactory: IFluidDataStoreFactory,\n\tsummaryVersion?: string,\n\tcontainerRuntimeFactoryType = ContainerRuntimeFactoryWithDefaultDataStore,\n\tregistryEntries?: NamedFluidDataStoreRegistryEntries,\n\tlogger?: ITelemetryBaseLogger,\n): Promise<{ container: IContainer; summarizer: ISummarizer }> {\n\tconst innerRequestHandler = async (request: IRequest, runtime: IContainerRuntimeBase) =>\n\t\truntime.IFluidHandleContext.resolveHandle(request);\n\tconst runtimeFactory = new containerRuntimeFactoryType(\n\t\tdataStoreFactory,\n\t\tregistryEntries ?? [[dataStoreFactory.type, Promise.resolve(dataStoreFactory)]],\n\t\tundefined,\n\t\t[innerRequestHandler],\n\t\t{ summaryOptions: defaultSummaryOptions },\n\t);\n\n\tconst loader = provider.createLoader([[provider.defaultCodeDetails, runtimeFactory]], {\n\t\tconfigProvider: mockConfigProvider(),\n\t\tlogger,\n\t});\n\treturn createSummarizerCore(container, loader, summaryVersion);\n}\n\n/**\n * Creates a summarizer client from the given container and returns the summarizer client's IContainer and ISummarizer.\n * The ISummarizer can be used to generate on-demand summaries. The IContainer can be used to fetch data stores, etc.\n */\nexport async function createSummarizer(\n\tprovider: ITestObjectProvider,\n\tcontainer: IContainer,\n\tsummaryVersion?: string,\n\tgcOptions?: IGCRuntimeOptions,\n\tconfigProvider: IConfigProviderBase = mockConfigProvider(),\n\tlogger?: ITelemetryBaseLogger,\n): Promise<{ container: IContainer; summarizer: ISummarizer }> {\n\tconst testContainerConfig: ITestContainerConfig = {\n\t\truntimeOptions: {\n\t\t\tsummaryOptions: defaultSummaryOptions,\n\t\t\tgcOptions,\n\t\t},\n\t\tloaderProps: { configProvider, logger },\n\t};\n\tconst loader = provider.makeTestLoader(testContainerConfig);\n\treturn createSummarizerCore(container, loader, summaryVersion);\n}\n\n/**\n * Creates a summarizer client from the given container and returns the summarizer client's IContainer and ISummarizer.\n * The ISummarizer can be used to generate on-demand summaries. The IContainer can be used to fetch data stores, etc.\n *\n * Can pass in a test config provider to enable/disable features.\n */\nexport async function createSummarizerWithTestConfig(\n\tprovider: ITestObjectProvider,\n\tcontainer: IContainer,\n\tconfig: ITestContainerConfig,\n\tsummaryVersion?: string,\n\tlogger?: ITelemetryBaseLogger,\n): Promise<{ container: IContainer; summarizer: ISummarizer }> {\n\tconst testContainerConfig: ITestContainerConfig = {\n\t\t...config,\n\t\truntimeOptions: {\n\t\t\t...config.runtimeOptions,\n\t\t\tsummaryOptions: defaultSummaryOptions,\n\t\t},\n\t\tloaderProps: {\n\t\t\t...config.loaderProps,\n\t\t\tconfigProvider: config.loaderProps?.configProvider ?? mockConfigProvider(),\n\t\t\tlogger,\n\t\t},\n\t};\n\tconst loader = provider.makeTestLoader(testContainerConfig);\n\treturn createSummarizerCore(container, loader, summaryVersion);\n}\n\n/**\n * Summarizes on demand and returns the summary tree, the version number and the reference sequence number of the\n * submitted summary.\n */\nexport async function summarizeNow(summarizer: ISummarizer, reason: string = \"end-to-end test\") {\n\tconst result = summarizer.summarizeOnDemand({ reason });\n\n\tconst submitResult = await timeoutAwait(result.summarySubmitted);\n\tassert(submitResult.success, \"on-demand summary should submit\");\n\tassert(\n\t\tsubmitResult.data.stage === \"submit\",\n\t\t\"on-demand summary submitted data stage should be submit\",\n\t);\n\tassert(submitResult.data.summaryTree !== undefined, \"summary tree should exist\");\n\n\tconst broadcastResult = await timeoutAwait(result.summaryOpBroadcasted);\n\tassert(broadcastResult.success, \"summary op should be broadcast\");\n\n\tconst ackNackResult = await timeoutAwait(result.receivedSummaryAckOrNack);\n\tassert(ackNackResult.success, \"summary op should be acked\");\n\n\tawait new Promise((resolve) => process.nextTick(resolve));\n\n\treturn {\n\t\tsummaryTree: submitResult.data.summaryTree,\n\t\tsummaryVersion: ackNackResult.data.summaryAckOp.contents.handle,\n\t\tsummaryRefSeq: submitResult.data.referenceSequenceNumber,\n\t};\n}\n"]}
package/dist/index.d.ts CHANGED
@@ -12,7 +12,7 @@ export { mockConfigProvider } from "./TestConfigs";
12
12
  export { createTestContainerRuntimeFactory, TestContainerRuntimeFactory, } from "./testContainerRuntimeFactory";
13
13
  export { ChannelFactoryRegistry, TestFluidObject, TestFluidObjectFactory } from "./testFluidObject";
14
14
  export { createDocumentId, DataObjectFactoryType, EventAndErrorTrackingLogger, getUnexpectedLogErrorException, IOpProcessingController, ITestContainerConfig, ITestObjectProvider, TestObjectProvider, } from "./testObjectProvider";
15
- export { createSummarizer, createSummarizerFromFactory, summarizeNow } from "./TestSummaryUtils";
15
+ export { createSummarizer, createSummarizerFromFactory, summarizeNow, createSummarizerWithTestConfig, } from "./TestSummaryUtils";
16
16
  export { defaultTimeoutDurationMs, timeoutAwait, timeoutPromise, TimeoutWithError, TimeoutWithValue, } from "./timeoutUtils";
17
17
  export { waitForContainerConnection } from "./containerUtils";
18
18
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACN,mBAAmB,EACnB,0BAA0B,EAC1B,0BAA0B,GAC1B,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,uBAAuB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACzE,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,yBAAyB,EAAE,MAAM,mBAAmB,CAAC;AAChG,OAAO,EAAE,wBAAwB,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AACvE,OAAO,EAAE,sBAAsB,EAAE,MAAM,SAAS,CAAC;AACjD,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,EACN,iCAAiC,EACjC,2BAA2B,GAC3B,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAAE,sBAAsB,EAAE,eAAe,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AACpG,OAAO,EACN,gBAAgB,EAChB,qBAAqB,EACrB,2BAA2B,EAC3B,8BAA8B,EAC9B,uBAAuB,EACvB,oBAAoB,EACpB,mBAAmB,EACnB,kBAAkB,GAClB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,gBAAgB,EAAE,2BAA2B,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACjG,OAAO,EACN,wBAAwB,EACxB,YAAY,EACZ,cAAc,EACd,gBAAgB,EAChB,gBAAgB,GAChB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,0BAA0B,EAAE,MAAM,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACN,mBAAmB,EACnB,0BAA0B,EAC1B,0BAA0B,GAC1B,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,uBAAuB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACzE,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,yBAAyB,EAAE,MAAM,mBAAmB,CAAC;AAChG,OAAO,EAAE,wBAAwB,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AACvE,OAAO,EAAE,sBAAsB,EAAE,MAAM,SAAS,CAAC;AACjD,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,EACN,iCAAiC,EACjC,2BAA2B,GAC3B,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAAE,sBAAsB,EAAE,eAAe,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AACpG,OAAO,EACN,gBAAgB,EAChB,qBAAqB,EACrB,2BAA2B,EAC3B,8BAA8B,EAC9B,uBAAuB,EACvB,oBAAoB,EACpB,mBAAmB,EACnB,kBAAkB,GAClB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACN,gBAAgB,EAChB,2BAA2B,EAC3B,YAAY,EACZ,8BAA8B,GAC9B,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACN,wBAAwB,EACxB,YAAY,EACZ,cAAc,EACd,gBAAgB,EAChB,gBAAgB,GAChB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,0BAA0B,EAAE,MAAM,kBAAkB,CAAC"}
package/dist/index.js CHANGED
@@ -4,7 +4,7 @@
4
4
  * Licensed under the MIT License.
5
5
  */
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.waitForContainerConnection = exports.timeoutPromise = exports.timeoutAwait = exports.defaultTimeoutDurationMs = exports.summarizeNow = exports.createSummarizerFromFactory = exports.createSummarizer = exports.TestObjectProvider = exports.getUnexpectedLogErrorException = exports.EventAndErrorTrackingLogger = exports.DataObjectFactoryType = exports.createDocumentId = exports.TestFluidObjectFactory = exports.TestFluidObject = exports.TestContainerRuntimeFactory = exports.createTestContainerRuntimeFactory = exports.mockConfigProvider = exports.retryWithEventualValue = exports.createLoader = exports.createAndAttachContainer = exports.LocalCodeLoader = exports.LoaderContainerTracker = exports.wrapDocumentStorageService = exports.wrapDocumentServiceFactory = exports.wrapDocumentService = void 0;
7
+ exports.waitForContainerConnection = exports.timeoutPromise = exports.timeoutAwait = exports.defaultTimeoutDurationMs = exports.createSummarizerWithTestConfig = exports.summarizeNow = exports.createSummarizerFromFactory = exports.createSummarizer = exports.TestObjectProvider = exports.getUnexpectedLogErrorException = exports.EventAndErrorTrackingLogger = exports.DataObjectFactoryType = exports.createDocumentId = exports.TestFluidObjectFactory = exports.TestFluidObject = exports.TestContainerRuntimeFactory = exports.createTestContainerRuntimeFactory = exports.mockConfigProvider = exports.retryWithEventualValue = exports.createLoader = exports.createAndAttachContainer = exports.LocalCodeLoader = exports.LoaderContainerTracker = exports.wrapDocumentStorageService = exports.wrapDocumentServiceFactory = exports.wrapDocumentService = void 0;
8
8
  var DriverWrappers_1 = require("./DriverWrappers");
9
9
  Object.defineProperty(exports, "wrapDocumentService", { enumerable: true, get: function () { return DriverWrappers_1.wrapDocumentService; } });
10
10
  Object.defineProperty(exports, "wrapDocumentServiceFactory", { enumerable: true, get: function () { return DriverWrappers_1.wrapDocumentServiceFactory; } });
@@ -36,6 +36,7 @@ var TestSummaryUtils_1 = require("./TestSummaryUtils");
36
36
  Object.defineProperty(exports, "createSummarizer", { enumerable: true, get: function () { return TestSummaryUtils_1.createSummarizer; } });
37
37
  Object.defineProperty(exports, "createSummarizerFromFactory", { enumerable: true, get: function () { return TestSummaryUtils_1.createSummarizerFromFactory; } });
38
38
  Object.defineProperty(exports, "summarizeNow", { enumerable: true, get: function () { return TestSummaryUtils_1.summarizeNow; } });
39
+ Object.defineProperty(exports, "createSummarizerWithTestConfig", { enumerable: true, get: function () { return TestSummaryUtils_1.createSummarizerWithTestConfig; } });
39
40
  var timeoutUtils_1 = require("./timeoutUtils");
40
41
  Object.defineProperty(exports, "defaultTimeoutDurationMs", { enumerable: true, get: function () { return timeoutUtils_1.defaultTimeoutDurationMs; } });
41
42
  Object.defineProperty(exports, "timeoutAwait", { enumerable: true, get: function () { return timeoutUtils_1.timeoutAwait; } });
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAEH,mDAI0B;AAHzB,qHAAA,mBAAmB,OAAA;AACnB,4HAAA,0BAA0B,OAAA;AAC1B,4HAAA,0BAA0B,OAAA;AAG3B,mEAAkE;AAAzD,gIAAA,sBAAsB,OAAA;AAC/B,qDAAgG;AAAtE,kHAAA,eAAe,OAAA;AACzC,6CAAuE;AAA9D,uHAAA,wBAAwB,OAAA;AAAE,2GAAA,YAAY,OAAA;AAC/C,iCAAiD;AAAxC,+GAAA,sBAAsB,OAAA;AAC/B,6CAAmD;AAA1C,iHAAA,kBAAkB,OAAA;AAC3B,6EAGuC;AAFtC,gJAAA,iCAAiC,OAAA;AACjC,0IAAA,2BAA2B,OAAA;AAE5B,qDAAoG;AAAnE,kHAAA,eAAe,OAAA;AAAE,yHAAA,sBAAsB,OAAA;AACxE,2DAS8B;AAR7B,sHAAA,gBAAgB,OAAA;AAChB,2HAAA,qBAAqB,OAAA;AACrB,iIAAA,2BAA2B,OAAA;AAC3B,oIAAA,8BAA8B,OAAA;AAI9B,wHAAA,kBAAkB,OAAA;AAEnB,uDAAiG;AAAxF,oHAAA,gBAAgB,OAAA;AAAE,+HAAA,2BAA2B,OAAA;AAAE,gHAAA,YAAY,OAAA;AACpE,+CAMwB;AALvB,wHAAA,wBAAwB,OAAA;AACxB,4GAAA,YAAY,OAAA;AACZ,8GAAA,cAAc,OAAA;AAIf,mDAA8D;AAArD,4HAAA,0BAA0B,OAAA","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nexport {\n\twrapDocumentService,\n\twrapDocumentServiceFactory,\n\twrapDocumentStorageService,\n} from \"./DriverWrappers\";\nexport { IProvideTestFluidObject, ITestFluidObject } from \"./interfaces\";\nexport { LoaderContainerTracker } from \"./loaderContainerTracker\";\nexport { fluidEntryPoint, LocalCodeLoader, SupportedExportInterfaces } from \"./localCodeLoader\";\nexport { createAndAttachContainer, createLoader } from \"./localLoader\";\nexport { retryWithEventualValue } from \"./retry\";\nexport { mockConfigProvider } from \"./TestConfigs\";\nexport {\n\tcreateTestContainerRuntimeFactory,\n\tTestContainerRuntimeFactory,\n} from \"./testContainerRuntimeFactory\";\nexport { ChannelFactoryRegistry, TestFluidObject, TestFluidObjectFactory } from \"./testFluidObject\";\nexport {\n\tcreateDocumentId,\n\tDataObjectFactoryType,\n\tEventAndErrorTrackingLogger,\n\tgetUnexpectedLogErrorException,\n\tIOpProcessingController,\n\tITestContainerConfig,\n\tITestObjectProvider,\n\tTestObjectProvider,\n} from \"./testObjectProvider\";\nexport { createSummarizer, createSummarizerFromFactory, summarizeNow } from \"./TestSummaryUtils\";\nexport {\n\tdefaultTimeoutDurationMs,\n\ttimeoutAwait,\n\ttimeoutPromise,\n\tTimeoutWithError,\n\tTimeoutWithValue,\n} from \"./timeoutUtils\";\nexport { waitForContainerConnection } from \"./containerUtils\";\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAEH,mDAI0B;AAHzB,qHAAA,mBAAmB,OAAA;AACnB,4HAAA,0BAA0B,OAAA;AAC1B,4HAAA,0BAA0B,OAAA;AAG3B,mEAAkE;AAAzD,gIAAA,sBAAsB,OAAA;AAC/B,qDAAgG;AAAtE,kHAAA,eAAe,OAAA;AACzC,6CAAuE;AAA9D,uHAAA,wBAAwB,OAAA;AAAE,2GAAA,YAAY,OAAA;AAC/C,iCAAiD;AAAxC,+GAAA,sBAAsB,OAAA;AAC/B,6CAAmD;AAA1C,iHAAA,kBAAkB,OAAA;AAC3B,6EAGuC;AAFtC,gJAAA,iCAAiC,OAAA;AACjC,0IAAA,2BAA2B,OAAA;AAE5B,qDAAoG;AAAnE,kHAAA,eAAe,OAAA;AAAE,yHAAA,sBAAsB,OAAA;AACxE,2DAS8B;AAR7B,sHAAA,gBAAgB,OAAA;AAChB,2HAAA,qBAAqB,OAAA;AACrB,iIAAA,2BAA2B,OAAA;AAC3B,oIAAA,8BAA8B,OAAA;AAI9B,wHAAA,kBAAkB,OAAA;AAEnB,uDAK4B;AAJ3B,oHAAA,gBAAgB,OAAA;AAChB,+HAAA,2BAA2B,OAAA;AAC3B,gHAAA,YAAY,OAAA;AACZ,kIAAA,8BAA8B,OAAA;AAE/B,+CAMwB;AALvB,wHAAA,wBAAwB,OAAA;AACxB,4GAAA,YAAY,OAAA;AACZ,8GAAA,cAAc,OAAA;AAIf,mDAA8D;AAArD,4HAAA,0BAA0B,OAAA","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nexport {\n\twrapDocumentService,\n\twrapDocumentServiceFactory,\n\twrapDocumentStorageService,\n} from \"./DriverWrappers\";\nexport { IProvideTestFluidObject, ITestFluidObject } from \"./interfaces\";\nexport { LoaderContainerTracker } from \"./loaderContainerTracker\";\nexport { fluidEntryPoint, LocalCodeLoader, SupportedExportInterfaces } from \"./localCodeLoader\";\nexport { createAndAttachContainer, createLoader } from \"./localLoader\";\nexport { retryWithEventualValue } from \"./retry\";\nexport { mockConfigProvider } from \"./TestConfigs\";\nexport {\n\tcreateTestContainerRuntimeFactory,\n\tTestContainerRuntimeFactory,\n} from \"./testContainerRuntimeFactory\";\nexport { ChannelFactoryRegistry, TestFluidObject, TestFluidObjectFactory } from \"./testFluidObject\";\nexport {\n\tcreateDocumentId,\n\tDataObjectFactoryType,\n\tEventAndErrorTrackingLogger,\n\tgetUnexpectedLogErrorException,\n\tIOpProcessingController,\n\tITestContainerConfig,\n\tITestObjectProvider,\n\tTestObjectProvider,\n} from \"./testObjectProvider\";\nexport {\n\tcreateSummarizer,\n\tcreateSummarizerFromFactory,\n\tsummarizeNow,\n\tcreateSummarizerWithTestConfig,\n} from \"./TestSummaryUtils\";\nexport {\n\tdefaultTimeoutDurationMs,\n\ttimeoutAwait,\n\ttimeoutPromise,\n\tTimeoutWithError,\n\tTimeoutWithValue,\n} from \"./timeoutUtils\";\nexport { waitForContainerConnection } from \"./containerUtils\";\n"]}
@@ -39,20 +39,17 @@ export declare class LoaderContainerTracker implements IOpProcessingController {
39
39
  * Make sure all the tracked containers are synchronized.
40
40
  *
41
41
  * No isDirty (non-readonly) containers
42
- *
43
42
  * No extra clientId in quorum of any container that is not tracked and still opened.
44
- *
45
43
  * - i.e. no pending Join/Leave message.
46
- *
47
44
  * No unresolved proposal (minSeqNum \>= lastProposalSeqNum)
48
- *
49
45
  * lastSequenceNumber of all container is the same
50
- *
51
46
  * clientSequenceNumberObserved is the same as clientSequenceNumber sent
52
- *
53
47
  * - this overlaps with !isDirty, but include task scheduler ops.
54
- *
55
48
  * - Trailing NoOp is tracked and don't count as pending ops.
49
+ *
50
+ * Containers that are already pause will resume process and paused again once
51
+ * everything is synchronized. Containers that aren't paused will remain unpaused when this
52
+ * function returns.
56
53
  */
57
54
  ensureSynchronized(...containers: IContainer[]): Promise<void>;
58
55
  /**
@@ -68,7 +65,8 @@ export declare class LoaderContainerTracker implements IOpProcessingController {
68
65
  *
69
66
  * @param containersToApply - the set of containers to check
70
67
  */
71
- private isSequenceNumberSynchronized;
68
+ private needSequenceNumberSynchronize;
69
+ private containerIndexStrings;
72
70
  /**
73
71
  * Utility to wait for any clientId in quorum that is NOT associated with any container we
74
72
  * tracked, indicating there is a pending join or leave op that we need to wait.
@@ -90,18 +88,37 @@ export declare class LoaderContainerTracker implements IOpProcessingController {
90
88
  /**
91
89
  * Pause all queue activities on the containers given, or all tracked containers
92
90
  * Any containers given that is not tracked will be ignored.
91
+ *
92
+ * When a container is paused, it is assumed that we want fine grain control over op
93
+ * sequencing. This function will prepare the container and force it into write mode to
94
+ * avoid missing join messages or change the sequence of event when switching from read to
95
+ * write mode.
93
96
  */
94
97
  pauseProcessing(...containers: IContainer[]): Promise<void>;
98
+ /**
99
+ * When a container is paused, it is assumed that we want fine grain control over op
100
+ * sequencing. This function will prepare the container and force it into write mode to
101
+ * avoid missing join messages or change the sequence of event when switching from read to
102
+ * write mode.
103
+ *
104
+ * @param container - the container to pause
105
+ * @param record - the record for the container
106
+ */
107
+ private pauseContainer;
95
108
  /**
96
109
  * Pause all queue activities on all tracked containers, and resume only
97
110
  * inbound to process ops until it is idle. All queues are left in the paused state
98
- * after the function
111
+ * after the function.
112
+ *
113
+ * Pausing will switch the container to write mode. See `pauseProcessing`
99
114
  */
100
115
  processIncoming(...containers: IContainer[]): Promise<void>;
101
116
  /**
102
117
  * Pause all queue activities on all tracked containers, and resume only
103
118
  * outbound to process ops until it is idle. All queues are left in the paused state
104
- * after the function
119
+ * after the function.
120
+ *
121
+ * Pausing will switch the container to write mode. See `pauseProcessing`
105
122
  */
106
123
  processOutgoing(...containers: IContainer[]): Promise<void>;
107
124
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"loaderContainerTracker.d.ts","sourceRoot":"","sources":["../src/loaderContainerTracker.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,UAAU,EAAe,WAAW,EAAE,MAAM,uCAAuC,CAAC;AAS7F,OAAO,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAsB/D,qBAAa,sBAAuB,YAAW,uBAAuB;IAIzD,OAAO,CAAC,QAAQ,CAAC,qBAAqB;IAHlD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA0C;IACrE,OAAO,CAAC,kBAAkB,CAAa;gBAEV,qBAAqB,GAAE,OAAe;IAEnE;;;OAGG;IACI,GAAG,CAAC,UAAU,SAAS,WAAW,EAAE,MAAM,EAAE,UAAU;IAmB7D;;;;OAIG;IACH,OAAO,CAAC,YAAY;IA2BpB;;;;;;OAMG;IACH,OAAO,CAAC,kBAAkB;IAuC1B,OAAO,CAAC,iBAAiB;IAQzB;;OAEG;IACI,KAAK;IAUZ;;;;OAIG;IACU,6BAA6B,CAAC,CAC1C,eAAe,EAAE,MAAM,GAAG,SAAS,EACnC,GAAG,UAAU,EAAE,UAAU,EAAE;IAK5B;;;;;;;;;;;;;;;;;;OAkBG;IACU,kBAAkB,CAAC,GAAG,UAAU,EAAE,UAAU,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IA4E3E;;;;;OAKG;IACH,OAAO,CAAC,iBAAiB;IA8BzB;;;;;OAKG;IACH,OAAO,CAAC,4BAA4B;IA4CpC;;;;;;;OAOG;YACW,qBAAqB;IAoCnC;;;OAGG;YACW,oBAAoB;IAclC;;OAEG;IACI,gBAAgB,CAAC,GAAG,UAAU,EAAE,UAAU,EAAE;IAgBnD;;;OAGG;IACU,eAAe,CAAC,GAAG,UAAU,EAAE,UAAU,EAAE;IAexD;;;;OAIG;IACU,eAAe,CAAC,GAAG,UAAU,EAAE,UAAU,EAAE;IAIxD;;;;OAIG;IACU,eAAe,CAAC,GAAG,UAAU,EAAE,UAAU,EAAE;IAIxD;;OAEG;YACW,YAAY;IA0C1B;;;;;;OAMG;IACH,OAAO,CAAC,iBAAiB;IA2BzB;;OAEG;IACH,OAAO,CAAC,UAAU;IAwElB;;;;OAIG;IACH,OAAO,CAAC,aAAa;CAKrB"}
1
+ {"version":3,"file":"loaderContainerTracker.d.ts","sourceRoot":"","sources":["../src/loaderContainerTracker.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,UAAU,EAAe,WAAW,EAAE,MAAM,uCAAuC,CAAC;AAU7F,OAAO,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAuB/D,qBAAa,sBAAuB,YAAW,uBAAuB;IAIzD,OAAO,CAAC,QAAQ,CAAC,qBAAqB;IAHlD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA0C;IACrE,OAAO,CAAC,kBAAkB,CAAa;gBAEV,qBAAqB,GAAE,OAAe;IAEnE;;;OAGG;IACI,GAAG,CAAC,UAAU,SAAS,WAAW,EAAE,MAAM,EAAE,UAAU;IAmB7D;;;;OAIG;IACH,OAAO,CAAC,YAAY;IA2BpB;;;;;;OAMG;IACH,OAAO,CAAC,kBAAkB;IAuC1B,OAAO,CAAC,iBAAiB;IAQzB;;OAEG;IACI,KAAK;IAUZ;;;;OAIG;IACU,6BAA6B,CAAC,CAC1C,eAAe,EAAE,MAAM,GAAG,SAAS,EACnC,GAAG,UAAU,EAAE,UAAU,EAAE;IAK5B;;;;;;;;;;;;;;;OAeG;IACU,kBAAkB,CAAC,GAAG,UAAU,EAAE,UAAU,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IA2E3E;;;;;OAKG;IACH,OAAO,CAAC,iBAAiB;IA8BzB;;;;;OAKG;IACH,OAAO,CAAC,6BAA6B;IAmErC,OAAO,CAAC,qBAAqB;IAO7B;;;;;;;OAOG;YACW,qBAAqB;IAoCnC;;;OAGG;YACW,oBAAoB;IAclC;;OAEG;IACI,gBAAgB,CAAC,GAAG,UAAU,EAAE,UAAU,EAAE;IAoBnD;;;;;;;;OAQG;IACU,eAAe,CAAC,GAAG,UAAU,EAAE,UAAU,EAAE;IAexD;;;;;;;;OAQG;YACW,cAAc;IAwD5B;;;;;;OAMG;IACU,eAAe,CAAC,GAAG,UAAU,EAAE,UAAU,EAAE;IAIxD;;;;;;OAMG;IACU,eAAe,CAAC,GAAG,UAAU,EAAE,UAAU,EAAE;IAIxD;;OAEG;YACW,YAAY;IAgD1B;;;;;;OAMG;IACH,OAAO,CAAC,iBAAiB;IA2BzB;;OAEG;IACH,OAAO,CAAC,UAAU;IAwElB;;;;OAIG;IACH,OAAO,CAAC,aAAa;CAKrB"}
@@ -9,6 +9,7 @@ const common_utils_1 = require("@fluidframework/common-utils");
9
9
  const container_loader_1 = require("@fluidframework/container-loader");
10
10
  const driver_utils_1 = require("@fluidframework/driver-utils");
11
11
  const protocol_definitions_1 = require("@fluidframework/protocol-definitions");
12
+ const containerUtils_1 = require("./containerUtils");
12
13
  const debug_1 = require("./debug");
13
14
  const timeoutUtils_1 = require("./timeoutUtils");
14
15
  const debugOp = debug_1.debug.extend("ops");
@@ -138,31 +139,32 @@ class LoaderContainerTracker {
138
139
  * Make sure all the tracked containers are synchronized.
139
140
  *
140
141
  * No isDirty (non-readonly) containers
141
- *
142
142
  * No extra clientId in quorum of any container that is not tracked and still opened.
143
- *
144
143
  * - i.e. no pending Join/Leave message.
145
- *
146
144
  * No unresolved proposal (minSeqNum \>= lastProposalSeqNum)
147
- *
148
145
  * lastSequenceNumber of all container is the same
149
- *
150
146
  * clientSequenceNumberObserved is the same as clientSequenceNumber sent
151
- *
152
147
  * - this overlaps with !isDirty, but include task scheduler ops.
153
- *
154
148
  * - Trailing NoOp is tracked and don't count as pending ops.
149
+ *
150
+ * Containers that are already pause will resume process and paused again once
151
+ * everything is synchronized. Containers that aren't paused will remain unpaused when this
152
+ * function returns.
155
153
  */
156
154
  async ensureSynchronized(...containers) {
157
155
  const resumed = this.resumeProcessing(...containers);
158
- let waitingSequenceNumberSynchronized = false;
156
+ let waitingSequenceNumberSynchronized;
159
157
  // eslint-disable-next-line no-constant-condition
160
158
  while (true) {
159
+ // yield a turn to allow side effect of resuming or the ops we just processed execute before we check
160
+ await new Promise((resolve) => {
161
+ setTimeout(resolve, 0);
162
+ });
161
163
  const containersToApply = this.getContainers(containers);
162
164
  if (containersToApply.length === 0) {
163
165
  break;
164
166
  }
165
- // Ignore readonly dirty containers, because it can't sent up and nothing can be done about it being dirty
167
+ // Ignore readonly dirty containers, because it can't sent ops and nothing can be done about it being dirty
166
168
  const dirtyContainers = containersToApply.filter((c) => {
167
169
  const { deltaManager, isDirty } = c;
168
170
  return deltaManager.readOnlyInfo.readonly !== true && isDirty;
@@ -171,21 +173,23 @@ class LoaderContainerTracker {
171
173
  // Wait for all the leave messages
172
174
  const pendingClients = this.getPendingClients(containersToApply);
173
175
  if (pendingClients.length === 0) {
174
- if (this.isSequenceNumberSynchronized(containersToApply)) {
176
+ const needSync = this.needSequenceNumberSynchronize(containersToApply);
177
+ if (needSync === undefined) {
175
178
  // done, we are in sync
176
179
  break;
177
180
  }
178
- if (!waitingSequenceNumberSynchronized) {
179
- // Only write it out once
180
- waitingSequenceNumberSynchronized = true;
181
- debugWait("Waiting for sequence number synchronized");
182
- await (0, timeoutUtils_1.timeoutAwait)(this.waitForAnyInboundOps(containersToApply), {
183
- errorMsg: "Timeout on waiting for sequence number synchronized",
184
- });
181
+ if (waitingSequenceNumberSynchronized !== needSync.reason) {
182
+ // Don't repeat writing to console if it is the same reason
183
+ waitingSequenceNumberSynchronized = needSync.reason;
184
+ debugWait(needSync.message);
185
185
  }
186
+ // Wait for one inbounds ops which might change the state of things
187
+ await (0, timeoutUtils_1.timeoutAwait)(this.waitForAnyInboundOps(containersToApply), {
188
+ errorMsg: `Timeout on ${needSync.message}`,
189
+ });
186
190
  }
187
191
  else {
188
- waitingSequenceNumberSynchronized = false;
192
+ waitingSequenceNumberSynchronized = undefined;
189
193
  await (0, timeoutUtils_1.timeoutAwait)(this.waitForPendingClients(pendingClients), {
190
194
  errorMsg: "Timeout on waiting for pending join or leave op",
191
195
  });
@@ -193,10 +197,8 @@ class LoaderContainerTracker {
193
197
  }
194
198
  else {
195
199
  // Wait for all the containers to be saved
196
- debugWait(`Waiting container to be saved ${dirtyContainers.map(
197
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
198
- (c) => this.containers.get(c).index)}`);
199
- waitingSequenceNumberSynchronized = false;
200
+ debugWait(`Waiting container to be saved ${this.containerIndexStrings(dirtyContainers)}`);
201
+ waitingSequenceNumberSynchronized = undefined;
200
202
  await Promise.all(dirtyContainers.map(async (c) => Promise.race([
201
203
  (0, timeoutUtils_1.timeoutPromise)((resolve) => c.once("saved", () => resolve()), {
202
204
  errorMsg: "Timeout on waiting a container to be saved",
@@ -204,10 +206,6 @@ class LoaderContainerTracker {
204
206
  new Promise((resolve) => c.once("closed", resolve)),
205
207
  ])));
206
208
  }
207
- // yield a turn to allow side effect of the ops we just processed execute before we check again
208
- await new Promise((resolve) => {
209
- setTimeout(resolve, 0);
210
- });
211
209
  }
212
210
  // Pause all container that was resumed
213
211
  // don't call pause if resumed is empty and pause everything, which is not what we want
@@ -255,42 +253,62 @@ class LoaderContainerTracker {
255
253
  *
256
254
  * @param containersToApply - the set of containers to check
257
255
  */
258
- isSequenceNumberSynchronized(containersToApply) {
256
+ needSequenceNumberSynchronize(containersToApply) {
257
+ // If there is a pending proposal, wait for it to be accepted
258
+ const minSeqNum = containersToApply[0].deltaManager.minimumSequenceNumber;
259
+ if (minSeqNum < this.lastProposalSeqNum) {
260
+ return {
261
+ reason: "Proposal",
262
+ message: `waiting for MSN to advance to proposal at sequence number ${this.lastProposalSeqNum}`,
263
+ };
264
+ }
259
265
  // clientSequenceNumber check detects ops in flight, both on the wire and in the outbound queue
260
266
  // We need both client sequence number and isDirty check because:
261
267
  // - Currently isDirty flag ignores ops for task scheduler, so we need the client sequence number check
262
268
  // - But isDirty flags include ops during forceReadonly and disconnected, because we don't submit
263
269
  // the ops in the first place, clientSequenceNumber is not assigned
264
- const isClientSequenceNumberSynchronized = containersToApply.every((container) => {
270
+ const containerWithInflightOps = containersToApply.filter((container) => {
265
271
  if (container.deltaManager.readOnlyInfo.readonly === true) {
266
272
  // Ignore readonly container. the clientSeqNum and clientSeqNumObserved might be out of sync
267
273
  // because we transition to readonly when outbound is not empty or the in transit op got lost
268
- return true;
274
+ return false;
269
275
  }
270
276
  // Note that in read only mode, the op won't be submitted
271
277
  let deltaManager = container.deltaManager;
272
278
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
273
279
  const { trailingNoOps } = this.containers.get(container);
274
- // Back-compat: clientSequenceNumber & clientSequenceNumberObserved moved to ConnectionManager in 0.53
280
+ // Back-compat: lastSubmittedClientId/clientSequenceNumber/clientSequenceNumberObserved moved to ConnectionManager in 0.53
275
281
  if (!("clientSequenceNumber" in deltaManager)) {
276
282
  deltaManager = deltaManager.connectionManager;
277
283
  }
278
284
  (0, common_utils_1.assert)("clientSequenceNumber" in deltaManager, "no clientSequenceNumber");
279
285
  (0, common_utils_1.assert)("clientSequenceNumberObserved" in deltaManager, "no clientSequenceNumber");
280
- return (deltaManager.clientSequenceNumber ===
281
- deltaManager.clientSequenceNumberObserved + trailingNoOps);
286
+ // If last submittedClientId isn't the current clientId, then we haven't send any ops
287
+ return (deltaManager.lastSubmittedClientId === container.clientId &&
288
+ deltaManager.clientSequenceNumber !==
289
+ deltaManager.clientSequenceNumberObserved + trailingNoOps);
282
290
  });
283
- if (!isClientSequenceNumberSynchronized) {
284
- return false;
285
- }
286
- const minSeqNum = containersToApply[0].deltaManager.minimumSequenceNumber;
287
- if (minSeqNum < this.lastProposalSeqNum) {
288
- // There is an unresolved proposal
289
- return false;
291
+ if (containerWithInflightOps.length !== 0) {
292
+ return {
293
+ reason: "InflightOps",
294
+ message: `waiting for containers with inflight ops: ${this.containerIndexStrings(containerWithInflightOps)}`,
295
+ };
290
296
  }
291
297
  // Check to see if all the container has process the same number of ops.
292
- const seqNum = containersToApply[0].deltaManager.lastSequenceNumber;
293
- return containersToApply.every((c) => c.deltaManager.lastSequenceNumber === seqNum);
298
+ const maxSeqNum = Math.max(...containersToApply.map((c) => c.deltaManager.lastSequenceNumber));
299
+ const containerWithPendingIncoming = containersToApply.filter((c) => c.deltaManager.lastSequenceNumber !== maxSeqNum);
300
+ if (containerWithPendingIncoming.length !== 0) {
301
+ return {
302
+ reason: "Pending",
303
+ message: `waiting for containers with pending incoming ops up to sequence number ${maxSeqNum}: ${this.containerIndexStrings(containerWithPendingIncoming)}`,
304
+ };
305
+ }
306
+ return undefined;
307
+ }
308
+ containerIndexStrings(containers) {
309
+ return containers.map(
310
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
311
+ (c) => this.containers.get(c).index);
294
312
  }
295
313
  /**
296
314
  * Utility to wait for any clientId in quorum that is NOT associated with any container we
@@ -352,6 +370,7 @@ class LoaderContainerTracker {
352
370
  const containersToApply = this.getContainers(containers);
353
371
  for (const container of containersToApply) {
354
372
  const record = this.containers.get(container);
373
+ (0, common_utils_1.assert)((record === null || record === void 0 ? void 0 : record.pauseP) === undefined, "Cannot resume container while pausing is in progress");
355
374
  if ((record === null || record === void 0 ? void 0 : record.paused) === true) {
356
375
  debugWait(`${record.index}: container resumed`);
357
376
  container.deltaManager.inbound.resume();
@@ -365,25 +384,86 @@ class LoaderContainerTracker {
365
384
  /**
366
385
  * Pause all queue activities on the containers given, or all tracked containers
367
386
  * Any containers given that is not tracked will be ignored.
387
+ *
388
+ * When a container is paused, it is assumed that we want fine grain control over op
389
+ * sequencing. This function will prepare the container and force it into write mode to
390
+ * avoid missing join messages or change the sequence of event when switching from read to
391
+ * write mode.
368
392
  */
369
393
  async pauseProcessing(...containers) {
370
- const pauseP = [];
394
+ const waitP = [];
371
395
  const containersToApply = this.getContainers(containers);
372
396
  for (const container of containersToApply) {
373
397
  const record = this.containers.get(container);
374
398
  if (record !== undefined && !record.paused) {
375
- debugWait(`${record.index}: container paused`);
376
- pauseP.push(container.deltaManager.inbound.pause());
377
- pauseP.push(container.deltaManager.outbound.pause());
378
- record.paused = true;
399
+ if (record.pauseP === undefined) {
400
+ record.pauseP = this.pauseContainer(container, record);
401
+ }
402
+ waitP.push(record.pauseP);
379
403
  }
380
404
  }
381
- await Promise.all(pauseP);
405
+ await Promise.all(waitP);
406
+ }
407
+ /**
408
+ * When a container is paused, it is assumed that we want fine grain control over op
409
+ * sequencing. This function will prepare the container and force it into write mode to
410
+ * avoid missing join messages or change the sequence of event when switching from read to
411
+ * write mode.
412
+ *
413
+ * @param container - the container to pause
414
+ * @param record - the record for the container
415
+ */
416
+ async pauseContainer(container, record) {
417
+ debugWait(`${record.index}: pausing container`);
418
+ (0, common_utils_1.assert)(!container.deltaManager.outbound.paused, "Container should not be paused yet");
419
+ (0, common_utils_1.assert)(!container.deltaManager.inbound.paused, "Container should not be paused yet");
420
+ // Pause outbound
421
+ debugWait(`${record.index}: pausing container outbound queues`);
422
+ await container.deltaManager.outbound.pause();
423
+ // Ensure the container is connected first.
424
+ if (container.connectionState !== container_loader_1.ConnectionState.Connected) {
425
+ debugWait(`${record.index}: Wait for container connection`);
426
+ await (0, containerUtils_1.waitForContainerConnection)(container);
427
+ }
428
+ // Check if the container is in write mode
429
+ if (!container.deltaManager.active) {
430
+ let proposalP;
431
+ if (container.deltaManager.outbound.idle) {
432
+ // Need to generate an op to force write mode
433
+ debugWait(`${record.index}: container force write connection`);
434
+ const maybeContainer = container;
435
+ const codeProposal = maybeContainer.getLoadedCodeDetails
436
+ ? // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
437
+ container.getLoadedCodeDetails()
438
+ : container.chaincodePackage;
439
+ proposalP = container.proposeCodeDetails(codeProposal);
440
+ }
441
+ // Wait for nack
442
+ debugWait(`${record.index}: Wait for container disconnect`);
443
+ container.deltaManager.outbound.resume();
444
+ await new Promise((resolve) => container.once("disconnected", resolve));
445
+ const accepted = proposalP ? await proposalP : false;
446
+ (0, common_utils_1.assert)(!accepted, "A proposal in read mode should be rejected");
447
+ await container.deltaManager.outbound.pause();
448
+ // Ensure the container is reconnect.
449
+ if (container.connectionState !== container_loader_1.ConnectionState.Connected) {
450
+ debugWait(`${record.index}: Wait for container reconnection`);
451
+ await (0, containerUtils_1.waitForContainerConnection)(container);
452
+ }
453
+ }
454
+ debugWait(`${record.index}: pausing container inbound queues`);
455
+ // Pause inbound
456
+ await container.deltaManager.inbound.pause();
457
+ debugWait(`${record.index}: container paused`);
458
+ record.pauseP = undefined;
459
+ record.paused = true;
382
460
  }
383
461
  /**
384
462
  * Pause all queue activities on all tracked containers, and resume only
385
463
  * inbound to process ops until it is idle. All queues are left in the paused state
386
- * after the function
464
+ * after the function.
465
+ *
466
+ * Pausing will switch the container to write mode. See `pauseProcessing`
387
467
  */
388
468
  async processIncoming(...containers) {
389
469
  return this.processQueue(containers, (container) => container.deltaManager.inbound);
@@ -391,7 +471,9 @@ class LoaderContainerTracker {
391
471
  /**
392
472
  * Pause all queue activities on all tracked containers, and resume only
393
473
  * outbound to process ops until it is idle. All queues are left in the paused state
394
- * after the function
474
+ * after the function.
475
+ *
476
+ * Pausing will switch the container to write mode. See `pauseProcessing`
395
477
  */
396
478
  async processOutgoing(...containers) {
397
479
  return this.processQueue(containers, (container) => container.deltaManager.outbound);
@@ -406,6 +488,7 @@ class LoaderContainerTracker {
406
488
  const inflightTracker = new Map();
407
489
  const cleanup = [];
408
490
  for (const container of containersToApply) {
491
+ (0, common_utils_1.assert)(container.deltaManager.active, "Container should be connected in write mode already");
409
492
  const queue = getQueue(container);
410
493
  // track the outgoing ops (if any) to make sure they make the round trip to at least to the same client
411
494
  // to make sure they are sequenced.