Skip to content

Commit e75b445

Browse files
addaleaxjasnell
authored andcommitted
fs,module: add module-loader-only realpath cache
Reintroduce a realpath cache with the same mechanisms which existed before b488b19 (`fs: optimize realpath using uv_fs_realpath()`), but only for the synchronous version and with the cache being passed as a hidden option to make sure it is only used internally. The cache is hidden from userland applications because it has been decided that fully reintroducing as part of the public API might stand in the way of future optimizations. PR-URL: #8100 Reviewed-By: Bartosz Sosnowski <[email protected]> Reviewed-By: James M Snell <[email protected]>
1 parent 757834e commit e75b445

File tree

2 files changed

+57
-23
lines changed

2 files changed

+57
-23
lines changed

lib/fs.js

+41-21
Original file line numberDiff line numberDiff line change
@@ -1564,6 +1564,10 @@ function encodeRealpathResult(result, options, err) {
15641564
}
15651565
}
15661566

1567+
// This is removed from the fs exports in lib/module.js in order to make
1568+
// sure that this stays internal.
1569+
const realpathCacheKey = fs.realpathCacheKey = Symbol('realpathCacheKey');
1570+
15671571
fs.realpathSync = function realpathSync(p, options) {
15681572
if (!options)
15691573
options = {};
@@ -1578,6 +1582,13 @@ fs.realpathSync = function realpathSync(p, options) {
15781582

15791583
const seenLinks = {};
15801584
const knownHard = {};
1585+
const cache = options[realpathCacheKey];
1586+
const original = p;
1587+
1588+
const maybeCachedResult = cache && cache.get(p);
1589+
if (maybeCachedResult) {
1590+
return maybeCachedResult;
1591+
}
15811592

15821593
// current character position in p
15831594
var pos;
@@ -1618,39 +1629,47 @@ fs.realpathSync = function realpathSync(p, options) {
16181629
pos = nextPartRe.lastIndex;
16191630

16201631
// continue if not a symlink
1621-
if (knownHard[base]) {
1632+
if (knownHard[base] || (cache && cache.get(base) === base)) {
16221633
continue;
16231634
}
16241635

16251636
var resolvedLink;
1626-
var stat = fs.lstatSync(base);
1627-
if (!stat.isSymbolicLink()) {
1628-
knownHard[base] = true;
1629-
continue;
1630-
}
1637+
const maybeCachedResolved = cache && cache.get(base);
1638+
if (maybeCachedResolved) {
1639+
resolvedLink = maybeCachedResolved;
1640+
} else {
1641+
var stat = fs.lstatSync(base);
1642+
if (!stat.isSymbolicLink()) {
1643+
knownHard[base] = true;
1644+
continue;
1645+
}
16311646

1632-
// read the link if it wasn't read before
1633-
// dev/ino always return 0 on windows, so skip the check.
1634-
var linkTarget = null;
1635-
if (!isWindows) {
1636-
var id = stat.dev.toString(32) + ':' + stat.ino.toString(32);
1637-
if (seenLinks.hasOwnProperty(id)) {
1638-
linkTarget = seenLinks[id];
1647+
// read the link if it wasn't read before
1648+
// dev/ino always return 0 on windows, so skip the check.
1649+
let linkTarget = null;
1650+
let id;
1651+
if (!isWindows) {
1652+
id = `${stat.dev.toString(32)}:${stat.ino.toString(32)}`;
1653+
if (seenLinks.hasOwnProperty(id)) {
1654+
linkTarget = seenLinks[id];
1655+
}
16391656
}
1640-
}
1641-
if (linkTarget === null) {
1642-
fs.statSync(base);
1643-
linkTarget = fs.readlinkSync(base);
1644-
}
1645-
resolvedLink = pathModule.resolve(previous, linkTarget);
1657+
if (linkTarget === null) {
1658+
fs.statSync(base);
1659+
linkTarget = fs.readlinkSync(base);
1660+
}
1661+
resolvedLink = pathModule.resolve(previous, linkTarget);
16461662

1647-
if (!isWindows) seenLinks[id] = linkTarget;
1663+
if (cache) cache.set(base, resolvedLink);
1664+
if (!isWindows) seenLinks[id] = linkTarget;
1665+
}
16481666

16491667
// resolve the link, then start over
16501668
p = pathModule.resolve(resolvedLink, p.slice(pos));
16511669
start();
16521670
}
16531671

1672+
if (cache) cache.set(original, p);
16541673
return encodeRealpathResult(p, options);
16551674
};
16561675

@@ -1746,8 +1765,9 @@ fs.realpath = function realpath(p, options, callback) {
17461765
// stat & read the link if not read before
17471766
// call gotTarget as soon as the link target is known
17481767
// dev/ino always return 0 on windows, so skip the check.
1768+
let id;
17491769
if (!isWindows) {
1750-
var id = stat.dev.toString(32) + ':' + stat.ino.toString(32);
1770+
id = `${stat.dev.toString(32)}:${stat.ino.toString(32)}`;
17511771
if (seenLinks.hasOwnProperty(id)) {
17521772
return gotTarget(null, seenLinks[id], base);
17531773
}

lib/module.js

+16-2
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,14 @@ function tryPackage(requestPath, exts, isMain) {
109109
tryExtensions(path.resolve(filename, 'index'), exts, isMain);
110110
}
111111

112+
// In order to minimize unnecessary lstat() calls,
113+
// this cache is a list of known-real paths.
114+
// Set to an empty Map to reset.
115+
const realpathCache = new Map();
116+
117+
const realpathCacheKey = fs.realpathCacheKey;
118+
delete fs.realpathCacheKey;
119+
112120
// check if the file exists and is not a directory
113121
// if using --preserve-symlinks and isMain is false,
114122
// keep symlinks intact, otherwise resolve to the
@@ -118,7 +126,13 @@ function tryFile(requestPath, isMain) {
118126
if (preserveSymlinks && !isMain) {
119127
return rc === 0 && path.resolve(requestPath);
120128
}
121-
return rc === 0 && fs.realpathSync(requestPath);
129+
return rc === 0 && toRealPath(requestPath);
130+
}
131+
132+
function toRealPath(requestPath) {
133+
return fs.realpathSync(requestPath, {
134+
[realpathCacheKey]: realpathCache
135+
});
122136
}
123137

124138
// given a path check a the file exists with any of the set extensions
@@ -164,7 +178,7 @@ Module._findPath = function(request, paths, isMain) {
164178
if (preserveSymlinks && !isMain) {
165179
filename = path.resolve(basePath);
166180
} else {
167-
filename = fs.realpathSync(basePath);
181+
filename = toRealPath(basePath);
168182
}
169183
} else if (rc === 1) { // Directory.
170184
if (exts === undefined)

0 commit comments

Comments
 (0)