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 }