github.com/matrixorigin/matrixone@v1.2.0/pkg/tests/service/logservice.go (about)

     1  // Copyright 2021 - 2022 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 service
    16  
    17  import (
    18  	"path/filepath"
    19  	"sync"
    20  
    21  	"github.com/google/uuid"
    22  	"github.com/lni/dragonboat/v4"
    23  	"github.com/lni/vfs"
    24  	"github.com/matrixorigin/matrixone/pkg/common/runtime"
    25  	"github.com/matrixorigin/matrixone/pkg/taskservice"
    26  	"github.com/stretchr/testify/assert"
    27  	"go.uber.org/zap"
    28  
    29  	"github.com/matrixorigin/matrixone/pkg/fileservice"
    30  	"github.com/matrixorigin/matrixone/pkg/logservice"
    31  	logpb "github.com/matrixorigin/matrixone/pkg/pb/logservice"
    32  )
    33  
    34  var (
    35  	defaultDeploymentID   uint64 = 1
    36  	defaultRTTMillisecond uint64 = 5
    37  )
    38  
    39  // LogService describes expected behavior for log service.
    40  type LogService interface {
    41  	// Start sends heartbeat and start to handle command.
    42  	Start() error
    43  	// Close stops store
    44  	Close() error
    45  	// Status returns the status of service
    46  	Status() ServiceStatus
    47  
    48  	// ID returns uuid of store
    49  	ID() string
    50  
    51  	// IsLeaderHakeeper checks hakeeper information.
    52  	IsLeaderHakeeper() (bool, error)
    53  
    54  	// GetClusterState returns cluster information from hakeeper leader.
    55  	GetClusterState() (*logpb.CheckerState, error)
    56  
    57  	// SetInitialClusterInfo sets cluster initialize state.
    58  	SetInitialClusterInfo(numOfLogShards, numOfTNShards, numOfLogReplicas uint64) error
    59  
    60  	// StartHAKeeperReplica starts hakeeper replicas.
    61  	StartHAKeeperReplica(replicaID uint64, initialReplicas map[uint64]dragonboat.Target, join bool) error
    62  
    63  	// GetTaskService returns the taskService
    64  	GetTaskService() (taskservice.TaskService, bool)
    65  }
    66  
    67  // logService wraps logservice.WrappedService.
    68  //
    69  // The main purpose of this structure is to maintain status
    70  type logService struct {
    71  	sync.Mutex
    72  	status ServiceStatus
    73  	svc    *logservice.WrappedService
    74  }
    75  
    76  func (ls *logService) Start() error {
    77  	ls.Lock()
    78  	defer ls.Unlock()
    79  
    80  	if ls.status == ServiceInitialized {
    81  		err := ls.svc.Start()
    82  		if err != nil {
    83  			return err
    84  		}
    85  		ls.status = ServiceStarted
    86  	}
    87  
    88  	return nil
    89  }
    90  
    91  func (ls *logService) Close() error {
    92  	ls.Lock()
    93  	defer ls.Unlock()
    94  
    95  	if ls.status == ServiceStarted {
    96  		err := ls.svc.Close()
    97  		if err != nil {
    98  			return err
    99  		}
   100  		ls.status = ServiceClosed
   101  	}
   102  
   103  	return nil
   104  }
   105  
   106  func (ls *logService) Status() ServiceStatus {
   107  	ls.Lock()
   108  	defer ls.Unlock()
   109  	return ls.status
   110  }
   111  
   112  func (ls *logService) ID() string {
   113  	return ls.svc.ID()
   114  }
   115  
   116  func (ls *logService) IsLeaderHakeeper() (bool, error) {
   117  	return ls.svc.IsLeaderHakeeper()
   118  }
   119  
   120  func (ls *logService) GetClusterState() (*logpb.CheckerState, error) {
   121  	return ls.svc.GetClusterState()
   122  }
   123  
   124  func (ls *logService) SetInitialClusterInfo(
   125  	numOfLogShards, numOfTNShards, numOfLogReplicas uint64,
   126  ) error {
   127  	return ls.svc.SetInitialClusterInfo(
   128  		numOfLogShards, numOfTNShards, numOfLogReplicas,
   129  	)
   130  }
   131  
   132  func (ls *logService) StartHAKeeperReplica(
   133  	replicaID uint64, initialReplicas map[uint64]dragonboat.Target, join bool,
   134  ) error {
   135  	return ls.svc.StartHAKeeperReplica(replicaID, initialReplicas, join)
   136  }
   137  
   138  func (ls *logService) GetTaskService() (taskservice.TaskService, bool) {
   139  	return ls.svc.GetTaskService()
   140  }
   141  
   142  // logOptions is options for a log service.
   143  type logOptions []logservice.Option
   144  
   145  // newLogService constructs an instance of `LogService`.
   146  func newLogService(
   147  	cfg logservice.Config,
   148  	fs fileservice.FileService,
   149  	opts logOptions,
   150  ) (LogService, error) {
   151  	svc, err := logservice.NewWrappedService(cfg, fs, nil, opts...)
   152  	if err != nil {
   153  		return nil, err
   154  	}
   155  	return &logService{status: ServiceInitialized, svc: svc}, nil
   156  }
   157  
   158  // buildLogConfig builds configuration for a log service.
   159  func buildLogConfig(
   160  	index int, opt Options, address *serviceAddresses,
   161  ) logservice.Config {
   162  	cfg := logservice.DefaultConfig()
   163  	uid, _ := uuid.NewV7()
   164  	cfg.UUID = uid.String()
   165  	cfg.FS = vfs.NewStrictMem()
   166  	cfg.DeploymentID = defaultDeploymentID
   167  	cfg.RTTMillisecond = defaultRTTMillisecond
   168  	cfg.LogServicePort = getPort(address.getLogListenAddress(index)) // hakeeper client use this address
   169  	cfg.RaftPort = getPort(address.getLogRaftAddress(index))
   170  	cfg.GossipPort = getPort(address.getLogGossipAddress(index))
   171  	cfg.GossipSeedAddresses = address.getLogGossipSeedAddresses()
   172  	cfg.GossipAllowSelfAsSeed = opt.initial.logReplicaNum == 1
   173  	cfg.DataDir = filepath.Join(opt.rootDataDir, cfg.UUID)
   174  	cfg.HeartbeatInterval.Duration = opt.heartbeat.log
   175  	cfg.HAKeeperCheckInterval.Duration = opt.hakeeper.checkInterval
   176  	cfg.HAKeeperClientConfig.ServiceAddresses = address.listHAKeeperListenAddresses()
   177  	// setting hakeeper configuration
   178  	cfg.HAKeeperConfig.TickPerSecond = opt.hakeeper.tickPerSecond
   179  	cfg.HAKeeperConfig.LogStoreTimeout.Duration = opt.hakeeper.logStoreTimeout
   180  	cfg.HAKeeperConfig.TNStoreTimeout.Duration = opt.hakeeper.tnStoreTimeout
   181  	cfg.HAKeeperConfig.CNStoreTimeout.Duration = opt.hakeeper.cnStoreTimeout
   182  
   183  	return cfg
   184  }
   185  
   186  // buildLogOptions builds options for a log service.
   187  //
   188  // NB: We need the filled version of logservice.Config.
   189  func buildLogOptions(cfg logservice.Config, filter FilterFunc) logOptions {
   190  	return []logservice.Option{
   191  		logservice.WithBackendFilter(filter),
   192  		logservice.WithRuntime(runtime.ProcessLevelRuntime()),
   193  	}
   194  }
   195  
   196  // startHAKeeperReplica selects the first `n` log services to start hakeeper replica.
   197  func (c *testCluster) startHAKeeperReplica() error {
   198  	selected := c.selectHAkeeperServices()
   199  	assert.NotZero(c.t, len(selected))
   200  
   201  	c.logger.Info("start hakeeper replicas", zap.Int("batch", len(selected)))
   202  
   203  	indexToReplicaID := func(index int) uint64 {
   204  		return uint64(index + 1)
   205  	}
   206  
   207  	// construct peers
   208  	peers := make(map[uint64]dragonboat.Target)
   209  	for i, logsvc := range selected {
   210  		replicaID := indexToReplicaID(i)
   211  		peers[replicaID] = logsvc.ID()
   212  	}
   213  
   214  	// start all hakeeper replicas
   215  	for i, logsvc := range selected {
   216  		replicaID := indexToReplicaID(i)
   217  		err := logsvc.StartHAKeeperReplica(replicaID, peers, false)
   218  		if err != nil {
   219  			c.logger.Error("fail to start hakeeper replica", zap.Error(err), zap.Int("index", i))
   220  			return err
   221  		}
   222  		c.logger.Info("hakeeper replica started", zap.Int("index", i))
   223  	}
   224  
   225  	return nil
   226  }
   227  
   228  // setInitialClusterInfo initializes cluster information.
   229  func (c *testCluster) setInitialClusterInfo() error {
   230  	errChan := make(chan error, 1)
   231  
   232  	initialize := func() {
   233  		var err error
   234  		defer func() {
   235  			errChan <- err
   236  		}()
   237  
   238  		selected := c.selectHAkeeperServices()
   239  		assert.NotZero(c.t, len(selected))
   240  
   241  		c.logger.Info("initialize cluster information")
   242  
   243  		err = selected[0].SetInitialClusterInfo(
   244  			c.opt.initial.logShardNum,
   245  			c.opt.initial.tnShardNum,
   246  			c.opt.initial.logReplicaNum,
   247  		)
   248  		if err != nil {
   249  			c.logger.Error("fail to initialize cluster", zap.Error(err))
   250  			return
   251  		}
   252  
   253  		c.logger.Info("cluster information initialized")
   254  	}
   255  
   256  	// initialize cluster only once
   257  	c.log.once.Do(initialize)
   258  	return <-errChan
   259  }
   260  
   261  // listHAKeeperService lists all log services that start hakeeper.
   262  func (c *testCluster) selectHAkeeperServices() []LogService {
   263  	n := haKeeperNum(c.opt.initial.logServiceNum)
   264  	svcs := make([]LogService, n)
   265  	for i := 0; i < n; i++ {
   266  		svcs[i] = c.log.svcs[i]
   267  	}
   268  	return svcs
   269  }