package proxy import ( "context" "os" "syscall" abciclient "github.com/tendermint/tendermint/abci/client" "github.com/tendermint/tendermint/libs/log" "github.com/tendermint/tendermint/libs/service" ) // 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, logger log.Logger, metrics *Metrics) AppConns { return NewMultiAppConn(clientCreator, logger, 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 logger log.Logger metrics *Metrics consensusConn AppConnConsensus mempoolConn AppConnMempool queryConn AppConnQuery snapshotConn AppConnSnapshot client stoppableClient clientCreator abciclient.Creator } // TODO: this is a totally internal and quasi permanent shim for // clients. eventually we can have a single client and have some kind // of reasonable lifecycle witout needing an explicit stop method. type stoppableClient interface { abciclient.Client Stop() } // NewMultiAppConn makes all necessary abci connections to the application. func NewMultiAppConn(clientCreator abciclient.Creator, logger log.Logger, metrics *Metrics) AppConns { multiAppConn := &multiAppConn{ logger: logger, metrics: metrics, clientCreator: clientCreator, } multiAppConn.BaseService = *service.NewBaseService(logger, "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(ctx context.Context) error { var err error defer func() { if err != nil { app.client.Stop() } }() var client abciclient.Client client, err = app.clientCreator(app.logger) if err != nil { return err } app.queryConn = NewAppConnQuery(client, app.metrics) app.snapshotConn = NewAppConnSnapshot(client, app.metrics) app.mempoolConn = NewAppConnMempool(client, app.metrics) app.consensusConn = NewAppConnConsensus(client, app.metrics) app.client = client.(stoppableClient) // Kill Tendermint if the ABCI application crashes. go func() { if !client.IsRunning() { return } app.client.Wait() if ctx.Err() != nil { return } if err := app.client.Error(); err != nil { app.logger.Error("client connection terminated. Did the application crash? Please restart tendermint", "err", err) if killErr := kill(); killErr != nil { app.logger.Error("Failed to kill this process - please do so manually", "err", killErr) } } }() return client.Start(ctx) } func (app *multiAppConn) OnStop() { app.client.Stop() } func kill() error { p, err := os.FindProcess(os.Getpid()) if err != nil { return err } return p.Signal(syscall.SIGTERM) }