code.vegaprotocol.io/vega@v0.79.0/datanode/networkhistory/store/index.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 store
    17  
    18  import (
    19  	"encoding/binary"
    20  	"encoding/json"
    21  	"errors"
    22  	"fmt"
    23  	"path/filepath"
    24  	"sort"
    25  
    26  	"code.vegaprotocol.io/vega/datanode/networkhistory/segment"
    27  	"code.vegaprotocol.io/vega/logging"
    28  
    29  	"github.com/syndtr/goleveldb/leveldb"
    30  	"github.com/syndtr/goleveldb/leveldb/opt"
    31  	"github.com/syndtr/goleveldb/leveldb/util"
    32  )
    33  
    34  var ErrIndexEntryNotFound = errors.New("index entry not found")
    35  
    36  type LevelDbBackedIndex struct {
    37  	db  *leveldb.DB
    38  	log *logging.Logger
    39  }
    40  
    41  func NewIndex(dataDir string, log *logging.Logger) (*LevelDbBackedIndex, error) {
    42  	db, err := leveldb.OpenFile(filepath.Join(dataDir, "index.db"), nil)
    43  	if err != nil {
    44  		return nil, fmt.Errorf("failed to open level db file: %w", err)
    45  	}
    46  
    47  	return &LevelDbBackedIndex{
    48  		db:  db,
    49  		log: log,
    50  	}, nil
    51  }
    52  
    53  func (l LevelDbBackedIndex) Get(height int64) (segment.Full, error) {
    54  	value, err := l.db.Get(heightToKey(height), &opt.ReadOptions{})
    55  	if errors.Is(err, leveldb.ErrNotFound) {
    56  		return segment.Full{}, ErrIndexEntryNotFound
    57  	}
    58  
    59  	if err != nil {
    60  		return segment.Full{}, fmt.Errorf("failed to get index entry: %w", err)
    61  	}
    62  
    63  	var indexEntry segment.Full
    64  	err = json.Unmarshal(value, &indexEntry)
    65  
    66  	if err != nil {
    67  		return segment.Full{}, fmt.Errorf("failed to unmarshal value: %w", err)
    68  	}
    69  
    70  	return indexEntry, nil
    71  }
    72  
    73  func heightToKey(height int64) []byte {
    74  	bytes := make([]byte, 8)
    75  	binary.LittleEndian.PutUint64(bytes, uint64(height))
    76  	return bytes
    77  }
    78  
    79  func (l LevelDbBackedIndex) Add(indexEntry segment.Full) error {
    80  	bytes, err := json.Marshal(indexEntry)
    81  	if err != nil {
    82  		return fmt.Errorf("failed to marshal index entry: %w", err)
    83  	}
    84  
    85  	err = l.db.Put(heightToKey(indexEntry.HeightTo), bytes, &opt.WriteOptions{})
    86  	if err != nil {
    87  		return fmt.Errorf("failed to put index entry: %w", err)
    88  	}
    89  
    90  	return nil
    91  }
    92  
    93  func (l LevelDbBackedIndex) Remove(indexEntry segment.Full) error {
    94  	if err := l.db.Delete(heightToKey(indexEntry.HeightTo), &opt.WriteOptions{}); err != nil {
    95  		return fmt.Errorf("failed to delete key: %w", err)
    96  	}
    97  
    98  	return nil
    99  }
   100  
   101  func (l LevelDbBackedIndex) ListAllEntriesOldestFirst() (segment.Segments[segment.Full], error) {
   102  	segments, err := l.listAllEntries()
   103  	if err != nil {
   104  		return nil, fmt.Errorf("failed to list all entries: %w", err)
   105  	}
   106  
   107  	sort.Slice(segments, func(i, j int) bool {
   108  		return segments[i].HeightFrom < segments[j].HeightFrom
   109  	})
   110  	return segments, nil
   111  }
   112  
   113  func (l LevelDbBackedIndex) ListAllEntriesMostRecentFirst() (segment.Segments[segment.Full], error) {
   114  	segments, err := l.listAllEntries()
   115  	if err != nil {
   116  		return nil, fmt.Errorf("failed to list all entries: %w", err)
   117  	}
   118  
   119  	sort.Slice(segments, func(i, j int) bool {
   120  		return segments[i].HeightFrom > segments[j].HeightFrom
   121  	})
   122  
   123  	return segments, nil
   124  }
   125  
   126  func (l LevelDbBackedIndex) listAllEntries() (segment.Segments[segment.Full], error) {
   127  	var segments []segment.Full
   128  	l.log.Debug("Creating iterator")
   129  
   130  	iter := l.db.NewIterator(&util.Range{
   131  		Start: nil,
   132  		Limit: nil,
   133  	}, &opt.ReadOptions{})
   134  	defer func() {
   135  		l.log.Debug("Closing iterator")
   136  		iter.Release()
   137  	}()
   138  
   139  	if !iter.Last() {
   140  		return segments, nil
   141  	}
   142  
   143  	for ok := iter.Last(); ok; ok = iter.Prev() {
   144  		bytes := iter.Value()
   145  		var indexEntry segment.Full
   146  		err := json.Unmarshal(bytes, &indexEntry)
   147  		if err != nil {
   148  			return nil, fmt.Errorf("failed to unmarshal index entry: %w", err)
   149  		}
   150  
   151  		segments = append(segments, indexEntry)
   152  	}
   153  
   154  	return segments, nil
   155  }
   156  
   157  func (l LevelDbBackedIndex) GetHighestBlockHeightEntry() (segment.Full, error) {
   158  	entries, err := l.ListAllEntriesOldestFirst()
   159  	if err != nil {
   160  		return segment.Full{}, fmt.Errorf("failed to list all entries: %w", err)
   161  	}
   162  
   163  	if len(entries) == 0 {
   164  		return segment.Full{}, ErrIndexEntryNotFound
   165  	}
   166  
   167  	return entries[len(entries)-1], nil
   168  }
   169  
   170  func (l LevelDbBackedIndex) Close() error {
   171  	return l.db.Close()
   172  }