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