mirror of
https://github.com/restic/restic.git
synced 2026-07-20 22:24:54 +00:00
move Backend interface to backend package
This commit is contained in:
@@ -6,22 +6,22 @@ import (
|
||||
"sync"
|
||||
|
||||
"github.com/cenkalti/backoff/v4"
|
||||
"github.com/restic/restic/internal/backend"
|
||||
"github.com/restic/restic/internal/errors"
|
||||
"github.com/restic/restic/internal/restic"
|
||||
)
|
||||
|
||||
// make sure that connectionLimitedBackend implements restic.Backend
|
||||
var _ restic.Backend = &connectionLimitedBackend{}
|
||||
// make sure that connectionLimitedBackend implements backend.Backend
|
||||
var _ backend.Backend = &connectionLimitedBackend{}
|
||||
|
||||
// connectionLimitedBackend limits the number of concurrent operations.
|
||||
type connectionLimitedBackend struct {
|
||||
restic.Backend
|
||||
backend.Backend
|
||||
sem semaphore
|
||||
freezeLock sync.Mutex
|
||||
}
|
||||
|
||||
// NewBackend creates a backend that limits the concurrent operations on the underlying backend
|
||||
func NewBackend(be restic.Backend) restic.Backend {
|
||||
func NewBackend(be backend.Backend) backend.Backend {
|
||||
sem, err := newSemaphore(be.Connections())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
@@ -35,9 +35,9 @@ func NewBackend(be restic.Backend) restic.Backend {
|
||||
|
||||
// typeDependentLimit acquire a token unless the FileType is a lock file. The returned function
|
||||
// must be called to release the token.
|
||||
func (be *connectionLimitedBackend) typeDependentLimit(t restic.FileType) func() {
|
||||
func (be *connectionLimitedBackend) typeDependentLimit(t backend.FileType) func() {
|
||||
// allow concurrent lock file operations to ensure that the lock refresh is always possible
|
||||
if t == restic.LockFile {
|
||||
if t == backend.LockFile {
|
||||
return func() {}
|
||||
}
|
||||
be.sem.GetToken()
|
||||
@@ -59,7 +59,7 @@ func (be *connectionLimitedBackend) Unfreeze() {
|
||||
}
|
||||
|
||||
// Save adds new Data to the backend.
|
||||
func (be *connectionLimitedBackend) Save(ctx context.Context, h restic.Handle, rd restic.RewindReader) error {
|
||||
func (be *connectionLimitedBackend) Save(ctx context.Context, h backend.Handle, rd backend.RewindReader) error {
|
||||
if err := h.Valid(); err != nil {
|
||||
return backoff.Permanent(err)
|
||||
}
|
||||
@@ -75,7 +75,7 @@ func (be *connectionLimitedBackend) Save(ctx context.Context, h restic.Handle, r
|
||||
|
||||
// Load runs fn with a reader that yields the contents of the file at h at the
|
||||
// given offset.
|
||||
func (be *connectionLimitedBackend) Load(ctx context.Context, h restic.Handle, length int, offset int64, fn func(rd io.Reader) error) error {
|
||||
func (be *connectionLimitedBackend) Load(ctx context.Context, h backend.Handle, length int, offset int64, fn func(rd io.Reader) error) error {
|
||||
if err := h.Valid(); err != nil {
|
||||
return backoff.Permanent(err)
|
||||
}
|
||||
@@ -96,22 +96,22 @@ func (be *connectionLimitedBackend) Load(ctx context.Context, h restic.Handle, l
|
||||
}
|
||||
|
||||
// Stat returns information about a file in the backend.
|
||||
func (be *connectionLimitedBackend) Stat(ctx context.Context, h restic.Handle) (restic.FileInfo, error) {
|
||||
func (be *connectionLimitedBackend) Stat(ctx context.Context, h backend.Handle) (backend.FileInfo, error) {
|
||||
if err := h.Valid(); err != nil {
|
||||
return restic.FileInfo{}, backoff.Permanent(err)
|
||||
return backend.FileInfo{}, backoff.Permanent(err)
|
||||
}
|
||||
|
||||
defer be.typeDependentLimit(h.Type)()
|
||||
|
||||
if ctx.Err() != nil {
|
||||
return restic.FileInfo{}, ctx.Err()
|
||||
return backend.FileInfo{}, ctx.Err()
|
||||
}
|
||||
|
||||
return be.Backend.Stat(ctx, h)
|
||||
}
|
||||
|
||||
// Remove deletes a file from the backend.
|
||||
func (be *connectionLimitedBackend) Remove(ctx context.Context, h restic.Handle) error {
|
||||
func (be *connectionLimitedBackend) Remove(ctx context.Context, h backend.Handle) error {
|
||||
if err := h.Valid(); err != nil {
|
||||
return backoff.Permanent(err)
|
||||
}
|
||||
@@ -125,6 +125,6 @@ func (be *connectionLimitedBackend) Remove(ctx context.Context, h restic.Handle)
|
||||
return be.Backend.Remove(ctx, h)
|
||||
}
|
||||
|
||||
func (be *connectionLimitedBackend) Unwrap() restic.Backend {
|
||||
func (be *connectionLimitedBackend) Unwrap() backend.Backend {
|
||||
return be.Backend
|
||||
}
|
||||
|
||||
@@ -8,37 +8,37 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/restic/restic/internal/backend"
|
||||
"github.com/restic/restic/internal/backend/mock"
|
||||
"github.com/restic/restic/internal/backend/sema"
|
||||
"github.com/restic/restic/internal/restic"
|
||||
"github.com/restic/restic/internal/test"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
func TestParameterValidationSave(t *testing.T) {
|
||||
m := mock.NewBackend()
|
||||
m.SaveFn = func(ctx context.Context, h restic.Handle, rd restic.RewindReader) error {
|
||||
m.SaveFn = func(ctx context.Context, h backend.Handle, rd backend.RewindReader) error {
|
||||
return nil
|
||||
}
|
||||
be := sema.NewBackend(m)
|
||||
|
||||
err := be.Save(context.TODO(), restic.Handle{}, nil)
|
||||
err := be.Save(context.TODO(), backend.Handle{}, nil)
|
||||
test.Assert(t, err != nil, "Save() with invalid handle did not return an error")
|
||||
}
|
||||
|
||||
func TestParameterValidationLoad(t *testing.T) {
|
||||
m := mock.NewBackend()
|
||||
m.OpenReaderFn = func(ctx context.Context, h restic.Handle, length int, offset int64) (io.ReadCloser, error) {
|
||||
m.OpenReaderFn = func(ctx context.Context, h backend.Handle, length int, offset int64) (io.ReadCloser, error) {
|
||||
return io.NopCloser(nil), nil
|
||||
}
|
||||
|
||||
be := sema.NewBackend(m)
|
||||
nilCb := func(rd io.Reader) error { return nil }
|
||||
|
||||
err := be.Load(context.TODO(), restic.Handle{}, 10, 0, nilCb)
|
||||
err := be.Load(context.TODO(), backend.Handle{}, 10, 0, nilCb)
|
||||
test.Assert(t, err != nil, "Load() with invalid handle did not return an error")
|
||||
|
||||
h := restic.Handle{Type: restic.PackFile, Name: "foobar"}
|
||||
h := backend.Handle{Type: backend.PackFile, Name: "foobar"}
|
||||
err = be.Load(context.TODO(), h, 10, -1, nilCb)
|
||||
test.Assert(t, err != nil, "Save() with negative offset did not return an error")
|
||||
err = be.Load(context.TODO(), h, -1, 0, nilCb)
|
||||
@@ -47,23 +47,23 @@ func TestParameterValidationLoad(t *testing.T) {
|
||||
|
||||
func TestParameterValidationStat(t *testing.T) {
|
||||
m := mock.NewBackend()
|
||||
m.StatFn = func(ctx context.Context, h restic.Handle) (restic.FileInfo, error) {
|
||||
return restic.FileInfo{}, nil
|
||||
m.StatFn = func(ctx context.Context, h backend.Handle) (backend.FileInfo, error) {
|
||||
return backend.FileInfo{}, nil
|
||||
}
|
||||
be := sema.NewBackend(m)
|
||||
|
||||
_, err := be.Stat(context.TODO(), restic.Handle{})
|
||||
_, err := be.Stat(context.TODO(), backend.Handle{})
|
||||
test.Assert(t, err != nil, "Stat() with invalid handle did not return an error")
|
||||
}
|
||||
|
||||
func TestParameterValidationRemove(t *testing.T) {
|
||||
m := mock.NewBackend()
|
||||
m.RemoveFn = func(ctx context.Context, h restic.Handle) error {
|
||||
m.RemoveFn = func(ctx context.Context, h backend.Handle) error {
|
||||
return nil
|
||||
}
|
||||
be := sema.NewBackend(m)
|
||||
|
||||
err := be.Remove(context.TODO(), restic.Handle{})
|
||||
err := be.Remove(context.TODO(), backend.Handle{})
|
||||
test.Assert(t, err != nil, "Remove() with invalid handle did not return an error")
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ func TestUnwrap(t *testing.T) {
|
||||
m := mock.NewBackend()
|
||||
be := sema.NewBackend(m)
|
||||
|
||||
unwrapper := be.(restic.BackendUnwrapper)
|
||||
unwrapper := be.(backend.Unwrapper)
|
||||
test.Assert(t, unwrapper.Unwrap() == m, "Unwrap() returned wrong backend")
|
||||
}
|
||||
|
||||
@@ -100,7 +100,7 @@ func countingBlocker() (func(), func(int) int) {
|
||||
return wait, unblock
|
||||
}
|
||||
|
||||
func concurrencyTester(t *testing.T, setup func(m *mock.Backend), handler func(be restic.Backend) func() error, unblock func(int) int, isUnlimited bool) {
|
||||
func concurrencyTester(t *testing.T, setup func(m *mock.Backend), handler func(be backend.Backend) func() error, unblock func(int) int, isUnlimited bool) {
|
||||
expectBlocked := int(2)
|
||||
workerCount := expectBlocked + 1
|
||||
|
||||
@@ -125,13 +125,13 @@ func concurrencyTester(t *testing.T, setup func(m *mock.Backend), handler func(b
|
||||
func TestConcurrencyLimitSave(t *testing.T) {
|
||||
wait, unblock := countingBlocker()
|
||||
concurrencyTester(t, func(m *mock.Backend) {
|
||||
m.SaveFn = func(ctx context.Context, h restic.Handle, rd restic.RewindReader) error {
|
||||
m.SaveFn = func(ctx context.Context, h backend.Handle, rd backend.RewindReader) error {
|
||||
wait()
|
||||
return nil
|
||||
}
|
||||
}, func(be restic.Backend) func() error {
|
||||
}, func(be backend.Backend) func() error {
|
||||
return func() error {
|
||||
h := restic.Handle{Type: restic.PackFile, Name: "foobar"}
|
||||
h := backend.Handle{Type: backend.PackFile, Name: "foobar"}
|
||||
return be.Save(context.TODO(), h, nil)
|
||||
}
|
||||
}, unblock, false)
|
||||
@@ -140,13 +140,13 @@ func TestConcurrencyLimitSave(t *testing.T) {
|
||||
func TestConcurrencyLimitLoad(t *testing.T) {
|
||||
wait, unblock := countingBlocker()
|
||||
concurrencyTester(t, func(m *mock.Backend) {
|
||||
m.OpenReaderFn = func(ctx context.Context, h restic.Handle, length int, offset int64) (io.ReadCloser, error) {
|
||||
m.OpenReaderFn = func(ctx context.Context, h backend.Handle, length int, offset int64) (io.ReadCloser, error) {
|
||||
wait()
|
||||
return io.NopCloser(nil), nil
|
||||
}
|
||||
}, func(be restic.Backend) func() error {
|
||||
}, func(be backend.Backend) func() error {
|
||||
return func() error {
|
||||
h := restic.Handle{Type: restic.PackFile, Name: "foobar"}
|
||||
h := backend.Handle{Type: backend.PackFile, Name: "foobar"}
|
||||
nilCb := func(rd io.Reader) error { return nil }
|
||||
return be.Load(context.TODO(), h, 10, 0, nilCb)
|
||||
}
|
||||
@@ -156,13 +156,13 @@ func TestConcurrencyLimitLoad(t *testing.T) {
|
||||
func TestConcurrencyLimitStat(t *testing.T) {
|
||||
wait, unblock := countingBlocker()
|
||||
concurrencyTester(t, func(m *mock.Backend) {
|
||||
m.StatFn = func(ctx context.Context, h restic.Handle) (restic.FileInfo, error) {
|
||||
m.StatFn = func(ctx context.Context, h backend.Handle) (backend.FileInfo, error) {
|
||||
wait()
|
||||
return restic.FileInfo{}, nil
|
||||
return backend.FileInfo{}, nil
|
||||
}
|
||||
}, func(be restic.Backend) func() error {
|
||||
}, func(be backend.Backend) func() error {
|
||||
return func() error {
|
||||
h := restic.Handle{Type: restic.PackFile, Name: "foobar"}
|
||||
h := backend.Handle{Type: backend.PackFile, Name: "foobar"}
|
||||
_, err := be.Stat(context.TODO(), h)
|
||||
return err
|
||||
}
|
||||
@@ -172,13 +172,13 @@ func TestConcurrencyLimitStat(t *testing.T) {
|
||||
func TestConcurrencyLimitDelete(t *testing.T) {
|
||||
wait, unblock := countingBlocker()
|
||||
concurrencyTester(t, func(m *mock.Backend) {
|
||||
m.RemoveFn = func(ctx context.Context, h restic.Handle) error {
|
||||
m.RemoveFn = func(ctx context.Context, h backend.Handle) error {
|
||||
wait()
|
||||
return nil
|
||||
}
|
||||
}, func(be restic.Backend) func() error {
|
||||
}, func(be backend.Backend) func() error {
|
||||
return func() error {
|
||||
h := restic.Handle{Type: restic.PackFile, Name: "foobar"}
|
||||
h := backend.Handle{Type: backend.PackFile, Name: "foobar"}
|
||||
return be.Remove(context.TODO(), h)
|
||||
}
|
||||
}, unblock, false)
|
||||
@@ -187,13 +187,13 @@ func TestConcurrencyLimitDelete(t *testing.T) {
|
||||
func TestConcurrencyUnlimitedLockSave(t *testing.T) {
|
||||
wait, unblock := countingBlocker()
|
||||
concurrencyTester(t, func(m *mock.Backend) {
|
||||
m.SaveFn = func(ctx context.Context, h restic.Handle, rd restic.RewindReader) error {
|
||||
m.SaveFn = func(ctx context.Context, h backend.Handle, rd backend.RewindReader) error {
|
||||
wait()
|
||||
return nil
|
||||
}
|
||||
}, func(be restic.Backend) func() error {
|
||||
}, func(be backend.Backend) func() error {
|
||||
return func() error {
|
||||
h := restic.Handle{Type: restic.LockFile, Name: "foobar"}
|
||||
h := backend.Handle{Type: backend.LockFile, Name: "foobar"}
|
||||
return be.Save(context.TODO(), h, nil)
|
||||
}
|
||||
}, unblock, true)
|
||||
@@ -202,13 +202,13 @@ func TestConcurrencyUnlimitedLockSave(t *testing.T) {
|
||||
func TestFreeze(t *testing.T) {
|
||||
var counter int64
|
||||
m := mock.NewBackend()
|
||||
m.SaveFn = func(ctx context.Context, h restic.Handle, rd restic.RewindReader) error {
|
||||
m.SaveFn = func(ctx context.Context, h backend.Handle, rd backend.RewindReader) error {
|
||||
atomic.AddInt64(&counter, 1)
|
||||
return nil
|
||||
}
|
||||
m.ConnectionsFn = func() uint { return 2 }
|
||||
be := sema.NewBackend(m)
|
||||
fb := be.(restic.FreezeBackend)
|
||||
fb := be.(backend.FreezeBackend)
|
||||
|
||||
// Freeze backend
|
||||
fb.Freeze()
|
||||
@@ -218,7 +218,7 @@ func TestFreeze(t *testing.T) {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
h := restic.Handle{Type: restic.PackFile, Name: "foobar"}
|
||||
h := backend.Handle{Type: backend.PackFile, Name: "foobar"}
|
||||
test.OK(t, be.Save(context.TODO(), h, nil))
|
||||
}()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user