mirror of https://github.com/restic/restic.git
new implementation of limiting repository size during backup
Changed saveAndEncrypt(...) to include tracking of current size and raising an error "MaxCapacityExceeded" when the limit has been exceeded. Added CurrentRepositorySize(ctx) to report current size Added MaxCapacityExceeded() to query if limit has been execced Added IsRepositoryLimitActive() to query if size monitoring is active In addition an interface definition added to fulfill the needs of internal/archiver for accessing repository functions. The issue descrition has been updated.
This commit is contained in:
parent
de9a040d27
commit
4ffb6f2c55
|
@ -0,0 +1,29 @@
|
|||
Enhancement: Limit repository size to a predefined maximum size during vackup
|
||||
|
||||
Restic backup can now limit the repository size to a given maximum value. The
|
||||
repository size limit can be specified as option `--max-repo-size`, with
|
||||
the usual meaning for size related options.
|
||||
|
||||
During backup, the current size is monitored by calculating its actual repository
|
||||
size plus the size of any new blob added during the backup process. Once the
|
||||
defined limit is exceeded, backup is prevented from creating new backup entries
|
||||
and the 'in progress' files are finalized, without adding large amounts of new data.
|
||||
|
||||
The size limit is a rough limit and cannot be taken as a precise limit,
|
||||
since indexes and snapshot file have to be finalized.
|
||||
Due to the highly parallel processing of a backup process
|
||||
the limit can be overshot by `packfile size` multiple times.
|
||||
With a current default of 16m for a packfile,
|
||||
you might experience differences of multiple megabytes between the expected limit
|
||||
and the actual size when the backup process terminates.
|
||||
|
||||
The new packfiles are created by the current backup process and a new snapshot
|
||||
will be created.
|
||||
|
||||
Currently the implemenation is incomplete. There are currently two indications
|
||||
that there is a incomplete backup:
|
||||
field `PartialSnapshot` exist and is set to true and
|
||||
the snapshot is tagged as `partial-snapshot`.
|
||||
|
||||
https://github.com/restic/restic/issues/4583
|
||||
https://github.com/restic/restic/pull/5215
|
|
@ -51,6 +51,8 @@ type Repository struct {
|
|||
allocDec sync.Once
|
||||
enc *zstd.Encoder
|
||||
dec *zstd.Decoder
|
||||
|
||||
MaxRepoCapReached bool
|
||||
}
|
||||
|
||||
// internalRepository allows using SaveUnpacked and RemoveUnpacked with all FileTypes
|
||||
|
@ -62,6 +64,8 @@ type Options struct {
|
|||
Compression CompressionMode
|
||||
PackSize uint
|
||||
NoExtraVerify bool
|
||||
RepoSizeMax uint64
|
||||
repoCurSize uint64
|
||||
}
|
||||
|
||||
// CompressionMode configures if data should be compressed.
|
||||
|
@ -391,7 +395,60 @@ func (r *Repository) saveAndEncrypt(ctx context.Context, t restic.BlobType, data
|
|||
panic(fmt.Sprintf("invalid type: %v", t))
|
||||
}
|
||||
|
||||
return pm.SaveBlob(ctx, t, id, ciphertext, uncompressedLength)
|
||||
length, err := pm.SaveBlob(ctx, t, id, ciphertext, uncompressedLength)
|
||||
|
||||
var m sync.Mutex
|
||||
|
||||
// maximum repository capacity exceeded?
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
if r.opts.RepoSizeMax > 0 {
|
||||
r.opts.repoCurSize += uint64(length)
|
||||
if r.opts.repoCurSize > r.opts.RepoSizeMax {
|
||||
r.MaxRepoCapReached = true
|
||||
debug.Log("MaxCapacityExceeded")
|
||||
return length, errors.New("MaxCapacityExceeded")
|
||||
}
|
||||
}
|
||||
return length, err
|
||||
}
|
||||
|
||||
// CurrentRepositorySize counts the sizes of the filetypes snapshot, index and packs
|
||||
func (r *Repository) CurrentRepositorySize(ctx context.Context) (uint64, error) {
|
||||
curSize := uint64(0)
|
||||
if r.opts.RepoSizeMax > 0 {
|
||||
for _, ft := range []restic.FileType{restic.SnapshotFile, restic.IndexFile, restic.PackFile} {
|
||||
err := r.List(ctx, ft, func(_ restic.ID, size int64) error {
|
||||
curSize += uint64(size)
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
r.opts.repoCurSize = curSize
|
||||
return curSize, nil
|
||||
}
|
||||
|
||||
return 0, errors.New("repository maximum size has not been set")
|
||||
}
|
||||
|
||||
// MaxCapacityExceeded reports if repository has a limit and if it is exceeded
|
||||
func (r *Repository) MaxCapacityExceeded() bool {
|
||||
if r.opts.RepoSizeMax == 0 {
|
||||
return false
|
||||
}
|
||||
return r.MaxRepoCapReached
|
||||
}
|
||||
|
||||
func (r *Repository) IsRepositoryLimitActive() bool {
|
||||
return r.opts.RepoSizeMax > 0
|
||||
}
|
||||
|
||||
// CapacityChecker has to satisfy restic.Repository interface needs
|
||||
type CapacityChecker interface {
|
||||
MaxCapacityExceeded() bool
|
||||
IsRepositoryLimitActive() bool
|
||||
}
|
||||
|
||||
func (r *Repository) verifyCiphertext(buf []byte, uncompressedLength int, id restic.ID) error {
|
||||
|
|
Loading…
Reference in New Issue