Skip to content

Commit 980c1ed

Browse files
addaleaxFishrock123
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 e1a774d commit 980c1ed

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
@@ -1585,6 +1585,10 @@ function encodeRealpathResult(result, options, err) {
15851585
}
15861586
}
15871587

1588+
// This is removed from the fs exports in lib/module.js in order to make
1589+
// sure that this stays internal.
1590+
const realpathCacheKey = fs.realpathCacheKey = Symbol('realpathCacheKey');
1591+
15881592
fs.realpathSync = function realpathSync(p, options) {
15891593
if (!options)
15901594
options = {};
@@ -1599,6 +1603,13 @@ fs.realpathSync = function realpathSync(p, options) {
15991603

16001604
const seenLinks = {};
16011605
const knownHard = {};
1606+
const cache = options[realpathCacheKey];
1607+
const original = p;
1608+
1609+
const maybeCachedResult = cache && cache.get(p);
1610+
if (maybeCachedResult) {
1611+
return maybeCachedResult;
1612+
}
16021613

16031614
// current character position in p
16041615
var pos;
@@ -1639,39 +1650,47 @@ fs.realpathSync = function realpathSync(p, options) {
16391650
pos = nextPartRe.lastIndex;
16401651

16411652
// continue if not a symlink
1642-
if (knownHard[base]) {
1653+
if (knownHard[base] || (cache && cache.get(base) === base)) {
16431654
continue;
16441655
}
16451656

16461657
var resolvedLink;
1647-
var stat = fs.lstatSync(base);
1648-
if (!stat.isSymbolicLink()) {
1649-
knownHard[base] = true;
1650-
continue;
1651-
}
1658+
const maybeCachedResolved = cache && cache.get(base);
1659+
if (maybeCachedResolved) {
1660+
resolvedLink = maybeCachedResolved;
1661+
} else {
1662+
var stat = fs.lstatSync(base);
1663+
if (!stat.isSymbolicLink()) {
1664+
knownHard[base] = true;
1665+
continue;
1666+
}
16521667

1653-
// read the link if it wasn't read before
1654-
// dev/ino always return 0 on windows, so skip the check.
1655-
var linkTarget = null;
1656-
if (!isWindows) {
1657-
var id = stat.dev.toString(32) + ':' + stat.ino.toString(32);
1658-
if (seenLinks.hasOwnProperty(id)) {
1659-
linkTarget = seenLinks[id];
1668+
// read the link if it wasn't read before
1669+
// dev/ino always return 0 on windows, so skip the check.
1670+
let linkTarget = null;
1671+
let id;
1672+
if (!isWindows) {
1673+
id = `${stat.dev.toString(32)}:${stat.ino.toString(32)}`;
1674+
if (seenLinks.hasOwnProperty(id)) {
1675+
linkTarget = seenLinks[id];
1676+
}
16601677
}
1661-
}
1662-
if (linkTarget === null) {
1663-
fs.statSync(base);
1664-
linkTarget = fs.readlinkSync(base);
1665-
}
1666-
resolvedLink = pathModule.resolve(previous, linkTarget);
1678+
if (linkTarget === null) {
1679+
fs.statSync(base);
1680+
linkTarget = fs.readlinkSync(base);
1681+
}
1682+
resolvedLink = pathModule.resolve(previous, linkTarget);
16671683

1668-
if (!isWindows) seenLinks[id] = linkTarget;
1684+
if (cache) cache.set(base, resolvedLink);
1685+
if (!isWindows) seenLinks[id] = linkTarget;
1686+
}
16691687

16701688
// resolve the link, then start over
16711689
p = pathModule.resolve(resolvedLink, p.slice(pos));
16721690
start();
16731691
}
16741692

1693+
if (cache) cache.set(original, p);
16751694
return encodeRealpathResult(p, options);
16761695
};
16771696

@@ -1767,8 +1786,9 @@ fs.realpath = function realpath(p, options, callback) {
17671786
// stat & read the link if not read before
17681787
// call gotTarget as soon as the link target is known
17691788
// dev/ino always return 0 on windows, so skip the check.
1789+
let id;
17701790
if (!isWindows) {
1771-
var id = stat.dev.toString(32) + ':' + stat.ino.toString(32);
1791+
id = `${stat.dev.toString(32)}:${stat.ino.toString(32)}`;
17721792
if (seenLinks.hasOwnProperty(id)) {
17731793
return gotTarget(null, seenLinks[id], base);
17741794
}

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)