packager: TransformCache: store last cache collection time

Summary: Oftentimes packager stalls for several seconds after requiring a bundle, because it's busy cleaning up old cache files. We shouldn't do that when, for example, we just restarted packager. This changeset stores the micro-timestamp of the last collection so that we don't waste time next time.

Reviewed By: cpojer

Differential Revision: D4969832

fbshipit-source-id: 3aa0495b18d5a8efa1b8f87dbf7a40b0673fd9fd
This commit is contained in:
Jean Lauliac 2017-04-28 09:17:58 -07:00 committed by Facebook Github Bot
parent 2818bf2347
commit e379a077ce
1 changed files with 56 additions and 25 deletions

View File

@ -20,8 +20,6 @@ const rimraf = require('rimraf');
const terminal = require('../lib/terminal');
const writeFileAtomicSync = require('write-file-atomic').sync;
const CACHE_NAME = 'react-native-packager-cache';
import type {Options as TransformOptions} from '../JSTransformer/worker/worker';
import type {SourceMap} from './SourceMap';
import type {Reporter} from './reporting';
@ -29,6 +27,9 @@ import type {Reporter} from './reporting';
type CacheFilePaths = {transformedCode: string, metadata: string};
export type GetTransformCacheKey = (sourceCode: string, filename: string, options: {}) => string;
const CACHE_NAME = 'react-native-packager-cache';
const CACHE_SUB_DIR = 'cache';
/**
* If packager is running for two different directories, we don't want the
* caches to conflict with each other. `__dirname` carries that because packager
@ -84,7 +85,7 @@ function getCacheFilePaths(props: {
const hash = hasher.digest('hex');
const prefix = hash.substr(0, 2);
const fileName = `${hash.substr(2)}`;
const base = path.join(getCacheDirPath(), prefix, fileName);
const base = path.join(getCacheDirPath(), CACHE_SUB_DIR, prefix, fileName);
return {transformedCode: base, metadata: base + '.meta'};
}
@ -181,34 +182,13 @@ const GARBAGE_COLLECTOR = new (class GarbageCollector {
this._cacheWasReset = false;
}
_collectSync() {
const cacheDirPath = getCacheDirPath();
mkdirp.sync(cacheDirPath);
const prefixDirs = fs.readdirSync(cacheDirPath);
for (let i = 0; i < prefixDirs.length; ++i) {
const prefixDir = path.join(cacheDirPath, prefixDirs[i]);
const cacheFileNames = fs.readdirSync(prefixDir);
for (let j = 0; j < cacheFileNames.length; ++j) {
const cacheFilePath = path.join(prefixDir, cacheFileNames[j]);
const stats = fs.lstatSync(cacheFilePath);
const timeSinceLastAccess = Date.now() - stats.atime.getTime();
if (
stats.isFile() &&
timeSinceLastAccess > CACHE_FILE_MAX_LAST_ACCESS_TIME
) {
fs.unlinkSync(cacheFilePath);
}
}
}
}
/**
* We want to avoid preventing tool use if the cleanup fails for some reason,
* but still provide some chance for people to report/fix things.
*/
_collectSyncNoThrow() {
try {
this._collectSync();
collectCacheIfOldSync();
} catch (error) {
terminal.log(error.stack);
terminal.log(
@ -242,6 +222,57 @@ const GARBAGE_COLLECTOR = new (class GarbageCollector {
})();
/**
* When restarting packager we want to avoid running the collection over again, so we store
* the last collection time in a file and we check that first.
*/
function collectCacheIfOldSync() {
const cacheDirPath = getCacheDirPath();
mkdirp.sync(cacheDirPath);
const cacheCollectionFilePath = path.join(cacheDirPath, 'last_collected');
const lastCollected = Number.parseInt(tryReadFileSync(cacheCollectionFilePath, 'utf8'), 10);
if (Number.isInteger(lastCollected) && Date.now() - lastCollected > GARBAGE_COLLECTION_PERIOD) {
return;
}
const effectiveCacheDirPath = path.join(cacheDirPath, CACHE_SUB_DIR);
mkdirp.sync(effectiveCacheDirPath);
collectCacheSync(effectiveCacheDirPath);
fs.writeFileSync(cacheCollectionFilePath, Date.now().toString());
}
/**
* Remove all the cache files from the specified folder that are older than a certain duration.
*/
function collectCacheSync(dirPath: string) {
const prefixDirs = fs.readdirSync(dirPath);
for (let i = 0; i < prefixDirs.length; ++i) {
const prefixDir = path.join(dirPath, prefixDirs[i]);
const cacheFileNames = fs.readdirSync(prefixDir);
for (let j = 0; j < cacheFileNames.length; ++j) {
const cacheFilePath = path.join(prefixDir, cacheFileNames[j]);
const stats = fs.lstatSync(cacheFilePath);
const timeSinceLastAccess = Date.now() - stats.atime.getTime();
if (
stats.isFile() &&
timeSinceLastAccess > CACHE_FILE_MAX_LAST_ACCESS_TIME
) {
fs.unlinkSync(cacheFilePath);
}
}
}
}
function tryReadFileSync(filePath: string): string {
try {
return fs.readFileSync(filePath, 'utf8');
} catch (error) {
if (error.code !== 'ENOENT') {
throw error;
}
return '';
}
}
function readMetadataFileSync(
metadataFilePath: string,
): ?{