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  }