Skip to content

Commit b77a47e

Browse files
committed
Delegate retry logic to the external library - @gar/promise-retry
1 parent 862f80b commit b77a47e

4 files changed

Lines changed: 9 additions & 28 deletions

File tree

DEPENDENCIES.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -532,6 +532,7 @@ graph LR;
532532
npmcli-arborist-->bin-links;
533533
npmcli-arborist-->cacache;
534534
npmcli-arborist-->common-ancestor-path;
535+
npmcli-arborist-->gar-promise-retry["@gar/promise-retry"];
535536
npmcli-arborist-->hosted-git-info;
536537
npmcli-arborist-->isaacs-string-locale-compare["@isaacs/string-locale-compare"];
537538
npmcli-arborist-->json-stringify-nice;

package-lock.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14440,6 +14440,7 @@
1444014440
"version": "9.4.0",
1444114441
"license": "ISC",
1444214442
"dependencies": {
14443+
"@gar/promise-retry": "^1.0.0",
1444314444
"@isaacs/string-locale-compare": "^1.1.0",
1444414445
"@npmcli/fs": "^5.0.0",
1444514446
"@npmcli/installed-package-contents": "^4.0.0",

workspaces/arborist/lib/arborist/rebuild.js

Lines changed: 6 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -9,36 +9,13 @@ const runScript = require('@npmcli/run-script')
99
const { callLimit: promiseCallLimit } = require('promise-call-limit')
1010
const { depth: dfwalk } = require('treeverse')
1111
const { isNodeGypPackage, defaultGypInstallScript } = require('@npmcli/node-gyp')
12+
const { promiseRetry } = require('@gar/promise-retry')
1213
const { log, time } = require('proc-log')
1314
const { resolve } = require('node:path')
1415

1516
const boolEnv = b => b ? '1' : ''
1617
const sortNodes = (a, b) => (a.depth - b.depth) || localeCompare(a.path, b.path)
1718

18-
// On Windows, antivirus/indexer can transiently lock files, causing
19-
// EPERM/EACCES/EBUSY on the rename inside write-file-atomic (used by
20-
// bin-links/fix-bin.js). Retry with backoff for up to ~7.5 seconds.
21-
/* istanbul ignore next */
22-
const retryBinLinks = async (binLinks, node, opts, retries = 4) => {
23-
const delay = (5 - retries) * 500
24-
await new Promise(r => setTimeout(r, delay))
25-
try {
26-
return await binLinks({
27-
pkg: node.package,
28-
path: node.path,
29-
top: !!(node.isTop || node.globalTop),
30-
force: opts.force,
31-
global: !!node.globalTop,
32-
})
33-
} catch (err) {
34-
if (retries > 0 &&
35-
(err.code === 'EPERM' || err.code === 'EACCES' || err.code === 'EBUSY')) {
36-
return retryBinLinks(binLinks, node, opts, retries - 1)
37-
}
38-
throw err
39-
}
40-
}
41-
4219
const _checkBins = Symbol.for('checkBins')
4320

4421
// defined by reify mixin
@@ -405,19 +382,20 @@ module.exports = cls => class Builder extends cls {
405382

406383
const timeEnd = time.start(`build:link:${node.location}`)
407384

408-
const p = binLinks({
385+
// On Windows, antivirus/indexer can transiently lock files, causing EPERM/EACCES/EBUSY on the rename inside write-file-atomic (used by bin-links/fix-bin.js), so, retry with backoff.
386+
const p = promiseRetry((retry) => binLinks({
409387
pkg: node.package,
410388
path: node.path,
411389
top: !!(node.isTop || node.globalTop),
412390
force: this.options.force,
413391
global: !!node.globalTop,
414-
}).catch(/* istanbul ignore next - Windows retry for antivirus locks */ err => {
392+
}).catch(/* istanbul ignore next - Windows-only transient antivirus locks */ err => {
415393
if (process.platform === 'win32' &&
416394
(err.code === 'EPERM' || err.code === 'EACCES' || err.code === 'EBUSY')) {
417-
return retryBinLinks(binLinks, node, this.options)
395+
return retry(err)
418396
}
419397
throw err
420-
})
398+
}), { retries: 5, minTimeout: 500 })
421399

422400
await (this.#doHandleOptionalFailure
423401
? this[_handleOptionalFailure](node, p)

workspaces/arborist/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"version": "9.4.0",
44
"description": "Manage node_modules trees",
55
"dependencies": {
6+
"@gar/promise-retry": "^1.0.0",
67
"@isaacs/string-locale-compare": "^1.1.0",
78
"@npmcli/fs": "^5.0.0",
89
"@npmcli/installed-package-contents": "^4.0.0",

0 commit comments

Comments
 (0)