code.vegaprotocol.io/vega@v0.79.0/cmd/data-node/commands/networkhistory/show.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  	"fmt"
    21  	"os"
    22  	"sort"
    23  
    24  	coreConfig "code.vegaprotocol.io/vega/core/config"
    25  	"code.vegaprotocol.io/vega/datanode/config"
    26  	"code.vegaprotocol.io/vega/datanode/networkhistory/segment"
    27  	"code.vegaprotocol.io/vega/datanode/sqlstore"
    28  	vgjson "code.vegaprotocol.io/vega/libs/json"
    29  	"code.vegaprotocol.io/vega/logging"
    30  	"code.vegaprotocol.io/vega/paths"
    31  	v2 "code.vegaprotocol.io/vega/protos/data-node/api/v2"
    32  
    33  	"github.com/jackc/pgx/v4/pgxpool"
    34  )
    35  
    36  type showCmd struct {
    37  	config.VegaHomeFlag
    38  	config.Config
    39  	coreConfig.OutputFlag
    40  
    41  	AllSegments bool `description:"show all segments for each contiguous history" long:"segments" short:"s"`
    42  }
    43  
    44  type showOutput struct {
    45  	Segments            []*v2.HistorySegment
    46  	ContiguousHistories []segment.ContiguousHistory[*v2.HistorySegment]
    47  	DataNodeBlockStart  int64
    48  	DataNodeBlockEnd    int64
    49  }
    50  
    51  func (o *showOutput) printHuman(allSegments bool) {
    52  	if len(o.ContiguousHistories) > 0 {
    53  		fmt.Printf("Available contiguous history spans:")
    54  		for _, contiguousHistory := range o.ContiguousHistories {
    55  			fmt.Printf("\n\nContiguous history from block height %d to %d, from segment id: %s to %s\n",
    56  				contiguousHistory.HeightFrom,
    57  				contiguousHistory.HeightTo,
    58  				contiguousHistory.Segments[0].GetHistorySegmentId(),
    59  				contiguousHistory.Segments[len(contiguousHistory.Segments)-1].GetHistorySegmentId(),
    60  			)
    61  
    62  			if allSegments {
    63  				for _, segment := range contiguousHistory.Segments {
    64  					fmt.Printf("\n%d to %d, id: %s, previous segment id: %s",
    65  						segment.GetFromHeight(),
    66  						segment.GetToHeight(),
    67  						segment.GetHistorySegmentId(),
    68  						segment.GetPreviousHistorySegmentId())
    69  				}
    70  			}
    71  		}
    72  	} else {
    73  		fmt.Printf("\nNo network history is available.  Use the fetch command to fetch network history\n")
    74  	}
    75  
    76  	if o.DataNodeBlockEnd > 0 {
    77  		fmt.Printf("\n\nDatanode currently has data from block height %d to %d\n", o.DataNodeBlockStart, o.DataNodeBlockEnd)
    78  	} else {
    79  		fmt.Printf("\n\nDatanode contains no data\n")
    80  	}
    81  }
    82  
    83  func (cmd *showCmd) Execute(_ []string) error {
    84  	ctx, cfunc := context.WithCancel(context.Background())
    85  	defer cfunc()
    86  	cfg := logging.NewDefaultConfig()
    87  	cfg.Custom.Zap.Level = logging.WarnLevel
    88  	cfg.Environment = "custom"
    89  	log := logging.NewLoggerFromConfig(
    90  		cfg,
    91  	)
    92  	defer log.AtExit()
    93  
    94  	vegaPaths := paths.New(cmd.VegaHome)
    95  	err := fixConfig(&cmd.Config, vegaPaths)
    96  	if err != nil {
    97  		handleErr(log, cmd.Output.IsJSON(), "failed to fix config", err)
    98  		os.Exit(1)
    99  	}
   100  
   101  	if !datanodeLive(cmd.Config) {
   102  		handleErr(log,
   103  			cmd.Output.IsJSON(),
   104  			"datanode must be running for this command to work",
   105  			fmt.Errorf("couldn't connect to datanode on %v:%v", cmd.Config.API.IP, cmd.Config.API.Port))
   106  		os.Exit(1)
   107  	}
   108  
   109  	client, conn, err := getDatanodeClient(cmd.Config)
   110  	if err != nil {
   111  		handleErr(log, cmd.Output.IsJSON(), "failed to get datanode client", err)
   112  		os.Exit(1)
   113  	}
   114  	defer func() { _ = conn.Close() }()
   115  
   116  	response, err := client.ListAllNetworkHistorySegments(ctx, &v2.ListAllNetworkHistorySegmentsRequest{})
   117  	if err != nil {
   118  		handleErr(log, cmd.Output.IsJSON(), "failed to list all network history segments", err)
   119  		os.Exit(1)
   120  	}
   121  
   122  	output := showOutput{}
   123  	output.Segments = response.Segments
   124  
   125  	sort.Slice(output.Segments, func(i int, j int) bool {
   126  		return output.Segments[i].ToHeight < output.Segments[j].ToHeight
   127  	})
   128  
   129  	segments := segment.Segments[*v2.HistorySegment](response.Segments)
   130  	output.ContiguousHistories = segments.AllContigousHistories()
   131  
   132  	pool, err := getCommandConnPool(ctx, cmd.Config.SQLStore.ConnectionConfig)
   133  	if err != nil {
   134  		handleErr(log, cmd.Output.IsJSON(), "failed to get command conn pool", err)
   135  	}
   136  	defer pool.Close()
   137  
   138  	span, err := sqlstore.GetDatanodeBlockSpan(ctx, pool)
   139  	if err != nil {
   140  		handleErr(log, cmd.Output.IsJSON(), "failed to get datanode block span", err)
   141  		os.Exit(1)
   142  	}
   143  
   144  	if span.HasData {
   145  		output.DataNodeBlockStart = span.FromHeight
   146  		output.DataNodeBlockEnd = span.ToHeight
   147  	}
   148  
   149  	if cmd.Output.IsJSON() {
   150  		if err := vgjson.Print(&output); err != nil {
   151  			handleErr(log, cmd.Output.IsJSON(), "failed to marshal output", err)
   152  			os.Exit(1)
   153  		}
   154  	} else {
   155  		output.printHuman(cmd.AllSegments)
   156  	}
   157  
   158  	return nil
   159  }
   160  
   161  func getCommandConnPool(ctx context.Context, conf sqlstore.ConnectionConfig) (*pgxpool.Pool, error) {
   162  	conf.MaxConnPoolSize = 3
   163  
   164  	connPool, err := sqlstore.CreateConnectionPool(ctx, conf)
   165  	if err != nil {
   166  		return nil, fmt.Errorf("failed to create connection pool: %w", err)
   167  	}
   168  
   169  	return connPool, nil
   170  }