forked from TrueCloudLab/restic
index: reduce size of compressed indexes
use the same index size for compressed and uncompressed indexes. Otherwise, decoding the index of a compressed repository requires significantly more memory.
This commit is contained in:
parent
77873f5a9d
commit
462b82a060
5 changed files with 10 additions and 30 deletions
|
@ -68,7 +68,7 @@ func TestRebuildIndexAlwaysFull(t *testing.T) {
|
||||||
defer func() {
|
defer func() {
|
||||||
index.IndexFull = indexFull
|
index.IndexFull = indexFull
|
||||||
}()
|
}()
|
||||||
index.IndexFull = func(*index.Index, bool) bool { return true }
|
index.IndexFull = func(*index.Index) bool { return true }
|
||||||
testRebuildIndex(t, nil)
|
testRebuildIndex(t, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -90,13 +90,12 @@ func (idx *Index) Final() bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
indexMaxBlobs = 50000
|
indexMaxBlobs = 50000
|
||||||
indexMaxBlobsCompressed = 3 * indexMaxBlobs
|
indexMaxAge = 10 * time.Minute
|
||||||
indexMaxAge = 10 * time.Minute
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// IndexFull returns true iff the index is "full enough" to be saved as a preliminary index.
|
// IndexFull returns true iff the index is "full enough" to be saved as a preliminary index.
|
||||||
var IndexFull = func(idx *Index, compress bool) bool {
|
var IndexFull = func(idx *Index) bool {
|
||||||
idx.m.RLock()
|
idx.m.RLock()
|
||||||
defer idx.m.RUnlock()
|
defer idx.m.RUnlock()
|
||||||
|
|
||||||
|
@ -107,18 +106,12 @@ var IndexFull = func(idx *Index, compress bool) bool {
|
||||||
blobs += idx.byType[typ].len()
|
blobs += idx.byType[typ].len()
|
||||||
}
|
}
|
||||||
age := time.Since(idx.created)
|
age := time.Since(idx.created)
|
||||||
var maxBlobs uint
|
|
||||||
if compress {
|
|
||||||
maxBlobs = indexMaxBlobsCompressed
|
|
||||||
} else {
|
|
||||||
maxBlobs = indexMaxBlobs
|
|
||||||
}
|
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case age >= indexMaxAge:
|
case age >= indexMaxAge:
|
||||||
debug.Log("index %p is old enough", idx, age)
|
debug.Log("index %p is old enough", idx, age)
|
||||||
return true
|
return true
|
||||||
case blobs >= maxBlobs:
|
case blobs >= indexMaxBlobs:
|
||||||
debug.Log("index %p has %d blobs", idx, blobs)
|
debug.Log("index %p has %d blobs", idx, blobs)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,6 @@ type MasterIndex struct {
|
||||||
idx []*Index
|
idx []*Index
|
||||||
pendingBlobs restic.BlobSet
|
pendingBlobs restic.BlobSet
|
||||||
idxMutex sync.RWMutex
|
idxMutex sync.RWMutex
|
||||||
compress bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewMasterIndex creates a new master index.
|
// NewMasterIndex creates a new master index.
|
||||||
|
@ -33,10 +32,6 @@ func (mi *MasterIndex) clear() {
|
||||||
mi.idx[0].Finalize()
|
mi.idx[0].Finalize()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mi *MasterIndex) MarkCompressed() {
|
|
||||||
mi.compress = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lookup queries all known Indexes for the ID and returns all matches.
|
// Lookup queries all known Indexes for the ID and returns all matches.
|
||||||
func (mi *MasterIndex) Lookup(bh restic.BlobHandle) (pbs []restic.PackedBlob) {
|
func (mi *MasterIndex) Lookup(bh restic.BlobHandle) (pbs []restic.PackedBlob) {
|
||||||
mi.idxMutex.RLock()
|
mi.idxMutex.RLock()
|
||||||
|
@ -211,7 +206,7 @@ func (mi *MasterIndex) finalizeFullIndexes() []*Index {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if IndexFull(idx, mi.compress) {
|
if IndexFull(idx) {
|
||||||
debug.Log("index %p is full", idx)
|
debug.Log("index %p is full", idx)
|
||||||
idx.Finalize()
|
idx.Finalize()
|
||||||
list = append(list, idx)
|
list = append(list, idx)
|
||||||
|
@ -417,7 +412,7 @@ func (mi *MasterIndex) Rewrite(ctx context.Context, repo restic.Unpacked, exclud
|
||||||
newIndex := NewIndex()
|
newIndex := NewIndex()
|
||||||
for task := range rewriteCh {
|
for task := range rewriteCh {
|
||||||
// always rewrite indexes using the old format, that include a pack that must be removed or that are not full
|
// always rewrite indexes using the old format, that include a pack that must be removed or that are not full
|
||||||
if !task.oldFormat && len(task.idx.Packs().Intersect(excludePacks)) == 0 && IndexFull(task.idx, mi.compress) {
|
if !task.oldFormat && len(task.idx.Packs().Intersect(excludePacks)) == 0 && IndexFull(task.idx) {
|
||||||
// make sure that each pack is only stored exactly once in the index
|
// make sure that each pack is only stored exactly once in the index
|
||||||
excludePacks.Merge(task.idx.Packs())
|
excludePacks.Merge(task.idx.Packs())
|
||||||
// index is already up to date
|
// index is already up to date
|
||||||
|
@ -433,7 +428,7 @@ func (mi *MasterIndex) Rewrite(ctx context.Context, repo restic.Unpacked, exclud
|
||||||
|
|
||||||
for pbs := range task.idx.EachByPack(wgCtx, excludePacks) {
|
for pbs := range task.idx.EachByPack(wgCtx, excludePacks) {
|
||||||
newIndex.StorePack(pbs.PackID, pbs.Blobs)
|
newIndex.StorePack(pbs.PackID, pbs.Blobs)
|
||||||
if IndexFull(newIndex, mi.compress) {
|
if IndexFull(newIndex) {
|
||||||
select {
|
select {
|
||||||
case saveCh <- newIndex:
|
case saveCh <- newIndex:
|
||||||
case <-wgCtx.Done():
|
case <-wgCtx.Done():
|
||||||
|
@ -527,7 +522,7 @@ func (mi *MasterIndex) SaveFallback(ctx context.Context, repo restic.SaverRemove
|
||||||
for pbs := range idx.EachByPack(wgCtx, excludePacks) {
|
for pbs := range idx.EachByPack(wgCtx, excludePacks) {
|
||||||
newIndex.StorePack(pbs.PackID, pbs.Blobs)
|
newIndex.StorePack(pbs.PackID, pbs.Blobs)
|
||||||
p.Add(1)
|
p.Add(1)
|
||||||
if IndexFull(newIndex, mi.compress) {
|
if IndexFull(newIndex) {
|
||||||
select {
|
select {
|
||||||
case ch <- newIndex:
|
case ch <- newIndex:
|
||||||
case <-wgCtx.Done():
|
case <-wgCtx.Done():
|
||||||
|
|
|
@ -588,19 +588,11 @@ func (r *Repository) ListPacksFromIndex(ctx context.Context, packs restic.IDSet)
|
||||||
// SetIndex instructs the repository to use the given index.
|
// SetIndex instructs the repository to use the given index.
|
||||||
func (r *Repository) SetIndex(i restic.MasterIndex) error {
|
func (r *Repository) SetIndex(i restic.MasterIndex) error {
|
||||||
r.idx = i.(*index.MasterIndex)
|
r.idx = i.(*index.MasterIndex)
|
||||||
r.configureIndex()
|
|
||||||
return r.prepareCache()
|
return r.prepareCache()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Repository) clearIndex() {
|
func (r *Repository) clearIndex() {
|
||||||
r.idx = index.NewMasterIndex()
|
r.idx = index.NewMasterIndex()
|
||||||
r.configureIndex()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Repository) configureIndex() {
|
|
||||||
if r.cfg.Version >= 2 {
|
|
||||||
r.idx.MarkCompressed()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadIndex loads all index files from the backend in parallel and stores them
|
// LoadIndex loads all index files from the backend in parallel and stores them
|
||||||
|
|
|
@ -376,7 +376,7 @@ func TestRepositoryIncrementalIndex(t *testing.T) {
|
||||||
func testRepositoryIncrementalIndex(t *testing.T, version uint) {
|
func testRepositoryIncrementalIndex(t *testing.T, version uint) {
|
||||||
repo, _ := repository.TestRepositoryWithVersion(t, version)
|
repo, _ := repository.TestRepositoryWithVersion(t, version)
|
||||||
|
|
||||||
index.IndexFull = func(*index.Index, bool) bool { return true }
|
index.IndexFull = func(*index.Index) bool { return true }
|
||||||
|
|
||||||
// add a few rounds of packs
|
// add a few rounds of packs
|
||||||
for j := 0; j < 5; j++ {
|
for j := 0; j < 5; j++ {
|
||||||
|
|
Loading…
Reference in a new issue