-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Asynchronously upload pack files #3489
Asynchronously upload pack files #3489
Conversation
ea2055c
to
3fc0e8a
Compare
3fc0e8a
to
52fd4e9
Compare
After testing this, I'm seeing much better and more consistent usage of my network connection. Hope to see this implemented soon. SFTP backend on my local network goes from 550mbps to 875mbps average when combined with #3475 (non-scientific anecdote) |
52fd4e9
to
98199e4
Compare
98199e4
to
ecd3270
Compare
ecd3270
to
3ad46cd
Compare
3ad46cd
to
3714f73
Compare
5d5aa68
to
d5d6107
Compare
We ran a benchmark with this in comparison to 0.13.1:
|
d5d6107
to
de09891
Compare
I've rebased the PR to the current master. The upload statistics introduce by #3733 required quite a few changes, as the asynchronous upload no longer allows retrieving the pack header overhead from the call to |
Previously, SaveAndEncrypt would assemble blobs into packs and either return immediately if the pack is not yet full or upload the pack file otherwise. The upload will block the current goroutine until it finishes. Now, the upload is done using separate goroutines. This requires changes to the error handling. As uploads are no longer tied to a SaveAndEncrypt call, failed uploads are signaled using an errgroup. To count the uploaded amount of data, the pack header overhead is no longer returned by `packer.Finalize` but rather by `packer.HeaderOverhead`. This helper method is necessary to continue returning the pack header overhead directly to the responsible call to `repository.SaveBlob`. Without the method this would not be possible, as packs are finalized asynchronously.
Now with the asynchronous uploaders there's no more benefit from using more blob savers than we have CPUs. Thus use just one blob saver for each CPU we are allowed to use.
Large amount of tree savers have no obvious benefit, however they can increase the amount of (potentially large) trees kept in memory.
Use only a single not completed pack file to keep the number of open and active pack files low. The main change here is to defer hashing the pack file to the upload step. This prevents the pack assembly step to become a bottleneck as the only task is now to write data to the temporary pack file. The tests are cleaned up to no longer reimplement packer manager functions.
de09891
to
753e56e
Compare
@Slind14 Thanks for testing. Does adding I'm going to merge this PR despite the rest-server performance regression, but we should still investigate it separately. The problem for me right now is that I have no way to reproduce the performance issues. A local backup to a nvme disk achieves 500MB/s throughput for me with several 1GB files and apparently does not suffer from the performance problem. |
Going into the hundreds of connections we can reach 3G. 3G seems to be the max restic can achieve. We get the same with a server right next to it in LAN. No matter if 10 or 100 connections. |
What does this PR change? What problem does it solve?
The backup pipeline can stall if lots of small files are backed up while using a high-latency backend, see #2696. This is caused by the fileSaver and blobSaver having to wait until a pack file is uploaded, if a blob filled up the pack file. By blocking the fileSaver this prevents processing further files and thus effectively limits the upload concurrency to 2 for small files.
This PR lets the repository upload finished packs in the background (in SaveAndEncrypt), without blocking the caller. The caller is only blocked if all uploader goroutines are busy. That way the fileSaver is no longer blocked when backing up small files. The archiver concurrency has been adapted to remove excess goroutines.
The concurrency of the pack uploader is based on the number of connections configured for each backend. For
local
a default of 2 and forsftp
a default of 5 connections is used. Configurable connections for these two backends are implemented in #3475.To keep the number of temporary pack files low, despite the increased upload concurrency, the packer manager now only maintains a single not-completed pack file. The pack file hash calculation is deferred to the uploader goroutines as otherwise the single pack file would become a bottleneck. Simply dumping data chunks into the temporary pack file is rather fast: for me
BenchmarkPackerManager
which measures the pack assembly step reports a throughput of 3GB/s.Was the change previously discussed in an issue or on the forum?
Fixes #2696
Should be combined with #3475.
Checklist
-o <backend>.connections=42
parameter somewherechangelog/unreleased/
that describes the changes for our users (see template).gofmt
on the code in all commits.