Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit c5ac10e

Browse files
committedAug 1, 2018
fix(perf): refactor content.read to avoid lstats
1 parent 1950490 commit c5ac10e

File tree

1 file changed

+45
-46
lines changed

1 file changed

+45
-46
lines changed
 

Diff for: ‎lib/content/read.js

+45-46
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ const pipe = BB.promisify(require('mississippi').pipe)
1010
const ssri = require('ssri')
1111
const Y = require('../util/y.js')
1212

13-
BB.promisifyAll(fs)
13+
const lstatAsync = BB.promisify(fs.lstat)
14+
const readFileAsync = BB.promisify(fs.readFile)
1415

1516
const ReadOpts = figgyPudding({
1617
size: {}
@@ -19,10 +20,8 @@ const ReadOpts = figgyPudding({
1920
module.exports = read
2021
function read (cache, integrity, opts) {
2122
opts = ReadOpts(opts)
22-
return pickContentSri(cache, integrity).then(content => {
23-
const sri = content.sri
24-
const cpath = contentPath(cache, sri)
25-
return fs.readFileAsync(cpath, null).then(data => {
23+
return withContentSri(cache, integrity, (cpath, sri) => {
24+
return readFileAsync(cpath, null).then(data => {
2625
if (typeof opts.size === 'number' && opts.size !== data.length) {
2726
throw sizeError(opts.size, data.length)
2827
} else if (ssri.checkData(data, sri)) {
@@ -39,12 +38,11 @@ module.exports.readStream = readStream
3938
function readStream (cache, integrity, opts) {
4039
opts = ReadOpts(opts)
4140
const stream = new PassThrough()
42-
pickContentSri(
43-
cache, integrity
44-
).then(content => {
45-
const sri = content.sri
41+
withContentSri(cache, integrity, (cpath, sri) => {
42+
return lstatAsync(cpath).then(stat => ({cpath, sri, stat}))
43+
}).then(({cpath, sri, stat}) => {
4644
return pipe(
47-
fs.createReadStream(contentPath(cache, sri)),
45+
fs.createReadStream(cpath),
4846
ssri.integrityStream({
4947
integrity: sri,
5048
size: opts.size
@@ -57,60 +55,61 @@ function readStream (cache, integrity, opts) {
5755
return stream
5856
}
5957

58+
let copyFileAsync
6059
if (fs.copyFile) {
6160
module.exports.copy = copy
61+
copyFileAsync = BB.promisify(fs.copyFile)
6262
}
6363
function copy (cache, integrity, dest, opts) {
6464
opts = ReadOpts(opts)
65-
return pickContentSri(cache, integrity).then(content => {
66-
const sri = content.sri
67-
const cpath = contentPath(cache, sri)
68-
return fs.copyFileAsync(cpath, dest).then(() => content.size)
65+
return withContentSri(cache, integrity, (cpath, sri) => {
66+
return copyFileAsync(cpath, dest)
6967
})
7068
}
7169

7270
module.exports.hasContent = hasContent
7371
function hasContent (cache, integrity) {
7472
if (!integrity) { return BB.resolve(false) }
75-
return pickContentSri(cache, integrity)
76-
.catch({code: 'ENOENT'}, () => false)
77-
.catch({code: 'EPERM'}, err => {
73+
return withContentSri(cache, integrity, (cpath, sri) => {
74+
return lstatAsync(cpath).then(stat => ({size: stat.size, sri}))
75+
}).catch(err => {
76+
if (err.code === 'ENOENT') { return false }
77+
if (err.code === 'EPERM') {
7878
if (process.platform !== 'win32') {
7979
throw err
8080
} else {
8181
return false
8282
}
83-
}).then(content => {
84-
if (!content.sri) return false
85-
return ({ sri: content.sri, size: content.stat.size })
86-
})
83+
}
84+
})
8785
}
8886

89-
module.exports._pickContentSri = pickContentSri
90-
function pickContentSri (cache, integrity) {
91-
const sri = ssri.parse(integrity)
92-
// If `integrity` has multiple entries, pick the first digest
93-
// with available local data.
94-
const algo = sri.pickAlgorithm()
95-
const digests = sri[algo]
96-
if (digests.length <= 1) {
97-
const cpath = contentPath(cache, digests[0])
98-
return fs.lstatAsync(cpath).then(stat => ({ sri: digests[0], stat }))
99-
} else {
100-
return BB.any(sri[sri.pickAlgorithm()].map(meta => {
101-
return pickContentSri(cache, meta)
102-
}))
103-
.catch(err => {
104-
if ([].some.call(err, e => e.code === 'ENOENT')) {
105-
throw Object.assign(
106-
new Error('No matching content found for ' + sri.toString()),
107-
{code: 'ENOENT'}
108-
)
109-
} else {
110-
throw err[0]
111-
}
112-
})
113-
}
87+
function withContentSri (cache, integrity, fn) {
88+
return BB.try(() => {
89+
const sri = ssri.parse(integrity)
90+
// If `integrity` has multiple entries, pick the first digest
91+
// with available local data.
92+
const algo = sri.pickAlgorithm()
93+
const digests = sri[algo]
94+
if (digests.length <= 1) {
95+
const cpath = contentPath(cache, digests[0])
96+
return fn(cpath, digests[0])
97+
} else {
98+
return BB.any(sri[sri.pickAlgorithm()].map(meta => {
99+
return withContentSri(cache, meta, fn)
100+
}, {concurrency: 1}))
101+
.catch(err => {
102+
if ([].some.call(err, e => e.code === 'ENOENT')) {
103+
throw Object.assign(
104+
new Error('No matching content found for ' + sri.toString()),
105+
{code: 'ENOENT'}
106+
)
107+
} else {
108+
throw err[0]
109+
}
110+
})
111+
}
112+
})
114113
}
115114

116115
function sizeError (expected, found) {

0 commit comments

Comments
 (0)
Failed to load comments.