feat(ngcc): allow async locking timeouts to be configured (#36838)
The commit adds support to the ngcc.config.js file for setting the `retryAttempts` and `retryDelay` options for the `AsyncLocker`. An integration test adds a new check for a timeout and actually uses the ngcc.config.js to reduce the timeout time to prevent the test from taking too long to complete. PR Close #36838
This commit is contained in:

committed by
Alex Rickabaugh

parent
98931bf9b5
commit
38f805cd06
@ -109,7 +109,7 @@ export function mainNgcc(options: NgccOptions): void|Promise<void> {
|
||||
const createTaskCompletedCallback =
|
||||
getCreateTaskCompletedCallback(pkgJsonUpdater, errorOnFailedEntryPoint, logger, fileSystem);
|
||||
const executor = getExecutor(
|
||||
async, inParallel, logger, fileWriter, pkgJsonUpdater, fileSystem,
|
||||
async, inParallel, logger, fileWriter, pkgJsonUpdater, fileSystem, config,
|
||||
createTaskCompletedCallback);
|
||||
|
||||
return executor.execute(analyzeEntryPoints, createCompileFn);
|
||||
@ -150,12 +150,13 @@ function getCreateTaskCompletedCallback(
|
||||
|
||||
function getExecutor(
|
||||
async: boolean, inParallel: boolean, logger: Logger, fileWriter: FileWriter,
|
||||
pkgJsonUpdater: PackageJsonUpdater, fileSystem: FileSystem,
|
||||
pkgJsonUpdater: PackageJsonUpdater, fileSystem: FileSystem, config: NgccConfiguration,
|
||||
createTaskCompletedCallback: CreateTaskCompletedCallback): Executor {
|
||||
const lockFile = new LockFileWithChildProcess(fileSystem, logger);
|
||||
if (async) {
|
||||
// Execute asynchronously (either serially or in parallel)
|
||||
const locker = new AsyncLocker(lockFile, logger, 500, 50);
|
||||
const {retryAttempts, retryDelay} = config.getLockingConfig();
|
||||
const locker = new AsyncLocker(lockFile, logger, retryDelay, retryAttempts);
|
||||
if (inParallel) {
|
||||
// Execute in parallel. Use up to 8 CPU cores for workers, always reserving one for master.
|
||||
const workerCount = Math.min(8, os.cpus().length - 1);
|
||||
|
@ -20,7 +20,27 @@ export interface NgccProjectConfig<T = NgccPackageConfig> {
|
||||
/**
|
||||
* The packages that are configured by this project config.
|
||||
*/
|
||||
packages: {[packagePath: string]: T};
|
||||
packages?: {[packagePath: string]: T};
|
||||
/**
|
||||
* Options that control how locking the process is handled.
|
||||
*/
|
||||
locking?: ProcessLockingConfiguration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Options that control how locking the process is handled.
|
||||
*/
|
||||
export interface ProcessLockingConfiguration {
|
||||
/**
|
||||
* The number of times the AsyncLocker will attempt to lock the process before failing.
|
||||
* Defaults to 50.
|
||||
*/
|
||||
retryAttempts?: number;
|
||||
/**
|
||||
* The number of milliseconds between attempts to lock the process.
|
||||
* Defaults to 500ms.
|
||||
* */
|
||||
retryDelay?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -126,12 +146,18 @@ export const DEFAULT_NGCC_CONFIG: NgccProjectConfig = {
|
||||
},
|
||||
},
|
||||
},
|
||||
locking: {
|
||||
retryDelay: 500,
|
||||
retryAttempts: 50,
|
||||
}
|
||||
};
|
||||
|
||||
interface VersionedPackageConfig extends NgccPackageConfig {
|
||||
versionRange: string;
|
||||
}
|
||||
|
||||
type ProcessedConfig = Required<NgccProjectConfig<VersionedPackageConfig[]>>;
|
||||
|
||||
const NGCC_CONFIG_FILENAME = 'ngcc.config.js';
|
||||
|
||||
/**
|
||||
@ -159,8 +185,8 @@ const NGCC_CONFIG_FILENAME = 'ngcc.config.js';
|
||||
* configuration for a package is returned.
|
||||
*/
|
||||
export class NgccConfiguration {
|
||||
private defaultConfig: NgccProjectConfig<VersionedPackageConfig[]>;
|
||||
private projectConfig: NgccProjectConfig<VersionedPackageConfig[]>;
|
||||
private defaultConfig: ProcessedConfig;
|
||||
private projectConfig: ProcessedConfig;
|
||||
private cache = new Map<string, VersionedPackageConfig>();
|
||||
readonly hash: string;
|
||||
|
||||
@ -170,6 +196,20 @@ export class NgccConfiguration {
|
||||
this.hash = this.computeHash();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the configuration options for locking the ngcc process.
|
||||
*/
|
||||
getLockingConfig(): Required<ProcessLockingConfiguration> {
|
||||
let {retryAttempts, retryDelay} = this.projectConfig.locking;
|
||||
if (retryAttempts === undefined) {
|
||||
retryAttempts = this.defaultConfig.locking.retryAttempts!;
|
||||
}
|
||||
if (retryDelay === undefined) {
|
||||
retryDelay = this.defaultConfig.locking.retryDelay!;
|
||||
}
|
||||
return {retryAttempts, retryDelay};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a configuration for the given `version` of a package at `packagePath`.
|
||||
*
|
||||
@ -183,8 +223,9 @@ export class NgccConfiguration {
|
||||
return this.cache.get(cacheKey)!;
|
||||
}
|
||||
|
||||
const projectLevelConfig =
|
||||
findSatisfactoryVersion(this.projectConfig.packages[packagePath], version);
|
||||
const projectLevelConfig = this.projectConfig.packages ?
|
||||
findSatisfactoryVersion(this.projectConfig.packages[packagePath], version) :
|
||||
null;
|
||||
if (projectLevelConfig !== null) {
|
||||
this.cache.set(cacheKey, projectLevelConfig);
|
||||
return projectLevelConfig;
|
||||
@ -196,8 +237,9 @@ export class NgccConfiguration {
|
||||
return packageLevelConfig;
|
||||
}
|
||||
|
||||
const defaultLevelConfig =
|
||||
findSatisfactoryVersion(this.defaultConfig.packages[packagePath], version);
|
||||
const defaultLevelConfig = this.defaultConfig.packages ?
|
||||
findSatisfactoryVersion(this.defaultConfig.packages[packagePath], version) :
|
||||
null;
|
||||
if (defaultLevelConfig !== null) {
|
||||
this.cache.set(cacheKey, defaultLevelConfig);
|
||||
return defaultLevelConfig;
|
||||
@ -207,8 +249,15 @@ export class NgccConfiguration {
|
||||
}
|
||||
|
||||
private processProjectConfig(baseDir: AbsoluteFsPath, projectConfig: NgccProjectConfig):
|
||||
NgccProjectConfig<VersionedPackageConfig[]> {
|
||||
const processedConfig: NgccProjectConfig<VersionedPackageConfig[]> = {packages: {}};
|
||||
ProcessedConfig {
|
||||
const processedConfig: ProcessedConfig = {packages: {}, locking: {}};
|
||||
|
||||
// locking configuration
|
||||
if (projectConfig.locking !== undefined) {
|
||||
processedConfig.locking = projectConfig.locking;
|
||||
}
|
||||
|
||||
// packages configuration
|
||||
for (const packagePathAndVersion in projectConfig.packages) {
|
||||
const packageConfig = projectConfig.packages[packagePathAndVersion];
|
||||
if (packageConfig) {
|
||||
@ -220,6 +269,7 @@ export class NgccConfiguration {
|
||||
{...packageConfig, versionRange, entryPoints});
|
||||
}
|
||||
}
|
||||
|
||||
return processedConfig;
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,7 @@ import {createHash} from 'crypto';
|
||||
import {absoluteFrom, FileSystem, getFileSystem} from '../../../src/ngtsc/file_system';
|
||||
import {runInEachFileSystem} from '../../../src/ngtsc/file_system/testing';
|
||||
import {loadTestFiles} from '../../../test/helpers';
|
||||
import {DEFAULT_NGCC_CONFIG, NgccConfiguration} from '../../src/packages/configuration';
|
||||
import {DEFAULT_NGCC_CONFIG, NgccConfiguration, ProcessLockingConfiguration} from '../../src/packages/configuration';
|
||||
|
||||
|
||||
runInEachFileSystem(() => {
|
||||
@ -51,7 +51,7 @@ runInEachFileSystem(() => {
|
||||
}]);
|
||||
const project1Conf = new NgccConfiguration(fs, project1);
|
||||
const expectedProject1Config = `{"packages":{"${project1Package1}":[{"entryPoints":{"${
|
||||
project1Package1EntryPoint1}":{}},"versionRange":"*"}]}}`;
|
||||
project1Package1EntryPoint1}":{}},"versionRange":"*"}]},"locking":{}}`;
|
||||
expect(project1Conf.hash)
|
||||
.toEqual(createHash('md5').update(expectedProject1Config).digest('hex'));
|
||||
|
||||
@ -72,7 +72,7 @@ runInEachFileSystem(() => {
|
||||
}]);
|
||||
const project2Conf = new NgccConfiguration(fs, project2);
|
||||
const expectedProject2Config = `{"packages":{"${project2Package1}":[{"entryPoints":{"${
|
||||
project2Package1EntryPoint1}":{"ignore":true}},"versionRange":"*"}]}}`;
|
||||
project2Package1EntryPoint1}":{"ignore":true}},"versionRange":"*"}]},"locking":{}}`;
|
||||
expect(project2Conf.hash)
|
||||
.toEqual(createHash('md5').update(expectedProject2Config).digest('hex'));
|
||||
});
|
||||
@ -80,7 +80,10 @@ runInEachFileSystem(() => {
|
||||
it('should compute a hash even if there is no project configuration', () => {
|
||||
loadTestFiles([{name: _Abs('/project-1/empty.js'), contents: ``}]);
|
||||
const configuration = new NgccConfiguration(fs, _Abs('/project-1'));
|
||||
expect(configuration.hash).toEqual('87c535c3ce0eac2a54c246892e0e21a1');
|
||||
expect(configuration.hash)
|
||||
.toEqual(createHash('md5')
|
||||
.update(JSON.stringify({packages: {}, locking: {}}))
|
||||
.digest('hex'));
|
||||
});
|
||||
});
|
||||
|
||||
@ -589,6 +592,61 @@ runInEachFileSystem(() => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getLockingConfig()', () => {
|
||||
let originalDefaultConfig: ProcessLockingConfiguration|undefined;
|
||||
beforeEach(() => {
|
||||
originalDefaultConfig = DEFAULT_NGCC_CONFIG.locking;
|
||||
DEFAULT_NGCC_CONFIG.locking = {retryAttempts: 17, retryDelay: 400};
|
||||
});
|
||||
afterEach(() => DEFAULT_NGCC_CONFIG.locking = originalDefaultConfig);
|
||||
|
||||
it('should return configuration for locking found in a project level file', () => {
|
||||
loadTestFiles([{
|
||||
name: _Abs('/project-1/ngcc.config.js'),
|
||||
contents: `
|
||||
module.exports = {
|
||||
locking: {
|
||||
retryAttempts: 4,
|
||||
retryDelay: 56,
|
||||
},
|
||||
};`
|
||||
}]);
|
||||
const configuration = new NgccConfiguration(fs, _Abs('/project-1'));
|
||||
const config = configuration.getLockingConfig();
|
||||
expect(config).toEqual({
|
||||
retryAttempts: 4,
|
||||
retryDelay: 56,
|
||||
});
|
||||
});
|
||||
|
||||
it('should return configuration for locking partially found in a project level file', () => {
|
||||
loadTestFiles([{
|
||||
name: _Abs('/project-1/ngcc.config.js'),
|
||||
contents: `
|
||||
module.exports = {
|
||||
locking: {
|
||||
retryAttempts: 4,
|
||||
},
|
||||
};`
|
||||
}]);
|
||||
const configuration = new NgccConfiguration(fs, _Abs('/project-1'));
|
||||
const config = configuration.getLockingConfig();
|
||||
expect(config).toEqual({
|
||||
retryAttempts: 4,
|
||||
retryDelay: 400,
|
||||
});
|
||||
});
|
||||
|
||||
it('should return default configuration for locking if no project level file', () => {
|
||||
const configuration = new NgccConfiguration(fs, _Abs('/project-1'));
|
||||
const config = configuration.getLockingConfig();
|
||||
expect(config).toEqual({
|
||||
retryAttempts: 17,
|
||||
retryDelay: 400,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function packageWithConfigFiles(
|
||||
|
Reference in New Issue
Block a user