github.com/aakash4dev/cometbft@v0.38.2/proxy/multi_app_conn.go (about)

     1  package proxy
     2  
     3  import (
     4  	"fmt"
     5  
     6  	abcicli "github.com/aakash4dev/cometbft/abci/client"
     7  	cmtlog "github.com/aakash4dev/cometbft/libs/log"
     8  	cmtos "github.com/aakash4dev/cometbft/libs/os"
     9  	"github.com/aakash4dev/cometbft/libs/service"
    10  )
    11  
    12  const (
    13  	connConsensus = "consensus"
    14  	connMempool   = "mempool"
    15  	connQuery     = "query"
    16  	connSnapshot  = "snapshot"
    17  )
    18  
    19  // AppConns is the CometBFT's interface to the application that consists of
    20  // multiple connections.
    21  type AppConns interface {
    22  	service.Service
    23  
    24  	// Mempool connection
    25  	Mempool() AppConnMempool
    26  	// Consensus connection
    27  	Consensus() AppConnConsensus
    28  	// Query connection
    29  	Query() AppConnQuery
    30  	// Snapshot connection
    31  	Snapshot() AppConnSnapshot
    32  }
    33  
    34  // NewAppConns calls NewMultiAppConn.
    35  func NewAppConns(clientCreator ClientCreator, metrics *Metrics) AppConns {
    36  	return NewMultiAppConn(clientCreator, metrics)
    37  }
    38  
    39  // multiAppConn implements AppConns.
    40  //
    41  // A multiAppConn is made of a few appConns and manages their underlying abci
    42  // clients.
    43  // TODO: on app restart, clients must reboot together
    44  type multiAppConn struct {
    45  	service.BaseService
    46  
    47  	metrics       *Metrics
    48  	consensusConn AppConnConsensus
    49  	mempoolConn   AppConnMempool
    50  	queryConn     AppConnQuery
    51  	snapshotConn  AppConnSnapshot
    52  
    53  	consensusConnClient abcicli.Client
    54  	mempoolConnClient   abcicli.Client
    55  	queryConnClient     abcicli.Client
    56  	snapshotConnClient  abcicli.Client
    57  
    58  	clientCreator ClientCreator
    59  }
    60  
    61  // NewMultiAppConn makes all necessary abci connections to the application.
    62  func NewMultiAppConn(clientCreator ClientCreator, metrics *Metrics) AppConns {
    63  	multiAppConn := &multiAppConn{
    64  		metrics:       metrics,
    65  		clientCreator: clientCreator,
    66  	}
    67  	multiAppConn.BaseService = *service.NewBaseService(nil, "multiAppConn", multiAppConn)
    68  	return multiAppConn
    69  }
    70  
    71  func (app *multiAppConn) Mempool() AppConnMempool {
    72  	return app.mempoolConn
    73  }
    74  
    75  func (app *multiAppConn) Consensus() AppConnConsensus {
    76  	return app.consensusConn
    77  }
    78  
    79  func (app *multiAppConn) Query() AppConnQuery {
    80  	return app.queryConn
    81  }
    82  
    83  func (app *multiAppConn) Snapshot() AppConnSnapshot {
    84  	return app.snapshotConn
    85  }
    86  
    87  func (app *multiAppConn) OnStart() error {
    88  	c, err := app.abciClientFor(connQuery)
    89  	if err != nil {
    90  		return err
    91  	}
    92  	app.queryConnClient = c
    93  	app.queryConn = NewAppConnQuery(c, app.metrics)
    94  
    95  	c, err = app.abciClientFor(connSnapshot)
    96  	if err != nil {
    97  		app.stopAllClients()
    98  		return err
    99  	}
   100  	app.snapshotConnClient = c
   101  	app.snapshotConn = NewAppConnSnapshot(c, app.metrics)
   102  
   103  	c, err = app.abciClientFor(connMempool)
   104  	if err != nil {
   105  		app.stopAllClients()
   106  		return err
   107  	}
   108  	app.mempoolConnClient = c
   109  	app.mempoolConn = NewAppConnMempool(c, app.metrics)
   110  
   111  	c, err = app.abciClientFor(connConsensus)
   112  	if err != nil {
   113  		app.stopAllClients()
   114  		return err
   115  	}
   116  	app.consensusConnClient = c
   117  	app.consensusConn = NewAppConnConsensus(c, app.metrics)
   118  
   119  	// Kill CometBFT if the ABCI application crashes.
   120  	go app.killTMOnClientError()
   121  
   122  	return nil
   123  }
   124  
   125  func (app *multiAppConn) OnStop() {
   126  	app.stopAllClients()
   127  }
   128  
   129  func (app *multiAppConn) killTMOnClientError() {
   130  	killFn := func(conn string, err error, logger cmtlog.Logger) {
   131  		logger.Error(
   132  			fmt.Sprintf("%s connection terminated. Did the application crash? Please restart CometBFT", conn),
   133  			"err", err)
   134  		killErr := cmtos.Kill()
   135  		if killErr != nil {
   136  			logger.Error("Failed to kill this process - please do so manually", "err", killErr)
   137  		}
   138  	}
   139  
   140  	select {
   141  	case <-app.consensusConnClient.Quit():
   142  		if err := app.consensusConnClient.Error(); err != nil {
   143  			killFn(connConsensus, err, app.Logger)
   144  		}
   145  	case <-app.mempoolConnClient.Quit():
   146  		if err := app.mempoolConnClient.Error(); err != nil {
   147  			killFn(connMempool, err, app.Logger)
   148  		}
   149  	case <-app.queryConnClient.Quit():
   150  		if err := app.queryConnClient.Error(); err != nil {
   151  			killFn(connQuery, err, app.Logger)
   152  		}
   153  	case <-app.snapshotConnClient.Quit():
   154  		if err := app.snapshotConnClient.Error(); err != nil {
   155  			killFn(connSnapshot, err, app.Logger)
   156  		}
   157  	}
   158  }
   159  
   160  func (app *multiAppConn) stopAllClients() {
   161  	if app.consensusConnClient != nil {
   162  		if err := app.consensusConnClient.Stop(); err != nil {
   163  			app.Logger.Error("error while stopping consensus client", "error", err)
   164  		}
   165  	}
   166  	if app.mempoolConnClient != nil {
   167  		if err := app.mempoolConnClient.Stop(); err != nil {
   168  			app.Logger.Error("error while stopping mempool client", "error", err)
   169  		}
   170  	}
   171  	if app.queryConnClient != nil {
   172  		if err := app.queryConnClient.Stop(); err != nil {
   173  			app.Logger.Error("error while stopping query client", "error", err)
   174  		}
   175  	}
   176  	if app.snapshotConnClient != nil {
   177  		if err := app.snapshotConnClient.Stop(); err != nil {
   178  			app.Logger.Error("error while stopping snapshot client", "error", err)
   179  		}
   180  	}
   181  }
   182  
   183  func (app *multiAppConn) abciClientFor(conn string) (abcicli.Client, error) {
   184  	c, err := app.clientCreator.NewABCIClient()
   185  	if err != nil {
   186  		return nil, fmt.Errorf("error creating ABCI client (%s connection): %w", conn, err)
   187  	}
   188  	c.SetLogger(app.Logger.With("module", "abci-client", "connection", conn))
   189  	if err := c.Start(); err != nil {
   190  		return nil, fmt.Errorf("error starting ABCI client (%s connection): %w", conn, err)
   191  	}
   192  	return c, nil
   193  }