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.

213 lines
7.6 KiB

  1. package proxy
  2. import (
  3. "context"
  4. "io"
  5. "os"
  6. "syscall"
  7. "time"
  8. "github.com/go-kit/kit/metrics"
  9. abciclient "github.com/tendermint/tendermint/abci/client"
  10. "github.com/tendermint/tendermint/abci/example/kvstore"
  11. "github.com/tendermint/tendermint/abci/types"
  12. "github.com/tendermint/tendermint/libs/log"
  13. "github.com/tendermint/tendermint/libs/service"
  14. e2e "github.com/tendermint/tendermint/test/e2e/app"
  15. )
  16. // ClientFactory returns a client object, which will create a local
  17. // client if addr is one of: 'kvstore', 'persistent_kvstore', 'e2e',
  18. // or 'noop', otherwise - a remote client.
  19. //
  20. // The Closer is a noop except for persistent_kvstore applications,
  21. // which will clean up the store.
  22. func ClientFactory(logger log.Logger, addr, transport, dbDir string) (abciclient.Client, io.Closer, error) {
  23. switch addr {
  24. case "kvstore":
  25. return abciclient.NewLocalClient(logger, kvstore.NewApplication()), noopCloser{}, nil
  26. case "persistent_kvstore":
  27. app := kvstore.NewPersistentKVStoreApplication(logger, dbDir)
  28. return abciclient.NewLocalClient(logger, app), app, nil
  29. case "e2e":
  30. app, err := e2e.NewApplication(e2e.DefaultConfig(dbDir))
  31. if err != nil {
  32. return nil, noopCloser{}, err
  33. }
  34. return abciclient.NewLocalClient(logger, app), noopCloser{}, nil
  35. case "noop":
  36. return abciclient.NewLocalClient(logger, types.NewBaseApplication()), noopCloser{}, nil
  37. default:
  38. const mustConnect = false // loop retrying
  39. client, err := abciclient.NewClient(logger, addr, transport, mustConnect)
  40. if err != nil {
  41. return nil, noopCloser{}, err
  42. }
  43. return client, noopCloser{}, nil
  44. }
  45. }
  46. type noopCloser struct{}
  47. func (noopCloser) Close() error { return nil }
  48. // proxyClient provides the application connection.
  49. type proxyClient struct {
  50. service.BaseService
  51. logger log.Logger
  52. client abciclient.Client
  53. metrics *Metrics
  54. }
  55. // New creates a proxy application interface.
  56. func New(client abciclient.Client, logger log.Logger, metrics *Metrics) abciclient.Client {
  57. conn := &proxyClient{
  58. logger: logger,
  59. metrics: metrics,
  60. client: client,
  61. }
  62. conn.BaseService = *service.NewBaseService(logger, "proxyClient", conn)
  63. return conn
  64. }
  65. func (app *proxyClient) OnStop() { tryCallStop(app.client) }
  66. func (app *proxyClient) Error() error { return app.client.Error() }
  67. func tryCallStop(client abciclient.Client) {
  68. if c, ok := client.(interface{ Stop() }); ok {
  69. c.Stop()
  70. }
  71. }
  72. func (app *proxyClient) OnStart(ctx context.Context) error {
  73. var err error
  74. defer func() {
  75. if err != nil {
  76. tryCallStop(app.client)
  77. }
  78. }()
  79. // Kill Tendermint if the ABCI application crashes.
  80. go func() {
  81. if !app.client.IsRunning() {
  82. return
  83. }
  84. app.client.Wait()
  85. if ctx.Err() != nil {
  86. return
  87. }
  88. if err := app.client.Error(); err != nil {
  89. app.logger.Error("client connection terminated. Did the application crash? Please restart tendermint",
  90. "err", err)
  91. if killErr := kill(); killErr != nil {
  92. app.logger.Error("Failed to kill this process - please do so manually",
  93. "err", killErr)
  94. }
  95. }
  96. }()
  97. return app.client.Start(ctx)
  98. }
  99. func kill() error {
  100. p, err := os.FindProcess(os.Getpid())
  101. if err != nil {
  102. return err
  103. }
  104. return p.Signal(syscall.SIGABRT)
  105. }
  106. func (app *proxyClient) InitChain(ctx context.Context, req types.RequestInitChain) (*types.ResponseInitChain, error) {
  107. defer addTimeSample(app.metrics.MethodTiming.With("method", "init_chain", "type", "sync"))()
  108. return app.client.InitChain(ctx, req)
  109. }
  110. func (app *proxyClient) PrepareProposal(ctx context.Context, req types.RequestPrepareProposal) (*types.ResponsePrepareProposal, error) {
  111. defer addTimeSample(app.metrics.MethodTiming.With("method", "prepare_proposal", "type", "sync"))()
  112. return app.client.PrepareProposal(ctx, req)
  113. }
  114. func (app *proxyClient) ProcessProposal(ctx context.Context, req types.RequestProcessProposal) (*types.ResponseProcessProposal, error) {
  115. defer addTimeSample(app.metrics.MethodTiming.With("method", "process_proposal", "type", "sync"))()
  116. return app.client.ProcessProposal(ctx, req)
  117. }
  118. func (app *proxyClient) ExtendVote(ctx context.Context, req types.RequestExtendVote) (*types.ResponseExtendVote, error) {
  119. defer addTimeSample(app.metrics.MethodTiming.With("method", "extend_vote", "type", "sync"))()
  120. return app.client.ExtendVote(ctx, req)
  121. }
  122. func (app *proxyClient) VerifyVoteExtension(ctx context.Context, req types.RequestVerifyVoteExtension) (*types.ResponseVerifyVoteExtension, error) {
  123. defer addTimeSample(app.metrics.MethodTiming.With("method", "verify_vote_extension", "type", "sync"))()
  124. return app.client.VerifyVoteExtension(ctx, req)
  125. }
  126. func (app *proxyClient) FinalizeBlock(ctx context.Context, req types.RequestFinalizeBlock) (*types.ResponseFinalizeBlock, error) {
  127. defer addTimeSample(app.metrics.MethodTiming.With("method", "finalize_block", "type", "sync"))()
  128. return app.client.FinalizeBlock(ctx, req)
  129. }
  130. func (app *proxyClient) Commit(ctx context.Context) (*types.ResponseCommit, error) {
  131. defer addTimeSample(app.metrics.MethodTiming.With("method", "commit", "type", "sync"))()
  132. return app.client.Commit(ctx)
  133. }
  134. func (app *proxyClient) Flush(ctx context.Context) error {
  135. defer addTimeSample(app.metrics.MethodTiming.With("method", "flush", "type", "sync"))()
  136. return app.client.Flush(ctx)
  137. }
  138. func (app *proxyClient) CheckTx(ctx context.Context, req types.RequestCheckTx) (*types.ResponseCheckTx, error) {
  139. defer addTimeSample(app.metrics.MethodTiming.With("method", "check_tx", "type", "sync"))()
  140. return app.client.CheckTx(ctx, req)
  141. }
  142. func (app *proxyClient) Echo(ctx context.Context, msg string) (*types.ResponseEcho, error) {
  143. defer addTimeSample(app.metrics.MethodTiming.With("method", "echo", "type", "sync"))()
  144. return app.client.Echo(ctx, msg)
  145. }
  146. func (app *proxyClient) Info(ctx context.Context, req types.RequestInfo) (*types.ResponseInfo, error) {
  147. defer addTimeSample(app.metrics.MethodTiming.With("method", "info", "type", "sync"))()
  148. return app.client.Info(ctx, req)
  149. }
  150. func (app *proxyClient) Query(ctx context.Context, reqQuery types.RequestQuery) (*types.ResponseQuery, error) {
  151. defer addTimeSample(app.metrics.MethodTiming.With("method", "query", "type", "sync"))()
  152. return app.client.Query(ctx, reqQuery)
  153. }
  154. func (app *proxyClient) ListSnapshots(ctx context.Context, req types.RequestListSnapshots) (*types.ResponseListSnapshots, error) {
  155. defer addTimeSample(app.metrics.MethodTiming.With("method", "list_snapshots", "type", "sync"))()
  156. return app.client.ListSnapshots(ctx, req)
  157. }
  158. func (app *proxyClient) OfferSnapshot(ctx context.Context, req types.RequestOfferSnapshot) (*types.ResponseOfferSnapshot, error) {
  159. defer addTimeSample(app.metrics.MethodTiming.With("method", "offer_snapshot", "type", "sync"))()
  160. return app.client.OfferSnapshot(ctx, req)
  161. }
  162. func (app *proxyClient) LoadSnapshotChunk(ctx context.Context, req types.RequestLoadSnapshotChunk) (*types.ResponseLoadSnapshotChunk, error) {
  163. defer addTimeSample(app.metrics.MethodTiming.With("method", "load_snapshot_chunk", "type", "sync"))()
  164. return app.client.LoadSnapshotChunk(ctx, req)
  165. }
  166. func (app *proxyClient) ApplySnapshotChunk(ctx context.Context, req types.RequestApplySnapshotChunk) (*types.ResponseApplySnapshotChunk, error) {
  167. defer addTimeSample(app.metrics.MethodTiming.With("method", "apply_snapshot_chunk", "type", "sync"))()
  168. return app.client.ApplySnapshotChunk(ctx, req)
  169. }
  170. // addTimeSample returns a function that, when called, adds an observation to m.
  171. // The observation added to m is the number of seconds ellapsed since addTimeSample
  172. // was initially called. addTimeSample is meant to be called in a defer to calculate
  173. // the amount of time a function takes to complete.
  174. func addTimeSample(m metrics.Histogram) func() {
  175. start := time.Now()
  176. return func() { m.Observe(time.Since(start).Seconds()) }
  177. }