restic/internal/archiver/blob_saver.go

106 lines
2.4 KiB
Go
Raw Normal View History

2018-03-30 20:43:18 +00:00
package archiver
import (
"context"
"fmt"
2018-03-30 20:43:18 +00:00
"github.com/restic/restic/internal/debug"
2018-03-30 20:43:18 +00:00
"github.com/restic/restic/internal/restic"
2022-05-27 17:08:50 +00:00
"golang.org/x/sync/errgroup"
2018-03-30 20:43:18 +00:00
)
2024-08-27 09:26:52 +00:00
// saver allows saving a blob.
type saver interface {
2022-05-01 12:26:57 +00:00
SaveBlob(ctx context.Context, t restic.BlobType, data []byte, id restic.ID, storeDuplicate bool) (restic.ID, bool, int, error)
2018-03-30 20:43:18 +00:00
}
2024-08-27 09:26:52 +00:00
// blobSaver concurrently saves incoming blobs to the repo.
type blobSaver struct {
repo saver
ch chan<- saveBlobJob
2018-03-30 20:43:18 +00:00
}
2024-08-27 09:26:52 +00:00
// newBlobSaver returns a new blob. A worker pool is started, it is stopped
2018-03-30 20:43:18 +00:00
// when ctx is cancelled.
2024-08-27 09:26:52 +00:00
func newBlobSaver(ctx context.Context, wg *errgroup.Group, repo saver, workers uint) *blobSaver {
2018-04-30 13:13:03 +00:00
ch := make(chan saveBlobJob)
2024-08-27 09:26:52 +00:00
s := &blobSaver{
repo: repo,
ch: ch,
2018-03-30 20:43:18 +00:00
}
for i := uint(0); i < workers; i++ {
2022-05-27 17:08:50 +00:00
wg.Go(func() error {
return s.worker(ctx, ch)
})
2018-03-30 20:43:18 +00:00
}
return s
}
2024-08-27 09:26:52 +00:00
func (s *blobSaver) TriggerShutdown() {
2022-05-27 17:08:50 +00:00
close(s.ch)
}
2018-03-30 20:43:18 +00:00
// Save stores a blob in the repo. It checks the index and the known blobs
// before saving anything. It takes ownership of the buffer passed in.
2024-08-27 09:26:52 +00:00
func (s *blobSaver) Save(ctx context.Context, t restic.BlobType, buf *buffer, filename string, cb func(res saveBlobResponse)) {
select {
case s.ch <- saveBlobJob{BlobType: t, buf: buf, fn: filename, cb: cb}:
case <-ctx.Done():
debug.Log("not sending job, context is cancelled")
}
}
2018-03-30 20:43:18 +00:00
type saveBlobJob struct {
restic.BlobType
2024-08-27 09:26:52 +00:00
buf *buffer
fn string
2024-08-27 09:26:52 +00:00
cb func(res saveBlobResponse)
2018-03-30 20:43:18 +00:00
}
2024-08-27 09:26:52 +00:00
type saveBlobResponse struct {
id restic.ID
length int
sizeInRepo int
known bool
2018-03-30 20:43:18 +00:00
}
2024-08-27 09:26:52 +00:00
func (s *blobSaver) saveBlob(ctx context.Context, t restic.BlobType, buf []byte) (saveBlobResponse, error) {
id, known, sizeInRepo, err := s.repo.SaveBlob(ctx, t, buf, restic.ID{}, false)
2018-03-30 20:43:18 +00:00
if err != nil {
2024-08-27 09:26:52 +00:00
return saveBlobResponse{}, err
}
2024-08-27 09:26:52 +00:00
return saveBlobResponse{
id: id,
length: len(buf),
sizeInRepo: sizeInRepo,
known: known,
}, nil
2018-03-30 20:43:18 +00:00
}
2024-08-27 09:26:52 +00:00
func (s *blobSaver) worker(ctx context.Context, jobs <-chan saveBlobJob) error {
2018-03-30 20:43:18 +00:00
for {
var job saveBlobJob
2022-05-27 17:08:50 +00:00
var ok bool
2018-03-30 20:43:18 +00:00
select {
case <-ctx.Done():
return nil
2022-05-27 17:08:50 +00:00
case job, ok = <-jobs:
if !ok {
return nil
}
2018-03-30 20:43:18 +00:00
}
res, err := s.saveBlob(ctx, job.BlobType, job.buf.Data)
if err != nil {
debug.Log("saveBlob returned error, exiting: %v", err)
return fmt.Errorf("failed to save blob from file %q: %w", job.fn, err)
}
job.cb(res)
2018-03-30 20:43:18 +00:00
job.buf.Release()
}
}