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 }