Skip to content

Commit 98b3742

Browse files
committed
fix: properly account strategy retries on a per strategy basis
Signed-off-by: Jérôme Benoit <[email protected]>
1 parent 3c7ad6e commit 98b3742

File tree

7 files changed

+97
-82
lines changed

7 files changed

+97
-82
lines changed

src/pools/abstract-pool.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -309,7 +309,8 @@ export abstract class AbstractPool<
309309
started: this.started,
310310
ready: this.ready,
311311
defaultStrategy: this.opts.workerChoiceStrategy!,
312-
strategyRetries: this.workerChoiceStrategiesContext?.retriesCount ?? 0,
312+
strategyRetries:
313+
this.workerChoiceStrategiesContext?.getStrategyRetries() ?? 0,
313314
minSize: this.minimumNumberOfWorkers,
314315
maxSize: this.maximumNumberOfWorkers ?? this.minimumNumberOfWorkers,
315316
...(taskStatisticsRequirements?.runTime.aggregate === true &&

src/pools/selection-strategies/abstract-worker-choice-strategy.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,18 @@ export abstract class AbstractWorkerChoiceStrategy<
2828
/** @inheritDoc */
2929
public abstract readonly name: WorkerChoiceStrategy
3030

31+
/** @inheritDoc */
32+
public retriesCount: number
33+
3134
/**
3235
* The next worker node key.
3336
*/
34-
protected nextWorkerNodeKey: number | undefined = 0
37+
protected nextWorkerNodeKey: number | undefined
3538

3639
/**
3740
* The previous worker node key.
3841
*/
39-
protected previousWorkerNodeKey = 0
42+
protected previousWorkerNodeKey: number
4043

4144
/** @inheritDoc */
4245
public readonly strategyPolicy: StrategyPolicy = Object.freeze({
@@ -62,6 +65,9 @@ export abstract class AbstractWorkerChoiceStrategy<
6265
protected readonly pool: IPool<Worker, Data, Response>,
6366
protected opts?: WorkerChoiceStrategyOptions,
6467
) {
68+
this.retriesCount = 0
69+
this.nextWorkerNodeKey = 0
70+
this.previousWorkerNodeKey = 0
6571
this.choose = this.choose.bind(this)
6672
this.setOptions(this.opts)
6773
}

src/pools/selection-strategies/selection-strategies-types.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,10 @@ export interface IWorkerChoiceStrategy {
194194
* @returns `true` if the reset is successful, `false` otherwise.
195195
*/
196196
readonly reset: () => boolean
197+
/**
198+
* The worker choice strategy execution retries count.
199+
*/
200+
retriesCount: number
197201
/**
198202
* Updates the worker node key strategy internals.
199203
* This is called after a task has been executed on a worker node.

src/pools/selection-strategies/worker-choice-strategies-context.ts

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,6 @@ export class WorkerChoiceStrategiesContext<
2828
Data = unknown,
2929
Response = unknown,
3030
> {
31-
/**
32-
* The number of worker choice strategies execution retries.
33-
*/
34-
public retriesCount: number
35-
3631
/**
3732
* The default worker choice strategy in the context.
3833
*/
@@ -92,7 +87,6 @@ export class WorkerChoiceStrategiesContext<
9287
buildWorkerChoiceStrategiesTaskStatisticsRequirements(
9388
this.workerChoiceStrategies,
9489
)
95-
this.retriesCount = 0
9690
this.retries = getWorkerChoiceStrategiesRetries<Worker, Data, Response>(
9791
this.pool,
9892
opts,
@@ -108,6 +102,17 @@ export class WorkerChoiceStrategiesContext<
108102
return this.workerChoiceStrategiesPolicy
109103
}
110104

105+
/**
106+
* Gets the number of worker choice strategies execution retries.
107+
* @returns The number of retries.
108+
*/
109+
public getStrategyRetries(): number {
110+
return Array.from(
111+
this.workerChoiceStrategies,
112+
([_, workerChoiceStrategy]) => workerChoiceStrategy.retriesCount,
113+
).reduce((accumulator, retries) => accumulator + retries, 0)
114+
}
115+
111116
/**
112117
* Gets the active worker choice strategies in the context task statistics requirements.
113118
*
@@ -173,13 +178,13 @@ export class WorkerChoiceStrategiesContext<
173178
let workerNodeKey: number | undefined = workerChoiceStrategy.choose()
174179
let retriesCount = 0
175180
while (workerNodeKey == null && retriesCount < this.retries) {
176-
workerNodeKey = workerChoiceStrategy.choose()
177181
retriesCount++
178-
this.retriesCount++
182+
workerNodeKey = workerChoiceStrategy.choose()
179183
}
184+
workerChoiceStrategy.retriesCount = retriesCount
180185
if (workerNodeKey == null) {
181186
throw new Error(
182-
`Worker node key chosen by ${workerChoiceStrategy.name} is null or undefined after ${retriesCount.toString()} retries (max: ${this.retries.toString()})`,
187+
`Worker node key chosen by ${workerChoiceStrategy.name} is null or undefined after ${workerChoiceStrategy.retriesCount.toString()} retries (max: ${this.retries.toString()})`,
183188
)
184189
}
185190
return workerNodeKey

src/worker/abstract-worker.ts

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -287,14 +287,10 @@ export abstract class AbstractWorker<
287287
DEFAULT_TASK_NAME,
288288
this.taskFunctions.get(DEFAULT_TASK_NAME),
289289
),
290-
...(defaultTaskFunctionName !== DEFAULT_TASK_NAME
291-
? [
292-
buildTaskFunctionProperties(
293-
defaultTaskFunctionName,
294-
this.taskFunctions.get(defaultTaskFunctionName),
295-
),
296-
]
297-
: []),
290+
buildTaskFunctionProperties(
291+
defaultTaskFunctionName,
292+
this.taskFunctions.get(defaultTaskFunctionName),
293+
),
298294
...taskFunctionsProperties,
299295
]
300296
}

tests/pools/selection-strategies/selection-strategies.test.mjs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,11 @@ describe('Selection strategies test suite', () => {
9797
workerChoiceStrategy,
9898
).name,
9999
).toBe(workerChoiceStrategy)
100+
expect(
101+
pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
102+
workerChoiceStrategy,
103+
).retriesCount,
104+
).toBe(0)
100105
expect(
101106
pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
102107
workerChoiceStrategy,

tests/pools/selection-strategies/worker-choice-strategies-context.test.mjs

Lines changed: 60 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -80,39 +80,58 @@ describe('Worker choice strategies context test suite', () => {
8080
expect(workerChoiceStrategiesContext.defaultWorkerChoiceStrategy).toBe(
8181
WorkerChoiceStrategies.ROUND_ROBIN,
8282
)
83-
const workerChoiceStrategyUndefinedStub =
84-
new RoundRobinWorkerChoiceStrategy(fixedPool)
83+
const workerChoiceStrategyUndefinedStub = workerChoiceStrategiesContext
84+
.workerChoiceStrategies.get(
85+
workerChoiceStrategiesContext.defaultWorkerChoiceStrategy,
86+
)
8587
stub(
8688
workerChoiceStrategyUndefinedStub,
8789
'choose',
8890
returnsNext(Array(7).fill(undefined)),
8991
)
90-
workerChoiceStrategiesContext.workerChoiceStrategies.set(
91-
workerChoiceStrategiesContext.defaultWorkerChoiceStrategy,
92-
workerChoiceStrategyUndefinedStub,
92+
let err
93+
try {
94+
workerChoiceStrategiesContext.execute()
95+
} catch (e) {
96+
err = e
97+
}
98+
expect(err).toBeInstanceOf(Error)
99+
expect(err.message).toBe(
100+
`Worker node key chosen by ${workerChoiceStrategyUndefinedStub.name} is null or undefined after ${workerChoiceStrategyUndefinedStub.retriesCount.toString()} retries (max: ${workerChoiceStrategiesContext.retries.toString()})`,
93101
)
94-
expect(() => workerChoiceStrategiesContext.execute()).toThrow(
95-
new Error(
96-
`Worker node key chosen by ${workerChoiceStrategyUndefinedStub.name} is null or undefined after ${workerChoiceStrategiesContext.retries.toString()} retries (max: ${workerChoiceStrategiesContext.retries.toString()})`,
97-
),
102+
assertSpyCalls(
103+
workerChoiceStrategyUndefinedStub.choose,
104+
workerChoiceStrategyUndefinedStub.retriesCount + 1,
98105
)
99-
workerChoiceStrategyUndefinedStub.choose.restore()
100-
const workerChoiceStrategyNullStub = new RoundRobinWorkerChoiceStrategy(
101-
fixedPool,
106+
expect(workerChoiceStrategiesContext.getStrategyRetries()).toBe(
107+
workerChoiceStrategyUndefinedStub.retriesCount,
102108
)
109+
workerChoiceStrategyUndefinedStub.choose.restore()
110+
const workerChoiceStrategyNullStub = workerChoiceStrategiesContext
111+
.workerChoiceStrategies.get(
112+
workerChoiceStrategiesContext.defaultWorkerChoiceStrategy,
113+
)
103114
stub(
104115
workerChoiceStrategyNullStub,
105116
'choose',
106117
returnsNext(Array(7).fill(null)),
107118
)
108-
workerChoiceStrategiesContext.workerChoiceStrategies.set(
109-
workerChoiceStrategiesContext.defaultWorkerChoiceStrategy,
110-
workerChoiceStrategyNullStub,
119+
err = undefined
120+
try {
121+
workerChoiceStrategiesContext.execute()
122+
} catch (e) {
123+
err = e
124+
}
125+
expect(err).toBeInstanceOf(Error)
126+
expect(err.message).toBe(
127+
`Worker node key chosen by ${workerChoiceStrategyNullStub.name} is null or undefined after ${workerChoiceStrategyNullStub.retriesCount.toString()} retries (max: ${workerChoiceStrategiesContext.retries.toString()})`,
111128
)
112-
expect(() => workerChoiceStrategiesContext.execute()).toThrow(
113-
new Error(
114-
`Worker node key chosen by ${workerChoiceStrategyNullStub.name} is null or undefined after ${workerChoiceStrategiesContext.retries.toString()} retries (max: ${workerChoiceStrategiesContext.retries.toString()})`,
115-
),
129+
assertSpyCalls(
130+
workerChoiceStrategyNullStub.choose,
131+
workerChoiceStrategyUndefinedStub.retriesCount + 1,
132+
)
133+
expect(workerChoiceStrategiesContext.getStrategyRetries()).toBe(
134+
workerChoiceStrategyNullStub.retriesCount,
116135
)
117136
workerChoiceStrategyNullStub.choose.restore()
118137
})
@@ -121,28 +140,21 @@ describe('Worker choice strategies context test suite', () => {
121140
const workerChoiceStrategiesContext = new WorkerChoiceStrategiesContext(
122141
fixedPool,
123142
)
124-
const workerChoiceStrategyStub = new RoundRobinWorkerChoiceStrategy(
125-
fixedPool,
143+
expect(workerChoiceStrategiesContext.defaultWorkerChoiceStrategy).toBe(
144+
WorkerChoiceStrategies.ROUND_ROBIN,
126145
)
146+
const workerChoiceStrategyStub = workerChoiceStrategiesContext
147+
.workerChoiceStrategies.get(
148+
workerChoiceStrategiesContext.defaultWorkerChoiceStrategy,
149+
)
127150
stub(
128151
workerChoiceStrategyStub,
129152
'choose',
130153
returnsNext(Array(5).fill(undefined).concat(Array(1).fill(1))),
131154
)
132-
expect(workerChoiceStrategiesContext.defaultWorkerChoiceStrategy).toBe(
133-
WorkerChoiceStrategies.ROUND_ROBIN,
134-
)
135-
workerChoiceStrategiesContext.workerChoiceStrategies.set(
136-
workerChoiceStrategiesContext.defaultWorkerChoiceStrategy,
137-
workerChoiceStrategyStub,
138-
)
139155
const chosenWorkerKey = workerChoiceStrategiesContext.execute()
140-
assertSpyCalls(
141-
workerChoiceStrategiesContext.workerChoiceStrategies.get(
142-
workerChoiceStrategiesContext.defaultWorkerChoiceStrategy,
143-
).choose,
144-
6,
145-
)
156+
assertSpyCalls(workerChoiceStrategyStub.choose, 6)
157+
expect(workerChoiceStrategiesContext.getStrategyRetries()).toBe(5)
146158
expect(chosenWorkerKey).toBe(1)
147159
workerChoiceStrategyStub.choose.restore()
148160
})
@@ -151,24 +163,17 @@ describe('Worker choice strategies context test suite', () => {
151163
const workerChoiceStrategiesContext = new WorkerChoiceStrategiesContext(
152164
fixedPool,
153165
)
154-
const workerChoiceStrategyStub = new RoundRobinWorkerChoiceStrategy(
155-
fixedPool,
156-
)
157-
stub(workerChoiceStrategyStub, 'choose', returnsNext([0]))
158166
expect(workerChoiceStrategiesContext.defaultWorkerChoiceStrategy).toBe(
159167
WorkerChoiceStrategies.ROUND_ROBIN,
160168
)
161-
workerChoiceStrategiesContext.workerChoiceStrategies.set(
162-
workerChoiceStrategiesContext.defaultWorkerChoiceStrategy,
163-
workerChoiceStrategyStub,
164-
)
165-
const chosenWorkerKey = workerChoiceStrategiesContext.execute()
166-
assertSpyCalls(
167-
workerChoiceStrategiesContext.workerChoiceStrategies.get(
169+
const workerChoiceStrategyStub = workerChoiceStrategiesContext
170+
.workerChoiceStrategies.get(
168171
workerChoiceStrategiesContext.defaultWorkerChoiceStrategy,
169-
).choose,
170-
1,
171-
)
172+
)
173+
stub(workerChoiceStrategyStub, 'choose', returnsNext([0]))
174+
const chosenWorkerKey = workerChoiceStrategiesContext.execute()
175+
assertSpyCalls(workerChoiceStrategyStub.choose, 1)
176+
expect(workerChoiceStrategiesContext.getStrategyRetries()).toBe(0)
172177
expect(chosenWorkerKey).toBe(0)
173178
workerChoiceStrategyStub.choose.restore()
174179
})
@@ -177,24 +182,17 @@ describe('Worker choice strategies context test suite', () => {
177182
const workerChoiceStrategiesContext = new WorkerChoiceStrategiesContext(
178183
dynamicPool,
179184
)
180-
const workerChoiceStrategyStub = new RoundRobinWorkerChoiceStrategy(
181-
dynamicPool,
182-
)
183-
stub(workerChoiceStrategyStub, 'choose', returnsNext([0]))
184185
expect(workerChoiceStrategiesContext.defaultWorkerChoiceStrategy).toBe(
185186
WorkerChoiceStrategies.ROUND_ROBIN,
186187
)
187-
workerChoiceStrategiesContext.workerChoiceStrategies.set(
188-
workerChoiceStrategiesContext.defaultWorkerChoiceStrategy,
189-
workerChoiceStrategyStub,
190-
)
191-
const chosenWorkerKey = workerChoiceStrategiesContext.execute()
192-
assertSpyCalls(
193-
workerChoiceStrategiesContext.workerChoiceStrategies.get(
188+
const workerChoiceStrategyStub = workerChoiceStrategiesContext
189+
.workerChoiceStrategies.get(
194190
workerChoiceStrategiesContext.defaultWorkerChoiceStrategy,
195-
).choose,
196-
1,
197-
)
191+
)
192+
stub(workerChoiceStrategyStub, 'choose', returnsNext([0]))
193+
const chosenWorkerKey = workerChoiceStrategiesContext.execute()
194+
assertSpyCalls(workerChoiceStrategyStub.choose, 1)
195+
expect(workerChoiceStrategiesContext.getStrategyRetries()).toBe(0)
198196
expect(chosenWorkerKey).toBe(0)
199197
workerChoiceStrategyStub.choose.restore()
200198
})

0 commit comments

Comments
 (0)