Skip to content

Commit

Permalink
Feature: cache path option (#1115)
Browse files Browse the repository at this point in the history
  • Loading branch information
emmercm committed May 8, 2024
1 parent a132387 commit 822d001
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 18 deletions.
8 changes: 8 additions & 0 deletions src/igir.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,18 @@ export default class Igir {
this.logger.trace('Windows has symlink permissions');
}

// File cache options
if (this.options.getDisableCache()) {
this.logger.trace('disabling the file cache');
FileCache.disable();
}
const cachePath = this.options.getCachePath() ?? Constants.GLOBAL_CACHE_FILE;
if (cachePath !== undefined) {
this.logger.trace(`setting the file cache path to '${cachePath}'`);
FileCache.loadFile(cachePath);
} else {
this.logger.trace('not using a file for the file cache');
}

// Scan and process input files
let dats = await this.processDATScanner();
Expand Down
11 changes: 10 additions & 1 deletion src/modules/argumentsParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -733,6 +733,7 @@ export default class ArgumentsParser {
group: groupReport,
description: 'Report output location (formatted with moment.js)',
type: 'string',
coerce: ArgumentsParser.getLastValue, // don't allow string[] values
requiresArg: true,
default: `./${Constants.COMMAND_NAME}_%YYYY-%MM-%DDT%HH:%mm:%ss.csv`,
})
Expand Down Expand Up @@ -777,9 +778,17 @@ export default class ArgumentsParser {
})
.option('disable-cache', {
group: groupHelpDebug,
description: 'Disable the file and archive entry checksum cache',
description: 'Disable the file checksum cache',
type: 'boolean',
})
.option('cache-path', {
group: groupHelpDebug,
description: 'Location for the file checksum cache',
type: 'string',
coerce: ArgumentsParser.getLastValue, // don't allow string[] values
requiresArg: true,
conflicts: ['disable-cache'],
})
.option('verbose', {
group: groupHelpDebug,
alias: 'v',
Expand Down
37 changes: 20 additions & 17 deletions src/types/files/fileCache.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { Stats } from 'node:fs';

import Constants from '../../constants.js';
import FsPoly from '../../polyfill/fsPoly.js';
import Cache from '../cache.js';
import Archive from './archives/archive.js';
Expand All @@ -22,27 +21,31 @@ enum ValueType {
export default class FileCache {
private static readonly VERSION = 2;

private static readonly CACHE = new Cache<CacheValue>({
filePath: process.env.NODE_ENV !== 'test' ? Constants.GLOBAL_CACHE_FILE : undefined,
fileFlushMillis: 30_000,
})
.load()
.then(async (cache) => {
// Delete keys from old cache versions
await Promise.all([...Array.from({ length: FileCache.VERSION }).keys()].slice(1)
.map(async (prevVersion) => {
const keyRegex = new RegExp(`^V${prevVersion}\\|`);
return cache.delete(keyRegex);
}));
return cache;
});
private static cache: Cache<CacheValue> | Promise<Cache<CacheValue>> = new Cache<CacheValue>();

private static enabled = true;

public static disable(): void {
this.enabled = false;
}

public static loadFile(filePath: string): void {
this.cache = new Cache<CacheValue>({
filePath,
fileFlushMillis: 30_000,
})
.load()
.then(async (cache) => {
// Delete keys from old cache versions
await Promise.all([...Array.from({ length: FileCache.VERSION }).keys()].slice(1)
.map(async (prevVersion) => {
const keyRegex = new RegExp(`^V${prevVersion}\\|`);
return cache.delete(keyRegex);
}));
return cache;
});
}

static async getOrComputeFile(
filePath: string,
checksumBitmask: number,
Expand All @@ -58,7 +61,7 @@ export default class FileCache {
// NOTE(cemmer): we're using the cache as a mutex here, so even if this function is called
// multiple times concurrently, entries will only be fetched once.
let computedFile: File | undefined;
const cachedValue = await (await this.CACHE).getOrCompute(
const cachedValue = await (await this.cache).getOrCompute(
cacheKey,
async () => {
computedFile = await File.fileOf({ filePath }, checksumBitmask);
Expand Down Expand Up @@ -111,7 +114,7 @@ export default class FileCache {
// NOTE(cemmer): we're using the cache as a mutex here, so even if this function is called
// multiple times concurrently, entries will only be fetched once.
let computedEntries: ArchiveEntry<T>[] | undefined;
const cachedValue = await (await this.CACHE).getOrCompute(
const cachedValue = await (await this.cache).getOrCompute(
cacheKey,
async () => {
computedEntries = await archive.getArchiveEntries(checksumBitmask) as ArchiveEntry<T>[];
Expand Down
8 changes: 8 additions & 0 deletions src/types/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ export interface OptionsProps {
readonly writerThreads?: number,
readonly writeRetry?: number,
readonly disableCache?: boolean,
readonly cachePath?: string,
readonly verbose?: number,
readonly help?: boolean,
}
Expand Down Expand Up @@ -337,6 +338,8 @@ export default class Options implements OptionsProps {

readonly disableCache: boolean;

readonly cachePath?: string;

readonly verbose: number;

readonly help: boolean;
Expand Down Expand Up @@ -444,6 +447,7 @@ export default class Options implements OptionsProps {
this.writerThreads = Math.max(options?.writerThreads ?? 0, 1);
this.writeRetry = Math.max(options?.writeRetry ?? 0, 0);
this.disableCache = options?.disableCache ?? false;
this.cachePath = options?.cachePath;
this.verbose = options?.verbose ?? 0;
this.help = options?.help ?? false;
}
Expand Down Expand Up @@ -1175,6 +1179,10 @@ export default class Options implements OptionsProps {
return this.disableCache;
}

getCachePath(): string | undefined {
return this.cachePath;
}

getLogLevel(): LogLevel {
if (this.verbose === 1) {
return LogLevel.INFO;
Expand Down
8 changes: 8 additions & 0 deletions test/modules/argumentsParser.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ describe('options', () => {
expect(options.getReaderThreads()).toEqual(8);
expect(options.getWriterThreads()).toEqual(4);
expect(options.getDisableCache()).toEqual(false);
expect(options.getCachePath()).toBeUndefined();
expect(options.getLogLevel()).toEqual(LogLevel.WARN);
expect(options.getHelp()).toEqual(false);
});
Expand Down Expand Up @@ -1169,6 +1170,13 @@ describe('options', () => {
expect(argumentsParser.parse([...dummyCommandAndRequiredArgs, '--disable-cache', 'true', '--disable-cache', 'false']).getDisableCache()).toEqual(false);
});

it('should parse "cache-path"', () => {
expect(argumentsParser.parse([...dummyCommandAndRequiredArgs]).getCachePath()).toBeUndefined();
expect(argumentsParser.parse([...dummyCommandAndRequiredArgs, '--cache-path', os.devNull]).getCachePath()).toEqual(os.devNull);
expect(argumentsParser.parse([...dummyCommandAndRequiredArgs, '--cache-path', os.devNull, '--cache-path', 'igir.cache']).getCachePath()).toEqual('igir.cache');
expect(() => argumentsParser.parse([...dummyCommandAndRequiredArgs, '--disable-cache', '--cache-path', os.devNull])).toThrow(/mutually exclusive/i);
});

it('should parse "verbose"', () => {
expect(argumentsParser.parse(dummyCommandAndRequiredArgs).getLogLevel()).toEqual(LogLevel.WARN);
expect(argumentsParser.parse([...dummyCommandAndRequiredArgs, '-v']).getLogLevel()).toEqual(LogLevel.INFO);
Expand Down

0 comments on commit 822d001

Please sign in to comment.