code.vegaprotocol.io/vega@v0.79.0/cmd/data-node/commands/networkhistory/load.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 networkhistory 17 18 import ( 19 "context" 20 "errors" 21 "fmt" 22 "strconv" 23 "strings" 24 "time" 25 26 "code.vegaprotocol.io/vega/cmd/vegawallet/commands/flags" 27 "code.vegaprotocol.io/vega/datanode/config" 28 "code.vegaprotocol.io/vega/datanode/networkhistory" 29 "code.vegaprotocol.io/vega/datanode/networkhistory/snapshot" 30 "code.vegaprotocol.io/vega/datanode/networkhistory/store" 31 "code.vegaprotocol.io/vega/datanode/sqlstore" 32 vgterm "code.vegaprotocol.io/vega/libs/term" 33 "code.vegaprotocol.io/vega/logging" 34 "code.vegaprotocol.io/vega/paths" 35 36 "github.com/jackc/pgx/v4/pgxpool" 37 "go.uber.org/zap" 38 ) 39 40 type loadCmd struct { 41 config.VegaHomeFlag 42 config.Config 43 44 Force bool `description:"do not prompt for confirmation" long:"force" short:"f"` 45 WipeExistingData bool `description:"Erase all data from the node before loading from network history" long:"wipe-existing-data" short:"w"` 46 OptimiseForAppend string `choice:"default" choice:"true" choice:"false" default:"default" description:"if true the load will be optimised for appending new segments onto existing datanode data, this is the default if the node already contains data" long:"optimise-for-append" required:"false" short:"a"` 47 } 48 49 func (cmd *loadCmd) Execute(args []string) error { 50 cfg := logging.NewDefaultConfig() 51 cfg.Custom.Zap.Level = logging.WarnLevel 52 cfg.Environment = "custom" 53 log := logging.NewLoggerFromConfig( 54 cfg, 55 ) 56 ctx, cancelFn := context.WithCancel(context.Background()) 57 defer cancelFn() 58 defer log.AtExit() 59 60 vegaPaths := paths.New(cmd.VegaHome) 61 err := fixConfig(&cmd.Config, vegaPaths) 62 if err != nil { 63 return fmt.Errorf("failed to fix config:%w", err) 64 } 65 66 if datanodeLive(cmd.Config) { 67 return fmt.Errorf("datanode must be shutdown before data can be loaded") 68 } 69 70 if !cmd.Force && vgterm.HasTTY() { 71 if !flags.YesOrNo("Running this command will kill all existing database connections, do you want to continue?") { 72 return nil 73 } 74 } 75 76 if err := networkhistory.KillAllConnectionsToDatabase(ctx, cmd.SQLStore.ConnectionConfig); err != nil { 77 return fmt.Errorf("failed to kill all connections to database: %w", err) 78 } 79 80 connPool, err := getCommandConnPool(ctx, cmd.Config.SQLStore.ConnectionConfig) 81 if err != nil { 82 return fmt.Errorf("failed to get command connection pool: %w", err) 83 } 84 defer connPool.Close() 85 86 hasSchema, err := sqlstore.HasVegaSchema(ctx, connPool) 87 if err != nil { 88 return fmt.Errorf("failed to check for existing schema:%w", err) 89 } 90 91 if hasSchema { 92 err = verifyChainID(ctx, cmd.SQLStore.ConnectionConfig, cmd.ChainID) 93 if err != nil { 94 if !errors.Is(err, networkhistory.ErrChainNotFound) { 95 return fmt.Errorf("failed to verify chain id:%w", err) 96 } 97 } 98 } 99 100 if hasSchema && cmd.WipeExistingData { 101 err := sqlstore.WipeDatabaseAndMigrateSchemaToVersion(log, cmd.Config.SQLStore.ConnectionConfig, 0, 102 sqlstore.EmbedMigrations, bool(cmd.Config.SQLStore.VerboseMigration)) 103 if err != nil { 104 return fmt.Errorf("failed to wipe database and migrate schema to version: %w", err) 105 } 106 } 107 108 networkHistoryService, err := createNetworkHistoryService(ctx, log, cmd.Config, connPool, vegaPaths) 109 if err != nil { 110 return fmt.Errorf("failed to created network history service: %w", err) 111 } 112 defer networkHistoryService.Stop() 113 114 segments, err := networkHistoryService.ListAllHistorySegments() 115 if err != nil { 116 return fmt.Errorf("failed to list history segments: %w", err) 117 } 118 119 mostRecentContiguousHistory, err := segments.MostRecentContiguousHistory() 120 if err != nil { 121 fmt.Println("No history is available to load. Data can be fetched using the fetch command") 122 return nil //nolint:nilerr 123 } 124 125 from := mostRecentContiguousHistory.HeightFrom 126 if len(args) >= 1 { 127 from, err = strconv.ParseInt(args[0], 10, 64) 128 if err != nil { 129 return fmt.Errorf("failed to parse from height: %w", err) 130 } 131 } 132 133 to := mostRecentContiguousHistory.HeightTo 134 if len(args) == 2 { 135 to, err = strconv.ParseInt(args[1], 10, 64) 136 if err != nil { 137 return fmt.Errorf("failed to parse to height: %w", err) 138 } 139 } 140 141 contiguousHistory, err := segments.ContiguousHistoryInRange(from, to) 142 if err != nil { 143 fmt.Printf("No contiguous history is available for block span %d to %d. From and To Heights must match "+ 144 "from and to heights of the segments in the contiguous history span.\nUse the show command with the '-s' "+ 145 "option to see all available segments\n", from, to) 146 return nil //nolint:nilerr 147 } 148 149 span, err := sqlstore.GetDatanodeBlockSpan(ctx, connPool) 150 if err != nil { 151 return fmt.Errorf("failed to get datanode block span:%w", err) 152 } 153 154 if from == to || (from >= span.FromHeight && to <= span.ToHeight) { 155 fmt.Println("No history is available to load. Data can be fetched using the fetch command") 156 return nil 157 } 158 159 if from < span.FromHeight && to <= span.ToHeight { 160 fmt.Printf("Available Network History data spans height %d to %d. The from height is before the datanodes' block span, %d to %d."+ 161 " To load history from before the datanodes oldest block you must specify the "+ 162 " \"--wipe-existing-data\" flag to wipe existing data from the datanode before loading.\n\n", from, to, span.FromHeight, span.ToHeight) 163 164 return nil 165 } 166 167 if from < span.FromHeight { 168 fmt.Printf("Available Network History data spans height %d to %d. However as the datanode already contains"+ 169 " data from height %d to %d only the history from height %d to %d will be loaded. To load all the available history data"+ 170 " run the load command with the \"wipe-existing-data\" flag which will empty the data node before restoring it from the history data\n\n", 171 from, to, span.FromHeight, span.ToHeight, span.ToHeight+1, to) 172 173 if !cmd.Force && vgterm.HasTTY() { 174 if !flags.YesOrNo(fmt.Sprintf("Do you wish to continue and load all history from height %d to %d ?", span.ToHeight+1, to)) { 175 return nil 176 } 177 } 178 } else { 179 fmt.Printf("Network history from block height %d to %d is available to load, current datanode block span is %d to %d\n\n", 180 from, to, span.FromHeight, span.ToHeight) 181 182 if !cmd.Force && vgterm.HasTTY() { 183 if !flags.YesOrNo("Do you want to load this history?") { 184 return nil 185 } 186 } 187 } 188 189 optimiseForAppend := false 190 switch strings.ToLower(cmd.OptimiseForAppend) { 191 case "true": 192 optimiseForAppend = true 193 case "false": 194 optimiseForAppend = false 195 default: 196 optimiseForAppend = span.HasData 197 } 198 199 if optimiseForAppend { 200 fmt.Println("Loading history, optimising for append") 201 } else { 202 fmt.Println("Loading history, optimising for bulk load") 203 } 204 205 loadLog := newLoadLog() 206 defer loadLog.AtExit() 207 loaded, err := networkHistoryService.LoadNetworkHistoryIntoDatanodeWithLog(ctx, loadLog, contiguousHistory, 208 cmd.Config.SQLStore.ConnectionConfig, optimiseForAppend, bool(cmd.Config.SQLStore.VerboseMigration)) 209 if err != nil { 210 return fmt.Errorf("failed to load all available history:%w", err) 211 } 212 213 fmt.Printf("Loaded history from height %d to %d into the datanode\n", loaded.LoadedFromHeight, loaded.LoadedToHeight) 214 215 return nil 216 } 217 218 func createNetworkHistoryService(ctx context.Context, log *logging.Logger, vegaConfig config.Config, connPool *pgxpool.Pool, vegaPaths paths.Paths) (*networkhistory.Service, error) { 219 networkHistoryStore, err := store.New(ctx, log, vegaConfig.ChainID, vegaConfig.NetworkHistory.Store, vegaPaths.StatePathFor(paths.DataNodeNetworkHistoryHome), 220 vegaConfig.MaxMemoryPercent) 221 if err != nil { 222 return nil, fmt.Errorf("failed to create network history store: %w", err) 223 } 224 225 snapshotService, err := snapshot.NewSnapshotService(log, vegaConfig.NetworkHistory.Snapshot, connPool, networkHistoryStore, 226 vegaPaths.StatePathFor(paths.DataNodeNetworkHistorySnapshotCopyTo), func(version int64) error { 227 if err := sqlstore.MigrateUpToSchemaVersion(log, vegaConfig.SQLStore, version, sqlstore.EmbedMigrations); err != nil { 228 return fmt.Errorf("failed to migrate to schema version %d: %w", version, err) 229 } 230 return nil 231 }, 232 func(version int64) error { 233 if err := sqlstore.MigrateDownToSchemaVersion(log, vegaConfig.SQLStore, version, sqlstore.EmbedMigrations); err != nil { 234 return fmt.Errorf("failed to migrate down to schema version %d: %w", version, err) 235 } 236 return nil 237 }) 238 if err != nil { 239 return nil, fmt.Errorf("failed to create snapshot service: %w", err) 240 } 241 242 networkHistoryService, err := networkhistory.New(ctx, log, vegaConfig.ChainID, vegaConfig.NetworkHistory, 243 connPool, snapshotService, networkHistoryStore, vegaConfig.API.Port, 244 vegaPaths.StatePathFor(paths.DataNodeNetworkHistorySnapshotCopyTo)) 245 if err != nil { 246 return nil, fmt.Errorf("failed new networkhistory service:%w", err) 247 } 248 return networkHistoryService, nil 249 } 250 251 type loadLog struct { 252 log *logging.Logger 253 } 254 255 func newLoadLog() *loadLog { 256 cfg := logging.NewDefaultConfig() 257 cfg.Custom.Zap.Level = logging.InfoLevel 258 cfg.Environment = "custom" 259 260 return &loadLog{ 261 log: logging.NewLoggerFromConfig(cfg), 262 } 263 } 264 265 func (l *loadLog) AtExit() { 266 l.log.AtExit() 267 } 268 269 func (l *loadLog) Infof(s string, args ...interface{}) { 270 currentTime := time.Now() 271 argsWithTime := []any{currentTime.Format("2006-01-02 15:04:05")} 272 argsWithTime = append(argsWithTime, args...) 273 fmt.Printf("%s "+s+"\n", argsWithTime...) 274 } 275 276 func (l *loadLog) Info(msg string, fields ...zap.Field) { 277 l.log.Info(msg, fields...) 278 } 279 280 func (l *loadLog) Error(msg string, fields ...zap.Field) { 281 l.log.Error(msg, fields...) 282 }