mirror of
https://github.com/octoleo/restic.git
synced 2024-11-26 23:06:32 +00:00
Add 'optimize' command that repacks blobs
This commit is contained in:
parent
cd948b56ac
commit
c4fc7b52ae
84
cmd/restic/cmd_optimize.go
Normal file
84
cmd/restic/cmd_optimize.go
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/restic/restic/backend"
|
||||||
|
"github.com/restic/restic/checker"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CmdOptimize struct {
|
||||||
|
global *GlobalOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
_, err := parser.AddCommand("optimize",
|
||||||
|
"optimize the repository",
|
||||||
|
"The optimize command reorganizes the repository and removes uneeded data",
|
||||||
|
&CmdOptimize{global: &globalOpts})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cmd CmdOptimize) Usage() string {
|
||||||
|
return "[optimize-options]"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cmd CmdOptimize) Execute(args []string) error {
|
||||||
|
if len(args) != 0 {
|
||||||
|
return errors.New("optimize has no arguments")
|
||||||
|
}
|
||||||
|
|
||||||
|
repo, err := cmd.global.OpenRepository()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.global.Verbosef("Create exclusive lock for repository\n")
|
||||||
|
lock, err := lockRepoExclusive(repo)
|
||||||
|
defer unlockRepo(lock)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
chkr := checker.New(repo)
|
||||||
|
|
||||||
|
cmd.global.Verbosef("Load indexes\n")
|
||||||
|
_, errs := chkr.LoadIndex()
|
||||||
|
|
||||||
|
if len(errs) > 0 {
|
||||||
|
for _, err := range errs {
|
||||||
|
cmd.global.Warnf("error: %v\n", err)
|
||||||
|
}
|
||||||
|
return fmt.Errorf("LoadIndex returned errors")
|
||||||
|
}
|
||||||
|
|
||||||
|
done := make(chan struct{})
|
||||||
|
errChan := make(chan error)
|
||||||
|
go chkr.Structure(errChan, done)
|
||||||
|
|
||||||
|
for err := range errChan {
|
||||||
|
if e, ok := err.(checker.TreeError); ok {
|
||||||
|
cmd.global.Warnf("error for tree %v:\n", e.ID.Str())
|
||||||
|
for _, treeErr := range e.Errors {
|
||||||
|
cmd.global.Warnf(" %v\n", treeErr)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cmd.global.Warnf("error: %v\n", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unusedBlobs := backend.NewIDSet(chkr.UnusedBlobs()...)
|
||||||
|
cmd.global.Verbosef("%d unused blobs found, repacking...\n", len(unusedBlobs))
|
||||||
|
|
||||||
|
repacker := checker.NewRepacker(repo, unusedBlobs)
|
||||||
|
err = repacker.Repack()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.global.Verbosef("repacking done\n")
|
||||||
|
return nil
|
||||||
|
}
|
@ -61,7 +61,7 @@ func cmdBackupExcludes(t testing.TB, global GlobalOptions, target []string, pare
|
|||||||
OK(t, cmd.Execute(target))
|
OK(t, cmd.Execute(target))
|
||||||
}
|
}
|
||||||
|
|
||||||
func cmdList(t testing.TB, global GlobalOptions, tpe string) []backend.ID {
|
func cmdList(t testing.TB, global GlobalOptions, tpe string) backend.IDs {
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
global.stdout = &buf
|
global.stdout = &buf
|
||||||
cmd := &CmdList{global: &global}
|
cmd := &CmdList{global: &global}
|
||||||
@ -87,7 +87,11 @@ func cmdRestoreIncludes(t testing.TB, global GlobalOptions, dir string, snapshot
|
|||||||
}
|
}
|
||||||
|
|
||||||
func cmdCheck(t testing.TB, global GlobalOptions) {
|
func cmdCheck(t testing.TB, global GlobalOptions) {
|
||||||
cmd := &CmdCheck{global: &global, ReadData: true}
|
cmd := &CmdCheck{
|
||||||
|
global: &global,
|
||||||
|
ReadData: true,
|
||||||
|
CheckUnused: true,
|
||||||
|
}
|
||||||
OK(t, cmd.Execute(nil))
|
OK(t, cmd.Execute(nil))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,6 +109,11 @@ func cmdRebuildIndex(t testing.TB, global GlobalOptions) {
|
|||||||
OK(t, cmd.Execute(nil))
|
OK(t, cmd.Execute(nil))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func cmdOptimize(t testing.TB, global GlobalOptions) {
|
||||||
|
cmd := &CmdOptimize{global: &global}
|
||||||
|
OK(t, cmd.Execute(nil))
|
||||||
|
}
|
||||||
|
|
||||||
func cmdLs(t testing.TB, global GlobalOptions, snapshotID string) []string {
|
func cmdLs(t testing.TB, global GlobalOptions, snapshotID string) []string {
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
global.stdout = &buf
|
global.stdout = &buf
|
||||||
@ -689,3 +698,17 @@ func TestRebuildIndexAlwaysFull(t *testing.T) {
|
|||||||
repository.IndexFull = func(*repository.Index) bool { return true }
|
repository.IndexFull = func(*repository.Index) bool { return true }
|
||||||
TestRebuildIndex(t)
|
TestRebuildIndex(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestOptimizeRemoveUnusedBlobs(t *testing.T) {
|
||||||
|
withTestEnvironment(t, func(env *testEnvironment, global GlobalOptions) {
|
||||||
|
datafile := filepath.Join("..", "..", "checker", "testdata", "checker-test-repo.tar.gz")
|
||||||
|
SetupTarTestFixture(t, env.base, datafile)
|
||||||
|
|
||||||
|
// snapshotIDs := cmdList(t, global, "snapshots")
|
||||||
|
// t.Logf("snapshots: %v", snapshotIDs)
|
||||||
|
|
||||||
|
OK(t, os.Remove(filepath.Join(env.repo, "snapshots", "a13c11e582b77a693dd75ab4e3a3ba96538a056594a4b9076e4cacebe6e06d43")))
|
||||||
|
cmdOptimize(t, global)
|
||||||
|
cmdCheck(t, global)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user