github.com/matrixorigin/matrixone@v1.2.0/pkg/proxy/server_conn.go (about)

     1  // Copyright 2021 - 2023 Matrix Origin
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package proxy
    16  
    17  import (
    18  	"context"
    19  	"net"
    20  	"sync"
    21  	"sync/atomic"
    22  	"time"
    23  
    24  	"github.com/fagongzi/goetty/v2"
    25  	"github.com/matrixorigin/matrixone/pkg/common/moerr"
    26  	"github.com/matrixorigin/matrixone/pkg/config"
    27  	"github.com/matrixorigin/matrixone/pkg/frontend"
    28  	"github.com/matrixorigin/matrixone/pkg/logutil"
    29  	"go.uber.org/zap"
    30  )
    31  
    32  // serverBaseConnID is the base connection ID for server.
    33  var serverBaseConnID uint32 = 1000
    34  
    35  // ServerConn is the connection to the backend server.
    36  type ServerConn interface {
    37  	// ConnID returns connection ID of the backend CN server.
    38  	ConnID() uint32
    39  	// RawConn return the raw connection.
    40  	RawConn() net.Conn
    41  	// HandleHandshake handles the handshake communication with CN server.
    42  	// handshakeResp is a auth packet received from client.
    43  	HandleHandshake(handshakeResp *frontend.Packet, timeout time.Duration) (*frontend.Packet, error)
    44  	// ExecStmt executes a simple statement, it sends a query to backend server.
    45  	// After it finished, server connection should be closed immediately because
    46  	// it is a temp connection.
    47  	// The first return value indicates that if the execution result is OK.
    48  	// NB: the stmt can only be simple stmt, which returns OK or Err only.
    49  	ExecStmt(stmt internalStmt, resp chan<- []byte) (bool, error)
    50  	// Close closes the connection to CN server.
    51  	Close() error
    52  }
    53  
    54  // serverConn is the connection between proxy and CN server.
    55  type serverConn struct {
    56  	// cnServer is the backend CN server.
    57  	cnServer *CNServer
    58  	// conn is the raw TCP connection between proxy and server.
    59  	conn goetty.IOSession
    60  	// connID is the proxy connection ID, which is not useful in most case.
    61  	connID uint32
    62  	// mysqlProto is used to build handshake info.
    63  	mysqlProto *frontend.MysqlProtocolImpl
    64  	// rebalancer is used to track connections between proxy and server.
    65  	rebalancer *rebalancer
    66  	// tun is the tunnel which this server connection belongs to.
    67  	tun *tunnel
    68  }
    69  
    70  var _ ServerConn = (*serverConn)(nil)
    71  
    72  // newServerConn creates a connection to CN server.
    73  func newServerConn(cn *CNServer, tun *tunnel, r *rebalancer, timeout time.Duration) (ServerConn, error) {
    74  	var logger *zap.Logger
    75  	if r != nil && r.logger != nil {
    76  		logger = r.logger.RawLogger()
    77  	}
    78  	c, err := cn.Connect(logger, timeout)
    79  	if err != nil {
    80  		return nil, err
    81  	}
    82  	s := &serverConn{
    83  		cnServer:   cn,
    84  		conn:       c,
    85  		connID:     nextServerConnID(),
    86  		rebalancer: r,
    87  		tun:        tun,
    88  	}
    89  	fp := config.FrontendParameters{}
    90  	fp.SetDefaultValues()
    91  	s.mysqlProto = frontend.NewMysqlClientProtocol(s.connID, c, 0, &fp)
    92  	return s, nil
    93  }
    94  
    95  // wrappedConn wraps the connection to disconnect from connection manager.
    96  type wrappedConn struct {
    97  	net.Conn
    98  	sync.Once
    99  	closeFn func()
   100  }
   101  
   102  // Close closes the wrapped connection, which calls closeFn to disconnect
   103  // from the connection manager.
   104  func (w *wrappedConn) Close() error {
   105  	if w != nil && w.closeFn != nil {
   106  		w.Once.Do(w.closeFn)
   107  	}
   108  	return w.Conn.Close()
   109  }
   110  
   111  // ConnID implements the ServerConn interface.
   112  func (s *serverConn) ConnID() uint32 {
   113  	return s.connID
   114  }
   115  
   116  // RawConn implements the ServerConn interface.
   117  func (s *serverConn) RawConn() net.Conn {
   118  	if s != nil {
   119  		if s.cnServer != nil {
   120  			return &wrappedConn{
   121  				Conn: s.conn.RawConn(),
   122  				closeFn: func() {
   123  					s.rebalancer.connManager.disconnect(s.cnServer, s.tun)
   124  				},
   125  			}
   126  		}
   127  		return s.conn.RawConn()
   128  	}
   129  	return nil
   130  }
   131  
   132  // HandleHandshake implements the ServerConn interface.
   133  func (s *serverConn) HandleHandshake(
   134  	handshakeResp *frontend.Packet, timeout time.Duration,
   135  ) (*frontend.Packet, error) {
   136  	ctx, cancel := context.WithTimeout(context.Background(), timeout)
   137  	defer cancel()
   138  
   139  	var r *frontend.Packet
   140  	var err error
   141  	ch := make(chan struct{})
   142  	go func() {
   143  		// Step 1, read initial handshake from CN server.
   144  		if err = s.readInitialHandshake(); err != nil {
   145  			return
   146  		}
   147  		// Step 2, write the handshake response to CN server, which is
   148  		// received from client earlier.
   149  		r, err = s.writeHandshakeResp(handshakeResp)
   150  		ch <- struct{}{}
   151  	}()
   152  
   153  	select {
   154  	case <-ch:
   155  		return r, err
   156  	case <-ctx.Done():
   157  		logutil.Errorf("handshake to cn %s timeout %v, conn ID: %d",
   158  			s.cnServer.addr, timeout, s.connID)
   159  		// Return a retryable error.
   160  		return nil, newConnectErr(context.DeadlineExceeded)
   161  	}
   162  }
   163  
   164  // ExecStmt implements the ServerConn interface.
   165  func (s *serverConn) ExecStmt(stmt internalStmt, resp chan<- []byte) (bool, error) {
   166  	req := make([]byte, 1, len(stmt.s)+1)
   167  	req[0] = byte(stmt.cmdType)
   168  	req = append(req, []byte(stmt.s)...)
   169  	s.mysqlProto.SetSequenceID(0)
   170  	if err := s.mysqlProto.WritePacket(req); err != nil {
   171  		return false, err
   172  	}
   173  	execOK := true
   174  	for {
   175  		// readPacket makes sure return value is a whole MySQL packet.
   176  		res, err := s.readPacket()
   177  		if err != nil {
   178  			return false, err
   179  		}
   180  		bs := packetToBytes(res)
   181  		if resp != nil {
   182  			sendResp(bs, resp)
   183  		}
   184  		if isEOFPacket(bs) || isOKPacket(bs) {
   185  			break
   186  		}
   187  		if isErrPacket(bs) {
   188  			execOK = false
   189  			break
   190  		}
   191  	}
   192  	return execOK, nil
   193  }
   194  
   195  // Close implements the ServerConn interface.
   196  func (s *serverConn) Close() error {
   197  	// Un-track the connection.
   198  	s.rebalancer.connManager.disconnect(s.cnServer, s.tun)
   199  	return nil
   200  }
   201  
   202  // readPacket reads packet from CN server, usually used in handshake phase.
   203  func (s *serverConn) readPacket() (*frontend.Packet, error) {
   204  	msg, err := s.conn.Read(goetty.ReadOptions{})
   205  	if err != nil {
   206  		return nil, err
   207  	}
   208  	packet, ok := msg.(*frontend.Packet)
   209  	if !ok {
   210  		return nil, moerr.NewInternalErrorNoCtx("message is not a Packet")
   211  	}
   212  	return packet, nil
   213  }
   214  
   215  // nextServerConnID increases baseConnID by 1 and returns the result.
   216  func nextServerConnID() uint32 {
   217  	return atomic.AddUint32(&serverBaseConnID, 1)
   218  }