@fluidframework/task-manager 2.0.0-dev-rc.3.0.0.250606 → 2.0.0-dev-rc.3.0.0.253463

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.
Files changed (40) hide show
  1. package/api-report/task-manager.api.md +1 -1
  2. package/dist/packageVersion.d.ts +1 -1
  3. package/dist/packageVersion.js +1 -1
  4. package/dist/packageVersion.js.map +1 -1
  5. package/dist/task-manager-alpha.d.ts +1 -1
  6. package/dist/task-manager-beta.d.ts +1 -1
  7. package/dist/task-manager-public.d.ts +1 -1
  8. package/dist/task-manager-untrimmed.d.ts +1 -1
  9. package/dist/taskManager.d.ts +2 -1
  10. package/dist/taskManager.d.ts.map +1 -1
  11. package/dist/taskManager.js +21 -21
  12. package/dist/taskManager.js.map +1 -1
  13. package/dist/taskManagerFactory.d.ts.map +1 -1
  14. package/dist/taskManagerFactory.js.map +1 -1
  15. package/lib/packageVersion.d.ts +1 -1
  16. package/lib/packageVersion.js +1 -1
  17. package/lib/packageVersion.js.map +1 -1
  18. package/lib/task-manager-alpha.d.ts +1 -1
  19. package/lib/task-manager-beta.d.ts +1 -1
  20. package/lib/task-manager-public.d.ts +1 -1
  21. package/lib/task-manager-untrimmed.d.ts +1 -1
  22. package/lib/taskManager.d.ts +2 -1
  23. package/lib/taskManager.d.ts.map +1 -1
  24. package/lib/taskManager.js +3 -3
  25. package/lib/taskManager.js.map +1 -1
  26. package/lib/taskManagerFactory.d.ts.map +1 -1
  27. package/lib/taskManagerFactory.js.map +1 -1
  28. package/lib/tsdoc-metadata.json +11 -0
  29. package/package.json +15 -27
  30. package/src/packageVersion.ts +1 -1
  31. package/src/taskManager.ts +5 -7
  32. package/src/taskManagerFactory.ts +1 -0
  33. package/lib/test/dirname.cjs +0 -16
  34. package/lib/test/dirname.cjs.map +0 -1
  35. package/lib/test/taskManager.fuzz.spec.js +0 -203
  36. package/lib/test/taskManager.fuzz.spec.js.map +0 -1
  37. package/lib/test/taskManager.spec.js +0 -845
  38. package/lib/test/taskManager.spec.js.map +0 -1
  39. package/lib/test/types/validateTaskManagerPrevious.generated.js +0 -12
  40. package/lib/test/types/validateTaskManagerPrevious.generated.js.map +0 -1
@@ -1,203 +0,0 @@
1
- /*!
2
- * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
- * Licensed under the MIT License.
4
- */
5
- import { strict as assert } from "assert";
6
- import * as path from "path";
7
- import { combineReducersAsync as combineReducers, createWeightedAsyncGenerator as createWeightedGenerator, makeRandom, takeAsync as take, } from "@fluid-private/stochastic-test-utils";
8
- import { createDDSFuzzSuite } from "@fluid-private/test-dds-utils";
9
- import { FlushMode } from "@fluidframework/runtime-definitions";
10
- import { TaskManagerFactory } from "../taskManagerFactory.js";
11
- import { _dirname } from "./dirname.cjs";
12
- const defaultOptions = {
13
- taskPoolSize: 3,
14
- taskStringLength: 5,
15
- validateInterval: 10,
16
- testCount: 10,
17
- operations: 100,
18
- };
19
- function makeOperationGenerator(optionsParam) {
20
- const options = { ...defaultOptions, ...(optionsParam ?? {}) };
21
- const taskIdPoolRandom = makeRandom(0);
22
- const dedupe = (arr) => Array.from(new Set(arr));
23
- const taskIdPool = dedupe(Array.from({ length: options.taskPoolSize }, () => taskIdPoolRandom.string(defaultOptions.taskStringLength)));
24
- async function volunteer(state) {
25
- return {
26
- type: "volunteer",
27
- taskId: state.taskId,
28
- };
29
- }
30
- async function abandon(state) {
31
- return {
32
- type: "abandon",
33
- taskId: state.taskId,
34
- };
35
- }
36
- async function subscribe(state) {
37
- return {
38
- type: "subscribe",
39
- taskId: state.taskId,
40
- };
41
- }
42
- async function complete(state) {
43
- return {
44
- type: "complete",
45
- taskId: state.taskId,
46
- };
47
- }
48
- const canVolunteer = ({ client }) => client.channel.canVolunteer();
49
- const isQueued = ({ client, taskId }) => client.channel.queued(taskId);
50
- const isAssigned = ({ client, taskId }) => client.channel.assigned(taskId);
51
- const clientBaseOperationGenerator = createWeightedGenerator([
52
- [volunteer, 1, canVolunteer],
53
- [abandon, 1, isQueued],
54
- [subscribe, 1],
55
- [complete, 1, isAssigned],
56
- ]);
57
- return async (state) => clientBaseOperationGenerator({
58
- ...state,
59
- taskId: state.random.pick(taskIdPool),
60
- });
61
- }
62
- function logCurrentState(state, loggingInfo) {
63
- for (const client of state.clients) {
64
- const taskManager = client.channel;
65
- assert(taskManager);
66
- if (loggingInfo.taskManagerNames.includes(client.containerRuntime.clientId)) {
67
- console.log(`TaskManager ${taskManager.id} (CanVolunteer: ${taskManager.canVolunteer()}):`);
68
- console.log(taskManager.taskQueues.get(loggingInfo.taskId));
69
- console.log("\n");
70
- }
71
- }
72
- }
73
- function makeReducer(loggingInfo) {
74
- const withLogging = (baseReducer) => async (state, operation) => {
75
- if (loggingInfo !== undefined && operation.taskId === loggingInfo.taskId) {
76
- logCurrentState(state, loggingInfo);
77
- console.log("-".repeat(20));
78
- console.log("Next operation:", JSON.stringify(operation, undefined, 4));
79
- }
80
- await baseReducer(state, operation);
81
- };
82
- const reducer = combineReducers({
83
- volunteer: async ({ client }, { taskId }) => {
84
- // Note: this is fire-and-forget as `volunteerForTask` resolves/rejects its returned
85
- // promise based on server responses, which will occur on later operations (and
86
- // processing those operations will raise the error directly)
87
- client.channel.volunteerForTask(taskId).catch((e) => {
88
- // We expect an error to be thrown if we are disconnected while volunteering
89
- const expectedErrors = [
90
- "Disconnected before acquiring task assignment",
91
- "Abandoned before acquiring task assignment",
92
- ];
93
- if (!expectedErrors.includes(e.message)) {
94
- throw e;
95
- }
96
- });
97
- },
98
- abandon: async ({ client }, { taskId }) => {
99
- client.channel.abandon(taskId);
100
- },
101
- subscribe: async ({ client }, { taskId }) => {
102
- client.channel.subscribeToTask(taskId);
103
- },
104
- complete: async ({ client }, { taskId }) => {
105
- client.channel.complete(taskId);
106
- },
107
- });
108
- return withLogging(reducer);
109
- }
110
- function assertEqualTaskManagers(a, b) {
111
- const queue1 = a.taskQueues;
112
- const queue2 = b.taskQueues;
113
- assert.strictEqual(queue1.size, queue2.size, "The number of tasks queues are not the same");
114
- for (const [key, val] of queue1) {
115
- const testVal = queue2.get(key);
116
- if (testVal === undefined) {
117
- assert(val === undefined, "Task queues are not both undefined");
118
- continue;
119
- }
120
- assert.strictEqual(testVal.length, val.length, "Task queues are not the same size");
121
- if (testVal.length > 0) {
122
- testVal.forEach((task, index) => {
123
- assert.strictEqual(task, val[index], `Task queues are not identical`);
124
- });
125
- }
126
- }
127
- }
128
- describe("TaskManager fuzz testing", () => {
129
- const model = {
130
- workloadName: "default configuration",
131
- generatorFactory: () => take(100, makeOperationGenerator()),
132
- reducer:
133
- // makeReducer supports a param for logging output which tracks the provided intervalId over time:
134
- // { taskManagerNames: ["A", "B", "C"], taskId: "" },
135
- makeReducer(),
136
- validateConsistency: assertEqualTaskManagers,
137
- factory: new TaskManagerFactory(),
138
- };
139
- createDDSFuzzSuite(model, {
140
- validationStrategy: { type: "fixedInterval", interval: defaultOptions.validateInterval },
141
- // AB#3985: TaskManager has some eventual consistency issue with reconnect enabled.
142
- // To make this configuration similar to pre-generic DDS fuzz harness refactor, this constant
143
- // should be 0.2.
144
- // Leaving the tests enabled without reconnect on mimics previous behavior (and provides more coverage
145
- // than skipping them)
146
- reconnectProbability: 0,
147
- detachedStartOptions: {
148
- numOpsBeforeAttach: 5,
149
- // similar to reconnect there are eventual consistency errors when we enter attaching before rehydrate
150
- // when fixed, detachedStartOptions can be removed from this config, and attachingBeforeRehydrateDisable
151
- // can be completely removed, as it is only used by this test. Rather than file more bugs. I'll just combine
152
- // this with AB#3985, as it looks like the dds has fundamental issue around lifecycle handling
153
- attachingBeforeRehydrateDisable: true,
154
- },
155
- clientJoinOptions: {
156
- maxNumberOfClients: 6,
157
- clientAddProbability: 0.05,
158
- stashableClientProbability: 0.2,
159
- },
160
- defaultTestCount: defaultOptions.testCount,
161
- saveFailures: { directory: path.join(_dirname, "../../src/test/results") },
162
- // Uncomment this line to replay a specific seed:
163
- // replay: 0,
164
- // This can be useful for quickly minimizing failure json while attempting to root-cause a failure.
165
- });
166
- });
167
- describe("TaskManager fuzz testing with rebasing", () => {
168
- const model = {
169
- workloadName: "default configuration and rebasing",
170
- generatorFactory: () => take(100, makeOperationGenerator()),
171
- reducer:
172
- // makeReducer supports a param for logging output which tracks the provided intervalId over time:
173
- // { taskManagerNames: ["A", "B", "C"], taskId: "" },
174
- makeReducer(),
175
- validateConsistency: assertEqualTaskManagers,
176
- factory: new TaskManagerFactory(),
177
- };
178
- createDDSFuzzSuite(model, {
179
- validationStrategy: { type: "fixedInterval", interval: defaultOptions.validateInterval },
180
- // AB#5185: enabling rebasing indicates some unknown eventual consistency issue
181
- skip: [5, 7],
182
- rebaseProbability: 0.15,
183
- containerRuntimeOptions: {
184
- flushMode: FlushMode.TurnBased,
185
- enableGroupedBatching: true,
186
- },
187
- clientJoinOptions: {
188
- maxNumberOfClients: 6,
189
- clientAddProbability: 0.05,
190
- stashableClientProbability: 0.2,
191
- },
192
- defaultTestCount: defaultOptions.testCount,
193
- saveFailures: { directory: path.join(_dirname, "../../src/test/results") },
194
- // AB#5341: enabling 'start from detached' within the fuzz harness demonstrates eventual consistency failures.
195
- detachedStartOptions: {
196
- numOpsBeforeAttach: 0,
197
- },
198
- // Uncomment this line to replay a specific seed:
199
- // replay: 0,
200
- // This can be useful for quickly minimizing failure json while attempting to root-cause a failure.
201
- });
202
- });
203
- //# sourceMappingURL=taskManager.fuzz.spec.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"taskManager.fuzz.spec.js","sourceRoot":"","sources":["../../src/test/taskManager.fuzz.spec.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 { strict as assert } from \"assert\";\nimport * as path from \"path\";\nimport {\n\tAsyncGenerator as Generator,\n\tAsyncReducer as Reducer,\n\tcombineReducersAsync as combineReducers,\n\tcreateWeightedAsyncGenerator as createWeightedGenerator,\n\tmakeRandom,\n\ttakeAsync as take,\n} from \"@fluid-private/stochastic-test-utils\";\nimport { DDSFuzzModel, DDSFuzzTestState, createDDSFuzzSuite } from \"@fluid-private/test-dds-utils\";\nimport { FlushMode } from \"@fluidframework/runtime-definitions\";\nimport { ITaskManager } from \"../interfaces.js\";\nimport { TaskManagerFactory } from \"../taskManagerFactory.js\";\nimport { _dirname } from \"./dirname.cjs\";\n\ntype FuzzTestState = DDSFuzzTestState<TaskManagerFactory>;\n\ninterface TaskOperation {\n\t/** The Id of the task that the operation applies to. */\n\ttaskId: string;\n}\n\ninterface Volunteer extends TaskOperation {\n\ttype: \"volunteer\";\n}\n\ninterface Abandon extends TaskOperation {\n\ttype: \"abandon\";\n}\n\ninterface Subscribe extends TaskOperation {\n\ttype: \"subscribe\";\n}\n\ninterface Complete extends TaskOperation {\n\ttype: \"complete\";\n}\n\ntype Operation = Volunteer | Abandon | Subscribe | Complete;\n\ninterface OperationGenerationConfig {\n\t/**\n\t * Number of task ids to be generated\n\t */\n\ttaskPoolSize?: number;\n\t/**\n\t * Length of taskId strings\n\t */\n\ttaskStringLength?: number;\n\t/**\n\t * Number of ops in between each synchronization/validation of the TaskManagers\n\t */\n\tvalidateInterval?: number;\n\t/**\n\t * Number of tests to generate\n\t */\n\ttestCount?: number;\n\t/**\n\t * Number of operations to perform in each test\n\t */\n\toperations?: number;\n}\n\nconst defaultOptions: Required<OperationGenerationConfig> = {\n\ttaskPoolSize: 3,\n\ttaskStringLength: 5,\n\tvalidateInterval: 10,\n\ttestCount: 10,\n\toperations: 100,\n};\n\nfunction makeOperationGenerator(\n\toptionsParam?: OperationGenerationConfig,\n): Generator<Operation, FuzzTestState> {\n\tconst options = { ...defaultOptions, ...(optionsParam ?? {}) };\n\ttype OpSelectionState = FuzzTestState & {\n\t\ttaskId: string;\n\t};\n\n\tconst taskIdPoolRandom = makeRandom(0);\n\tconst dedupe = <T>(arr: T[]): T[] => Array.from(new Set(arr));\n\tconst taskIdPool = dedupe(\n\t\tArray.from({ length: options.taskPoolSize }, () =>\n\t\t\ttaskIdPoolRandom.string(defaultOptions.taskStringLength),\n\t\t),\n\t);\n\n\tasync function volunteer(state: OpSelectionState): Promise<Volunteer> {\n\t\treturn {\n\t\t\ttype: \"volunteer\",\n\t\t\ttaskId: state.taskId,\n\t\t};\n\t}\n\n\tasync function abandon(state: OpSelectionState): Promise<Abandon> {\n\t\treturn {\n\t\t\ttype: \"abandon\",\n\t\t\ttaskId: state.taskId,\n\t\t};\n\t}\n\n\tasync function subscribe(state: OpSelectionState): Promise<Subscribe> {\n\t\treturn {\n\t\t\ttype: \"subscribe\",\n\t\t\ttaskId: state.taskId,\n\t\t};\n\t}\n\n\tasync function complete(state: OpSelectionState): Promise<Complete> {\n\t\treturn {\n\t\t\ttype: \"complete\",\n\t\t\ttaskId: state.taskId,\n\t\t};\n\t}\n\n\tconst canVolunteer = ({ client }: OpSelectionState): boolean => client.channel.canVolunteer();\n\tconst isQueued = ({ client, taskId }: OpSelectionState): boolean =>\n\t\tclient.channel.queued(taskId);\n\tconst isAssigned = ({ client, taskId }: OpSelectionState): boolean =>\n\t\tclient.channel.assigned(taskId);\n\n\tconst clientBaseOperationGenerator = createWeightedGenerator<Operation, OpSelectionState>([\n\t\t[volunteer, 1, canVolunteer],\n\t\t[abandon, 1, isQueued],\n\t\t[subscribe, 1],\n\t\t[complete, 1, isAssigned],\n\t]);\n\n\treturn async (state: FuzzTestState) =>\n\t\tclientBaseOperationGenerator({\n\t\t\t...state,\n\t\t\ttaskId: state.random.pick(taskIdPool),\n\t\t});\n}\n\ninterface LoggingInfo {\n\t/** ids of the Task Managers to track over time */\n\ttaskManagerNames: string[];\n\t/** ids of tasks to track over time */\n\ttaskId: string;\n}\n\nfunction logCurrentState(state: FuzzTestState, loggingInfo: LoggingInfo): void {\n\tfor (const client of state.clients) {\n\t\tconst taskManager = client.channel;\n\t\tassert(taskManager);\n\t\tif (loggingInfo.taskManagerNames.includes(client.containerRuntime.clientId)) {\n\t\t\tconsole.log(\n\t\t\t\t`TaskManager ${taskManager.id} (CanVolunteer: ${taskManager.canVolunteer()}):`,\n\t\t\t);\n\t\t\tconsole.log((taskManager as any).taskQueues.get(loggingInfo.taskId));\n\t\t\tconsole.log(\"\\n\");\n\t\t}\n\t}\n}\n\nfunction makeReducer(loggingInfo?: LoggingInfo): Reducer<Operation, FuzzTestState> {\n\tconst withLogging =\n\t\t<T>(baseReducer: Reducer<T, FuzzTestState>): Reducer<T, FuzzTestState> =>\n\t\tasync (state, operation) => {\n\t\t\tif (loggingInfo !== undefined && (operation as any).taskId === loggingInfo.taskId) {\n\t\t\t\tlogCurrentState(state, loggingInfo);\n\t\t\t\tconsole.log(\"-\".repeat(20));\n\t\t\t\tconsole.log(\"Next operation:\", JSON.stringify(operation, undefined, 4));\n\t\t\t}\n\t\t\tawait baseReducer(state, operation);\n\t\t};\n\n\tconst reducer = combineReducers<Operation, FuzzTestState>({\n\t\tvolunteer: async ({ client }, { taskId }) => {\n\t\t\t// Note: this is fire-and-forget as `volunteerForTask` resolves/rejects its returned\n\t\t\t// promise based on server responses, which will occur on later operations (and\n\t\t\t// processing those operations will raise the error directly)\n\t\t\tclient.channel.volunteerForTask(taskId).catch((e: Error) => {\n\t\t\t\t// We expect an error to be thrown if we are disconnected while volunteering\n\t\t\t\tconst expectedErrors = [\n\t\t\t\t\t\"Disconnected before acquiring task assignment\",\n\t\t\t\t\t\"Abandoned before acquiring task assignment\",\n\t\t\t\t];\n\t\t\t\tif (!expectedErrors.includes(e.message)) {\n\t\t\t\t\tthrow e;\n\t\t\t\t}\n\t\t\t});\n\t\t},\n\t\tabandon: async ({ client }, { taskId }) => {\n\t\t\tclient.channel.abandon(taskId);\n\t\t},\n\t\tsubscribe: async ({ client }, { taskId }) => {\n\t\t\tclient.channel.subscribeToTask(taskId);\n\t\t},\n\t\tcomplete: async ({ client }, { taskId }) => {\n\t\t\tclient.channel.complete(taskId);\n\t\t},\n\t});\n\n\treturn withLogging(reducer);\n}\n\nfunction assertEqualTaskManagers(a: ITaskManager, b: ITaskManager) {\n\tconst queue1 = (a as any).taskQueues;\n\tconst queue2 = (b as any).taskQueues;\n\tassert.strictEqual(queue1.size, queue2.size, \"The number of tasks queues are not the same\");\n\tfor (const [key, val] of queue1) {\n\t\tconst testVal = queue2.get(key);\n\t\tif (testVal === undefined) {\n\t\t\tassert(val === undefined, \"Task queues are not both undefined\");\n\t\t\tcontinue;\n\t\t}\n\t\tassert.strictEqual(testVal.length, val.length, \"Task queues are not the same size\");\n\t\tif (testVal.length > 0) {\n\t\t\ttestVal.forEach((task: string, index: number) => {\n\t\t\t\tassert.strictEqual(task, val[index], `Task queues are not identical`);\n\t\t\t});\n\t\t}\n\t}\n}\n\ndescribe(\"TaskManager fuzz testing\", () => {\n\tconst model: DDSFuzzModel<TaskManagerFactory, Operation, FuzzTestState> = {\n\t\tworkloadName: \"default configuration\",\n\t\tgeneratorFactory: () => take(100, makeOperationGenerator()),\n\t\treducer:\n\t\t\t// makeReducer supports a param for logging output which tracks the provided intervalId over time:\n\t\t\t// { taskManagerNames: [\"A\", \"B\", \"C\"], taskId: \"\" },\n\t\t\tmakeReducer(),\n\t\tvalidateConsistency: assertEqualTaskManagers,\n\t\tfactory: new TaskManagerFactory(),\n\t};\n\n\tcreateDDSFuzzSuite(model, {\n\t\tvalidationStrategy: { type: \"fixedInterval\", interval: defaultOptions.validateInterval },\n\t\t// AB#3985: TaskManager has some eventual consistency issue with reconnect enabled.\n\t\t// To make this configuration similar to pre-generic DDS fuzz harness refactor, this constant\n\t\t// should be 0.2.\n\t\t// Leaving the tests enabled without reconnect on mimics previous behavior (and provides more coverage\n\t\t// than skipping them)\n\t\treconnectProbability: 0,\n\t\tdetachedStartOptions: {\n\t\t\tnumOpsBeforeAttach: 5,\n\t\t\t// similar to reconnect there are eventual consistency errors when we enter attaching before rehydrate\n\t\t\t// when fixed, detachedStartOptions can be removed from this config, and attachingBeforeRehydrateDisable\n\t\t\t// can be completely removed, as it is only used by this test. Rather than file more bugs. I'll just combine\n\t\t\t// this with AB#3985, as it looks like the dds has fundamental issue around lifecycle handling\n\t\t\tattachingBeforeRehydrateDisable: true,\n\t\t},\n\t\tclientJoinOptions: {\n\t\t\tmaxNumberOfClients: 6,\n\t\t\tclientAddProbability: 0.05,\n\t\t\tstashableClientProbability: 0.2,\n\t\t},\n\t\tdefaultTestCount: defaultOptions.testCount,\n\t\tsaveFailures: { directory: path.join(_dirname, \"../../src/test/results\") },\n\t\t// Uncomment this line to replay a specific seed:\n\t\t// replay: 0,\n\t\t// This can be useful for quickly minimizing failure json while attempting to root-cause a failure.\n\t});\n});\n\ndescribe(\"TaskManager fuzz testing with rebasing\", () => {\n\tconst model: DDSFuzzModel<TaskManagerFactory, Operation, FuzzTestState> = {\n\t\tworkloadName: \"default configuration and rebasing\",\n\t\tgeneratorFactory: () => take(100, makeOperationGenerator()),\n\t\treducer:\n\t\t\t// makeReducer supports a param for logging output which tracks the provided intervalId over time:\n\t\t\t// { taskManagerNames: [\"A\", \"B\", \"C\"], taskId: \"\" },\n\t\t\tmakeReducer(),\n\t\tvalidateConsistency: assertEqualTaskManagers,\n\t\tfactory: new TaskManagerFactory(),\n\t};\n\n\tcreateDDSFuzzSuite(model, {\n\t\tvalidationStrategy: { type: \"fixedInterval\", interval: defaultOptions.validateInterval },\n\t\t// AB#5185: enabling rebasing indicates some unknown eventual consistency issue\n\t\tskip: [5, 7],\n\t\trebaseProbability: 0.15,\n\t\tcontainerRuntimeOptions: {\n\t\t\tflushMode: FlushMode.TurnBased,\n\t\t\tenableGroupedBatching: true,\n\t\t},\n\t\tclientJoinOptions: {\n\t\t\tmaxNumberOfClients: 6,\n\t\t\tclientAddProbability: 0.05,\n\t\t\tstashableClientProbability: 0.2,\n\t\t},\n\t\tdefaultTestCount: defaultOptions.testCount,\n\t\tsaveFailures: { directory: path.join(_dirname, \"../../src/test/results\") },\n\t\t// AB#5341: enabling 'start from detached' within the fuzz harness demonstrates eventual consistency failures.\n\t\tdetachedStartOptions: {\n\t\t\tnumOpsBeforeAttach: 0,\n\t\t},\n\t\t// Uncomment this line to replay a specific seed:\n\t\t// replay: 0,\n\t\t// This can be useful for quickly minimizing failure json while attempting to root-cause a failure.\n\t});\n});\n"]}