github.1485827954.workers.dev/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 }