forked from npm/cli
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix: limit packument cache size based on heap size
This adds a new packument cache that is an instance of `lru-cache`. It uses that package's ability to limit content based on size, and has some multipliers based on research to mostly correctly approximate the correlation between packument size and its memory usage. It also limits the total size of the cache based on the actual heap available. Closes: npm#7276 Related: npm/pacote#369
- Loading branch information
Showing
10 changed files
with
113 additions
and
14 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
const { LRUCache } = require('lru-cache') | ||
const { getHeapStatistics } = require('node:v8') | ||
const { log } = require('proc-log') | ||
|
||
// This is an in-memory cache that Pacote uses for packuments. | ||
// Packuments are usually cached on disk. This allows for rapid re-requests | ||
// of the same packument to bypass disk reads. The tradeoff here is memory | ||
// usage for disk reads. | ||
class PackumentCache extends LRUCache { | ||
static #heapLimit = Math.floor(getHeapStatistics().heap_size_limit) | ||
|
||
#sizeKey | ||
#disposed = new Set() | ||
|
||
#log (...args) { | ||
log.silly('packumentCache', ...args) | ||
} | ||
|
||
constructor ({ | ||
// How much of this.#heapLimit to take up | ||
heapFactor = 0.25, | ||
// How much of this.#maxSize we allow any one packument to take up | ||
// Anything over this is not cached | ||
maxEntryFactor = 0.5, | ||
sizeKey = '_contentLength', | ||
} = {}) { | ||
const maxSize = Math.floor(PackumentCache.#heapLimit * heapFactor) | ||
const maxEntrySize = Math.floor(maxSize * maxEntryFactor) | ||
super({ | ||
maxSize, | ||
maxEntrySize, | ||
sizeCalculation: (p) => { | ||
// Don't cache if we dont know the size | ||
// Some versions of pacote set this to `0`, newer versions set it to `null` | ||
if (!p[sizeKey]) { | ||
return maxEntrySize + 1 | ||
} | ||
if (p[sizeKey] < 10_000) { | ||
return p[sizeKey] * 2 | ||
} | ||
if (p[sizeKey] < 1_000_000) { | ||
return Math.floor(p[sizeKey] * 1.5) | ||
} | ||
// It is less beneficial to store a small amount of super large things | ||
// at the cost of all other packuments. | ||
return maxEntrySize + 1 | ||
}, | ||
dispose: (v, k) => { | ||
this.#disposed.add(k) | ||
this.#log(k, 'dispose') | ||
}, | ||
}) | ||
this.#sizeKey = sizeKey | ||
this.#log(`heap:${PackumentCache.#heapLimit} maxSize:${maxSize} maxEntrySize:${maxEntrySize}`) | ||
} | ||
|
||
set (k, v, ...args) { | ||
// we use disposed only for a logging signal if we are setting packuments that | ||
// have already been evicted from the cache previously. logging here could help | ||
// us tune this in the future. | ||
const disposed = this.#disposed.has(k) | ||
/* istanbul ignore next - this doesnt happen consistently so hard to test without resorting to unit tests */ | ||
if (disposed) { | ||
this.#disposed.delete(k) | ||
} | ||
this.#log(k, 'set', `size:${v[this.#sizeKey]} disposed:${disposed}`) | ||
return super.set(k, v, ...args) | ||
} | ||
|
||
has (k, ...args) { | ||
const has = super.has(k, ...args) | ||
this.#log(k, `cache-${has ? 'hit' : 'miss'}`) | ||
return has | ||
} | ||
} | ||
|
||
module.exports = PackumentCache |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -197,7 +197,7 @@ t.test('verify dep flags in script environments', async t => { | |
const file = resolve(path, 'node_modules', pkg, 'env') | ||
t.strictSame(flags, fs.readFileSync(file, 'utf8').split('\n'), pkg) | ||
} | ||
t.strictSame(checkLogs().sort((a, b) => | ||
t.strictSame(checkLogs().filter(l => l[0] === 'info' && l[1] === 'run').sort((a, b) => | ||
localeCompare(a[2], b[2]) || (typeof a[4] === 'string' ? -1 : 1)), [ | ||
['info', 'run', '[email protected]', 'postinstall', 'node_modules/devdep', 'node ../../env.js'], | ||
['info', 'run', '[email protected]', 'postinstall', { code: 0, signal: null }], | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters