github.com/DFWallet/tendermint-cosmos@v0.0.2/proxy/multi_app_conn.go (about)

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