code.vegaprotocol.io/vega@v0.79.0/core/snapshot/databases/metadata/level_db.go (about) 1 // Copyright (C) 2023 Gobalsky Labs Limited 2 // 3 // This program is free software: you can redistribute it and/or modify 4 // it under the terms of the GNU Affero General Public License as 5 // published by the Free Software Foundation, either version 3 of the 6 // License, or (at your option) any later version. 7 // 8 // This program is distributed in the hope that it will be useful, 9 // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 // GNU Affero General Public License for more details. 12 // 13 // You should have received a copy of the GNU Affero General Public License 14 // along with this program. If not, see <http://www.gnu.org/licenses/>. 15 16 package metadata 17 18 import ( 19 "fmt" 20 "os" 21 "strconv" 22 23 "code.vegaprotocol.io/vega/libs/proto" 24 "code.vegaprotocol.io/vega/paths" 25 26 cometbftdb "github.com/cometbft/cometbft-db" 27 tmtypes "github.com/cometbft/cometbft/abci/types" 28 "github.com/syndtr/goleveldb/leveldb/opt" 29 ) 30 31 const metaDBName = "snapshot_meta" 32 33 type LevelDBDatabase struct { 34 dbFile string 35 dbDirectory string 36 37 underlyingAdapter *cometbftdb.GoLevelDB 38 } 39 40 func (d *LevelDBDatabase) Save(version int64, state *tmtypes.Snapshot) error { 41 serializedVersion := strconv.FormatInt(version, 10) 42 43 serializedState, err := proto.Marshal(state) 44 if err != nil { 45 return fmt.Errorf("could not serialize snaspshot state: %w", err) 46 } 47 48 return d.underlyingAdapter.Set([]byte(serializedVersion), serializedState) 49 } 50 51 func (d *LevelDBDatabase) Load(version int64) (*tmtypes.Snapshot, error) { 52 serializedVersion := strconv.FormatInt(version, 10) 53 54 serializedState, err := d.underlyingAdapter.Get([]byte(serializedVersion)) 55 if err != nil { 56 return nil, fmt.Errorf("could not retrieve metadata for key %q: %w", serializedVersion, err) 57 } else if serializedState == nil && err == nil { 58 return nil, noMetadataForSnapshotVersion(version) 59 } 60 61 snapshot := &tmtypes.Snapshot{} 62 if err := proto.Unmarshal(serializedState, snapshot); err != nil { 63 return nil, fmt.Errorf("could not deserialize snapshot state: %w", err) 64 } 65 66 return snapshot, err 67 } 68 69 func (d *LevelDBDatabase) Close() error { 70 return d.underlyingAdapter.Close() 71 } 72 73 func (d *LevelDBDatabase) IsEmpty() bool { 74 iter := d.underlyingAdapter.DB().NewIterator(nil, nil) 75 defer iter.Release() 76 return !iter.Next() 77 } 78 79 func (d *LevelDBDatabase) FindVersionByBlockHeight(blockHeight uint64) (int64, error) { 80 iter := d.underlyingAdapter.DB().NewIterator(nil, nil) 81 defer iter.Release() 82 83 for iter.Next() { 84 snapshot := &tmtypes.Snapshot{} 85 if err := proto.Unmarshal(iter.Value(), snapshot); err != nil { 86 return -1, fmt.Errorf("could not deserialize snapshot state: %w", err) 87 } 88 89 if snapshot.Height == blockHeight { 90 version, err := strconv.ParseInt(string(iter.Key()), 10, 64) 91 if err != nil { 92 return -1, fmt.Errorf("could not deserialize the snapshot version for block height %d: %w", blockHeight, err) 93 } 94 return version, nil 95 } 96 } 97 if err := iter.Error(); err != nil { 98 return -1, fmt.Errorf("an error occurred while iterating over the metadata: %w", err) 99 } 100 101 return -1, nil 102 } 103 104 func (d *LevelDBDatabase) Delete(version int64) error { 105 serializedVersion := strconv.FormatInt(version, 10) 106 107 if err := d.underlyingAdapter.Delete([]byte(serializedVersion)); err != nil { 108 return fmt.Errorf("could not delete metadata for key %q: %w", serializedVersion, err) 109 } 110 111 return nil 112 } 113 114 func (d *LevelDBDatabase) DeleteRange(fromVersion, toVersion int64) error { 115 iter := d.underlyingAdapter.DB().NewIterator(nil, nil) 116 defer iter.Release() 117 for iter.Next() { 118 version, err := strconv.ParseInt(string(iter.Key()), 10, 64) 119 if err != nil { 120 return fmt.Errorf("could not deserialize the version %q: %w", iter.Key(), err) 121 } 122 123 if version >= fromVersion && version < toVersion { 124 if err := d.underlyingAdapter.Delete(iter.Key()); err != nil { 125 return fmt.Errorf("could not delete metadata for key %q: %w", iter.Key(), err) 126 } 127 } 128 } 129 130 if err := iter.Error(); err != nil { 131 return fmt.Errorf("an error occurred while iterating over the metadata: %w", err) 132 } 133 134 return nil 135 } 136 137 func (d *LevelDBDatabase) Clear() error { 138 if err := d.underlyingAdapter.Close(); err != nil { 139 return fmt.Errorf("could not close the connection: %w", err) 140 } 141 142 if err := os.RemoveAll(d.dbFile); err != nil { 143 return fmt.Errorf("could not remove the database file: %w", err) 144 } 145 146 underlyingAdapter, err := initializeUnderlyingAdapter(d.dbDirectory) 147 if err != nil { 148 return err 149 } 150 d.underlyingAdapter = underlyingAdapter 151 152 return nil 153 } 154 155 func NewLevelDBDatabase(vegaPaths paths.Paths) (*LevelDBDatabase, error) { 156 dbDirectory := vegaPaths.StatePathFor(paths.SnapshotStateHome) 157 158 // This has to be in sync with the `metaDBName` constant. 159 dbFile := vegaPaths.StatePathFor(paths.SnapshotMetadataDBStateFile) 160 161 underlyingAdapter, err := initializeUnderlyingAdapter(dbDirectory) 162 if err != nil { 163 return nil, err 164 } 165 166 return &LevelDBDatabase{ 167 dbFile: dbFile, 168 dbDirectory: dbDirectory, 169 underlyingAdapter: underlyingAdapter, 170 }, nil 171 } 172 173 func initializeUnderlyingAdapter(dbDirectory string) (*cometbftdb.GoLevelDB, error) { 174 underlyingAdapter, err := cometbftdb.NewGoLevelDBWithOpts( 175 metaDBName, 176 dbDirectory, 177 &opt.Options{ 178 BlockCacher: opt.NoCacher, 179 OpenFilesCacher: opt.NoCacher, 180 }, 181 ) 182 if err != nil { 183 return nil, fmt.Errorf("could not initialize LevelDB adapter: %w", err) 184 } 185 return underlyingAdapter, nil 186 }