github.com/ethereum/go-ethereum@v1.14.3/core/rawdb/freezer_resettable.go (about)

     1  // Copyright 2022 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  	"os"
    21  	"path/filepath"
    22  	"sync"
    23  
    24  	"github.com/ethereum/go-ethereum/ethdb"
    25  	"github.com/ethereum/go-ethereum/log"
    26  )
    27  
    28  const tmpSuffix = ".tmp"
    29  
    30  // freezerOpenFunc is the function used to open/create a freezer.
    31  type freezerOpenFunc = func() (*Freezer, error)
    32  
    33  // resettableFreezer is a wrapper of the freezer which makes the
    34  // freezer resettable.
    35  type resettableFreezer struct {
    36  	freezer *Freezer
    37  	opener  freezerOpenFunc
    38  	datadir string
    39  	lock    sync.RWMutex
    40  }
    41  
    42  // newResettableFreezer creates a resettable freezer, note freezer is
    43  // only resettable if the passed file directory is exclusively occupied
    44  // by the freezer. And also the user-configurable ancient root directory
    45  // is **not** supported for reset since it might be a mount and rename
    46  // will cause a copy of hundreds of gigabyte into local directory. It
    47  // needs some other file based solutions.
    48  //
    49  // The reset function will delete directory atomically and re-create the
    50  // freezer from scratch.
    51  func newResettableFreezer(datadir string, namespace string, readonly bool, maxTableSize uint32, tables map[string]bool) (*resettableFreezer, error) {
    52  	if err := cleanup(datadir); err != nil {
    53  		return nil, err
    54  	}
    55  	opener := func() (*Freezer, error) {
    56  		return NewFreezer(datadir, namespace, readonly, maxTableSize, tables)
    57  	}
    58  	freezer, err := opener()
    59  	if err != nil {
    60  		return nil, err
    61  	}
    62  	return &resettableFreezer{
    63  		freezer: freezer,
    64  		opener:  opener,
    65  		datadir: datadir,
    66  	}, nil
    67  }
    68  
    69  // Reset deletes the file directory exclusively occupied by the freezer and
    70  // recreate the freezer from scratch. The atomicity of directory deletion
    71  // is guaranteed by the rename operation, the leftover directory will be
    72  // cleaned up in next startup in case crash happens after rename.
    73  func (f *resettableFreezer) Reset() error {
    74  	f.lock.Lock()
    75  	defer f.lock.Unlock()
    76  
    77  	if err := f.freezer.Close(); err != nil {
    78  		return err
    79  	}
    80  	tmp := tmpName(f.datadir)
    81  	if err := os.Rename(f.datadir, tmp); err != nil {
    82  		return err
    83  	}
    84  	if err := os.RemoveAll(tmp); err != nil {
    85  		return err
    86  	}
    87  	freezer, err := f.opener()
    88  	if err != nil {
    89  		return err
    90  	}
    91  	f.freezer = freezer
    92  	return nil
    93  }
    94  
    95  // Close terminates the chain freezer, unmapping all the data files.
    96  func (f *resettableFreezer) Close() error {
    97  	f.lock.RLock()
    98  	defer f.lock.RUnlock()
    99  
   100  	return f.freezer.Close()
   101  }
   102  
   103  // HasAncient returns an indicator whether the specified ancient data exists
   104  // in the freezer
   105  func (f *resettableFreezer) HasAncient(kind string, number uint64) (bool, error) {
   106  	f.lock.RLock()
   107  	defer f.lock.RUnlock()
   108  
   109  	return f.freezer.HasAncient(kind, number)
   110  }
   111  
   112  // Ancient retrieves an ancient binary blob from the append-only immutable files.
   113  func (f *resettableFreezer) Ancient(kind string, number uint64) ([]byte, error) {
   114  	f.lock.RLock()
   115  	defer f.lock.RUnlock()
   116  
   117  	return f.freezer.Ancient(kind, number)
   118  }
   119  
   120  // AncientRange retrieves multiple items in sequence, starting from the index 'start'.
   121  // It will return
   122  //   - at most 'count' items,
   123  //   - if maxBytes is specified: at least 1 item (even if exceeding the maxByteSize),
   124  //     but will otherwise return as many items as fit into maxByteSize.
   125  //   - if maxBytes is not specified, 'count' items will be returned if they are present.
   126  func (f *resettableFreezer) AncientRange(kind string, start, count, maxBytes uint64) ([][]byte, error) {
   127  	f.lock.RLock()
   128  	defer f.lock.RUnlock()
   129  
   130  	return f.freezer.AncientRange(kind, start, count, maxBytes)
   131  }
   132  
   133  // Ancients returns the length of the frozen items.
   134  func (f *resettableFreezer) Ancients() (uint64, error) {
   135  	f.lock.RLock()
   136  	defer f.lock.RUnlock()
   137  
   138  	return f.freezer.Ancients()
   139  }
   140  
   141  // Tail returns the number of first stored item in the freezer.
   142  func (f *resettableFreezer) Tail() (uint64, error) {
   143  	f.lock.RLock()
   144  	defer f.lock.RUnlock()
   145  
   146  	return f.freezer.Tail()
   147  }
   148  
   149  // AncientSize returns the ancient size of the specified category.
   150  func (f *resettableFreezer) AncientSize(kind string) (uint64, error) {
   151  	f.lock.RLock()
   152  	defer f.lock.RUnlock()
   153  
   154  	return f.freezer.AncientSize(kind)
   155  }
   156  
   157  // ReadAncients runs the given read operation while ensuring that no writes take place
   158  // on the underlying freezer.
   159  func (f *resettableFreezer) ReadAncients(fn func(ethdb.AncientReaderOp) error) (err error) {
   160  	f.lock.RLock()
   161  	defer f.lock.RUnlock()
   162  
   163  	return f.freezer.ReadAncients(fn)
   164  }
   165  
   166  // ModifyAncients runs the given write operation.
   167  func (f *resettableFreezer) ModifyAncients(fn func(ethdb.AncientWriteOp) error) (writeSize int64, err error) {
   168  	f.lock.RLock()
   169  	defer f.lock.RUnlock()
   170  
   171  	return f.freezer.ModifyAncients(fn)
   172  }
   173  
   174  // TruncateHead discards any recent data above the provided threshold number.
   175  // It returns the previous head number.
   176  func (f *resettableFreezer) TruncateHead(items uint64) (uint64, error) {
   177  	f.lock.RLock()
   178  	defer f.lock.RUnlock()
   179  
   180  	return f.freezer.TruncateHead(items)
   181  }
   182  
   183  // TruncateTail discards any recent data below the provided threshold number.
   184  // It returns the previous value
   185  func (f *resettableFreezer) TruncateTail(tail uint64) (uint64, error) {
   186  	f.lock.RLock()
   187  	defer f.lock.RUnlock()
   188  
   189  	return f.freezer.TruncateTail(tail)
   190  }
   191  
   192  // Sync flushes all data tables to disk.
   193  func (f *resettableFreezer) Sync() error {
   194  	f.lock.RLock()
   195  	defer f.lock.RUnlock()
   196  
   197  	return f.freezer.Sync()
   198  }
   199  
   200  // MigrateTable processes the entries in a given table in sequence
   201  // converting them to a new format if they're of an old format.
   202  func (f *resettableFreezer) MigrateTable(kind string, convert convertLegacyFn) error {
   203  	f.lock.RLock()
   204  	defer f.lock.RUnlock()
   205  
   206  	return f.freezer.MigrateTable(kind, convert)
   207  }
   208  
   209  // cleanup removes the directory located in the specified path
   210  // has the name with deletion marker suffix.
   211  func cleanup(path string) error {
   212  	parent := filepath.Dir(path)
   213  	if _, err := os.Lstat(parent); os.IsNotExist(err) {
   214  		return nil
   215  	}
   216  	dir, err := os.Open(parent)
   217  	if err != nil {
   218  		return err
   219  	}
   220  	names, err := dir.Readdirnames(0)
   221  	if err != nil {
   222  		return err
   223  	}
   224  	if cerr := dir.Close(); cerr != nil {
   225  		return cerr
   226  	}
   227  	for _, name := range names {
   228  		if name == filepath.Base(path)+tmpSuffix {
   229  			log.Info("Removed leftover freezer directory", "name", name)
   230  			return os.RemoveAll(filepath.Join(parent, name))
   231  		}
   232  	}
   233  	return nil
   234  }
   235  
   236  func tmpName(path string) string {
   237  	return filepath.Join(filepath.Dir(path), filepath.Base(path)+tmpSuffix)
   238  }