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  }