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 }