github.com/unicornultrafoundation/go-u2u@v1.0.0-rc1.0.20240205080301-e74a83d3fadc/utils/dbutil/compactdb/compactdb.go (about) 1 package compactdb 2 3 import ( 4 "errors" 5 "math/big" 6 "strconv" 7 8 "github.com/unicornultrafoundation/go-helios/u2udb" 9 "github.com/unicornultrafoundation/go-helios/u2udb/table" 10 11 "github.com/unicornultrafoundation/go-u2u/utils" 12 ) 13 14 type contCompacter struct { 15 u2udb.Store 16 prev []byte 17 } 18 19 func (s *contCompacter) Compact(_ []byte, limit []byte) error { 20 err := s.Store.Compact(s.prev, limit) 21 if err != nil { 22 return err 23 } 24 s.prev = limit 25 return nil 26 } 27 28 func compact(db *contCompacter, prefix []byte, iters int) error { 29 // use heuristic to locate tables 30 nonEmptyPrefixes := make([]byte, 0, 256) 31 for b := 0; b < 256; b++ { 32 if !isEmptyDB(table.New(db, append(prefix, byte(b)))) { 33 nonEmptyPrefixes = append(nonEmptyPrefixes, byte(b)) 34 } 35 } 36 if len(nonEmptyPrefixes) == 0 { 37 return nil 38 } 39 if len(nonEmptyPrefixes) != 1 && len(nonEmptyPrefixes) < 50 { 40 // compact each table individually 41 for _, b := range nonEmptyPrefixes { 42 if err := compact(db, append(prefix, b), iters); err != nil { 43 return err 44 } 45 } 46 return nil 47 } 48 49 // once a table is located, split the range into *iters* chunks for compaction 50 prefixed := utils.NewTableOrSelf(db, append(prefix)) 51 first, _, diff := keysRange(prefixed) 52 if diff.Cmp(big.NewInt(int64(iters+10000))) < 0 { 53 // skip if too few keys and compact it along with next range 54 return nil 55 } 56 firstBn := new(big.Int).SetBytes(first) 57 for i := iters; i >= 1; i-- { 58 until := addToKey(firstBn, new(big.Int).Div(diff, big.NewInt(int64(i))), len(first)) 59 if err := prefixed.Compact(nil, until); err != nil { 60 return err 61 } 62 } 63 return nil 64 } 65 66 func Compact(db u2udb.Store, loggingName string, sizePerIter uint64) error { 67 loggedDB := &loggedCompacter{ 68 Store: db, 69 name: loggingName, 70 quit: make(chan struct{}), 71 } 72 loggedDB.StartLogging() 73 defer loggedDB.StopLogging() 74 75 // scale number of iterations based on total DB size and sizePerIter 76 diskSizeStr, err := db.Stat("disk.size") 77 if err != nil { 78 return err 79 } 80 var nDiskSize int64 81 if nDiskSize, err = strconv.ParseInt(diskSizeStr, 10, 64); err != nil || nDiskSize < 0 { 82 return errors.New("bad syntax of disk size entry") 83 } 84 85 iters := uint64(nDiskSize) / sizePerIter 86 if iters <= 1 { 87 // short circuit if too few iterations 88 return loggedDB.Compact(nil, nil) 89 } 90 91 compacter := &contCompacter{ 92 Store: loggedDB, 93 } 94 err = compact(compacter, []byte{}, int(iters)) 95 if err != nil { 96 return err 97 } 98 return compacter.Compact(nil, nil) 99 }