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