-
Notifications
You must be signed in to change notification settings - Fork 215
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Improve the efficiency of writing snapshots #6248
Conversation
const snapReader = createReadStream(tmpSnapPath); | ||
cleanup.push(() => snapReader.destroy()); | ||
await fsStreamReady(snapReader); | ||
// TODO: hoist. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll be resolving these "hoist" TODOs while the PR goes through CI, along with updating now-redundant definitions for e.g. path vs. stream parameters. They were defined inline during development, and left in place for now to avoid delaying the PR (which can be merged with them depending upon urgency).
6cbaca6
to
93dd458
Compare
@gibson042 I just pushed the changes we paired on |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mhofman Some further cleanup.
unlink, | ||
}, | ||
{ keepSnapshots = false } = {}, | ||
) { | ||
/** @type {(opts: unknown) => Promise<string>} */ | ||
const ptmpName = promisify(tmpName); | ||
|
||
/** @type {(fd: number) => Promise<void>} */ | ||
const pfsync = promisify(fsync); | ||
|
||
/** | ||
* Returns the result of calling a function with the name | ||
* of a temp file that exists only for the duration of |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would withTempName
be simpler on top of aggregateTryFinally
?
async function withTempName(fn, prefix = 'tmp') {
const name = await ptmpName({
tmpdir: root,
template: `${prefix}-XXXXXX.xss`,
});
return aggregateTryFinally(
() => fn(name),
// Ignore file deletion errors.
() => unlink(name).catch(sink),
);
}
Or maybe the point is moot, since load
can be refactored to eliminate withTempName
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suppose we can use the same tmpFile approach for load.
cleanup.push(() => { | ||
snapReader.destroy(); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cleanup ignores return values and the other uses of cleanup.push
don't suppress them like this.
cleanup.push(() => { | |
snapReader.destroy(); | |
}); | |
cleanup.push(() => snapReader.destroy()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The return value would be adopted in the promise result in the finally
stage, meaning the stream object would be kept around for longer than necessary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But just in the time between running the function in PromiseAllOrErrors
and throwing away the aggregated value, which should be inconsequential.
155f0fa
to
591f3a2
Compare
591f3a2
to
d3b60e8
Compare
const cleanup = []; | ||
return aggregateTryFinally( | ||
async () => { | ||
// TODO: Refactor to use tmpFile rather than tmpName. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thinking about this, we can't use tmpFile
here since it's not this process that opens the file, but not worth another CI churn to change the comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The refactoring I have in mind would be updating the signature of saveRaw
to accept a file handle or stream rather than a file name, but I understand that may not even be possible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh yeah we decompress to the file, then tell xsnap the filename, so it'd work.
await PromiseAllOrErrors( | ||
cleanup.reverse().map(fn => Promise.resolve().then(() => fn())), | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thinking about this more, shouldn't we wait for previous steps to be done to continue with the next cleanup step? Disposal of resource is hard!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not an actual problem since only the last cleanup (first queued) is actually async. Let's follow-up to clean this up though.
* Use TMPDIR for temporary files. * Parallelize reading raw snapshot data for hash computation and compression. Fixes #6225
d3b60e8
to
7bac0b9
Compare
For future reference, internally the
|
closes: #6225
refs: #6188
Description
Security Considerations
n/a
Documentation Considerations
We might want to mention somewhere that snapshot creation puts some temporary files in the system temporary directory (as communicated by e.g. the POSIX
TMPDIR
environment variable).Testing Considerations
Unit tests verify stable functionality, but the performance changes theirselves should be evaluated on a real network.