added 'storage sweep' subcommand which removes unreferenced storage blocks

This commit is contained in:
Jarek Kowalski committed 2018-02-10 13:07:10 -08:00
1 parent 4971671f3d
commit 06b4bec246
6 files changed
+93 -8

No files matched your search

+9
View File
@@ -560,6 +560,15 @@ func (bm *Manager) WriteBlock(data []byte, prefix string) (string, error) {
return blockID, err
}
// IsStorageBlockInUse determines whether given storage block is in use by currently loaded pack indexes.
func (bm *Manager) IsStorageBlockInUse(storageBlockID string) bool {
bm.lock()
defer bm.unlock()
bm.ensurePackIndexesLoaded()
return bm.packBlockIDToIndex[storageBlockID] != nil
}
// Repackage reorganizes all pack blocks belonging to a given group that are not bigger than given size.
func (bm *Manager) Repackage(maxLength uint64) error {
bm.lock()
+19 -2
View File
@@ -21,7 +21,24 @@ func runShowBlockIndexesAction(context *kingpin.ParseContext) error {
rep := mustOpenRepository(nil)
defer rep.Close()
for _, blockID := range *blockIndexShowIDs {
blockIDs := *blockIndexShowIDs
if len(blockIDs) == 1 || blockIDs[0] == "active" {
b, err := rep.Blocks.ActiveIndexBlocks()
if err != nil {
return err
}
sort.Slice(b, func(i, j int) bool {
return b[i].Timestamp.Before(b[j].Timestamp)
})
blockIDs = nil
for _, bi := range b {
blockIDs = append(blockIDs, bi.BlockID)
}
}
for _, blockID := range blockIDs {
data, err := rep.Blocks.GetBlock(blockID)
if err != nil {
return fmt.Errorf("can't read block %q: %v", blockID, err)
@@ -33,7 +50,7 @@ func runShowBlockIndexesAction(context *kingpin.ParseContext) error {
}
for _, ndx := range d.Indexes {
fmt.Printf("pack %v len: %v created %v\n", ndx.PackBlockId, ndx.PackLength, time.Unix(0, int64(ndx.CreateTimeNanos)).Local())
fmt.Printf("pack:%v len:%v created:%v\n", ndx.PackBlockId, ndx.PackLength, time.Unix(0, int64(ndx.CreateTimeNanos)).Local())
type unpacked struct {
blockID string
offset uint32
+60
View File
@@ -0,0 +1,60 @@
package cli
import (
"fmt"
"time"
"github.com/kopia/kopia/repo"
kingpin "gopkg.in/alecthomas/kingpin.v2"
)
var (
storageSweepCommand = storageCommands.Command("sweep", "Remove unused storage blocks")
storageSweepAgeThreshold = storageSweepCommand.Flag("min-age", "Minimum age of storage block to delete").Default("24h").Duration()
storageSweepPrintUnused = storageSweepCommand.Flag("print", "Do not delete blocks, but print which ones would be deleted").Short('n').Bool()
)
func runStorageSweepAction(context *kingpin.ParseContext) error {
rep := mustOpenRepository(nil)
defer rep.Close()
indexBlocks, err := rep.Blocks.ActiveIndexBlocks()
if err != nil {
return err
}
inUseIndexBlocks := map[string]bool{}
for _, ib := range indexBlocks {
inUseIndexBlocks[ib.BlockID] = true
}
ch, cancel := rep.Storage.ListBlocks("")
defer cancel()
for bm := range ch {
if bm.Error != nil {
return bm.Error
}
age := time.Since(bm.TimeStamp)
inUse := rep.Blocks.IsStorageBlockInUse(bm.BlockID) || inUseIndexBlocks[bm.BlockID] || bm.BlockID == repo.FormatBlockID
keep := inUse || age < *storageSweepAgeThreshold
if !keep {
if *storageSweepPrintUnused {
fmt.Printf("unused block %v age %v\n", bm.BlockID, age)
} else {
fmt.Printf("deleting unused block %v age %v\n", bm.BlockID, age)
if err := rep.Storage.DeleteBlock(bm.BlockID); err != nil {
return err
}
}
}
}
return nil
}
func init() {
storageSweepCommand.Action(runStorageSweepAction)
}
+1 -1
View File
@@ -32,7 +32,7 @@ type ConnectOptions struct {
// Connect connects to the repository in the specified storage and persists the configuration and credentials in the file provided.
func Connect(ctx context.Context, configFile string, st storage.Storage, creds auth.Credentials, opt ConnectOptions) error {
formatBytes, err := st.GetBlock(formatBlockID, 0, -1)
formatBytes, err := st.GetBlock(FormatBlockID, 0, -1)
if err != nil {
return fmt.Errorf("unable to read format block: %v", err)
}
+3 -4
View File
@@ -14,9 +14,8 @@ import (
"github.com/kopia/kopia/storage"
)
const (
formatBlockID = "kopia.repository"
)
// FormatBlockID is the identifier of a storage block that describes repository format.
const FormatBlockID = "kopia.repository"
var (
purposeAESKey = []byte("AES")
@@ -59,7 +58,7 @@ func writeFormatBlock(st storage.Storage, f *formatBlock) error {
return fmt.Errorf("unable to marshal format block: %v", err)
}
if err := st.PutBlock(formatBlockID, buf.Bytes()); err != nil {
if err := st.PutBlock(FormatBlockID, buf.Bytes()); err != nil {
return fmt.Errorf("unable to write format block: %v", err)
}
+1 -1
View File
@@ -172,7 +172,7 @@ func readAndCacheFormatBlock(st storage.Storage, cacheDirectory string) (*format
}
}
b, err := st.GetBlock(formatBlockID, 0, -1)
b, err := st.GetBlock(FormatBlockID, 0, -1)
if err != nil {
return nil, err
}