github.1485827954.workers.dev/ethereum/go-ethereum@v1.14.3/core/rawdb/freezer_memory.go (about) 1 // Copyright 2024 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package rawdb 18 19 import ( 20 "errors" 21 "fmt" 22 "sync" 23 24 "github.com/ethereum/go-ethereum/common" 25 "github.com/ethereum/go-ethereum/common/math" 26 "github.com/ethereum/go-ethereum/ethdb" 27 "github.com/ethereum/go-ethereum/log" 28 "github.com/ethereum/go-ethereum/rlp" 29 ) 30 31 // memoryTable is used to store a list of sequential items in memory. 32 type memoryTable struct { 33 name string // Table name 34 items uint64 // Number of stored items in the table, including the deleted ones 35 offset uint64 // Number of deleted items from the table 36 data [][]byte // List of rlp-encoded items, sort in order 37 size uint64 // Total memory size occupied by the table 38 lock sync.RWMutex 39 } 40 41 // newMemoryTable initializes the memory table. 42 func newMemoryTable(name string) *memoryTable { 43 return &memoryTable{name: name} 44 } 45 46 // has returns an indicator whether the specified data exists. 47 func (t *memoryTable) has(number uint64) bool { 48 t.lock.RLock() 49 defer t.lock.RUnlock() 50 51 return number >= t.offset && number < t.items 52 } 53 54 // retrieve retrieves multiple items in sequence, starting from the index 'start'. 55 // It will return: 56 // - at most 'count' items, 57 // - if maxBytes is specified: at least 1 item (even if exceeding the maxByteSize), 58 // but will otherwise return as many items as fit into maxByteSize. 59 // - if maxBytes is not specified, 'count' items will be returned if they are present 60 func (t *memoryTable) retrieve(start uint64, count, maxBytes uint64) ([][]byte, error) { 61 t.lock.RLock() 62 defer t.lock.RUnlock() 63 64 var ( 65 size uint64 66 batch [][]byte 67 ) 68 // Ensure the start is written, not deleted from the tail, and that the 69 // caller actually wants something. 70 if t.items <= start || t.offset > start || count == 0 { 71 return nil, errOutOfBounds 72 } 73 // Cap the item count if the retrieval is out of bound. 74 if start+count > t.items { 75 count = t.items - start 76 } 77 for n := start; n < start+count; n++ { 78 index := n - t.offset 79 if len(batch) != 0 && maxBytes != 0 && size+uint64(len(t.data[index])) > maxBytes { 80 return batch, nil 81 } 82 batch = append(batch, t.data[index]) 83 size += uint64(len(t.data[index])) 84 } 85 return batch, nil 86 } 87 88 // truncateHead discards any recent data above the provided threshold number. 89 func (t *memoryTable) truncateHead(items uint64) error { 90 t.lock.Lock() 91 defer t.lock.Unlock() 92 93 // Short circuit if nothing to delete. 94 if t.items <= items { 95 return nil 96 } 97 if items < t.offset { 98 return errors.New("truncation below tail") 99 } 100 t.data = t.data[:items-t.offset] 101 t.items = items 102 return nil 103 } 104 105 // truncateTail discards any recent data before the provided threshold number. 106 func (t *memoryTable) truncateTail(items uint64) error { 107 t.lock.Lock() 108 defer t.lock.Unlock() 109 110 // Short circuit if nothing to delete. 111 if t.offset >= items { 112 return nil 113 } 114 if t.items < items { 115 return errors.New("truncation above head") 116 } 117 t.data = t.data[items-t.offset:] 118 t.offset = items 119 return nil 120 } 121 122 // commit merges the given item batch into table. It's presumed that the 123 // batch is ordered and continuous with table. 124 func (t *memoryTable) commit(batch [][]byte) error { 125 t.lock.Lock() 126 defer t.lock.Unlock() 127 128 for _, item := range batch { 129 t.size += uint64(len(item)) 130 } 131 t.data = append(t.data, batch...) 132 t.items += uint64(len(batch)) 133 return nil 134 } 135 136 // memoryBatch is the singleton batch used for ancient write. 137 type memoryBatch struct { 138 data map[string][][]byte 139 next map[string]uint64 140 size map[string]int64 141 } 142 143 func newMemoryBatch() *memoryBatch { 144 return &memoryBatch{ 145 data: make(map[string][][]byte), 146 next: make(map[string]uint64), 147 size: make(map[string]int64), 148 } 149 } 150 151 func (b *memoryBatch) reset(freezer *MemoryFreezer) { 152 b.data = make(map[string][][]byte) 153 b.next = make(map[string]uint64) 154 b.size = make(map[string]int64) 155 156 for name, table := range freezer.tables { 157 b.next[name] = table.items 158 } 159 } 160 161 // Append adds an RLP-encoded item. 162 func (b *memoryBatch) Append(kind string, number uint64, item interface{}) error { 163 if b.next[kind] != number { 164 return errOutOrderInsertion 165 } 166 blob, err := rlp.EncodeToBytes(item) 167 if err != nil { 168 return err 169 } 170 b.data[kind] = append(b.data[kind], blob) 171 b.next[kind]++ 172 b.size[kind] += int64(len(blob)) 173 return nil 174 } 175 176 // AppendRaw adds an item without RLP-encoding it. 177 func (b *memoryBatch) AppendRaw(kind string, number uint64, blob []byte) error { 178 if b.next[kind] != number { 179 return errOutOrderInsertion 180 } 181 b.data[kind] = append(b.data[kind], common.CopyBytes(blob)) 182 b.next[kind]++ 183 b.size[kind] += int64(len(blob)) 184 return nil 185 } 186 187 // commit is called at the end of a write operation and writes all remaining 188 // data to tables. 189 func (b *memoryBatch) commit(freezer *MemoryFreezer) (items uint64, writeSize int64, err error) { 190 // Check that count agrees on all batches. 191 items = math.MaxUint64 192 for name, next := range b.next { 193 if items < math.MaxUint64 && next != items { 194 return 0, 0, fmt.Errorf("table %s is at item %d, want %d", name, next, items) 195 } 196 items = next 197 } 198 // Commit all table batches. 199 for name, batch := range b.data { 200 table := freezer.tables[name] 201 if err := table.commit(batch); err != nil { 202 return 0, 0, err 203 } 204 writeSize += b.size[name] 205 } 206 return items, writeSize, nil 207 } 208 209 // MemoryFreezer is an ephemeral ancient store. It implements the ethdb.AncientStore 210 // interface and can be used along with ephemeral key-value store. 211 type MemoryFreezer struct { 212 items uint64 // Number of items stored 213 tail uint64 // Number of the first stored item in the freezer 214 readonly bool // Flag if the freezer is only for reading 215 lock sync.RWMutex // Lock to protect fields 216 tables map[string]*memoryTable // Tables for storing everything 217 writeBatch *memoryBatch // Pre-allocated write batch 218 } 219 220 // NewMemoryFreezer initializes an in-memory freezer instance. 221 func NewMemoryFreezer(readonly bool, tableName map[string]bool) *MemoryFreezer { 222 tables := make(map[string]*memoryTable) 223 for name := range tableName { 224 tables[name] = newMemoryTable(name) 225 } 226 return &MemoryFreezer{ 227 writeBatch: newMemoryBatch(), 228 readonly: readonly, 229 tables: tables, 230 } 231 } 232 233 // HasAncient returns an indicator whether the specified data exists. 234 func (f *MemoryFreezer) HasAncient(kind string, number uint64) (bool, error) { 235 f.lock.RLock() 236 defer f.lock.RUnlock() 237 238 if table := f.tables[kind]; table != nil { 239 return table.has(number), nil 240 } 241 return false, nil 242 } 243 244 // Ancient retrieves an ancient binary blob from the in-memory freezer. 245 func (f *MemoryFreezer) Ancient(kind string, number uint64) ([]byte, error) { 246 f.lock.RLock() 247 defer f.lock.RUnlock() 248 249 t := f.tables[kind] 250 if t == nil { 251 return nil, errUnknownTable 252 } 253 data, err := t.retrieve(number, 1, 0) 254 if err != nil { 255 return nil, err 256 } 257 return data[0], nil 258 } 259 260 // AncientRange retrieves multiple items in sequence, starting from the index 'start'. 261 // It will return 262 // - at most 'count' items, 263 // - if maxBytes is specified: at least 1 item (even if exceeding the maxByteSize), 264 // but will otherwise return as many items as fit into maxByteSize. 265 // - if maxBytes is not specified, 'count' items will be returned if they are present 266 func (f *MemoryFreezer) AncientRange(kind string, start, count, maxBytes uint64) ([][]byte, error) { 267 f.lock.RLock() 268 defer f.lock.RUnlock() 269 270 t := f.tables[kind] 271 if t == nil { 272 return nil, errUnknownTable 273 } 274 return t.retrieve(start, count, maxBytes) 275 } 276 277 // Ancients returns the ancient item numbers in the freezer. 278 func (f *MemoryFreezer) Ancients() (uint64, error) { 279 f.lock.RLock() 280 defer f.lock.RUnlock() 281 282 return f.items, nil 283 } 284 285 // Tail returns the number of first stored item in the freezer. 286 // This number can also be interpreted as the total deleted item numbers. 287 func (f *MemoryFreezer) Tail() (uint64, error) { 288 f.lock.RLock() 289 defer f.lock.RUnlock() 290 291 return f.tail, nil 292 } 293 294 // AncientSize returns the ancient size of the specified category. 295 func (f *MemoryFreezer) AncientSize(kind string) (uint64, error) { 296 f.lock.RLock() 297 defer f.lock.RUnlock() 298 299 if table := f.tables[kind]; table != nil { 300 return table.size, nil 301 } 302 return 0, errUnknownTable 303 } 304 305 // ReadAncients runs the given read operation while ensuring that no writes take place 306 // on the underlying freezer. 307 func (f *MemoryFreezer) ReadAncients(fn func(ethdb.AncientReaderOp) error) (err error) { 308 f.lock.RLock() 309 defer f.lock.RUnlock() 310 311 return fn(f) 312 } 313 314 // ModifyAncients runs the given write operation. 315 func (f *MemoryFreezer) ModifyAncients(fn func(ethdb.AncientWriteOp) error) (writeSize int64, err error) { 316 f.lock.Lock() 317 defer f.lock.Unlock() 318 319 if f.readonly { 320 return 0, errReadOnly 321 } 322 // Roll back all tables to the starting position in case of error. 323 defer func(old uint64) { 324 if err == nil { 325 return 326 } 327 // The write operation has failed. Go back to the previous item position. 328 for name, table := range f.tables { 329 err := table.truncateHead(old) 330 if err != nil { 331 log.Error("Freezer table roll-back failed", "table", name, "index", old, "err", err) 332 } 333 } 334 }(f.items) 335 336 // Modify the ancients in batch. 337 f.writeBatch.reset(f) 338 if err := fn(f.writeBatch); err != nil { 339 return 0, err 340 } 341 item, writeSize, err := f.writeBatch.commit(f) 342 if err != nil { 343 return 0, err 344 } 345 f.items = item 346 return writeSize, nil 347 } 348 349 // TruncateHead discards any recent data above the provided threshold number. 350 // It returns the previous head number. 351 func (f *MemoryFreezer) TruncateHead(items uint64) (uint64, error) { 352 f.lock.Lock() 353 defer f.lock.Unlock() 354 355 if f.readonly { 356 return 0, errReadOnly 357 } 358 old := f.items 359 if old <= items { 360 return old, nil 361 } 362 for _, table := range f.tables { 363 if err := table.truncateHead(items); err != nil { 364 return 0, err 365 } 366 } 367 f.items = items 368 return old, nil 369 } 370 371 // TruncateTail discards any recent data below the provided threshold number. 372 func (f *MemoryFreezer) TruncateTail(tail uint64) (uint64, error) { 373 f.lock.Lock() 374 defer f.lock.Unlock() 375 376 if f.readonly { 377 return 0, errReadOnly 378 } 379 old := f.tail 380 if old >= tail { 381 return old, nil 382 } 383 for _, table := range f.tables { 384 if err := table.truncateTail(tail); err != nil { 385 return 0, err 386 } 387 } 388 f.tail = tail 389 return old, nil 390 } 391 392 // Sync flushes all data tables to disk. 393 func (f *MemoryFreezer) Sync() error { 394 return nil 395 } 396 397 // MigrateTable processes and migrates entries of a given table to a new format. 398 // The second argument is a function that takes a raw entry and returns it 399 // in the newest format. 400 func (f *MemoryFreezer) MigrateTable(string, func([]byte) ([]byte, error)) error { 401 return errors.New("not implemented") 402 } 403 404 // Close releases all the sources held by the memory freezer. It will panic if 405 // any following invocation is made to a closed freezer. 406 func (f *MemoryFreezer) Close() error { 407 f.lock.Lock() 408 defer f.lock.Unlock() 409 410 f.tables = nil 411 f.writeBatch = nil 412 return nil 413 } 414 415 // Reset drops all the data cached in the memory freezer and reset itself 416 // back to default state. 417 func (f *MemoryFreezer) Reset() error { 418 f.lock.Lock() 419 defer f.lock.Unlock() 420 421 tables := make(map[string]*memoryTable) 422 for name := range f.tables { 423 tables[name] = newMemoryTable(name) 424 } 425 f.tables = tables 426 f.items, f.tail = 0, 0 427 return nil 428 }