github.com/ledgerwatch/erigon-lib@v1.0.0/kv/memdb/memory_mutation.go (about)

     1  /*
     2     Copyright 2022 Erigon contributors
     3     Licensed under the Apache License, Version 2.0 (the "License");
     4     you may not use this file except in compliance with the License.
     5     You may obtain a copy of the License at
     6         http://www.apache.org/licenses/LICENSE-2.0
     7     Unless required by applicable law or agreed to in writing, software
     8     distributed under the License is distributed on an "AS IS" BASIS,
     9     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    10     See the License for the specific language governing permissions and
    11     limitations under the License.
    12  */
    13  
    14  package memdb
    15  
    16  import (
    17  	"bytes"
    18  	"context"
    19  
    20  	"github.com/ledgerwatch/erigon-lib/common"
    21  	"github.com/ledgerwatch/erigon-lib/kv/iter"
    22  	"github.com/ledgerwatch/erigon-lib/kv/order"
    23  	"github.com/ledgerwatch/log/v3"
    24  
    25  	"github.com/ledgerwatch/erigon-lib/kv"
    26  	"github.com/ledgerwatch/erigon-lib/kv/mdbx"
    27  )
    28  
    29  type MemoryMutation struct {
    30  	memTx            kv.RwTx
    31  	memDb            kv.RwDB
    32  	deletedEntries   map[string]map[string]struct{}
    33  	clearedTables    map[string]struct{}
    34  	db               kv.Tx
    35  	statelessCursors map[string]kv.RwCursor
    36  }
    37  
    38  // NewMemoryBatch - starts in-mem batch
    39  //
    40  // Common pattern:
    41  //
    42  // batch := NewMemoryBatch(db, tmpDir)
    43  // defer batch.Rollback()
    44  // ... some calculations on `batch`
    45  // batch.Commit()
    46  func NewMemoryBatch(tx kv.Tx, tmpDir string) *MemoryMutation {
    47  	tmpDB := mdbx.NewMDBX(log.New()).InMem(tmpDir).MustOpen()
    48  	memTx, err := tmpDB.BeginRw(context.Background())
    49  	if err != nil {
    50  		panic(err)
    51  	}
    52  	if err := initSequences(tx, memTx); err != nil {
    53  		return nil
    54  	}
    55  
    56  	return &MemoryMutation{
    57  		db:             tx,
    58  		memDb:          tmpDB,
    59  		memTx:          memTx,
    60  		deletedEntries: make(map[string]map[string]struct{}),
    61  		clearedTables:  make(map[string]struct{}),
    62  	}
    63  }
    64  
    65  func NewMemoryBatchWithCustomDB(tx kv.Tx, db kv.RwDB, uTx kv.RwTx, tmpDir string) *MemoryMutation {
    66  	return &MemoryMutation{
    67  		db:             tx,
    68  		memDb:          db,
    69  		memTx:          uTx,
    70  		deletedEntries: make(map[string]map[string]struct{}),
    71  		clearedTables:  make(map[string]struct{}),
    72  	}
    73  }
    74  
    75  func (m *MemoryMutation) UpdateTxn(tx kv.Tx) {
    76  	m.db = tx
    77  	m.statelessCursors = nil
    78  }
    79  
    80  func (m *MemoryMutation) isTableCleared(table string) bool {
    81  	_, ok := m.clearedTables[table]
    82  	return ok
    83  }
    84  
    85  func (m *MemoryMutation) isEntryDeleted(table string, key []byte) bool {
    86  	_, ok := m.deletedEntries[table]
    87  	if !ok {
    88  		return ok
    89  	}
    90  	_, ok = m.deletedEntries[table][string(key)]
    91  	return ok
    92  }
    93  
    94  func (m *MemoryMutation) DBSize() (uint64, error) {
    95  	panic("not implemented")
    96  }
    97  
    98  func initSequences(db kv.Tx, memTx kv.RwTx) error {
    99  	cursor, err := db.Cursor(kv.Sequence)
   100  	if err != nil {
   101  		return err
   102  	}
   103  	for k, v, err := cursor.First(); k != nil; k, v, err = cursor.Next() {
   104  		if err != nil {
   105  			return err
   106  		}
   107  		if err := memTx.Put(kv.Sequence, k, v); err != nil {
   108  			return err
   109  		}
   110  	}
   111  	return nil
   112  }
   113  
   114  func (m *MemoryMutation) IncrementSequence(bucket string, amount uint64) (uint64, error) {
   115  	return m.memTx.IncrementSequence(bucket, amount)
   116  }
   117  
   118  func (m *MemoryMutation) ReadSequence(bucket string) (uint64, error) {
   119  	return m.memTx.ReadSequence(bucket)
   120  }
   121  
   122  func (m *MemoryMutation) ForAmount(bucket string, prefix []byte, amount uint32, walker func(k, v []byte) error) error {
   123  	if amount == 0 {
   124  		return nil
   125  	}
   126  	c, err := m.Cursor(bucket)
   127  	if err != nil {
   128  		return err
   129  	}
   130  	defer c.Close()
   131  
   132  	for k, v, err := c.Seek(prefix); k != nil && amount > 0; k, v, err = c.Next() {
   133  		if err != nil {
   134  			return err
   135  		}
   136  		if err := walker(k, v); err != nil {
   137  			return err
   138  		}
   139  		amount--
   140  	}
   141  	return nil
   142  }
   143  
   144  func (m *MemoryMutation) statelessCursor(table string) (kv.RwCursor, error) {
   145  	if m.statelessCursors == nil {
   146  		m.statelessCursors = make(map[string]kv.RwCursor)
   147  	}
   148  	c, ok := m.statelessCursors[table]
   149  	if !ok {
   150  		var err error
   151  		c, err = m.RwCursor(table)
   152  		if err != nil {
   153  			return nil, err
   154  		}
   155  		m.statelessCursors[table] = c
   156  	}
   157  	return c, nil
   158  }
   159  
   160  // Can only be called from the worker thread
   161  func (m *MemoryMutation) GetOne(table string, key []byte) ([]byte, error) {
   162  	c, err := m.statelessCursor(table)
   163  	if err != nil {
   164  		return nil, err
   165  	}
   166  	_, v, err := c.SeekExact(key)
   167  	return v, err
   168  }
   169  
   170  func (m *MemoryMutation) Last(table string) ([]byte, []byte, error) {
   171  	panic("not implemented. (MemoryMutation.Last)")
   172  }
   173  
   174  // Has return whether a key is present in a certain table.
   175  func (m *MemoryMutation) Has(table string, key []byte) (bool, error) {
   176  	c, err := m.statelessCursor(table)
   177  	if err != nil {
   178  		return false, err
   179  	}
   180  	k, _, err := c.Seek(key)
   181  	if err != nil {
   182  		return false, err
   183  	}
   184  	return bytes.Equal(key, k), nil
   185  }
   186  
   187  func (m *MemoryMutation) Put(table string, k, v []byte) error {
   188  	return m.memTx.Put(table, k, v)
   189  }
   190  
   191  func (m *MemoryMutation) Append(table string, key []byte, value []byte) error {
   192  	return m.memTx.Append(table, key, value)
   193  }
   194  
   195  func (m *MemoryMutation) AppendDup(table string, key []byte, value []byte) error {
   196  	c, err := m.statelessCursor(table)
   197  	if err != nil {
   198  		return err
   199  	}
   200  	return c.(*memoryMutationCursor).AppendDup(key, value)
   201  }
   202  
   203  func (m *MemoryMutation) ForEach(bucket string, fromPrefix []byte, walker func(k, v []byte) error) error {
   204  	c, err := m.Cursor(bucket)
   205  	if err != nil {
   206  		return err
   207  	}
   208  	defer c.Close()
   209  
   210  	for k, v, err := c.Seek(fromPrefix); k != nil; k, v, err = c.Next() {
   211  		if err != nil {
   212  			return err
   213  		}
   214  		if err := walker(k, v); err != nil {
   215  			return err
   216  		}
   217  	}
   218  	return nil
   219  }
   220  
   221  func (m *MemoryMutation) Prefix(table string, prefix []byte) (iter.KV, error) {
   222  	nextPrefix, ok := kv.NextSubtree(prefix)
   223  	if !ok {
   224  		return m.Stream(table, prefix, nil)
   225  	}
   226  	return m.Stream(table, prefix, nextPrefix)
   227  }
   228  func (m *MemoryMutation) Stream(table string, fromPrefix, toPrefix []byte) (iter.KV, error) {
   229  	panic("please implement me")
   230  }
   231  func (m *MemoryMutation) StreamAscend(table string, fromPrefix, toPrefix []byte, limit int) (iter.KV, error) {
   232  	panic("please implement me")
   233  }
   234  func (m *MemoryMutation) StreamDescend(table string, fromPrefix, toPrefix []byte, limit int) (iter.KV, error) {
   235  	panic("please implement me")
   236  }
   237  func (m *MemoryMutation) Range(table string, fromPrefix, toPrefix []byte) (iter.KV, error) {
   238  	panic("please implement me")
   239  }
   240  func (m *MemoryMutation) RangeAscend(table string, fromPrefix, toPrefix []byte, limit int) (iter.KV, error) {
   241  	panic("please implement me")
   242  }
   243  func (m *MemoryMutation) RangeDescend(table string, fromPrefix, toPrefix []byte, limit int) (iter.KV, error) {
   244  	panic("please implement me")
   245  }
   246  func (m *MemoryMutation) RangeDupSort(table string, key []byte, fromPrefix, toPrefix []byte, asc order.By, limit int) (iter.KV, error) {
   247  	panic("please implement me")
   248  }
   249  
   250  func (m *MemoryMutation) ForPrefix(bucket string, prefix []byte, walker func(k, v []byte) error) error {
   251  	c, err := m.Cursor(bucket)
   252  	if err != nil {
   253  		return err
   254  	}
   255  	defer c.Close()
   256  
   257  	for k, v, err := c.Seek(prefix); k != nil; k, v, err = c.Next() {
   258  		if err != nil {
   259  			return err
   260  		}
   261  		if !bytes.HasPrefix(k, prefix) {
   262  			break
   263  		}
   264  		if err := walker(k, v); err != nil {
   265  			return err
   266  		}
   267  	}
   268  	return nil
   269  }
   270  
   271  func (m *MemoryMutation) Delete(table string, k []byte) error {
   272  	if _, ok := m.deletedEntries[table]; !ok {
   273  		m.deletedEntries[table] = make(map[string]struct{})
   274  	}
   275  	m.deletedEntries[table][string(k)] = struct{}{}
   276  	return m.memTx.Delete(table, k)
   277  }
   278  
   279  func (m *MemoryMutation) Commit() error {
   280  	m.statelessCursors = nil
   281  	return nil
   282  }
   283  
   284  func (m *MemoryMutation) Rollback() {
   285  	m.memTx.Rollback()
   286  	m.memDb.Close()
   287  	m.statelessCursors = nil
   288  }
   289  
   290  func (m *MemoryMutation) Close() {
   291  	m.Rollback()
   292  }
   293  
   294  func (m *MemoryMutation) BucketSize(bucket string) (uint64, error) {
   295  	return m.memTx.BucketSize(bucket)
   296  }
   297  
   298  func (m *MemoryMutation) DropBucket(bucket string) error {
   299  	panic("Not implemented")
   300  }
   301  
   302  func (m *MemoryMutation) ExistsBucket(bucket string) (bool, error) {
   303  	panic("Not implemented")
   304  }
   305  
   306  func (m *MemoryMutation) ListBuckets() ([]string, error) {
   307  	panic("Not implemented")
   308  }
   309  
   310  func (m *MemoryMutation) ClearBucket(bucket string) error {
   311  	m.clearedTables[bucket] = struct{}{}
   312  	return m.memTx.ClearBucket(bucket)
   313  }
   314  
   315  func (m *MemoryMutation) CollectMetrics() {
   316  }
   317  
   318  func (m *MemoryMutation) CreateBucket(bucket string) error {
   319  	return m.memTx.CreateBucket(bucket)
   320  }
   321  
   322  func (m *MemoryMutation) Flush(tx kv.RwTx) error {
   323  	// Obtain buckets touched.
   324  	buckets, err := m.memTx.ListBuckets()
   325  	if err != nil {
   326  		return err
   327  	}
   328  	// Obliterate buckets who are to be deleted
   329  	for bucket := range m.clearedTables {
   330  		if err := tx.ClearBucket(bucket); err != nil {
   331  			return err
   332  		}
   333  	}
   334  	// Obliterate entries who are to be deleted
   335  	for bucket, keys := range m.deletedEntries {
   336  		for key := range keys {
   337  			if err := tx.Delete(bucket, []byte(key)); err != nil {
   338  				return err
   339  			}
   340  		}
   341  	}
   342  	// Iterate over each bucket and apply changes accordingly.
   343  	for _, bucket := range buckets {
   344  		if isTablePurelyDupsort(bucket) {
   345  			cbucket, err := m.memTx.CursorDupSort(bucket)
   346  			if err != nil {
   347  				return err
   348  			}
   349  			defer cbucket.Close()
   350  			dbCursor, err := tx.RwCursorDupSort(bucket)
   351  			if err != nil {
   352  				return err
   353  			}
   354  			defer dbCursor.Close()
   355  			for k, v, err := cbucket.First(); k != nil; k, v, err = cbucket.Next() {
   356  				if err != nil {
   357  					return err
   358  				}
   359  				if err := dbCursor.Put(k, v); err != nil {
   360  					return err
   361  				}
   362  			}
   363  		} else {
   364  			cbucket, err := m.memTx.Cursor(bucket)
   365  			if err != nil {
   366  				return err
   367  			}
   368  			defer cbucket.Close()
   369  			for k, v, err := cbucket.First(); k != nil; k, v, err = cbucket.Next() {
   370  				if err != nil {
   371  					return err
   372  				}
   373  				if err := tx.Put(bucket, k, v); err != nil {
   374  					return err
   375  				}
   376  			}
   377  		}
   378  	}
   379  	return nil
   380  }
   381  
   382  func (m *MemoryMutation) Diff() (*MemoryDiff, error) {
   383  	memDiff := &MemoryDiff{
   384  		diff:           make(map[table][]entry),
   385  		deletedEntries: make(map[string][]string),
   386  	}
   387  	// Obtain buckets touched.
   388  	buckets, err := m.memTx.ListBuckets()
   389  	if err != nil {
   390  		return nil, err
   391  	}
   392  	// Obliterate buckets who are to be deleted
   393  	for bucket := range m.clearedTables {
   394  		memDiff.clearedTableNames = append(memDiff.clearedTableNames, bucket)
   395  	}
   396  	// Obliterate entries who are to be deleted
   397  	for bucket, keys := range m.deletedEntries {
   398  		for key := range keys {
   399  			memDiff.deletedEntries[bucket] = append(memDiff.deletedEntries[bucket], key)
   400  		}
   401  	}
   402  	// Iterate over each bucket and apply changes accordingly.
   403  	for _, bucket := range buckets {
   404  		if isTablePurelyDupsort(bucket) {
   405  			cbucket, err := m.memTx.CursorDupSort(bucket)
   406  			if err != nil {
   407  				return nil, err
   408  			}
   409  			defer cbucket.Close()
   410  
   411  			t := table{
   412  				name:    bucket,
   413  				dupsort: true,
   414  			}
   415  			for k, v, err := cbucket.First(); k != nil; k, v, err = cbucket.Next() {
   416  				if err != nil {
   417  					return nil, err
   418  				}
   419  				memDiff.diff[t] = append(memDiff.diff[t], entry{
   420  					k: common.Copy(k),
   421  					v: common.Copy(v),
   422  				})
   423  			}
   424  		} else {
   425  			cbucket, err := m.memTx.Cursor(bucket)
   426  			if err != nil {
   427  				return nil, err
   428  			}
   429  			defer cbucket.Close()
   430  			t := table{
   431  				name:    bucket,
   432  				dupsort: false,
   433  			}
   434  			for k, v, err := cbucket.First(); k != nil; k, v, err = cbucket.Next() {
   435  				if err != nil {
   436  					return nil, err
   437  				}
   438  				memDiff.diff[t] = append(memDiff.diff[t], entry{
   439  					k: common.Copy(k),
   440  					v: common.Copy(v),
   441  				})
   442  			}
   443  		}
   444  	}
   445  	return memDiff, nil
   446  }
   447  
   448  // Check if a bucket is dupsorted and has dupsort conversion off
   449  func isTablePurelyDupsort(bucket string) bool {
   450  	config, ok := kv.ChaindataTablesCfg[bucket]
   451  	// If we do not have the configuration we assume it is not dupsorted
   452  	if !ok {
   453  		return false
   454  	}
   455  	return !config.AutoDupSortKeysConversion && config.Flags == kv.DupSort
   456  }
   457  
   458  func (m *MemoryMutation) MemDB() kv.RwDB {
   459  	return m.memDb
   460  }
   461  
   462  func (m *MemoryMutation) MemTx() kv.RwTx {
   463  	return m.memTx
   464  }
   465  
   466  // Cursor creates a new cursor (the real fun begins here)
   467  func (m *MemoryMutation) makeCursor(bucket string) (kv.RwCursorDupSort, error) {
   468  	c := &memoryMutationCursor{}
   469  	// We can filter duplicates in dup sorted table
   470  	c.table = bucket
   471  
   472  	var err error
   473  	c.cursor, err = m.db.CursorDupSort(bucket)
   474  	if err != nil {
   475  		return nil, err
   476  	}
   477  	c.memCursor, err = m.memTx.RwCursorDupSort(bucket)
   478  	if err != nil {
   479  		return nil, err
   480  	}
   481  	c.mutation = m
   482  	return c, err
   483  }
   484  
   485  // Cursor creates a new cursor (the real fun begins here)
   486  func (m *MemoryMutation) RwCursorDupSort(bucket string) (kv.RwCursorDupSort, error) {
   487  	return m.makeCursor(bucket)
   488  }
   489  
   490  // Cursor creates a new cursor (the real fun begins here)
   491  func (m *MemoryMutation) RwCursor(bucket string) (kv.RwCursor, error) {
   492  	return m.makeCursor(bucket)
   493  }
   494  
   495  // Cursor creates a new cursor (the real fun begins here)
   496  func (m *MemoryMutation) CursorDupSort(bucket string) (kv.CursorDupSort, error) {
   497  	return m.makeCursor(bucket)
   498  }
   499  
   500  // Cursor creates a new cursor (the real fun begins here)
   501  func (m *MemoryMutation) Cursor(bucket string) (kv.Cursor, error) {
   502  	return m.makeCursor(bucket)
   503  }
   504  
   505  func (m *MemoryMutation) ViewID() uint64 {
   506  	panic("ViewID Not implemented")
   507  }