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:
Michael Eischer 2024-05-20 00:34:54 +02:00
parent 77873f5a9d
commit 462b82a060
5 changed files with 10 additions and 30 deletions

View file

@ -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)
} }

View file

@ -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
} }

View file

@ -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():

View file

@ -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

View file

@ -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++ {