chunkedreader: add --vfs-read-chunk-streams to parallel read chunks
This converts the ChunkedReader into an interface and provides two implementations one sequential and one parallel. This can be used to improve the performance of the VFS on high bandwidth or high latency links. Fixes #4760
This commit is contained in:
+5
-3
@@ -75,7 +75,8 @@ func (fh *ReadFileHandle) openPending() (err error) {
|
||||
return nil
|
||||
}
|
||||
o := fh.file.getObject()
|
||||
r, err := chunkedreader.New(context.TODO(), o, int64(fh.file.VFS().Opt.ChunkSize), int64(fh.file.VFS().Opt.ChunkSizeLimit)).Open()
|
||||
opt := &fh.file.VFS().Opt
|
||||
r, err := chunkedreader.New(context.TODO(), o, int64(opt.ChunkSize), int64(opt.ChunkSizeLimit), opt.ChunkStreams).Open()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -127,7 +128,7 @@ func (fh *ReadFileHandle) seek(offset int64, reopen bool) (err error) {
|
||||
}
|
||||
fh.r.StopBuffering() // stop the background reading first
|
||||
oldReader := fh.r.GetReader()
|
||||
r, ok := oldReader.(*chunkedreader.ChunkedReader)
|
||||
r, ok := oldReader.(chunkedreader.ChunkedReader)
|
||||
if !ok {
|
||||
fs.Logf(fh.remote, "ReadFileHandle.Read expected reader to be a ChunkedReader, got %T", oldReader)
|
||||
reopen = true
|
||||
@@ -148,7 +149,8 @@ func (fh *ReadFileHandle) seek(offset int64, reopen bool) (err error) {
|
||||
}
|
||||
// re-open with a seek
|
||||
o := fh.file.getObject()
|
||||
r = chunkedreader.New(context.TODO(), o, int64(fh.file.VFS().Opt.ChunkSize), int64(fh.file.VFS().Opt.ChunkSizeLimit))
|
||||
opt := &fh.file.VFS().Opt
|
||||
r = chunkedreader.New(context.TODO(), o, int64(opt.ChunkSize), int64(opt.ChunkSizeLimit), opt.ChunkStreams)
|
||||
_, err := r.Seek(offset, 0)
|
||||
if err != nil {
|
||||
fs.Debugf(fh.remote, "ReadFileHandle.Read seek failed: %v", err)
|
||||
|
||||
+29
@@ -230,6 +230,11 @@ These flags control the chunking:
|
||||
|
||||
--vfs-read-chunk-size SizeSuffix Read the source objects in chunks (default 128M)
|
||||
--vfs-read-chunk-size-limit SizeSuffix Max chunk doubling size (default off)
|
||||
--vfs-read-chunk-streams int The number of parallel streams to read at once
|
||||
|
||||
The chunking behaves differently depending on the `--vfs-read-chunk-streams` parameter.
|
||||
|
||||
#### `--vfs-read-chunk-streams` == 0
|
||||
|
||||
Rclone will start reading a chunk of size `--vfs-read-chunk-size`,
|
||||
and then double the size for each read. When `--vfs-read-chunk-size-limit` is
|
||||
@@ -245,6 +250,30 @@ When `--vfs-read-chunk-size-limit 500M` is specified, the result would be
|
||||
|
||||
Setting `--vfs-read-chunk-size` to `0` or "off" disables chunked reading.
|
||||
|
||||
The chunks will not be buffered in memory.
|
||||
|
||||
#### `--vfs-read-chunk-streams` > 0
|
||||
|
||||
Rclone reads `--vfs-read-chunk-streams` chunks of size
|
||||
`--vfs-read-chunk-size` concurrently. The size for each read will stay
|
||||
constant.
|
||||
|
||||
This improves performance performance massively on high latency links
|
||||
or very high bandwidth links to high performance object stores.
|
||||
|
||||
Some experimentation will be needed to find the optimum values of
|
||||
`--vfs-read-chunk-size` and `--vfs-read-chunk-streams` as these will
|
||||
depend on the backend in use and the latency to the backend.
|
||||
|
||||
For high performance object stores (eg AWS S3) a reasonable place to
|
||||
start might be `--vfs-read-chunk-streams 16` and
|
||||
`--vfs-read-chunk-size 4M`. In testing with AWS S3 the performance
|
||||
scaled roughly as the `--vfs-read-chunk-streams` setting.
|
||||
|
||||
Similar settings should work for high latency links, but depending on
|
||||
the latency they may need more `--vfs-read-chunk-streams` in order to
|
||||
get the throughput.
|
||||
|
||||
### VFS Performance
|
||||
|
||||
These flags may be used to enable/disable features of the VFS for
|
||||
|
||||
@@ -540,7 +540,7 @@ func (dl *downloader) open(offset int64) (err error) {
|
||||
// }
|
||||
// in0, err := operations.NewReOpen(dl.dls.ctx, dl.dls.src, ci.LowLevelRetries, dl.dls.item.c.hashOption, rangeOption)
|
||||
|
||||
in0 := chunkedreader.New(context.TODO(), dl.dls.src, int64(dl.dls.opt.ChunkSize), int64(dl.dls.opt.ChunkSizeLimit))
|
||||
in0 := chunkedreader.New(context.TODO(), dl.dls.src, int64(dl.dls.opt.ChunkSize), int64(dl.dls.opt.ChunkSizeLimit), dl.dls.opt.ChunkStreams)
|
||||
_, err = in0.Seek(offset, 0)
|
||||
if err != nil {
|
||||
return fmt.Errorf("vfs reader: failed to open source file: %w", err)
|
||||
|
||||
@@ -79,6 +79,11 @@ var OptionsInfo = fs.Options{{
|
||||
Default: fs.SizeSuffix(-1),
|
||||
Help: "If greater than --vfs-read-chunk-size, double the chunk size after each chunk read, until the limit is reached ('off' is unlimited)",
|
||||
Groups: "VFS",
|
||||
}, {
|
||||
Name: "vfs_read_chunk_streams",
|
||||
Default: 0,
|
||||
Help: "The number of parallel streams to read at once",
|
||||
Groups: "VFS",
|
||||
}, {
|
||||
Name: "dir_perms",
|
||||
Default: FileMode(0777),
|
||||
@@ -171,6 +176,7 @@ type Options struct {
|
||||
FilePerms FileMode `config:"file_perms"`
|
||||
ChunkSize fs.SizeSuffix `config:"vfs_read_chunk_size"` // if > 0 read files in chunks
|
||||
ChunkSizeLimit fs.SizeSuffix `config:"vfs_read_chunk_size_limit"` // if > ChunkSize double the chunk size after each chunk until reached
|
||||
ChunkStreams int `config:"vfs_read_chunk_streams"` // Number of download streams to use
|
||||
CacheMode CacheMode `config:"vfs_cache_mode"`
|
||||
CacheMaxAge fs.Duration `config:"vfs_cache_max_age"`
|
||||
CacheMaxSize fs.SizeSuffix `config:"vfs_cache_max_size"`
|
||||
|
||||
Reference in New Issue
Block a user