Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

server, tidb-server: improve unix socket handling #8836

Merged
merged 17 commits into from
Jan 9, 2019
Merged
59 changes: 54 additions & 5 deletions server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,12 @@ import (
"crypto/tls"
"crypto/x509"
"fmt"
"io"
"io/ioutil"
"math/rand"
"net"
"net/http"
"os"
// For pprof
_ "net/http/pprof"
"sync"
Expand Down Expand Up @@ -133,6 +135,35 @@ func (s *Server) isUnixSocket() bool {
return s.cfg.Socket != ""
}

func (s *Server) forwardUnixSocketToTCP(socket string, addr string) {
if sock, err := net.Listen("unix", socket); err == nil {
morgo marked this conversation as resolved.
Show resolved Hide resolved
log.Infof("Server redirecting [%s] to [%s]", socket, addr)
for {
if uconn, err := sock.Accept(); err == nil {
log.Infof("server socket forwarding from [%s] to [%s]", socket, addr)
go s.handleForwardedConnection(uconn, addr)
} else {
log.Warningf("server failed to forward from [%s] to [%s], err: %s", socket, addr, err)
morgo marked this conversation as resolved.
Show resolved Hide resolved
}
}
} else {
log.Fatalf("err: %s", err) // in use?
}
}

func (s *Server) handleForwardedConnection(uconn net.Conn, addr string) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than logging warnings, I would return an error and have the caller deal with it (the caller can log or retry).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this case, handleForwardedConnection is an async call (called as a go routine), so I think it is important to handle its own?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, the caller would expand to an anonymous function that calls this function and does something with the error.

if tconn, err := net.Dial("tcp", addr); err == nil {
if _, err := io.Copy(tconn, uconn); err != nil {
log.Warningf("socket forward copy failed: %s", err)
}
} else {
log.Warningf("socket forward failed: could not connect to [%s], err: %s", addr, err)
}
if err := uconn.Close(); err != nil {
morgo marked this conversation as resolved.
Show resolved Hide resolved
log.Warningf("socket failed to close: %s", err)
}
}

// NewServer creates a new Server.
func NewServer(cfg *config.Config, driver IDriver) (*Server, error) {
s := &Server{
Expand All @@ -151,15 +182,23 @@ func NewServer(cfg *config.Config, driver IDriver) (*Server, error) {
}

var err error
if cfg.Socket != "" {
if s.listener, err = net.Listen("unix", cfg.Socket); err == nil {
log.Infof("Server is running MySQL Protocol through Socket [%s]", cfg.Socket)
}
} else {

if s.cfg.Host != "" && s.cfg.Port != 0 {
morgo marked this conversation as resolved.
Show resolved Hide resolved
addr := fmt.Sprintf("%s:%d", s.cfg.Host, s.cfg.Port)
if s.listener, err = net.Listen("tcp", addr); err == nil {
log.Infof("Server is running MySQL Protocol at [%s]", addr)
}
if cfg.Socket != "" {
go s.forwardUnixSocketToTCP(cfg.Socket, addr) // listen on both
}
} else if cfg.Socket != "" {
if s.listener, err = net.Listen("unix", cfg.Socket); err == nil {
log.Infof("Server is running MySQL Protocol through Socket [%s]", cfg.Socket)
} else {
log.Fatalf("err: %s", err) // in use?
morgo marked this conversation as resolved.
Show resolved Hide resolved
}
} else {
log.Fatal("Server not configured to listen on either -socket or -host and -port!")
}

if cfg.ProxyProtocol.Networks != "" {
Expand Down Expand Up @@ -405,6 +444,16 @@ func (s *Server) GracefulDown() {
}
}

// CleanupSocketFile cleans up socket file if it was created.
func (s *Server) CleanupSocketFile() {
if s.cfg.Socket != "" {
log.Infof("[server] removing socket file [%s]", s.cfg.Socket)
if err := os.Remove(s.cfg.Socket); err != nil {
log.Warningf("[server] failed to remove socket file! err: %s", err)
morgo marked this conversation as resolved.
Show resolved Hide resolved
}
}
}

func (s *Server) kickIdleConnection() {
var conns []*clientConn
s.rwlock.RLock()
Expand Down
3 changes: 3 additions & 0 deletions server/tidb_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,9 @@ func (ts *TidbTestSuite) TestMultiStatements(c *C) {
func (ts *TidbTestSuite) TestSocket(c *C) {
cfg := config.NewConfig()
cfg.Socket = "/tmp/tidbtest.sock"
cfg.Host = ""
morgo marked this conversation as resolved.
Show resolved Hide resolved
cfg.Port = 0
os.Remove(cfg.Socket)
cfg.Status.ReportStatus = false

server, err := NewServer(cfg, ts.tidbdrv)
Expand Down
1 change: 1 addition & 0 deletions tidb-server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -548,4 +548,5 @@ func cleanup() {
svr.KillAllConnections()
}
closeDomainAndStorage()
svr.CleanupSocketFile()
morgo marked this conversation as resolved.
Show resolved Hide resolved
}