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