code.vegaprotocol.io/vega@v0.79.0/datanode/networkhistory/segment/segment.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 segment
    17  
    18  import (
    19  	"fmt"
    20  	"path"
    21  	"regexp"
    22  	"sort"
    23  	"strconv"
    24  )
    25  
    26  // Base is the base struct for all segment types.
    27  type Base struct {
    28  	HeightFrom      int64
    29  	HeightTo        int64
    30  	DatabaseVersion int64
    31  	ChainID         string
    32  }
    33  
    34  func (m Base) String() string {
    35  	return fmt.Sprintf("{Network History Segment for Chain ID:%s Height From:%d Height To:%d}", m.ChainID, m.HeightFrom, m.HeightTo)
    36  }
    37  
    38  func (m Base) ZipFileName() string {
    39  	return fmt.Sprintf("%s-%d-%d-%d.zip", m.ChainID, m.DatabaseVersion, m.HeightFrom, m.HeightTo)
    40  }
    41  
    42  func (m Base) SnapshotDataDirectory() string {
    43  	return fmt.Sprintf("%s-%d-%d-%d", m.ChainID, m.DatabaseVersion, m.HeightFrom, m.HeightTo)
    44  }
    45  
    46  func NewFromSnapshotDataDirectory(dirName string) (Base, error) {
    47  	re, err := regexp.Compile(`(.*)-(\d+)-(\d+)-(\d+)`)
    48  	if err != nil {
    49  		return Base{}, fmt.Errorf("failed to compile reg exp:%w", err)
    50  	}
    51  
    52  	matches := re.FindStringSubmatch(dirName)
    53  	if len(matches) != 5 {
    54  		return Base{}, fmt.Errorf("failed to find matches in zip file name:%s", dirName)
    55  	}
    56  
    57  	dbVersion, err := strconv.ParseInt(matches[2], 10, 64)
    58  	if err != nil {
    59  		return Base{}, err
    60  	}
    61  
    62  	heightFrom, err := strconv.ParseInt(matches[3], 10, 64)
    63  	if err != nil {
    64  		return Base{}, err
    65  	}
    66  
    67  	heightTo, err := strconv.ParseInt(matches[4], 10, 64)
    68  	if err != nil {
    69  		return Base{}, err
    70  	}
    71  
    72  	result := Base{
    73  		ChainID:         matches[1],
    74  		HeightFrom:      heightFrom,
    75  		HeightTo:        heightTo,
    76  		DatabaseVersion: dbVersion,
    77  	}
    78  	return result, nil
    79  }
    80  
    81  func (m Base) GetFromHeight() int64 {
    82  	return m.HeightFrom
    83  }
    84  
    85  func (m Base) GetToHeight() int64 {
    86  	return m.HeightTo
    87  }
    88  
    89  func (m Base) GetDatabaseVersion() int64 {
    90  	return m.DatabaseVersion
    91  }
    92  
    93  func (m Base) GetChainId() string {
    94  	return m.ChainID
    95  }
    96  
    97  // MetaData adds a PreviousHistorySegmentID, and is the struct that gets serialized into
    98  // the JSON metadata when a segment is added to the store.
    99  type MetaData struct {
   100  	Base
   101  	PreviousHistorySegmentID string
   102  }
   103  
   104  func (m MetaData) GetPreviousHistorySegmentId() string {
   105  	return m.PreviousHistorySegmentID
   106  }
   107  
   108  // Full is a segment that has been added to the store and has been assigned a segment ID.
   109  type Full struct {
   110  	MetaData
   111  	HistorySegmentID string
   112  }
   113  
   114  func (f Full) GetHistorySegmentId() string {
   115  	return f.HistorySegmentID
   116  }
   117  
   118  // Staged is a segment which has been added to the store and then fetched back again.
   119  type Staged struct {
   120  	Full
   121  	Directory string
   122  }
   123  
   124  func (s Staged) ZipFilePath() string {
   125  	return path.Join(s.Directory, s.ZipFileName())
   126  }
   127  
   128  // Unpublished is a segment that has just been dumped from the database into a zip file but
   129  // hasn't yet been added to the store so doesn't have any extra metadata.
   130  type Unpublished struct {
   131  	Base
   132  	Directory string
   133  }
   134  
   135  func (s Unpublished) UnpublishedSnapshotDataDirectory() string {
   136  	return path.Join(s.Directory, s.SnapshotDataDirectory())
   137  }
   138  
   139  func (s Unpublished) InProgressFilePath() string {
   140  	return path.Join(s.Directory, fmt.Sprintf("%s-%d.snapshotinprogress", s.ChainID, s.HeightTo))
   141  }
   142  
   143  // Segments is just a list of segments with a bit of syntactic sugar for getting contiguous
   144  // histories of segments in a nice way.
   145  type Segments[T blockSpanner] []T
   146  
   147  func (s Segments[T]) MostRecentContiguousHistory() (ContiguousHistory[T], error) {
   148  	all := s.AllContigousHistories()
   149  	if len(all) == 0 {
   150  		return ContiguousHistory[T]{}, fmt.Errorf("no segments")
   151  	}
   152  	return all[len(all)-1], nil
   153  }
   154  
   155  func (s Segments[T]) AllContigousHistories() []ContiguousHistory[T] {
   156  	sort.Slice(s, func(i, j int) bool {
   157  		return s[i].GetFromHeight() < s[j].GetFromHeight()
   158  	})
   159  
   160  	var histories []ContiguousHistory[T]
   161  	for _, segment := range s {
   162  		added := false
   163  		for i := range histories {
   164  			if histories[i].Add(segment) {
   165  				added = true
   166  				break
   167  			}
   168  		}
   169  		if !added {
   170  			ch := ContiguousHistory[T]{}
   171  			ch.Add(segment)
   172  			histories = append(histories, ch)
   173  		}
   174  	}
   175  	return histories
   176  }
   177  
   178  func (s Segments[T]) ContiguousHistoryInRange(fromHeight int64, toHeight int64) (ContiguousHistory[T], error) {
   179  	c := s.AllContigousHistories()
   180  	for _, ch := range c {
   181  		if ch.HeightFrom <= fromHeight && ch.HeightTo >= toHeight {
   182  			fromSegmentFound := false
   183  			toSegmentFound := false
   184  			for _, segment := range ch.Segments {
   185  				if !fromSegmentFound && segment.GetFromHeight() == fromHeight {
   186  					fromSegmentFound = true
   187  					if toSegmentFound {
   188  						break
   189  					}
   190  				}
   191  
   192  				if !toSegmentFound && segment.GetToHeight() == toHeight {
   193  					toSegmentFound = true
   194  					if fromSegmentFound {
   195  						break
   196  					}
   197  				}
   198  			}
   199  
   200  			if fromSegmentFound && toSegmentFound {
   201  				return ch.Slice(fromHeight, toHeight), nil
   202  			}
   203  		}
   204  	}
   205  
   206  	return ContiguousHistory[T]{}, fmt.Errorf("heights %d to %d do not lie within a continuous segment range", fromHeight, toHeight)
   207  }