You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

202 lines
5.1 KiB

package proxy
import (
"fmt"
"os"
"syscall"
abciclient "github.com/tendermint/tendermint/abci/client"
tmlog "github.com/tendermint/tendermint/libs/log"
"github.com/tendermint/tendermint/libs/service"
)
const (
connConsensus = "consensus"
connMempool = "mempool"
connQuery = "query"
connSnapshot = "snapshot"
)
// AppConns is the Tendermint's interface to the application that consists of
// multiple connections.
type AppConns interface {
service.Service
// Mempool connection
Mempool() AppConnMempool
// Consensus connection
Consensus() AppConnConsensus
// Query connection
Query() AppConnQuery
// Snapshot connection
Snapshot() AppConnSnapshot
}
// NewAppConns calls NewMultiAppConn.
func NewAppConns(clientCreator abciclient.Creator, metrics *Metrics) AppConns {
return NewMultiAppConn(clientCreator, metrics)
}
// multiAppConn implements AppConns.
//
// A multiAppConn is made of a few appConns and manages their underlying abci
// clients.
// TODO: on app restart, clients must reboot together
type multiAppConn struct {
service.BaseService
metrics *Metrics
consensusConn AppConnConsensus
mempoolConn AppConnMempool
queryConn AppConnQuery
snapshotConn AppConnSnapshot
consensusConnClient abciclient.Client
mempoolConnClient abciclient.Client
queryConnClient abciclient.Client
snapshotConnClient abciclient.Client
clientCreator abciclient.Creator
}
// NewMultiAppConn makes all necessary abci connections to the application.
func NewMultiAppConn(clientCreator abciclient.Creator, metrics *Metrics) AppConns {
multiAppConn := &multiAppConn{
metrics: metrics,
clientCreator: clientCreator,
}
multiAppConn.BaseService = *service.NewBaseService(nil, "multiAppConn", multiAppConn)
return multiAppConn
}
func (app *multiAppConn) Mempool() AppConnMempool {
return app.mempoolConn
}
func (app *multiAppConn) Consensus() AppConnConsensus {
return app.consensusConn
}
func (app *multiAppConn) Query() AppConnQuery {
return app.queryConn
}
func (app *multiAppConn) Snapshot() AppConnSnapshot {
return app.snapshotConn
}
func (app *multiAppConn) OnStart() error {
c, err := app.abciClientFor(connQuery)
if err != nil {
return err
}
app.queryConnClient = c
app.queryConn = NewAppConnQuery(c, app.metrics)
c, err = app.abciClientFor(connSnapshot)
if err != nil {
app.stopAllClients()
return err
}
app.snapshotConnClient = c
app.snapshotConn = NewAppConnSnapshot(c, app.metrics)
c, err = app.abciClientFor(connMempool)
if err != nil {
app.stopAllClients()
return err
}
app.mempoolConnClient = c
app.mempoolConn = NewAppConnMempool(c, app.metrics)
c, err = app.abciClientFor(connConsensus)
if err != nil {
app.stopAllClients()
return err
}
app.consensusConnClient = c
app.consensusConn = NewAppConnConsensus(c, app.metrics)
// Kill Tendermint if the ABCI application crashes.
go app.killTMOnClientError()
return nil
}
func (app *multiAppConn) OnStop() {
app.stopAllClients()
}
func (app *multiAppConn) killTMOnClientError() {
killFn := func(conn string, err error, logger tmlog.Logger) {
logger.Error(
fmt.Sprintf("%s connection terminated. Did the application crash? Please restart tendermint", conn),
"err", err)
if killErr := kill(); killErr != nil {
logger.Error("Failed to kill this process - please do so manually", "err", killErr)
}
}
select {
case <-app.consensusConnClient.Quit():
if err := app.consensusConnClient.Error(); err != nil {
killFn(connConsensus, err, app.Logger)
}
case <-app.mempoolConnClient.Quit():
if err := app.mempoolConnClient.Error(); err != nil {
killFn(connMempool, err, app.Logger)
}
case <-app.queryConnClient.Quit():
if err := app.queryConnClient.Error(); err != nil {
killFn(connQuery, err, app.Logger)
}
case <-app.snapshotConnClient.Quit():
if err := app.snapshotConnClient.Error(); err != nil {
killFn(connSnapshot, err, app.Logger)
}
}
}
func (app *multiAppConn) stopAllClients() {
if app.consensusConnClient != nil {
if err := app.consensusConnClient.Stop(); err != nil {
app.Logger.Error("error while stopping consensus client", "error", err)
}
}
if app.mempoolConnClient != nil {
if err := app.mempoolConnClient.Stop(); err != nil {
app.Logger.Error("error while stopping mempool client", "error", err)
}
}
if app.queryConnClient != nil {
if err := app.queryConnClient.Stop(); err != nil {
app.Logger.Error("error while stopping query client", "error", err)
}
}
if app.snapshotConnClient != nil {
if err := app.snapshotConnClient.Stop(); err != nil {
app.Logger.Error("error while stopping snapshot client", "error", err)
}
}
}
func (app *multiAppConn) abciClientFor(conn string) (abciclient.Client, error) {
c, err := app.clientCreator()
if err != nil {
return nil, fmt.Errorf("error creating ABCI client (%s connection): %w", conn, err)
}
c.SetLogger(app.Logger.With("module", "abci-client", "connection", conn))
if err := c.Start(); err != nil {
return nil, fmt.Errorf("error starting ABCI client (%s connection): %w", conn, err)
}
return c, nil
}
func kill() error {
p, err := os.FindProcess(os.Getpid())
if err != nil {
return err
}
return p.Signal(syscall.SIGTERM)
}