2019-05-27 13:51:49 +00:00
|
|
|
package ethdb
|
|
|
|
|
|
|
|
import (
|
2020-10-27 22:30:18 +00:00
|
|
|
"bytes"
|
2020-06-04 09:35:42 +00:00
|
|
|
"context"
|
2020-11-30 11:49:52 +00:00
|
|
|
"encoding/binary"
|
|
|
|
"errors"
|
2020-10-29 13:19:31 +00:00
|
|
|
"fmt"
|
2020-10-27 22:30:18 +00:00
|
|
|
"strings"
|
|
|
|
"sync"
|
2020-11-01 08:28:11 +00:00
|
|
|
"time"
|
2020-10-27 22:30:18 +00:00
|
|
|
"unsafe"
|
|
|
|
|
|
|
|
"github.com/google/btree"
|
2021-05-20 18:25:53 +00:00
|
|
|
"github.com/ledgerwatch/erigon/common"
|
|
|
|
"github.com/ledgerwatch/erigon/common/dbutils"
|
|
|
|
"github.com/ledgerwatch/erigon/log"
|
|
|
|
"github.com/ledgerwatch/erigon/metrics"
|
2019-05-27 13:51:49 +00:00
|
|
|
)
|
|
|
|
|
2020-07-21 08:33:03 +00:00
|
|
|
var (
|
2021-03-29 03:58:45 +00:00
|
|
|
dbCommitBigBatchTimer = metrics.NewRegisteredTimer("db/commit/big_batch", nil)
|
2020-07-21 08:33:03 +00:00
|
|
|
)
|
2020-06-15 12:30:54 +00:00
|
|
|
|
2019-05-27 13:51:49 +00:00
|
|
|
type mutation struct {
|
2020-10-27 22:30:18 +00:00
|
|
|
puts *btree.BTree
|
2021-06-16 04:00:35 +00:00
|
|
|
db Database
|
2020-10-27 22:30:18 +00:00
|
|
|
searchItem MutationItem
|
2021-06-16 04:00:35 +00:00
|
|
|
mu sync.RWMutex
|
2020-10-27 22:30:18 +00:00
|
|
|
size int
|
|
|
|
}
|
|
|
|
|
|
|
|
type MutationItem struct {
|
|
|
|
table string
|
|
|
|
key []byte
|
|
|
|
value []byte
|
|
|
|
}
|
|
|
|
|
2021-04-25 04:20:50 +00:00
|
|
|
func NewBatch(tx RwTx) *mutation {
|
2021-04-05 06:00:35 +00:00
|
|
|
return &mutation{
|
2021-04-20 14:41:46 +00:00
|
|
|
db: &TxDb{tx: tx, cursors: map[string]Cursor{}},
|
2021-04-05 06:00:35 +00:00
|
|
|
puts: btree.New(32),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-27 22:30:18 +00:00
|
|
|
func (mi *MutationItem) Less(than btree.Item) bool {
|
|
|
|
i := than.(*MutationItem)
|
|
|
|
c := strings.Compare(mi.table, i.table)
|
|
|
|
if c != 0 {
|
|
|
|
return c < 0
|
|
|
|
}
|
|
|
|
return bytes.Compare(mi.key, i.key) < 0
|
2019-05-27 13:51:49 +00:00
|
|
|
}
|
|
|
|
|
2021-03-30 09:53:54 +00:00
|
|
|
func (m *mutation) RwKV() RwKV {
|
|
|
|
if casted, ok := m.db.(HasRwKV); ok {
|
|
|
|
return casted.RwKV()
|
2020-03-20 11:30:14 +00:00
|
|
|
}
|
2020-05-15 08:58:36 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-10-27 22:30:18 +00:00
|
|
|
func (m *mutation) getMem(table string, key []byte) ([]byte, bool) {
|
2019-05-27 13:51:49 +00:00
|
|
|
m.mu.RLock()
|
|
|
|
defer m.mu.RUnlock()
|
2020-10-27 22:30:18 +00:00
|
|
|
m.searchItem.table = table
|
|
|
|
m.searchItem.key = key
|
|
|
|
i := m.puts.Get(&m.searchItem)
|
|
|
|
if i == nil {
|
|
|
|
return nil, false
|
|
|
|
}
|
|
|
|
return i.(*MutationItem).value, true
|
2019-05-27 13:51:49 +00:00
|
|
|
}
|
|
|
|
|
2021-03-20 14:12:54 +00:00
|
|
|
func (m *mutation) IncrementSequence(bucket string, amount uint64) (res uint64, err error) {
|
2020-11-30 11:49:52 +00:00
|
|
|
v, ok := m.getMem(dbutils.Sequence, []byte(bucket))
|
|
|
|
if !ok && m.db != nil {
|
|
|
|
v, err = m.db.Get(dbutils.Sequence, []byte(bucket))
|
|
|
|
if err != nil && !errors.Is(err, ErrKeyNotFound) {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
var currentV uint64 = 0
|
|
|
|
if len(v) > 0 {
|
|
|
|
currentV = binary.BigEndian.Uint64(v)
|
|
|
|
}
|
|
|
|
|
|
|
|
newVBytes := make([]byte, 8)
|
|
|
|
binary.BigEndian.PutUint64(newVBytes, currentV+amount)
|
|
|
|
if err = m.Put(dbutils.Sequence, []byte(bucket), newVBytes); err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return currentV, nil
|
2020-11-14 13:48:29 +00:00
|
|
|
}
|
2021-03-20 14:12:54 +00:00
|
|
|
func (m *mutation) ReadSequence(bucket string) (res uint64, err error) {
|
|
|
|
v, ok := m.getMem(dbutils.Sequence, []byte(bucket))
|
|
|
|
if !ok && m.db != nil {
|
|
|
|
v, err = m.db.Get(dbutils.Sequence, []byte(bucket))
|
|
|
|
if err != nil && !errors.Is(err, ErrKeyNotFound) {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
var currentV uint64 = 0
|
|
|
|
if len(v) > 0 {
|
|
|
|
currentV = binary.BigEndian.Uint64(v)
|
|
|
|
}
|
|
|
|
|
|
|
|
return currentV, nil
|
|
|
|
}
|
2020-11-14 13:48:29 +00:00
|
|
|
|
2019-05-27 13:51:49 +00:00
|
|
|
// Can only be called from the worker thread
|
2021-04-05 06:00:35 +00:00
|
|
|
func (m *mutation) GetOne(table string, key []byte) ([]byte, error) {
|
2020-10-27 22:30:18 +00:00
|
|
|
if value, ok := m.getMem(table, key); ok {
|
2019-05-27 13:51:49 +00:00
|
|
|
if value == nil {
|
2021-04-05 06:00:35 +00:00
|
|
|
return nil, nil
|
2019-05-27 13:51:49 +00:00
|
|
|
}
|
|
|
|
return value, nil
|
|
|
|
}
|
|
|
|
if m.db != nil {
|
2021-04-05 06:00:35 +00:00
|
|
|
// TODO: simplify when tx can no longer be parent of mutation
|
|
|
|
value, err := m.db.Get(table, key)
|
|
|
|
if err != nil && !errors.Is(err, ErrKeyNotFound) {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return value, nil
|
2019-05-27 13:51:49 +00:00
|
|
|
}
|
2021-04-05 06:00:35 +00:00
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Can only be called from the worker thread
|
|
|
|
func (m *mutation) Get(table string, key []byte) ([]byte, error) {
|
|
|
|
value, err := m.GetOne(table, key)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if value == nil {
|
|
|
|
return nil, ErrKeyNotFound
|
|
|
|
}
|
|
|
|
|
|
|
|
return value, nil
|
2019-05-27 13:51:49 +00:00
|
|
|
}
|
|
|
|
|
2020-10-27 22:30:18 +00:00
|
|
|
func (m *mutation) Last(table string) ([]byte, []byte, error) {
|
|
|
|
return m.db.Last(table)
|
2020-08-12 03:49:52 +00:00
|
|
|
}
|
|
|
|
|
2020-10-27 22:30:18 +00:00
|
|
|
func (m *mutation) hasMem(table string, key []byte) bool {
|
2020-04-20 10:35:33 +00:00
|
|
|
m.mu.RLock()
|
|
|
|
defer m.mu.RUnlock()
|
2020-10-27 22:30:18 +00:00
|
|
|
m.searchItem.table = table
|
|
|
|
m.searchItem.key = key
|
|
|
|
return m.puts.Has(&m.searchItem)
|
2019-05-27 13:51:49 +00:00
|
|
|
}
|
|
|
|
|
2020-10-27 22:30:18 +00:00
|
|
|
func (m *mutation) Has(table string, key []byte) (bool, error) {
|
|
|
|
if m.hasMem(table, key) {
|
2019-05-27 13:51:49 +00:00
|
|
|
return true, nil
|
|
|
|
}
|
|
|
|
if m.db != nil {
|
2020-10-27 22:30:18 +00:00
|
|
|
return m.db.Has(table, key)
|
2019-05-27 13:51:49 +00:00
|
|
|
}
|
|
|
|
return false, nil
|
|
|
|
}
|
|
|
|
|
2020-10-27 22:30:18 +00:00
|
|
|
func (m *mutation) Put(table string, key []byte, value []byte) error {
|
2019-05-27 13:51:49 +00:00
|
|
|
m.mu.Lock()
|
|
|
|
defer m.mu.Unlock()
|
2019-11-21 18:38:00 +00:00
|
|
|
|
2020-10-27 22:30:18 +00:00
|
|
|
newMi := &MutationItem{table: table, key: key, value: value}
|
|
|
|
i := m.puts.ReplaceOrInsert(newMi)
|
|
|
|
m.size += int(unsafe.Sizeof(newMi)) + len(key) + len(value)
|
|
|
|
if i != nil {
|
|
|
|
oldMi := i.(*MutationItem)
|
|
|
|
m.size -= (int(unsafe.Sizeof(oldMi)) + len(oldMi.key) + len(oldMi.value))
|
|
|
|
}
|
2019-05-27 13:51:49 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-10-27 22:30:18 +00:00
|
|
|
func (m *mutation) Append(table string, key []byte, value []byte) error {
|
|
|
|
return m.Put(table, key, value)
|
2020-09-01 06:48:25 +00:00
|
|
|
}
|
|
|
|
|
2020-11-28 14:24:47 +00:00
|
|
|
func (m *mutation) AppendDup(table string, key []byte, value []byte) error {
|
|
|
|
return m.Put(table, key, value)
|
|
|
|
}
|
|
|
|
|
2019-05-27 13:51:49 +00:00
|
|
|
func (m *mutation) MultiPut(tuples ...[]byte) (uint64, error) {
|
|
|
|
m.mu.Lock()
|
|
|
|
defer m.mu.Unlock()
|
|
|
|
l := len(tuples)
|
|
|
|
for i := 0; i < l; i += 3 {
|
2020-10-27 22:30:18 +00:00
|
|
|
newMi := &MutationItem{table: string(tuples[i]), key: tuples[i+1], value: tuples[i+2]}
|
|
|
|
i := m.puts.ReplaceOrInsert(newMi)
|
|
|
|
m.size += int(unsafe.Sizeof(newMi)) + len(newMi.key) + len(newMi.value)
|
|
|
|
if i != nil {
|
|
|
|
oldMi := i.(*MutationItem)
|
|
|
|
m.size -= (int(unsafe.Sizeof(oldMi)) + len(oldMi.key) + len(oldMi.value))
|
|
|
|
}
|
2019-05-27 13:51:49 +00:00
|
|
|
}
|
|
|
|
return 0, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *mutation) BatchSize() int {
|
|
|
|
m.mu.RLock()
|
|
|
|
defer m.mu.RUnlock()
|
2020-10-27 22:30:18 +00:00
|
|
|
return m.size
|
2019-05-27 13:51:49 +00:00
|
|
|
}
|
|
|
|
|
2021-06-05 15:17:04 +00:00
|
|
|
func (m *mutation) ForEach(bucket string, fromPrefix []byte, walker func(k, v []byte) error) error {
|
2019-12-20 12:25:40 +00:00
|
|
|
m.panicOnEmptyDB()
|
2021-06-05 15:17:04 +00:00
|
|
|
return m.db.ForEach(bucket, fromPrefix, walker)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *mutation) ForPrefix(bucket string, prefix []byte, walker func(k, v []byte) error) error {
|
|
|
|
m.panicOnEmptyDB()
|
|
|
|
return m.db.ForPrefix(bucket, prefix, walker)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *mutation) ForAmount(bucket string, prefix []byte, amount uint32, walker func(k, v []byte) error) error {
|
|
|
|
m.panicOnEmptyDB()
|
|
|
|
return m.db.ForAmount(bucket, prefix, amount, walker)
|
2019-05-27 13:51:49 +00:00
|
|
|
}
|
|
|
|
|
2020-10-29 13:19:31 +00:00
|
|
|
func (m *mutation) Delete(table string, k, v []byte) error {
|
|
|
|
if v != nil {
|
2020-11-16 12:08:28 +00:00
|
|
|
return m.db.Delete(table, k, v) // TODO: mutation to support DupSort deletes
|
2020-10-29 13:19:31 +00:00
|
|
|
}
|
|
|
|
//m.puts.Delete(table, k)
|
|
|
|
return m.Put(table, k, nil)
|
2019-05-27 13:51:49 +00:00
|
|
|
}
|
|
|
|
|
2020-09-28 17:18:36 +00:00
|
|
|
func (m *mutation) CommitAndBegin(ctx context.Context) error {
|
2021-03-22 12:41:52 +00:00
|
|
|
err := m.Commit()
|
2020-08-24 11:07:59 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-09-28 17:18:36 +00:00
|
|
|
func (m *mutation) RollbackAndBegin(ctx context.Context) error {
|
|
|
|
m.Rollback()
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-03-21 13:15:25 +00:00
|
|
|
func (m *mutation) doCommit(tx RwTx) error {
|
2020-10-27 22:30:18 +00:00
|
|
|
var prevTable string
|
2021-03-21 13:15:25 +00:00
|
|
|
var c RwCursor
|
2020-10-27 22:30:18 +00:00
|
|
|
var innerErr error
|
|
|
|
var isEndOfBucket bool
|
2020-11-01 08:28:11 +00:00
|
|
|
logEvery := time.NewTicker(30 * time.Second)
|
|
|
|
defer logEvery.Stop()
|
|
|
|
count := 0
|
|
|
|
total := float64(m.puts.Len())
|
|
|
|
|
2020-10-27 22:30:18 +00:00
|
|
|
m.puts.Ascend(func(i btree.Item) bool {
|
|
|
|
mi := i.(*MutationItem)
|
|
|
|
if mi.table != prevTable {
|
|
|
|
if c != nil {
|
|
|
|
c.Close()
|
|
|
|
}
|
2021-04-02 06:36:49 +00:00
|
|
|
var err error
|
|
|
|
c, err = tx.RwCursor(mi.table)
|
|
|
|
if err != nil {
|
|
|
|
innerErr = err
|
|
|
|
return false
|
|
|
|
}
|
2020-10-27 22:30:18 +00:00
|
|
|
prevTable = mi.table
|
|
|
|
firstKey, _, err := c.Seek(mi.key)
|
|
|
|
if err != nil {
|
|
|
|
innerErr = err
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
isEndOfBucket = firstKey == nil
|
|
|
|
}
|
|
|
|
if isEndOfBucket {
|
|
|
|
if len(mi.value) > 0 {
|
|
|
|
if err := c.Append(mi.key, mi.value); err != nil {
|
|
|
|
innerErr = err
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if len(mi.value) == 0 {
|
2020-10-29 13:19:31 +00:00
|
|
|
if err := c.Delete(mi.key, nil); err != nil {
|
2020-10-27 22:30:18 +00:00
|
|
|
innerErr = err
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if err := c.Put(mi.key, mi.value); err != nil {
|
|
|
|
innerErr = err
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
2020-11-01 08:28:11 +00:00
|
|
|
|
|
|
|
count++
|
|
|
|
|
|
|
|
select {
|
|
|
|
default:
|
|
|
|
case <-logEvery.C:
|
|
|
|
progress := fmt.Sprintf("%.1fM/%.1fM", float64(count)/1_000_000, total/1_000_000)
|
|
|
|
log.Info("Write to db", "progress", progress, "current table", mi.table)
|
2021-05-06 10:02:50 +00:00
|
|
|
tx.CollectMetrics()
|
2020-11-01 08:28:11 +00:00
|
|
|
}
|
2020-10-27 22:30:18 +00:00
|
|
|
return true
|
|
|
|
})
|
2021-05-06 10:02:50 +00:00
|
|
|
tx.CollectMetrics()
|
2020-10-27 22:30:18 +00:00
|
|
|
return innerErr
|
|
|
|
}
|
|
|
|
|
2021-03-22 12:41:52 +00:00
|
|
|
func (m *mutation) Commit() error {
|
2019-05-27 13:51:49 +00:00
|
|
|
if m.db == nil {
|
2021-03-22 12:41:52 +00:00
|
|
|
return nil
|
2019-05-27 13:51:49 +00:00
|
|
|
}
|
|
|
|
m.mu.Lock()
|
|
|
|
defer m.mu.Unlock()
|
2020-10-27 22:30:18 +00:00
|
|
|
if tx, ok := m.db.(HasTx); ok {
|
2021-03-21 13:15:25 +00:00
|
|
|
if err := m.doCommit(tx.Tx().(RwTx)); err != nil {
|
2021-03-22 12:41:52 +00:00
|
|
|
return err
|
2020-10-27 22:30:18 +00:00
|
|
|
}
|
|
|
|
} else {
|
2021-03-30 09:53:54 +00:00
|
|
|
if err := m.db.(HasRwKV).RwKV().Update(context.Background(), func(tx RwTx) error {
|
2020-10-27 22:30:18 +00:00
|
|
|
return m.doCommit(tx)
|
|
|
|
}); err != nil {
|
2021-03-22 12:41:52 +00:00
|
|
|
return err
|
2019-11-21 18:38:00 +00:00
|
|
|
}
|
2020-10-27 14:35:25 +00:00
|
|
|
}
|
|
|
|
|
2020-10-27 22:30:18 +00:00
|
|
|
m.puts.Clear(false /* addNodesToFreelist */)
|
|
|
|
m.size = 0
|
2021-03-22 12:41:52 +00:00
|
|
|
return nil
|
2019-05-27 13:51:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (m *mutation) Rollback() {
|
|
|
|
m.mu.Lock()
|
|
|
|
defer m.mu.Unlock()
|
2020-10-27 22:30:18 +00:00
|
|
|
m.puts.Clear(false /* addNodesToFreelist */)
|
|
|
|
m.size = 0
|
2019-05-27 13:51:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (m *mutation) Keys() ([][]byte, error) {
|
|
|
|
m.mu.RLock()
|
|
|
|
defer m.mu.RUnlock()
|
2020-05-23 10:27:05 +00:00
|
|
|
tuples := common.NewTuples(m.puts.Len(), 2, 1)
|
2020-10-27 22:30:18 +00:00
|
|
|
var innerErr error
|
|
|
|
m.puts.Ascend(func(i btree.Item) bool {
|
|
|
|
mi := i.(*MutationItem)
|
|
|
|
if err := tuples.Append([]byte(mi.table), mi.key); err != nil {
|
|
|
|
innerErr = err
|
|
|
|
return false
|
2019-11-21 18:38:00 +00:00
|
|
|
}
|
2020-10-27 22:30:18 +00:00
|
|
|
return true
|
|
|
|
})
|
|
|
|
return tuples.Values, innerErr
|
2019-05-27 13:51:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (m *mutation) Close() {
|
|
|
|
m.Rollback()
|
|
|
|
}
|
|
|
|
|
2019-10-30 17:33:01 +00:00
|
|
|
func (m *mutation) NewBatch() DbWithPendingMutations {
|
2019-05-27 13:51:49 +00:00
|
|
|
mm := &mutation{
|
2020-04-15 09:33:22 +00:00
|
|
|
db: m,
|
2020-10-27 22:30:18 +00:00
|
|
|
puts: btree.New(32),
|
2019-05-27 13:51:49 +00:00
|
|
|
}
|
|
|
|
return mm
|
|
|
|
}
|
|
|
|
|
2020-10-25 08:38:55 +00:00
|
|
|
func (m *mutation) Begin(ctx context.Context, flags TxFlags) (DbWithPendingMutations, error) {
|
|
|
|
return m.db.Begin(ctx, flags)
|
2020-08-17 06:45:52 +00:00
|
|
|
}
|
|
|
|
|
2021-04-03 06:26:00 +00:00
|
|
|
func (m *mutation) BeginGetter(ctx context.Context) (GetterTx, error) {
|
|
|
|
return m.db.BeginGetter(ctx)
|
2021-03-30 09:53:54 +00:00
|
|
|
}
|
|
|
|
|
2019-12-20 12:25:40 +00:00
|
|
|
func (m *mutation) panicOnEmptyDB() {
|
|
|
|
if m.db == nil {
|
|
|
|
panic("Not implemented")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-27 13:51:49 +00:00
|
|
|
func (m *mutation) MemCopy() Database {
|
2019-12-20 12:25:40 +00:00
|
|
|
m.panicOnEmptyDB()
|
|
|
|
return m.db
|
2019-05-27 13:51:49 +00:00
|
|
|
}
|
|
|
|
|
2021-03-30 09:53:54 +00:00
|
|
|
func (m *mutation) SetRwKV(kv RwKV) {
|
|
|
|
m.db.(HasRwKV).SetRwKV(kv)
|
2020-10-28 03:18:10 +00:00
|
|
|
}
|