fix(pool): handle worker start failures gracefully (#9337) · vitest-dev/vitest@200dadb

1+

import type { Span } from '@opentelemetry/api'

12

import type { ContextTestEnvironment } from '../../types/worker'

23

import type { Logger } from '../logger'

34

import type { StateManager } from '../state'

@@ -118,21 +119,27 @@ export class Pool {

118119

WORKER_START_TIMEOUT,

119120

)

120121121-

await runner.start({ workerId: task.context.workerId }).finally(() => clearTimeout(id))

122+

await runner.start({ workerId: task.context.workerId })

123+

.catch(error =>

124+

resolver.reject(

125+

new Error(`[vitest-pool]: Failed to start ${task.worker} worker for test files ${formatFiles(task)}.`, { cause: error }),

126+

),

127+

)

128+

.finally(() => clearTimeout(id))

122129

}

123130124-

const span = runner.startTracesSpan(`vitest.worker.${method}`)

125-

// Start running the test in the worker

126-

runner.request(method, task.context)

131+

let span: Span | undefined

132+133+

if (!resolver.isRejected) {

134+

span = runner.startTracesSpan(`vitest.worker.${method}`)

135+136+

// Start running the test in the worker

137+

runner.request(method, task.context)

138+

}

127139128140

await resolver.promise

129-

.catch((error) => {

130-

span.recordException(error)

131-

throw error

132-

})

133-

.finally(() => {

134-

span.end()

135-

})

141+

.catch(error => span?.recordException(error))

142+

.finally(() => span?.end())

136143137144

const index = this.activeTasks.indexOf(activeTask)

138145

if (index !== -1) {

@@ -158,7 +165,7 @@ export class Pool {

158165

)

159166160167

this.exitPromises.push(

161-

runner.stop()

168+

runner.stop({ force: resolver.isRejected })

162169

.then(() => clearTimeout(id))

163170

.catch(error => this.logger.error(`[vitest-pool]: Failed to terminate ${task.worker} worker for test files ${formatFiles(task)}.`, error)),

164171

)

@@ -281,7 +288,17 @@ function withResolvers() {

281288

reject = rej

282289

})

283290284-

return { resolve, reject, promise }

291+

const resolver = {

292+

promise,

293+

resolve,

294+

reject: (reason: unknown) => {

295+

resolver.isRejected = true

296+

reject(reason)

297+

},

298+

isRejected: false,

299+

}

300+301+

return resolver

285302

}

286303287304

function formatFiles(task: PoolTask) {