mirror of
https://github.com/fluencelabs/tendermint
synced 2025-06-24 10:11:48 +00:00
Ensure WriteTimeout > TimeoutBroadcastTxCommit (#3443)
* Make sure config.TimeoutBroadcastTxCommit < rpcserver.WriteTimeout() * remove redundant comment * libs/rpc/http_server: move Read/WriteTimeout into Config * increase defaults for read/write timeouts Based on this article https://www.digitalocean.com/community/tutorials/how-to-optimize-nginx-configuration * WriteTimeout should be larger than TimeoutBroadcastTxCommit * set a deadline for subscribing to txs * extract duration into const * add two changelog entries * Update CHANGELOG_PENDING.md Co-Authored-By: melekes <anton.kalyaev@gmail.com> * Update CHANGELOG_PENDING.md Co-Authored-By: melekes <anton.kalyaev@gmail.com> * 12 -> 10 * changelog * changelog
This commit is contained in:
committed by
Ethan Buchman
parent
5f68fbae37
commit
1e3469789d
@ -2,6 +2,7 @@ package rpcserver
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
@ -439,6 +440,9 @@ type wsConnection struct {
|
||||
|
||||
// callback which is called upon disconnect
|
||||
onDisconnect func(remoteAddr string)
|
||||
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
}
|
||||
|
||||
// NewWSConnection wraps websocket.Conn.
|
||||
@ -532,6 +536,10 @@ func (wsc *wsConnection) OnStop() {
|
||||
if wsc.onDisconnect != nil {
|
||||
wsc.onDisconnect(wsc.remoteAddr)
|
||||
}
|
||||
|
||||
if wsc.ctx != nil {
|
||||
wsc.cancel()
|
||||
}
|
||||
}
|
||||
|
||||
// GetRemoteAddr returns the remote address of the underlying connection.
|
||||
@ -569,6 +577,16 @@ func (wsc *wsConnection) Codec() *amino.Codec {
|
||||
return wsc.cdc
|
||||
}
|
||||
|
||||
// Context returns the connection's context.
|
||||
// The context is canceled when the client's connection closes.
|
||||
func (wsc *wsConnection) Context() context.Context {
|
||||
if wsc.ctx != nil {
|
||||
return wsc.ctx
|
||||
}
|
||||
wsc.ctx, wsc.cancel = context.WithCancel(context.Background())
|
||||
return wsc.ctx
|
||||
}
|
||||
|
||||
// Read from the socket and subscribe to or unsubscribe from events
|
||||
func (wsc *wsConnection) readRoutine() {
|
||||
defer func() {
|
||||
|
@ -18,9 +18,23 @@ import (
|
||||
types "github.com/tendermint/tendermint/rpc/lib/types"
|
||||
)
|
||||
|
||||
// Config is an RPC server configuration.
|
||||
// Config is a RPC server configuration.
|
||||
type Config struct {
|
||||
// see netutil.LimitListener
|
||||
MaxOpenConnections int
|
||||
// mirrors http.Server#ReadTimeout
|
||||
ReadTimeout time.Duration
|
||||
// mirrors http.Server#WriteTimeout
|
||||
WriteTimeout time.Duration
|
||||
}
|
||||
|
||||
// DefaultConfig returns a default configuration.
|
||||
func DefaultConfig() *Config {
|
||||
return &Config{
|
||||
MaxOpenConnections: 0, // unlimited
|
||||
ReadTimeout: 10 * time.Second,
|
||||
WriteTimeout: 10 * time.Second,
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
@ -30,25 +44,17 @@ const (
|
||||
|
||||
// same as the net/http default
|
||||
maxHeaderBytes = 1 << 20
|
||||
|
||||
// Timeouts for reading/writing to the http connection.
|
||||
// Public so handlers can read them -
|
||||
// /broadcast_tx_commit has it's own timeout, which should
|
||||
// be less than the WriteTimeout here.
|
||||
// TODO: use a config instead.
|
||||
ReadTimeout = 3 * time.Second
|
||||
WriteTimeout = 20 * time.Second
|
||||
)
|
||||
|
||||
// StartHTTPServer takes a listener and starts an HTTP server with the given handler.
|
||||
// It wraps handler with RecoverAndLogHandler.
|
||||
// NOTE: This function blocks - you may want to call it in a go-routine.
|
||||
func StartHTTPServer(listener net.Listener, handler http.Handler, logger log.Logger) error {
|
||||
func StartHTTPServer(listener net.Listener, handler http.Handler, logger log.Logger, config *Config) error {
|
||||
logger.Info(fmt.Sprintf("Starting RPC HTTP server on %s", listener.Addr()))
|
||||
s := &http.Server{
|
||||
Handler: RecoverAndLogHandler(maxBytesHandler{h: handler, n: maxBodyBytes}, logger),
|
||||
ReadTimeout: ReadTimeout,
|
||||
WriteTimeout: WriteTimeout,
|
||||
ReadTimeout: config.ReadTimeout,
|
||||
WriteTimeout: config.WriteTimeout,
|
||||
MaxHeaderBytes: maxHeaderBytes,
|
||||
}
|
||||
err := s.Serve(listener)
|
||||
@ -64,13 +70,14 @@ func StartHTTPAndTLSServer(
|
||||
handler http.Handler,
|
||||
certFile, keyFile string,
|
||||
logger log.Logger,
|
||||
config *Config,
|
||||
) error {
|
||||
logger.Info(fmt.Sprintf("Starting RPC HTTPS server on %s (cert: %q, key: %q)",
|
||||
listener.Addr(), certFile, keyFile))
|
||||
s := &http.Server{
|
||||
Handler: RecoverAndLogHandler(maxBytesHandler{h: handler, n: maxBodyBytes}, logger),
|
||||
ReadTimeout: ReadTimeout,
|
||||
WriteTimeout: WriteTimeout,
|
||||
ReadTimeout: config.ReadTimeout,
|
||||
WriteTimeout: config.WriteTimeout,
|
||||
MaxHeaderBytes: maxHeaderBytes,
|
||||
}
|
||||
err := s.ServeTLS(listener, certFile, keyFile)
|
||||
@ -180,7 +187,7 @@ func (h maxBytesHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// Listen starts a new net.Listener on the given address.
|
||||
// It returns an error if the address is invalid or the call to Listen() fails.
|
||||
func Listen(addr string, config Config) (listener net.Listener, err error) {
|
||||
func Listen(addr string, config *Config) (listener net.Listener, err error) {
|
||||
parts := strings.SplitN(addr, "://", 2)
|
||||
if len(parts) != 2 {
|
||||
return nil, errors.Errorf(
|
||||
|
@ -30,10 +30,12 @@ func TestMaxOpenConnections(t *testing.T) {
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
fmt.Fprint(w, "some body")
|
||||
})
|
||||
l, err := Listen("tcp://127.0.0.1:0", Config{MaxOpenConnections: max})
|
||||
config := DefaultConfig()
|
||||
config.MaxOpenConnections = max
|
||||
l, err := Listen("tcp://127.0.0.1:0", config)
|
||||
require.NoError(t, err)
|
||||
defer l.Close()
|
||||
go StartHTTPServer(l, mux, log.TestingLogger())
|
||||
go StartHTTPServer(l, mux, log.TestingLogger(), config)
|
||||
|
||||
// Make N GET calls to the server.
|
||||
attempts := max * 2
|
||||
@ -64,15 +66,17 @@ func TestMaxOpenConnections(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestStartHTTPAndTLSServer(t *testing.T) {
|
||||
config := DefaultConfig()
|
||||
config.MaxOpenConnections = 1
|
||||
// set up fixtures
|
||||
listenerAddr := "tcp://0.0.0.0:0"
|
||||
listener, err := Listen(listenerAddr, Config{MaxOpenConnections: 1})
|
||||
listener, err := Listen(listenerAddr, config)
|
||||
require.NoError(t, err)
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {})
|
||||
|
||||
// test failure
|
||||
err = StartHTTPAndTLSServer(listener, mux, "", "", log.TestingLogger())
|
||||
err = StartHTTPAndTLSServer(listener, mux, "", "", log.TestingLogger(), config)
|
||||
require.IsType(t, (*os.PathError)(nil), err)
|
||||
|
||||
// TODO: test that starting the server can actually work
|
||||
|
Reference in New Issue
Block a user