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 }