github.com/matrixorigin/matrixone@v1.2.0/pkg/queryservice/query_service.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 queryservice
    16  
    17  import (
    18  	"context"
    19  	"sync"
    20  	"time"
    21  
    22  	"github.com/matrixorigin/matrixone/pkg/common/moerr"
    23  	"github.com/matrixorigin/matrixone/pkg/common/morpc"
    24  	pb "github.com/matrixorigin/matrixone/pkg/pb/query"
    25  	"github.com/matrixorigin/matrixone/pkg/queryservice/client"
    26  	"github.com/pkg/errors"
    27  )
    28  
    29  // QueryService is used to send query request to another CN service.
    30  type QueryService interface {
    31  	// ServiceID return the uuid of current CN service
    32  	ServiceID() string
    33  	// Start starts the service.
    34  	Start() error
    35  	// Close closes the service.
    36  	Close() error
    37  	// AddHandleFunc add message handler.
    38  	AddHandleFunc(method pb.CmdMethod, h func(context.Context, *pb.Request, *pb.Response) error, async bool)
    39  	// SetReleaseFunc sets the release handler.
    40  	SetReleaseFunc(resp *pb.Response, f func())
    41  }
    42  
    43  // queryService is a query service started in CN service.
    44  type queryService struct {
    45  	// serviceID is the UUID of CN service.
    46  	serviceID string
    47  	handler   morpc.MessageHandler[*pb.Request, *pb.Response]
    48  
    49  	mu struct {
    50  		sync.Mutex
    51  		releaser map[*pb.Response]func()
    52  	}
    53  }
    54  
    55  // NewQueryService creates a new queryService instance.
    56  func NewQueryService(serviceID string, address string, cfg morpc.Config) (QueryService, error) {
    57  	serviceName := "query-service"
    58  	qs := &queryService{
    59  		serviceID: serviceID,
    60  	}
    61  
    62  	qs.mu.releaser = make(map[*pb.Response]func())
    63  
    64  	pool := morpc.NewMessagePool(
    65  		func() *pb.Request { return &pb.Request{} },
    66  		func() *pb.Response { return &pb.Response{} })
    67  
    68  	h, err := morpc.NewMessageHandler(serviceName, address, cfg, pool,
    69  		morpc.WithHandlerRespReleaseFunc[*pb.Request, *pb.Response](func(m morpc.Message) {
    70  			resp := m.(*pb.Response)
    71  			if resp.CmdMethod == pb.CmdMethod_GetCacheData {
    72  				qs.mu.Lock()
    73  				defer qs.mu.Unlock()
    74  				release, ok := qs.mu.releaser[resp]
    75  				if ok {
    76  					release()
    77  					delete(qs.mu.releaser, resp)
    78  				}
    79  			}
    80  			pool.ReleaseResponse(resp)
    81  		}),
    82  	)
    83  	if err != nil {
    84  		return nil, err
    85  	}
    86  	qs.handler = h
    87  	qs.initHandleFunc()
    88  	return qs, nil
    89  }
    90  
    91  // AddHandleFunc implements the QueryService interface.
    92  func (s *queryService) AddHandleFunc(method pb.CmdMethod, h func(context.Context, *pb.Request, *pb.Response) error, async bool) {
    93  	s.handler.RegisterHandleFunc(uint32(method), h, async)
    94  }
    95  
    96  // SetReleaseFunc implements the QueryService interface.
    97  func (s *queryService) SetReleaseFunc(resp *pb.Response, f func()) {
    98  	s.mu.Lock()
    99  	defer s.mu.Unlock()
   100  	s.mu.releaser[resp] = f
   101  }
   102  
   103  func (s *queryService) initHandleFunc() {
   104  	s.AddHandleFunc(pb.CmdMethod_GetProtocolVersion, handleGetProtocolVersion, false)
   105  	s.AddHandleFunc(pb.CmdMethod_SetProtocolVersion, handleSetProtocolVersion, false)
   106  	s.AddHandleFunc(pb.CmdMethod_CoreDumpConfig, handleCoreDumpConfig, false)
   107  }
   108  
   109  // Start implements the QueryService interface.
   110  func (s *queryService) Start() error {
   111  	return s.handler.Start()
   112  }
   113  
   114  // Close implements the QueryService interface.
   115  func (s *queryService) Close() error {
   116  	return s.handler.Close()
   117  }
   118  
   119  func (s *queryService) ServiceID() string {
   120  	return s.serviceID
   121  }
   122  
   123  type nodeResponse struct {
   124  	nodeAddr string      //address of cn
   125  	response interface{} //response to the request
   126  	err      error
   127  }
   128  
   129  // RequestMultipleCn sends the request to multiple cn and wait the responses.
   130  // nodes : the address of the multiple cn
   131  // qs : QueryService
   132  // genRequest : generate the specific Request based on the business
   133  // handleValidResponse : valid response handler
   134  // handleInvalidResponse : invalid response handler
   135  func RequestMultipleCn(ctx context.Context,
   136  	nodes []string,
   137  	qc client.QueryClient,
   138  	genRequest func() *pb.Request,
   139  	handleValidResponse func(string, *pb.Response),
   140  	handleInvalidResponse func(string),
   141  ) error {
   142  	if genRequest == nil {
   143  		return moerr.NewInternalError(ctx, "invalid request generate function")
   144  	}
   145  	if handleValidResponse == nil {
   146  		return moerr.NewInternalError(ctx, "invalid response handle function")
   147  	}
   148  	nodesLeft := len(nodes)
   149  	responseChan := make(chan nodeResponse, nodesLeft)
   150  
   151  	ctx, cancel := context.WithTimeout(ctx, time.Second*5)
   152  	defer cancel()
   153  	var retErr error
   154  
   155  	for _, node := range nodes {
   156  		// Invalid node address, ignore it.
   157  		if len(node) == 0 {
   158  			nodesLeft--
   159  			continue
   160  		}
   161  
   162  		go func(addr string) {
   163  			// gen request and send it
   164  			if genRequest != nil {
   165  				req := genRequest()
   166  				resp, err := qc.SendMessage(ctx, addr, req)
   167  				responseChan <- nodeResponse{nodeAddr: addr, response: resp, err: err}
   168  			}
   169  		}(node)
   170  	}
   171  
   172  	// Wait for all responses.
   173  	for nodesLeft > 0 {
   174  		select {
   175  		case res := <-responseChan:
   176  			if res.err != nil && retErr != nil {
   177  				retErr = errors.Wrapf(res.err, "failed to get result from %s", res.nodeAddr)
   178  			} else {
   179  				queryResp, ok := res.response.(*pb.Response)
   180  				if ok {
   181  					//save response
   182  					if handleValidResponse != nil {
   183  						handleValidResponse(res.nodeAddr, queryResp)
   184  					}
   185  					if queryResp != nil {
   186  						qc.Release(queryResp)
   187  					}
   188  				} else {
   189  					if handleInvalidResponse != nil {
   190  						handleInvalidResponse(res.nodeAddr)
   191  					}
   192  				}
   193  			}
   194  		case <-ctx.Done():
   195  			retErr = moerr.NewInternalError(ctx, "context deadline exceeded")
   196  		}
   197  		nodesLeft--
   198  	}
   199  	return retErr
   200  }