feat(core): remove old cache records

This commit is contained in:
Victor Savkin 2020-01-09 18:59:18 -05:00 committed by Victor Savkin
parent dddc1b1e6c
commit f84c55d5aa
4 changed files with 106 additions and 5 deletions

View File

@ -4,7 +4,9 @@ import { findWorkspaceRoot } from './find-workspace-root';
const workspace = findWorkspaceRoot(process.cwd()); const workspace = findWorkspaceRoot(process.cwd());
setUpOutputWatching(); if (process.env.NX_TERMINAL_OUTPUT_PATH) {
setUpOutputWatching();
}
requireCli(); requireCli();
function requireCli() { function requireCli() {

View File

@ -7,6 +7,7 @@ import { join } from 'path';
import { Hasher } from './hasher'; import { Hasher } from './hasher';
import * as fsExtra from 'fs-extra'; import * as fsExtra from 'fs-extra';
import { DefaultTasksRunnerOptions } from './tasks-runner-v2'; import { DefaultTasksRunnerOptions } from './tasks-runner-v2';
import { fork, spawn } from 'child_process';
export type CachedResult = { terminalOutput: string; outputsPath: string }; export type CachedResult = { terminalOutput: string; outputsPath: string };
export type TaskWithCachedResult = { task: Task; cachedResult: CachedResult }; export type TaskWithCachedResult = { task: Task; cachedResult: CachedResult };
@ -40,6 +41,35 @@ export class Cache {
private readonly options: DefaultTasksRunnerOptions private readonly options: DefaultTasksRunnerOptions
) {} ) {}
removeOldCacheRecords() {
/**
* Even though spawning a process is fast, we don't want to do it every time
* the user runs a command. Instead, we want to do it once in a while.
*/
const shouldSpawnProcess = Math.floor(Math.random() * 50) === 1;
if (shouldSpawnProcess) {
const scriptPath = join(
this.root,
'node_modules',
'@nrwl',
'workspace',
'src',
'tasks-runner',
'remove-old-cache-records.js'
);
try {
const p = spawn('node', [`"${scriptPath}"`, `"${this.cachePath}"`], {
stdio: 'ignore',
detached: true
});
p.unref();
} catch (e) {
console.log(`Unable to start remove-old-cache-records script:`);
console.log(e.message);
}
}
}
async get(task: Task): Promise<CachedResult> { async get(task: Task): Promise<CachedResult> {
if (!this.cacheConfig.isCacheableTask(task)) return null; if (!this.cacheConfig.isCacheableTask(task)) return null;
@ -60,7 +90,6 @@ export class Cache {
} }
async put(task: Task, terminalOutputPath: string, folders: string[]) { async put(task: Task, terminalOutputPath: string, folders: string[]) {
if (!this.cacheConfig.isCacheableTask(task)) return;
const terminalOutput = readFileSync(terminalOutputPath).toString(); const terminalOutput = readFileSync(terminalOutputPath).toString();
const hash = await this.hasher.hash(task); const hash = await this.hasher.hash(task);
const td = join(this.cachePath, hash); const td = join(this.cachePath, hash);
@ -115,7 +144,11 @@ export class Cache {
} }
async temporaryOutputPath(task: Task) { async temporaryOutputPath(task: Task) {
return join(this.terminalOutputsDir, await this.hasher.hash(task)); if (this.cacheConfig.isCacheableTask(task)) {
return join(this.terminalOutputsDir, await this.hasher.hash(task));
} else {
return null;
}
} }
private async getFromLocalDir(task: Task) { private async getFromLocalDir(task: Task) {

View File

@ -0,0 +1,61 @@
import * as fs from 'fs';
import * as fsExtra from 'fs-extra';
import * as path from 'path';
const WEEK_IN_MS = 1000 * 60 * 60 * 24 * 7;
const folder = process.argv[2];
removeOld(terminalOutputs());
removeOld(cachedFiles());
function terminalOutputs() {
try {
return fs.readdirSync(path.join(folder, 'terminalOutputs'));
} catch (e) {
return [];
}
}
function cachedFiles() {
try {
return fs.readdirSync(folder).filter(f => !f.endsWith('terminalOutputs'));
} catch (e) {
return [];
}
}
function removeOld(records: string[]) {
try {
const time = mostRecentMTime(records);
records.forEach(r => {
const child = path.join(folder, r);
try {
const s = fs.statSync(child);
if (time - s.mtimeMs > WEEK_IN_MS) {
if (s.isDirectory()) {
try {
fsExtra.removeSync(`${child}.commit`);
} catch (e) {}
}
fsExtra.removeSync(child);
}
} catch (e) {}
});
} catch (e) {}
}
function mostRecentMTime(records: string[]) {
let mostRecentTime = 0;
records.forEach(r => {
const child = path.join(folder, r);
try {
const s = fs.statSync(child);
if (s.mtimeMs > mostRecentTime) {
mostRecentTime = s.mtimeMs;
}
} catch (e) {}
});
return mostRecentTime;
}

View File

@ -28,6 +28,7 @@ export class TaskOrchestrator {
const r1 = await this.applyCachedResults(cached); const r1 = await this.applyCachedResults(cached);
const r2 = await this.runRest(rest); const r2 = await this.runRest(rest);
this.cache.removeOldCacheRecords();
return [...r1, ...r2]; return [...r1, ...r2];
} }
@ -109,12 +110,16 @@ export class TaskOrchestrator {
return this.cache.temporaryOutputPath(task).then(outputPath => { return this.cache.temporaryOutputPath(task).then(outputPath => {
return new Promise((res, rej) => { return new Promise((res, rej) => {
try { try {
const env = { ...process.env };
if (outputPath) {
env.NX_TERMINAL_OUTPUT_PATH = outputPath;
}
const p = fork(this.getCommand(), this.getCommandArgs(task), { const p = fork(this.getCommand(), this.getCommandArgs(task), {
stdio: ['inherit', 'inherit', 'inherit', 'ipc'], stdio: ['inherit', 'inherit', 'inherit', 'ipc'],
env: { ...process.env, NX_TERMINAL_OUTPUT_PATH: outputPath } env
}); });
p.on('close', code => { p.on('close', code => {
if (code === 0) { if (outputPath && code === 0) {
this.cache.put(task, outputPath, taskOutputs).then(() => { this.cache.put(task, outputPath, taskOutputs).then(() => {
res(code); res(code);
}); });