Skip to content

Commit e4f5224

Browse files
feat(core): add maxCacheSize option to limit local artifact size (#29654)
<!-- Please make sure you have read the submission guidelines before posting an PR --> <!-- https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr --> <!-- Please make sure that your commit message follows our format --> <!-- Example: `fix(nx): must begin with lowercase` --> <!-- If this is a particularly complex change or feature addition, you can request a dedicated Nx release for this pull request branch. Mention someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they will confirm if the PR warrants its own release for testing purposes, and generate it for you if appropriate. --> ## Current Behavior Cache artifacts are removed based on age at a random interval. There is not a way to set a max size for the cache, so it can grow quite large in certain repos ## Expected Behavior Cache size can be controlled via `maxCacheSize` in `nx.json`. Cache artifacts are removed based on usage until the limit has been reached. ## Related Issue(s) <!-- Please link the issue being fixed so it gets closed when this is merged. --> Fixes # --------- Co-authored-by: FrozenPandaz <[email protected]>
1 parent c57932e commit e4f5224

File tree

13 files changed

+438
-46
lines changed

13 files changed

+438
-46
lines changed

Cargo.lock

Lines changed: 92 additions & 11 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/generated/devkit/NxJsonConfiguration.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ Nx.json configuration
2828
- [generators](../../devkit/documents/NxJsonConfiguration#generators): Object
2929
- [implicitDependencies](../../devkit/documents/NxJsonConfiguration#implicitdependencies): ImplicitDependencyEntry<T>
3030
- [installation](../../devkit/documents/NxJsonConfiguration#installation): NxInstallationConfiguration
31+
- [maxCacheSize](../../devkit/documents/NxJsonConfiguration#maxcachesize): string
3132
- [namedInputs](../../devkit/documents/NxJsonConfiguration#namedinputs): Object
3233
- [neverConnectToCloud](../../devkit/documents/NxJsonConfiguration#neverconnecttocloud): boolean
3334
- [nxCloudAccessToken](../../devkit/documents/NxJsonConfiguration#nxcloudaccesstoken): string
@@ -162,6 +163,14 @@ useful for workspaces that don't have a root package.json + node_modules.
162163

163164
---
164165

166+
### maxCacheSize
167+
168+
`Optional` **maxCacheSize**: `string`
169+
170+
Sets the maximum size of the local cache. Accepts a number followed by a unit (e.g. 100MB). Accepted units are B, KB, MB, and GB.
171+
172+
---
173+
165174
### namedInputs
166175

167176
`Optional` **namedInputs**: `Object`

docs/generated/devkit/Workspace.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ use ProjectsConfigurations or NxJsonConfiguration
2626
- [generators](../../devkit/documents/Workspace#generators): Object
2727
- [implicitDependencies](../../devkit/documents/Workspace#implicitdependencies): ImplicitDependencyEntry<string[] | "\*">
2828
- [installation](../../devkit/documents/Workspace#installation): NxInstallationConfiguration
29+
- [maxCacheSize](../../devkit/documents/Workspace#maxcachesize): string
2930
- [namedInputs](../../devkit/documents/Workspace#namedinputs): Object
3031
- [neverConnectToCloud](../../devkit/documents/Workspace#neverconnecttocloud): boolean
3132
- [nxCloudAccessToken](../../devkit/documents/Workspace#nxcloudaccesstoken): string
@@ -202,6 +203,18 @@ useful for workspaces that don't have a root package.json + node_modules.
202203

203204
---
204205

206+
### maxCacheSize
207+
208+
`Optional` **maxCacheSize**: `string`
209+
210+
Sets the maximum size of the local cache. Accepts a number followed by a unit (e.g. 100MB). Accepted units are B, KB, MB, and GB.
211+
212+
#### Inherited from
213+
214+
[NxJsonConfiguration](../../devkit/documents/NxJsonConfiguration).[maxCacheSize](../../devkit/documents/NxJsonConfiguration#maxcachesize)
215+
216+
---
217+
205218
### namedInputs
206219

207220
`Optional` **namedInputs**: `Object`

e2e/nx/src/cache.test.ts

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ import {
1111
updateFile,
1212
updateJson,
1313
} from '@nx/e2e/utils';
14+
15+
import { readdir, stat } from 'fs/promises';
16+
1417
import { join } from 'path';
1518

1619
describe('cache', () => {
@@ -361,6 +364,66 @@ describe('cache', () => {
361364
expect(fourthRun).toContain('read the output from the cache');
362365
}, 120000);
363366

367+
it('should evict cache if larger than max cache size', async () => {
368+
runCLI('reset');
369+
updateJson(`nx.json`, (c) => {
370+
c.maxCacheSize = '500KB';
371+
return c;
372+
});
373+
374+
const lib = uniq('cache-size');
375+
376+
updateFile(
377+
`tools/copy.js`,
378+
'require("fs").cpSync(process.argv[2], process.argv[3], { recursive: true, force: true });'
379+
);
380+
updateFile(
381+
`libs/${lib}/project.json`,
382+
JSON.stringify({
383+
name: lib,
384+
targets: {
385+
write: {
386+
cache: true,
387+
command: 'node tools/copy.js {projectRoot}/src dist/{projectRoot}',
388+
inputs: ['{projectRoot}/hash.txt'],
389+
outputs: ['{workspaceRoot}/dist/{projectRoot}'],
390+
},
391+
},
392+
})
393+
);
394+
// 100KB file
395+
updateFile(`libs/${lib}/src/data.txt`, 'a'.repeat(100 * 1024));
396+
for (let i = 0; i < 10; ++i) {
397+
updateFile(`libs/${lib}/hash.txt`, i.toString());
398+
runCLI(`write ${lib}`);
399+
}
400+
401+
// Expect that size of cache artifacts in cacheDir is less than 1MB
402+
const cacheDir = tmpProjPath('.nx/cache');
403+
const cacheFiles = listFiles('.nx/cache');
404+
let cacheEntries = 0;
405+
let cacheEntriesSize = 0;
406+
for (const file of cacheFiles) {
407+
if (file.match(/^[0-9]+$/)) {
408+
cacheEntries += 1;
409+
cacheEntriesSize += await dirSize(join(cacheDir, file));
410+
console.log(
411+
'Checked cache entry',
412+
file,
413+
'size',
414+
cacheEntriesSize,
415+
'total entries',
416+
cacheEntries
417+
);
418+
}
419+
}
420+
console.log('Cache entries:', cacheEntries);
421+
console.log('Cache size:', cacheEntriesSize);
422+
expect(cacheEntries).toBeGreaterThan(1);
423+
expect(cacheEntries).toBeLessThan(10);
424+
expect(cacheEntriesSize).toBeLessThanOrEqual(500 * 1024);
425+
});
426+
364427
function expectCached(
365428
actualOutput: string,
366429
expectedCachedProjects: string[]
@@ -404,3 +467,27 @@ describe('cache', () => {
404467
expect(matchingProjects).toEqual(expectedProjects);
405468
}
406469
});
470+
471+
const dirSize = async (dir) => {
472+
const files = await readdir(dir, { withFileTypes: true });
473+
474+
const paths = files.map(async (file) => {
475+
const path = join(dir, file.name);
476+
477+
if (file.isDirectory()) return await dirSize(path);
478+
479+
if (file.isFile()) {
480+
const { size } = await stat(path);
481+
482+
return size;
483+
}
484+
485+
console.log('Unknown file type', path);
486+
487+
return 0;
488+
});
489+
490+
return (await Promise.all(paths))
491+
.flat(Infinity)
492+
.reduce((i, size) => i + size, 0);
493+
};

packages/nx/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ swc_common = "0.31.16"
4343
swc_ecma_parser = { version = "0.137.1", features = ["typescript"] }
4444
swc_ecma_visit = "0.93.0"
4545
swc_ecma_ast = "0.107.0"
46-
46+
rand = "0.9.0"
4747
[target.'cfg(windows)'.dependencies]
4848
winapi = { version = "0.3", features = ["fileapi"] }
4949

packages/nx/src/adapter/compat.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ export const allowedWorkspaceExtensions = [
8282
'neverConnectToCloud',
8383
'sync',
8484
'useLegacyCache',
85+
'maxCacheSize',
8586
] as const;
8687

8788
if (!patched) {

packages/nx/src/config/nx-json.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -544,6 +544,11 @@ export interface NxJsonConfiguration<T = '*' | string[]> {
544544
* Use the legacy file system cache instead of the db cache
545545
*/
546546
useLegacyCache?: boolean;
547+
548+
/**
549+
* Sets the maximum size of the local cache. Accepts a number followed by a unit (e.g. 100MB). Accepted units are B, KB, MB, and GB.
550+
*/
551+
maxCacheSize?: string;
547552
}
548553

549554
export type PluginConfiguration = string | ExpandedPluginConfiguration;

0 commit comments

Comments
 (0)