-
Notifications
You must be signed in to change notification settings - Fork 251
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix(checker): retry on process crash (#3059)
Retry checker processes when they crash unexpectedly. * Add decorator pattern to checkers (like with test runners) * Split "resource" related shared code in its own class * Implement the `CheckerRetryDecorator` * Add integration tests to validate that the behavior works correctly.
- Loading branch information
Showing
27 changed files
with
517 additions
and
184 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import { Checker, CheckResult, CheckStatus } from '@stryker-mutator/api/check'; | ||
import { Mutant, StrykerOptions } from '@stryker-mutator/api/core'; | ||
import { Disposable } from 'typed-inject'; | ||
|
||
import { ChildProcessProxy } from '../child-proxy/child-process-proxy'; | ||
import { LoggingClientContext } from '../logging'; | ||
import { Resource } from '../concurrent/pool'; | ||
|
||
import { CheckerWorker } from './checker-worker'; | ||
|
||
export class CheckerChildProcessProxy implements Checker, Disposable, Resource { | ||
private readonly childProcess: ChildProcessProxy<CheckerWorker>; | ||
|
||
constructor(options: StrykerOptions, loggingContext: LoggingClientContext) { | ||
this.childProcess = ChildProcessProxy.create(require.resolve('./checker-worker'), loggingContext, options, {}, process.cwd(), CheckerWorker, []); | ||
} | ||
|
||
public async dispose(): Promise<void> { | ||
await this.childProcess?.dispose(); | ||
} | ||
|
||
public async init(): Promise<void> { | ||
await this.childProcess?.proxy.init(); | ||
} | ||
|
||
public async check(mutant: Mutant): Promise<CheckResult> { | ||
if (this.childProcess) { | ||
return this.childProcess.proxy.check(mutant); | ||
} | ||
return { | ||
status: CheckStatus.Passed, | ||
}; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import { CheckResult } from '@stryker-mutator/api/check'; | ||
import { Mutant } from '@stryker-mutator/api/core'; | ||
|
||
import { ChildProcessCrashedError } from '../child-proxy/child-process-crashed-error'; | ||
import { ResourceDecorator } from '../concurrent'; | ||
|
||
import { CheckerResource } from './checker-resource'; | ||
|
||
export class CheckerDecorator extends ResourceDecorator<CheckerResource> { | ||
public async check(mutant: Mutant): Promise<CheckResult> { | ||
try { | ||
return await this.innerResource.check(mutant); | ||
} catch (err) { | ||
if (err instanceof ChildProcessCrashedError) { | ||
await this.recover(); | ||
return this.innerResource.check(mutant); | ||
} else { | ||
throw err; //oops | ||
} | ||
} | ||
} | ||
} |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import { StrykerOptions } from '@stryker-mutator/api/core'; | ||
import { LoggerFactoryMethod } from '@stryker-mutator/api/logging'; | ||
import { commonTokens, tokens } from '@stryker-mutator/api/plugin'; | ||
|
||
import { coreTokens } from '../di'; | ||
import { LoggingClientContext } from '../logging/logging-client-context'; | ||
|
||
import { CheckerChildProcessProxy } from './checker-child-process-proxy'; | ||
import { CheckerResource } from './checker-resource'; | ||
import { CheckerRetryDecorator } from './checker-retry-decorator'; | ||
|
||
createCheckerFactory.inject = tokens(commonTokens.options, coreTokens.loggingContext, commonTokens.getLogger); | ||
export function createCheckerFactory( | ||
options: StrykerOptions, | ||
loggingContext: LoggingClientContext, | ||
getLogger: LoggerFactoryMethod | ||
): () => CheckerResource { | ||
return () => new CheckerRetryDecorator(() => new CheckerChildProcessProxy(options, loggingContext), getLogger(CheckerRetryDecorator.name)); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import { Checker } from '@stryker-mutator/api/check'; | ||
|
||
import { Resource } from '../concurrent'; | ||
|
||
export type CheckerResource = Checker & Resource; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import { CheckResult } from '@stryker-mutator/api/check'; | ||
import { Mutant } from '@stryker-mutator/api/core'; | ||
import { Logger } from '@stryker-mutator/api/logging'; | ||
|
||
import { ChildProcessCrashedError } from '../child-proxy/child-process-crashed-error'; | ||
import { OutOfMemoryError } from '../child-proxy/out-of-memory-error'; | ||
import { ResourceDecorator } from '../concurrent'; | ||
|
||
import { CheckerResource } from './checker-resource'; | ||
|
||
export class CheckerRetryDecorator extends ResourceDecorator<CheckerResource> implements CheckerResource { | ||
constructor(producer: () => CheckerResource, private readonly log: Logger) { | ||
super(producer); | ||
} | ||
|
||
public async check(mutant: Mutant): Promise<CheckResult> { | ||
try { | ||
return await this.innerResource.check(mutant); | ||
} catch (error) { | ||
if (error instanceof ChildProcessCrashedError) { | ||
if (error instanceof OutOfMemoryError) { | ||
this.log.warn(`Checker process [${error.pid}] ran out of memory. Retrying in a new process.`); | ||
} else { | ||
this.log.warn(`Checker process [${error.pid}] crashed with exit code ${error.exitCode}. Retrying in a new process.`, error); | ||
} | ||
await this.recover(); | ||
return this.innerResource.check(mutant); | ||
} else { | ||
throw error; //oops | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from './checker-factory'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
export * from './pool'; | ||
export * from './concurrency-token-provider'; | ||
export * from './resource-decorator'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import { Resource } from './pool'; | ||
|
||
export abstract class ResourceDecorator<T extends Resource> implements Resource { | ||
protected innerResource: T; | ||
|
||
constructor(private readonly producer: () => T) { | ||
this.innerResource = producer(); | ||
} | ||
|
||
public async init(): Promise<void> { | ||
await this.innerResource.init?.(); | ||
} | ||
|
||
public async dispose(): Promise<void> { | ||
await this.innerResource.dispose?.(); | ||
} | ||
/** | ||
* Disposes the current test runner and creates a new one | ||
* To be used in decorators that need recreation. | ||
*/ | ||
protected async recover(): Promise<void> { | ||
await this.dispose(); | ||
this.innerResource = this.producer(); | ||
return this.init(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.