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  }