2018-03-30 20:43:18 +00:00
|
|
|
package archiver
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2024-02-10 22:41:11 +00:00
|
|
|
"fmt"
|
2018-03-30 20:43:18 +00:00
|
|
|
|
2018-05-08 20:28:37 +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
|
2020-06-06 20:20:44 +00:00
|
|
|
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{
|
2020-06-06 20:20:44 +00:00
|
|
|
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-05-08 20:28:37 +00:00
|
|
|
})
|
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
|
2020-06-11 11:34:05 +00:00
|
|
|
// 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)) {
|
2018-05-08 20:28:37 +00:00
|
|
|
select {
|
2024-02-10 22:41:11 +00:00
|
|
|
case s.ch <- saveBlobJob{BlobType: t, buf: buf, fn: filename, cb: cb}:
|
2018-05-08 20:28:37 +00:00
|
|
|
case <-ctx.Done():
|
|
|
|
debug.Log("not sending job, context is cancelled")
|
|
|
|
}
|
2022-05-01 12:41:36 +00:00
|
|
|
}
|
|
|
|
|
2018-03-30 20:43:18 +00:00
|
|
|
type saveBlobJob struct {
|
|
|
|
restic.BlobType
|
2024-08-27 09:26:52 +00:00
|
|
|
buf *buffer
|
2024-02-10 22:41:11 +00:00
|
|
|
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 {
|
2022-05-22 13:14:25 +00:00
|
|
|
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) {
|
2022-05-22 13:14:25 +00:00
|
|
|
id, known, sizeInRepo, err := s.repo.SaveBlob(ctx, t, buf, restic.ID{}, false)
|
2018-03-30 20:43:18 +00:00
|
|
|
|
2018-05-08 20:28:37 +00:00
|
|
|
if err != nil {
|
2024-08-27 09:26:52 +00:00
|
|
|
return saveBlobResponse{}, err
|
2018-05-08 20:28:37 +00:00
|
|
|
}
|
|
|
|
|
2024-08-27 09:26:52 +00:00
|
|
|
return saveBlobResponse{
|
2022-05-22 13:14:25 +00:00
|
|
|
id: id,
|
|
|
|
length: len(buf),
|
|
|
|
sizeInRepo: sizeInRepo,
|
|
|
|
known: known,
|
2018-05-08 20:28:37 +00:00
|
|
|
}, 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():
|
2018-05-08 20:28:37 +00:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2018-05-08 20:28:37 +00:00
|
|
|
res, err := s.saveBlob(ctx, job.BlobType, job.buf.Data)
|
|
|
|
if err != nil {
|
2018-05-12 19:40:31 +00:00
|
|
|
debug.Log("saveBlob returned error, exiting: %v", err)
|
2024-02-10 22:41:11 +00:00
|
|
|
return fmt.Errorf("failed to save blob from file %q: %w", job.fn, err)
|
2018-05-08 20:28:37 +00:00
|
|
|
}
|
2022-10-07 18:23:38 +00:00
|
|
|
job.cb(res)
|
2018-03-30 20:43:18 +00:00
|
|
|
job.buf.Release()
|
|
|
|
}
|
|
|
|
}
|