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 }