github.com/amazechain/amc@v0.1.3/modules/ethdb/olddb/mutation.go (about)

     1  package olddb
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"encoding/binary"
     7  	"fmt"
     8  	"strings"
     9  	"sync"
    10  	"time"
    11  	"unsafe"
    12  
    13  	"github.com/amazechain/amc/modules/ethdb"
    14  	"github.com/google/btree"
    15  	"github.com/ledgerwatch/erigon-lib/common"
    16  	"github.com/ledgerwatch/erigon-lib/kv"
    17  	"github.com/ledgerwatch/log/v3"
    18  )
    19  
    20  type mutation struct {
    21  	puts       *btree.BTree
    22  	db         kv.RwTx
    23  	quit       <-chan struct{}
    24  	clean      func()
    25  	searchItem MutationItem
    26  	mu         sync.RWMutex
    27  	size       int
    28  }
    29  
    30  type MutationItem struct {
    31  	table string
    32  	key   []byte
    33  	value []byte
    34  }
    35  
    36  // NewBatch - starts in-mem batch
    37  //
    38  // Common pattern:
    39  //
    40  // batch := db.NewBatch()
    41  // defer batch.Rollback()
    42  // ... some calculations on `batch`
    43  // batch.Commit()
    44  func NewBatch(tx kv.RwTx, quit <-chan struct{}) *mutation {
    45  	clean := func() {}
    46  	if quit == nil {
    47  		ch := make(chan struct{})
    48  		clean = func() { close(ch) }
    49  		quit = ch
    50  	}
    51  	return &mutation{
    52  		db:    tx,
    53  		puts:  btree.New(32),
    54  		quit:  quit,
    55  		clean: clean,
    56  	}
    57  }
    58  
    59  func (mi *MutationItem) Less(than btree.Item) bool {
    60  	i, ok := than.(*MutationItem)
    61  	if !ok {
    62  		log.Warn("Failed to convert btree.Item to MutationItem pointer")
    63  	}
    64  	c := strings.Compare(mi.table, i.table)
    65  	if c != 0 {
    66  		return c < 0
    67  	}
    68  	return bytes.Compare(mi.key, i.key) < 0
    69  }
    70  
    71  func (m *mutation) RwKV() kv.RwDB {
    72  	if casted, ok := m.db.(ethdb.HasRwKV); ok {
    73  		return casted.RwKV()
    74  	}
    75  	return nil
    76  }
    77  
    78  func (m *mutation) getMem(table string, key []byte) ([]byte, bool) {
    79  	m.mu.RLock()
    80  	defer m.mu.RUnlock()
    81  	m.searchItem.table = table
    82  	m.searchItem.key = key
    83  	i := m.puts.Get(&m.searchItem)
    84  	if i == nil {
    85  		return nil, false
    86  	}
    87  	return i.(*MutationItem).value, true
    88  }
    89  
    90  func (m *mutation) IncrementSequence(bucket string, amount uint64) (res uint64, err error) {
    91  	v, ok := m.getMem(kv.Sequence, []byte(bucket))
    92  	if !ok && m.db != nil {
    93  		v, err = m.db.GetOne(kv.Sequence, []byte(bucket))
    94  		if err != nil {
    95  			return 0, err
    96  		}
    97  	}
    98  
    99  	var currentV uint64 = 0
   100  	if len(v) > 0 {
   101  		currentV = binary.BigEndian.Uint64(v)
   102  	}
   103  
   104  	newVBytes := make([]byte, 8)
   105  	binary.BigEndian.PutUint64(newVBytes, currentV+amount)
   106  	if err = m.Put(kv.Sequence, []byte(bucket), newVBytes); err != nil {
   107  		return 0, err
   108  	}
   109  
   110  	return currentV, nil
   111  }
   112  func (m *mutation) ReadSequence(bucket string) (res uint64, err error) {
   113  	v, ok := m.getMem(kv.Sequence, []byte(bucket))
   114  	if !ok && m.db != nil {
   115  		v, err = m.db.GetOne(kv.Sequence, []byte(bucket))
   116  		if err != nil {
   117  			return 0, err
   118  		}
   119  	}
   120  	var currentV uint64 = 0
   121  	if len(v) > 0 {
   122  		currentV = binary.BigEndian.Uint64(v)
   123  	}
   124  
   125  	return currentV, nil
   126  }
   127  
   128  // Can only be called from the worker thread
   129  func (m *mutation) GetOne(table string, key []byte) ([]byte, error) {
   130  	if value, ok := m.getMem(table, key); ok {
   131  		if value == nil {
   132  			return nil, nil
   133  		}
   134  		return value, nil
   135  	}
   136  	if m.db != nil {
   137  		// TODO: simplify when tx can no longer be parent of mutation
   138  		value, err := m.db.GetOne(table, key)
   139  		if err != nil {
   140  			return nil, err
   141  		}
   142  
   143  		return value, nil
   144  	}
   145  	return nil, nil
   146  }
   147  
   148  // Can only be called from the worker thread
   149  func (m *mutation) Get(table string, key []byte) ([]byte, error) {
   150  	value, err := m.GetOne(table, key)
   151  	if err != nil {
   152  		return nil, err
   153  	}
   154  
   155  	if value == nil {
   156  		return nil, ethdb.ErrKeyNotFound
   157  	}
   158  
   159  	return value, nil
   160  }
   161  
   162  func (m *mutation) Last(table string) ([]byte, []byte, error) {
   163  	c, err := m.db.Cursor(table)
   164  	if err != nil {
   165  		return nil, nil, err
   166  	}
   167  	defer c.Close()
   168  	return c.Last()
   169  }
   170  
   171  func (m *mutation) hasMem(table string, key []byte) bool {
   172  	m.mu.RLock()
   173  	defer m.mu.RUnlock()
   174  	m.searchItem.table = table
   175  	m.searchItem.key = key
   176  	return m.puts.Has(&m.searchItem)
   177  }
   178  
   179  func (m *mutation) Has(table string, key []byte) (bool, error) {
   180  	if m.hasMem(table, key) {
   181  		return true, nil
   182  	}
   183  	if m.db != nil {
   184  		return m.db.Has(table, key)
   185  	}
   186  	return false, nil
   187  }
   188  
   189  func (m *mutation) Put(table string, k, v []byte) error {
   190  	m.mu.Lock()
   191  	defer m.mu.Unlock()
   192  
   193  	newMi := &MutationItem{table: table, key: k, value: v}
   194  	i := m.puts.ReplaceOrInsert(newMi)
   195  	m.size += int(unsafe.Sizeof(newMi)) + len(k) + len(v)
   196  	if i != nil {
   197  		oldMi := i.(*MutationItem)
   198  		m.size -= int(unsafe.Sizeof(oldMi)) + len(oldMi.key) + len(oldMi.value)
   199  	}
   200  	return nil
   201  }
   202  
   203  func (m *mutation) Append(table string, key []byte, value []byte) error {
   204  	return m.Put(table, key, value)
   205  }
   206  
   207  func (m *mutation) AppendDup(table string, key []byte, value []byte) error {
   208  	return m.Put(table, key, value)
   209  }
   210  
   211  func (m *mutation) BatchSize() int {
   212  	m.mu.RLock()
   213  	defer m.mu.RUnlock()
   214  	return m.size
   215  }
   216  
   217  func (m *mutation) ForEach(bucket string, fromPrefix []byte, walker func(k, v []byte) error) error {
   218  	m.panicOnEmptyDB()
   219  	return m.db.ForEach(bucket, fromPrefix, walker)
   220  }
   221  
   222  func (m *mutation) ForPrefix(bucket string, prefix []byte, walker func(k, v []byte) error) error {
   223  	m.panicOnEmptyDB()
   224  	return m.db.ForPrefix(bucket, prefix, walker)
   225  }
   226  
   227  func (m *mutation) ForAmount(bucket string, prefix []byte, amount uint32, walker func(k, v []byte) error) error {
   228  	m.panicOnEmptyDB()
   229  	return m.db.ForAmount(bucket, prefix, amount, walker)
   230  }
   231  
   232  func (m *mutation) Delete(table string, k []byte) error {
   233  	//m.puts.Delete(table, k)
   234  	return m.Put(table, k, nil)
   235  }
   236  
   237  func (m *mutation) doCommit(tx kv.RwTx) error {
   238  	var prevTable string
   239  	var c kv.RwCursor
   240  	var innerErr error
   241  	var isEndOfBucket bool
   242  	logEvery := time.NewTicker(30 * time.Second)
   243  	defer logEvery.Stop()
   244  	count := 0
   245  	total := float64(m.puts.Len())
   246  
   247  	m.puts.Ascend(func(i btree.Item) bool {
   248  		mi := i.(*MutationItem)
   249  		if mi.table != prevTable {
   250  			if c != nil {
   251  				c.Close()
   252  			}
   253  			var err error
   254  			c, err = tx.RwCursor(mi.table)
   255  			if err != nil {
   256  				innerErr = err
   257  				return false
   258  			}
   259  			prevTable = mi.table
   260  			firstKey, _, err := c.Seek(mi.key)
   261  			if err != nil {
   262  				innerErr = err
   263  				return false
   264  			}
   265  			isEndOfBucket = firstKey == nil
   266  		}
   267  		if isEndOfBucket {
   268  			if len(mi.value) > 0 {
   269  				if err := c.Append(mi.key, mi.value); err != nil {
   270  					innerErr = err
   271  					return false
   272  				}
   273  			}
   274  		} else if len(mi.value) == 0 {
   275  			if err := c.Delete(mi.key); err != nil {
   276  				innerErr = err
   277  				return false
   278  			}
   279  		} else {
   280  			if err := c.Put(mi.key, mi.value); err != nil {
   281  				innerErr = err
   282  				return false
   283  			}
   284  		}
   285  
   286  		count++
   287  
   288  		select {
   289  		default:
   290  		case <-logEvery.C:
   291  			progress := fmt.Sprintf("%.1fM/%.1fM", float64(count)/1_000_000, total/1_000_000)
   292  			log.Info("Write to db", "progress", progress, "current table", mi.table)
   293  			tx.CollectMetrics()
   294  		case <-m.quit:
   295  			innerErr = common.ErrStopped
   296  			return false
   297  		}
   298  		return true
   299  	})
   300  	tx.CollectMetrics()
   301  	return innerErr
   302  }
   303  
   304  func (m *mutation) Commit() error {
   305  	if m.db == nil {
   306  		return nil
   307  	}
   308  	m.mu.Lock()
   309  	defer m.mu.Unlock()
   310  	if err := m.doCommit(m.db); err != nil {
   311  		return err
   312  	}
   313  
   314  	m.puts.Clear(false /* addNodesToFreelist */)
   315  	m.size = 0
   316  	m.clean()
   317  	return nil
   318  }
   319  
   320  func (m *mutation) Rollback() {
   321  	m.mu.Lock()
   322  	defer m.mu.Unlock()
   323  	m.puts.Clear(false /* addNodesToFreelist */)
   324  	m.size = 0
   325  	m.clean()
   326  }
   327  
   328  func (m *mutation) Close() {
   329  	m.Rollback()
   330  }
   331  
   332  func (m *mutation) Begin(ctx context.Context, flags ethdb.TxFlags) (ethdb.DbWithPendingMutations, error) {
   333  	panic("mutation can't start transaction, because doesn't own it")
   334  }
   335  
   336  func (m *mutation) panicOnEmptyDB() {
   337  	if m.db == nil {
   338  		panic("Not implemented")
   339  	}
   340  }
   341  
   342  func (m *mutation) SetRwKV(kv kv.RwDB) {
   343  	m.db.(ethdb.HasRwKV).SetRwKV(kv)
   344  }