Repository navigation
sort: keep to its own temporary directory and files - #15135
Conversation
Merging this PR will not alter performance
|
| assert!(batch.len() < batch_size); | ||
| let merger = merge_without_limit(batch.into_iter(), settings)?; | ||
|
|
||
| let mut files = files; |
There was a problem hiding this comment.
the batching change could be its own commit, no?
and mut files: F in the signature would do
There was a problem hiding this comment.
done, it's the first commit now, with mut files: F.
| /// The directory created by [`TmpDirWrapper`]. Its files are named `0`, `1`, ... | ||
| /// in the order they were created. | ||
| /// | ||
| /// On Unix (except Redox), files are only created and removed through a |
There was a problem hiding this comment.
please make this comment shorter :)
There was a problem hiding this comment.
shortened to three lines.
| } | ||
|
|
||
| /// The device and inode of a file. | ||
| #[cfg(all(unix, not(target_os = "redox")))] |
There was a problem hiding this comment.
all(unix, not(target_os = "redox")) is repeated about 15 times here, could we use a cfg alias or move the unix part into its own module?
There was a problem hiding this comment.
moved the descriptor-based code into its own module next to the path-based one, so the cfg is only on the two module lines now.
| impl SortTmpDir { | ||
| #[cfg_attr( | ||
| not(all(unix, not(target_os = "redox"))), | ||
| allow(clippy::unnecessary_wraps) |
There was a problem hiding this comment.
please don't silence clippy, can new be split per platform instead?
There was a problem hiding this comment.
done: each module has its own new, which also creates the directory, so it's fallible on both and there's no allow.
| } | ||
|
|
||
| /// Create the file `path` of this directory, which must not exist yet. | ||
| fn create(self: &Arc<Self>, path: PathBuf) -> io::Result<(File, TmpPath)> { |
There was a problem hiding this comment.
why not pass the index here? then you wouldn't need the file_name().unwrap() below
There was a problem hiding this comment.
done, create takes the index and the name comes from it.
| input | ||
| .write_all("willow\ncedar\n".repeat(8192).as_bytes()) | ||
| .unwrap(); | ||
| let tmp_dir = (0..100) |
There was a problem hiding this comment.
polling with sleeps for 2s, i am worried this will be flaky in CI
There was a problem hiding this comment.
it now waits up to 60s and checks every 10ms, so it only fails if sort never writes its first file. it still polls: sort writes that file from another thread, with nothing to wait on.
2bdb7bd to
7ab395f
Compare
|
the fd-limit test failed on musl and the cross gnu images (glibc 2.23): there, starting the compress program takes two more descriptors for a moment, for the pipe that reports a failed exec. main needs the same, so the compress case now runs at 10 instead of 8; GNU 9.12 needs 20 for that command on that input. the aarch64 failure I couldn't reproduce outside CI. |
| fs::rename(tmp, dir.path().join("moved")).unwrap(); | ||
| std::os::unix::fs::symlink(other.path(), tmp).unwrap(); | ||
|
|
||
| if let Ok(mut file) = open_input(&files[0], copy.as_ref()) { |
There was a problem hiding this comment.
assert!(open_input(&files[0], copy.as_ref()).is_err()) would be simpler, no?
7ab395f to
42a43a8
Compare
42a43a8 to
02e7e21
Compare
|
Rebased. The aarch64 failure isn't from sort: commands inherit the PTYs of terminal-simulated tests running at the same time, which leaves sort too few descriptors. Fixed in #15217. |
When a merge has more inputs than it can open at once, each batch is merged into a temporary file. That file was created after the batch's inputs had been opened. Create it first, so that creating it doesn't compete with them for file descriptors, and take each batch straight from the input iterator instead of collecting it first.
Temporary files were created, reopened and deleted by path, and cleanup emptied the temporary directory by path. Once the directory's path led to another directory, sort read the files there as its own and deleted them. The directory and each file are identified by device and inode when created. Files are created and deleted through a descriptor of the directory that is checked first, and reopened only if they are still the file sort created. Cleanup deletes only the names sort created. Redox and non-Unix systems keep the path-based code. The directory descriptor is closed after each use, so with merges creating their output before opening their inputs, sort needs no more file descriptors than before.
02e7e21 to
b6d6b10
Compare
|
Thanks for your PR |
sort created, reopened and deleted its temporary files by path, and its cleanup emptied the temporary directory by path too. Once that path led to another directory, sort read the files there as its own and deleted them.
The temporary directory and each file are now identified by device and inode when they are created. Files are created and deleted through a descriptor of the directory that is checked first, and reopened only if they are still the file sort created, including the copy made when the output file is also an input. Cleanup, at exit and on Ctrl-C, deletes only the files sort created. If the path is replaced while sort runs, sort still stops with an error, but leaves the other directory alone. Redox and non-Unix systems keep the path-based code.
The directory descriptor is only open for a moment, and a merge now creates its output before opening its inputs, so sort needs no more file descriptors than before:
sort -m, a sort that spills to disk and--compress-programwork at the same limits as on main.test_temporary_files_with_few_file_descriptorschecks the lowest that hold everywhere: 6 for-mand a spilling sort, 10 for--compress-program, which needs two more descriptors on musl and older glibc while the program starts.test_tmp_dir_replaced_by_a_symlinkfails on main and passes here. In GNU'stests/sort, the same two tests fail as on main (sort,sort-compress-proc), andsort-merge-fdlimitpasses.Closes #15134