github.com/EgonCoin/EgonChain@v1.10.16/core/rawdb/freezer.go (about) 1 // Copyright 2019 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 "math" 23 "os" 24 "path/filepath" 25 "sync" 26 "sync/atomic" 27 "time" 28 29 "github.com/EgonCoin/EgonChain/common" 30 "github.com/EgonCoin/EgonChain/ethdb" 31 "github.com/EgonCoin/EgonChain/log" 32 "github.com/EgonCoin/EgonChain/metrics" 33 "github.com/EgonCoin/EgonChain/params" 34 "github.com/prometheus/tsdb/fileutil" 35 ) 36 37 var ( 38 // errReadOnly is returned if the freezer is opened in read only mode. All the 39 // mutations are disallowed. 40 errReadOnly = errors.New("read only") 41 42 // errUnknownTable is returned if the user attempts to read from a table that is 43 // not tracked by the freezer. 44 errUnknownTable = errors.New("unknown table") 45 46 // errOutOrderInsertion is returned if the user attempts to inject out-of-order 47 // binary blobs into the freezer. 48 errOutOrderInsertion = errors.New("the append operation is out-order") 49 50 // errSymlinkDatadir is returned if the ancient directory specified by user 51 // is a symbolic link. 52 errSymlinkDatadir = errors.New("symbolic link datadir is not supported") 53 ) 54 55 const ( 56 // freezerRecheckInterval is the frequency to check the key-value database for 57 // chain progression that might permit new blocks to be frozen into immutable 58 // storage. 59 freezerRecheckInterval = time.Minute 60 61 // freezerBatchLimit is the maximum number of blocks to freeze in one batch 62 // before doing an fsync and deleting it from the key-value store. 63 freezerBatchLimit = 30000 64 65 // freezerTableSize defines the maximum size of freezer data files. 66 freezerTableSize = 2 * 1000 * 1000 * 1000 67 ) 68 69 // freezer is an memory mapped append-only database to store immutable chain data 70 // into flat files: 71 // 72 // - The append only nature ensures that disk writes are minimized. 73 // - The memory mapping ensures we can max out system memory for caching without 74 // reserving it for go-ethereum. This would also reduce the memory requirements 75 // of Geth, and thus also GC overhead. 76 type freezer struct { 77 // WARNING: The `frozen` field is accessed atomically. On 32 bit platforms, only 78 // 64-bit aligned fields can be atomic. The struct is guaranteed to be so aligned, 79 // so take advantage of that (https://golang.org/pkg/sync/atomic/#pkg-note-BUG). 80 frozen uint64 // Number of blocks already frozen 81 threshold uint64 // Number of recent blocks not to freeze (params.FullImmutabilityThreshold apart from tests) 82 83 // This lock synchronizes writers and the truncate operation, as well as 84 // the "atomic" (batched) read operations. 85 writeLock sync.RWMutex 86 writeBatch *freezerBatch 87 88 readonly bool 89 tables map[string]*freezerTable // Data tables for storing everything 90 instanceLock fileutil.Releaser // File-system lock to prevent double opens 91 92 trigger chan chan struct{} // Manual blocking freeze trigger, test determinism 93 94 quit chan struct{} 95 wg sync.WaitGroup 96 closeOnce sync.Once 97 } 98 99 // newFreezer creates a chain freezer that moves ancient chain data into 100 // append-only flat file containers. 101 // 102 // The 'tables' argument defines the data tables. If the value of a map 103 // entry is true, snappy compression is disabled for the table. 104 func newFreezer(datadir string, namespace string, readonly bool, maxTableSize uint32, tables map[string]bool) (*freezer, error) { 105 // Create the initial freezer object 106 var ( 107 readMeter = metrics.NewRegisteredMeter(namespace+"ancient/read", nil) 108 writeMeter = metrics.NewRegisteredMeter(namespace+"ancient/write", nil) 109 sizeGauge = metrics.NewRegisteredGauge(namespace+"ancient/size", nil) 110 ) 111 // Ensure the datadir is not a symbolic link if it exists. 112 if info, err := os.Lstat(datadir); !os.IsNotExist(err) { 113 if info.Mode()&os.ModeSymlink != 0 { 114 log.Warn("Symbolic link ancient database is not supported", "path", datadir) 115 return nil, errSymlinkDatadir 116 } 117 } 118 // Leveldb uses LOCK as the filelock filename. To prevent the 119 // name collision, we use FLOCK as the lock name. 120 lock, _, err := fileutil.Flock(filepath.Join(datadir, "FLOCK")) 121 if err != nil { 122 return nil, err 123 } 124 // Open all the supported data tables 125 freezer := &freezer{ 126 readonly: readonly, 127 threshold: params.FullImmutabilityThreshold, 128 tables: make(map[string]*freezerTable), 129 instanceLock: lock, 130 trigger: make(chan chan struct{}), 131 quit: make(chan struct{}), 132 } 133 134 // Create the tables. 135 for name, disableSnappy := range tables { 136 table, err := newTable(datadir, name, readMeter, writeMeter, sizeGauge, maxTableSize, disableSnappy, readonly) 137 if err != nil { 138 for _, table := range freezer.tables { 139 table.Close() 140 } 141 lock.Release() 142 return nil, err 143 } 144 freezer.tables[name] = table 145 } 146 147 if freezer.readonly { 148 // In readonly mode only validate, don't truncate. 149 // validate also sets `freezer.frozen`. 150 err = freezer.validate() 151 } else { 152 // Truncate all tables to common length. 153 err = freezer.repair() 154 } 155 if err != nil { 156 for _, table := range freezer.tables { 157 table.Close() 158 } 159 lock.Release() 160 return nil, err 161 } 162 163 // Create the write batch. 164 freezer.writeBatch = newFreezerBatch(freezer) 165 166 log.Info("Opened ancient database", "database", datadir, "readonly", readonly) 167 return freezer, nil 168 } 169 170 // Close terminates the chain freezer, unmapping all the data files. 171 func (f *freezer) Close() error { 172 f.writeLock.Lock() 173 defer f.writeLock.Unlock() 174 175 var errs []error 176 f.closeOnce.Do(func() { 177 close(f.quit) 178 // Wait for any background freezing to stop 179 f.wg.Wait() 180 for _, table := range f.tables { 181 if err := table.Close(); err != nil { 182 errs = append(errs, err) 183 } 184 } 185 if err := f.instanceLock.Release(); err != nil { 186 errs = append(errs, err) 187 } 188 }) 189 if errs != nil { 190 return fmt.Errorf("%v", errs) 191 } 192 return nil 193 } 194 195 // HasAncient returns an indicator whether the specified ancient data exists 196 // in the freezer. 197 func (f *freezer) HasAncient(kind string, number uint64) (bool, error) { 198 if table := f.tables[kind]; table != nil { 199 return table.has(number), nil 200 } 201 return false, nil 202 } 203 204 // Ancient retrieves an ancient binary blob from the append-only immutable files. 205 func (f *freezer) Ancient(kind string, number uint64) ([]byte, error) { 206 if table := f.tables[kind]; table != nil { 207 return table.Retrieve(number) 208 } 209 return nil, errUnknownTable 210 } 211 212 // AncientRange retrieves multiple items in sequence, starting from the index 'start'. 213 // It will return 214 // - at most 'max' items, 215 // - at least 1 item (even if exceeding the maxByteSize), but will otherwise 216 // return as many items as fit into maxByteSize. 217 func (f *freezer) AncientRange(kind string, start, count, maxBytes uint64) ([][]byte, error) { 218 if table := f.tables[kind]; table != nil { 219 return table.RetrieveItems(start, count, maxBytes) 220 } 221 return nil, errUnknownTable 222 } 223 224 // Ancients returns the length of the frozen items. 225 func (f *freezer) Ancients() (uint64, error) { 226 return atomic.LoadUint64(&f.frozen), nil 227 } 228 229 // AncientSize returns the ancient size of the specified category. 230 func (f *freezer) AncientSize(kind string) (uint64, error) { 231 // This needs the write lock to avoid data races on table fields. 232 // Speed doesn't matter here, AncientSize is for debugging. 233 f.writeLock.RLock() 234 defer f.writeLock.RUnlock() 235 236 if table := f.tables[kind]; table != nil { 237 return table.size() 238 } 239 return 0, errUnknownTable 240 } 241 242 // ReadAncients runs the given read operation while ensuring that no writes take place 243 // on the underlying freezer. 244 func (f *freezer) ReadAncients(fn func(ethdb.AncientReader) error) (err error) { 245 f.writeLock.RLock() 246 defer f.writeLock.RUnlock() 247 return fn(f) 248 } 249 250 // ModifyAncients runs the given write operation. 251 func (f *freezer) ModifyAncients(fn func(ethdb.AncientWriteOp) error) (writeSize int64, err error) { 252 if f.readonly { 253 return 0, errReadOnly 254 } 255 f.writeLock.Lock() 256 defer f.writeLock.Unlock() 257 258 // Roll back all tables to the starting position in case of error. 259 prevItem := f.frozen 260 defer func() { 261 if err != nil { 262 // The write operation has failed. Go back to the previous item position. 263 for name, table := range f.tables { 264 err := table.truncate(prevItem) 265 if err != nil { 266 log.Error("Freezer table roll-back failed", "table", name, "index", prevItem, "err", err) 267 } 268 } 269 } 270 }() 271 272 f.writeBatch.reset() 273 if err := fn(f.writeBatch); err != nil { 274 return 0, err 275 } 276 item, writeSize, err := f.writeBatch.commit() 277 if err != nil { 278 return 0, err 279 } 280 atomic.StoreUint64(&f.frozen, item) 281 return writeSize, nil 282 } 283 284 // TruncateAncients discards any recent data above the provided threshold number. 285 func (f *freezer) TruncateAncients(items uint64) error { 286 if f.readonly { 287 return errReadOnly 288 } 289 f.writeLock.Lock() 290 defer f.writeLock.Unlock() 291 292 if atomic.LoadUint64(&f.frozen) <= items { 293 return nil 294 } 295 for _, table := range f.tables { 296 if err := table.truncate(items); err != nil { 297 return err 298 } 299 } 300 atomic.StoreUint64(&f.frozen, items) 301 return nil 302 } 303 304 // Sync flushes all data tables to disk. 305 func (f *freezer) Sync() error { 306 var errs []error 307 for _, table := range f.tables { 308 if err := table.Sync(); err != nil { 309 errs = append(errs, err) 310 } 311 } 312 if errs != nil { 313 return fmt.Errorf("%v", errs) 314 } 315 return nil 316 } 317 318 // validate checks that every table has the same length. 319 // Used instead of `repair` in readonly mode. 320 func (f *freezer) validate() error { 321 if len(f.tables) == 0 { 322 return nil 323 } 324 var ( 325 length uint64 326 name string 327 ) 328 // Hack to get length of any table 329 for kind, table := range f.tables { 330 length = atomic.LoadUint64(&table.items) 331 name = kind 332 break 333 } 334 // Now check every table against that length 335 for kind, table := range f.tables { 336 items := atomic.LoadUint64(&table.items) 337 if length != items { 338 return fmt.Errorf("freezer tables %s and %s have differing lengths: %d != %d", kind, name, items, length) 339 } 340 } 341 atomic.StoreUint64(&f.frozen, length) 342 return nil 343 } 344 345 // repair truncates all data tables to the same length. 346 func (f *freezer) repair() error { 347 min := uint64(math.MaxUint64) 348 for _, table := range f.tables { 349 items := atomic.LoadUint64(&table.items) 350 if min > items { 351 min = items 352 } 353 } 354 for _, table := range f.tables { 355 if err := table.truncate(min); err != nil { 356 return err 357 } 358 } 359 atomic.StoreUint64(&f.frozen, min) 360 return nil 361 } 362 363 // freeze is a background thread that periodically checks the blockchain for any 364 // import progress and moves ancient data from the fast database into the freezer. 365 // 366 // This functionality is deliberately broken off from block importing to avoid 367 // incurring additional data shuffling delays on block propagation. 368 func (f *freezer) freeze(db ethdb.KeyValueStore) { 369 nfdb := &nofreezedb{KeyValueStore: db} 370 371 var ( 372 backoff bool 373 triggered chan struct{} // Used in tests 374 ) 375 for { 376 select { 377 case <-f.quit: 378 log.Info("Freezer shutting down") 379 return 380 default: 381 } 382 if backoff { 383 // If we were doing a manual trigger, notify it 384 if triggered != nil { 385 triggered <- struct{}{} 386 triggered = nil 387 } 388 select { 389 case <-time.NewTimer(freezerRecheckInterval).C: 390 backoff = false 391 case triggered = <-f.trigger: 392 backoff = false 393 case <-f.quit: 394 return 395 } 396 } 397 // Retrieve the freezing threshold. 398 hash := ReadHeadBlockHash(nfdb) 399 if hash == (common.Hash{}) { 400 log.Debug("Current full block hash unavailable") // new chain, empty database 401 backoff = true 402 continue 403 } 404 number := ReadHeaderNumber(nfdb, hash) 405 threshold := atomic.LoadUint64(&f.threshold) 406 407 switch { 408 case number == nil: 409 log.Error("Current full block number unavailable", "hash", hash) 410 backoff = true 411 continue 412 413 case *number < threshold: 414 log.Debug("Current full block not old enough", "number", *number, "hash", hash, "delay", threshold) 415 backoff = true 416 continue 417 418 case *number-threshold <= f.frozen: 419 log.Debug("Ancient blocks frozen already", "number", *number, "hash", hash, "frozen", f.frozen) 420 backoff = true 421 continue 422 } 423 head := ReadHeader(nfdb, hash, *number) 424 if head == nil { 425 log.Error("Current full block unavailable", "number", *number, "hash", hash) 426 backoff = true 427 continue 428 } 429 430 // Seems we have data ready to be frozen, process in usable batches 431 var ( 432 start = time.Now() 433 first, _ = f.Ancients() 434 limit = *number - threshold 435 ) 436 if limit-first > freezerBatchLimit { 437 limit = first + freezerBatchLimit 438 } 439 ancients, err := f.freezeRange(nfdb, first, limit) 440 if err != nil { 441 log.Error("Error in block freeze operation", "err", err) 442 backoff = true 443 continue 444 } 445 446 // Batch of blocks have been frozen, flush them before wiping from leveldb 447 if err := f.Sync(); err != nil { 448 log.Crit("Failed to flush frozen tables", "err", err) 449 } 450 451 // Wipe out all data from the active database 452 batch := db.NewBatch() 453 for i := 0; i < len(ancients); i++ { 454 // Always keep the genesis block in active database 455 if first+uint64(i) != 0 { 456 DeleteBlockWithoutNumber(batch, ancients[i], first+uint64(i)) 457 DeleteCanonicalHash(batch, first+uint64(i)) 458 } 459 } 460 if err := batch.Write(); err != nil { 461 log.Crit("Failed to delete frozen canonical blocks", "err", err) 462 } 463 batch.Reset() 464 465 // Wipe out side chains also and track dangling side chains 466 var dangling []common.Hash 467 for number := first; number < f.frozen; number++ { 468 // Always keep the genesis block in active database 469 if number != 0 { 470 dangling = ReadAllHashes(db, number) 471 for _, hash := range dangling { 472 log.Trace("Deleting side chain", "number", number, "hash", hash) 473 DeleteBlock(batch, hash, number) 474 } 475 } 476 } 477 if err := batch.Write(); err != nil { 478 log.Crit("Failed to delete frozen side blocks", "err", err) 479 } 480 batch.Reset() 481 482 // Step into the future and delete and dangling side chains 483 if f.frozen > 0 { 484 tip := f.frozen 485 for len(dangling) > 0 { 486 drop := make(map[common.Hash]struct{}) 487 for _, hash := range dangling { 488 log.Debug("Dangling parent from freezer", "number", tip-1, "hash", hash) 489 drop[hash] = struct{}{} 490 } 491 children := ReadAllHashes(db, tip) 492 for i := 0; i < len(children); i++ { 493 // Dig up the child and ensure it's dangling 494 child := ReadHeader(nfdb, children[i], tip) 495 if child == nil { 496 log.Error("Missing dangling header", "number", tip, "hash", children[i]) 497 continue 498 } 499 if _, ok := drop[child.ParentHash]; !ok { 500 children = append(children[:i], children[i+1:]...) 501 i-- 502 continue 503 } 504 // Delete all block data associated with the child 505 log.Debug("Deleting dangling block", "number", tip, "hash", children[i], "parent", child.ParentHash) 506 DeleteBlock(batch, children[i], tip) 507 } 508 dangling = children 509 tip++ 510 } 511 if err := batch.Write(); err != nil { 512 log.Crit("Failed to delete dangling side blocks", "err", err) 513 } 514 } 515 516 // Log something friendly for the user 517 context := []interface{}{ 518 "blocks", f.frozen - first, "elapsed", common.PrettyDuration(time.Since(start)), "number", f.frozen - 1, 519 } 520 if n := len(ancients); n > 0 { 521 context = append(context, []interface{}{"hash", ancients[n-1]}...) 522 } 523 log.Info("Deep froze chain segment", context...) 524 525 // Avoid database thrashing with tiny writes 526 if f.frozen-first < freezerBatchLimit { 527 backoff = true 528 } 529 } 530 } 531 532 func (f *freezer) freezeRange(nfdb *nofreezedb, number, limit uint64) (hashes []common.Hash, err error) { 533 hashes = make([]common.Hash, 0, limit-number) 534 535 _, err = f.ModifyAncients(func(op ethdb.AncientWriteOp) error { 536 for ; number <= limit; number++ { 537 // Retrieve all the components of the canonical block. 538 hash := ReadCanonicalHash(nfdb, number) 539 if hash == (common.Hash{}) { 540 return fmt.Errorf("canonical hash missing, can't freeze block %d", number) 541 } 542 header := ReadHeaderRLP(nfdb, hash, number) 543 if len(header) == 0 { 544 return fmt.Errorf("block header missing, can't freeze block %d", number) 545 } 546 body := ReadBodyRLP(nfdb, hash, number) 547 if len(body) == 0 { 548 return fmt.Errorf("block body missing, can't freeze block %d", number) 549 } 550 receipts := ReadReceiptsRLP(nfdb, hash, number) 551 if len(receipts) == 0 { 552 return fmt.Errorf("block receipts missing, can't freeze block %d", number) 553 } 554 td := ReadTdRLP(nfdb, hash, number) 555 if len(td) == 0 { 556 return fmt.Errorf("total difficulty missing, can't freeze block %d", number) 557 } 558 559 // Write to the batch. 560 if err := op.AppendRaw(freezerHashTable, number, hash[:]); err != nil { 561 return fmt.Errorf("can't write hash to freezer: %v", err) 562 } 563 if err := op.AppendRaw(freezerHeaderTable, number, header); err != nil { 564 return fmt.Errorf("can't write header to freezer: %v", err) 565 } 566 if err := op.AppendRaw(freezerBodiesTable, number, body); err != nil { 567 return fmt.Errorf("can't write body to freezer: %v", err) 568 } 569 if err := op.AppendRaw(freezerReceiptTable, number, receipts); err != nil { 570 return fmt.Errorf("can't write receipts to freezer: %v", err) 571 } 572 if err := op.AppendRaw(freezerDifficultyTable, number, td); err != nil { 573 return fmt.Errorf("can't write td to freezer: %v", err) 574 } 575 576 hashes = append(hashes, hash) 577 } 578 return nil 579 }) 580 581 return hashes, err 582 }