mirror of
https://github.com/octoleo/restic.git
synced 2024-11-27 07:16:40 +00:00
dump: load blobs of a file from repository in parallel
This commit is contained in:
parent
ffe5439149
commit
45509eafc8
@ -9,6 +9,7 @@ import (
|
|||||||
"github.com/restic/restic/internal/errors"
|
"github.com/restic/restic/internal/errors"
|
||||||
"github.com/restic/restic/internal/restic"
|
"github.com/restic/restic/internal/restic"
|
||||||
"github.com/restic/restic/internal/walker"
|
"github.com/restic/restic/internal/walker"
|
||||||
|
"golang.org/x/sync/errgroup"
|
||||||
)
|
)
|
||||||
|
|
||||||
// A Dumper writes trees and files from a repository to a Writer
|
// A Dumper writes trees and files from a repository to a Writer
|
||||||
@ -16,11 +17,11 @@ import (
|
|||||||
type Dumper struct {
|
type Dumper struct {
|
||||||
cache *bloblru.Cache
|
cache *bloblru.Cache
|
||||||
format string
|
format string
|
||||||
repo restic.BlobLoader
|
repo restic.Loader
|
||||||
w io.Writer
|
w io.Writer
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(format string, repo restic.BlobLoader, w io.Writer) *Dumper {
|
func New(format string, repo restic.Loader, w io.Writer) *Dumper {
|
||||||
return &Dumper{
|
return &Dumper{
|
||||||
cache: bloblru.New(64 << 20),
|
cache: bloblru.New(64 << 20),
|
||||||
format: format,
|
format: format,
|
||||||
@ -103,27 +104,81 @@ func (d *Dumper) WriteNode(ctx context.Context, node *restic.Node) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *Dumper) writeNode(ctx context.Context, w io.Writer, node *restic.Node) error {
|
func (d *Dumper) writeNode(ctx context.Context, w io.Writer, node *restic.Node) error {
|
||||||
var (
|
type loadTask struct {
|
||||||
buf []byte
|
id restic.ID
|
||||||
err error
|
out chan<- []byte
|
||||||
)
|
}
|
||||||
for _, id := range node.Content {
|
type writeTask struct {
|
||||||
blob, ok := d.cache.Get(id)
|
data <-chan []byte
|
||||||
if !ok {
|
|
||||||
blob, err = d.repo.LoadBlob(ctx, restic.DataBlob, id, buf)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
buf = d.cache.Add(id, blob) // Reuse evicted buffer.
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := w.Write(blob); err != nil {
|
|
||||||
return errors.Wrap(err, "Write")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
loaderCh := make(chan loadTask)
|
||||||
|
// per worker: allows for one blob that gets download + one blob thats queue for writing
|
||||||
|
writerCh := make(chan writeTask, d.repo.Connections()*2)
|
||||||
|
|
||||||
|
wg, ctx := errgroup.WithContext(ctx)
|
||||||
|
|
||||||
|
wg.Go(func() error {
|
||||||
|
defer close(loaderCh)
|
||||||
|
defer close(writerCh)
|
||||||
|
for _, id := range node.Content {
|
||||||
|
// non-blocking blob handover to allow the loader to load the next blob
|
||||||
|
// while the old one is still written
|
||||||
|
ch := make(chan []byte, 1)
|
||||||
|
select {
|
||||||
|
case loaderCh <- loadTask{id: id, out: ch}:
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case writerCh <- writeTask{data: ch}:
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
for i := uint(0); i < d.repo.Connections(); i++ {
|
||||||
|
wg.Go(func() error {
|
||||||
|
for task := range loaderCh {
|
||||||
|
var err error
|
||||||
|
blob, ok := d.cache.Get(task.id)
|
||||||
|
if !ok {
|
||||||
|
blob, err = d.repo.LoadBlob(ctx, restic.DataBlob, task.id, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
d.cache.Add(task.id, blob)
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case task.out <- blob:
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
wg.Go(func() error {
|
||||||
|
for result := range writerCh {
|
||||||
|
select {
|
||||||
|
case data := <-result.data:
|
||||||
|
if _, err := w.Write(data); err != nil {
|
||||||
|
return errors.Wrap(err, "Write")
|
||||||
|
}
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
return wg.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsDir checks if the given node is a directory.
|
// IsDir checks if the given node is a directory.
|
||||||
|
Loading…
Reference in New Issue
Block a user