github.com/matrixorigin/matrixone@v1.2.0/pkg/pb/logservice/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 logservice
    16  
    17  import (
    18  	"fmt"
    19  	"reflect"
    20  	"sort"
    21  	"time"
    22  
    23  	"github.com/matrixorigin/matrixone/pkg/pb/metadata"
    24  )
    25  
    26  const (
    27  	// NoLeader is the replica ID of the leader node.
    28  	NoLeader uint64 = 0
    29  	// HeaderSize is the size of the header for each logservice and
    30  	// hakeeper command.
    31  	HeaderSize = 4
    32  )
    33  
    34  // ResizePayload resizes the payload length to length bytes.
    35  func (m *LogRecord) ResizePayload(length int) {
    36  	m.Data = m.Data[:HeaderSize+8+length]
    37  }
    38  
    39  // Payload returns the payload byte slice.
    40  func (m *LogRecord) Payload() []byte {
    41  	return m.Data[HeaderSize+8:]
    42  }
    43  
    44  // NewRSMState creates a new HAKeeperRSMState instance.
    45  func NewRSMState() HAKeeperRSMState {
    46  	return HAKeeperRSMState{
    47  		NextIDByKey:      make(map[string]uint64),
    48  		ScheduleCommands: make(map[string]CommandBatch),
    49  		LogShards:        make(map[string]uint64),
    50  		CNState:          NewCNState(),
    51  		TNState:          NewTNState(),
    52  		LogState:         NewLogState(),
    53  		ProxyState:       NewProxyState(),
    54  		ClusterInfo:      newClusterInfo(),
    55  	}
    56  }
    57  
    58  func newClusterInfo() ClusterInfo {
    59  	return ClusterInfo{
    60  		TNShards:  make([]metadata.TNShardRecord, 0),
    61  		LogShards: make([]metadata.LogShardRecord, 0),
    62  	}
    63  }
    64  
    65  // NewCNState creates a new CNState.
    66  func NewCNState() CNState {
    67  	return CNState{
    68  		Stores: make(map[string]CNStoreInfo),
    69  	}
    70  }
    71  
    72  // Update applies the incoming CNStoreHeartbeat into HAKeeper. Tick is the
    73  // current tick of the HAKeeper which is used as the timestamp of the heartbeat.
    74  func (s *CNState) Update(hb CNStoreHeartbeat, tick uint64) {
    75  	storeInfo, ok := s.Stores[hb.UUID]
    76  	if !ok {
    77  		storeInfo = CNStoreInfo{}
    78  		storeInfo.Labels = make(map[string]metadata.LabelList)
    79  		storeInfo.UpTime = time.Now().UnixNano()
    80  	}
    81  	if storeInfo.WorkState == metadata.WorkState_Unknown { // set init value
    82  		v, ok := metadata.WorkState_value[metadata.ToTitle(hb.InitWorkState)]
    83  		if !ok || v == int32(metadata.WorkState_Unknown) {
    84  			storeInfo.WorkState = metadata.WorkState_Working
    85  		} else {
    86  			storeInfo.WorkState = metadata.WorkState(v)
    87  		}
    88  	}
    89  	storeInfo.Tick = tick
    90  	storeInfo.ServiceAddress = hb.ServiceAddress
    91  	storeInfo.SQLAddress = hb.SQLAddress
    92  	storeInfo.LockServiceAddress = hb.LockServiceAddress
    93  	storeInfo.Role = hb.Role
    94  	storeInfo.TaskServiceCreated = hb.TaskServiceCreated
    95  	storeInfo.QueryAddress = hb.QueryAddress
    96  	storeInfo.GossipAddress = hb.GossipAddress
    97  	storeInfo.GossipJoined = hb.GossipJoined
    98  	if hb.ConfigData != nil {
    99  		storeInfo.ConfigData = hb.ConfigData
   100  	}
   101  	storeInfo.Resource = hb.Resource
   102  	s.Stores[hb.UUID] = storeInfo
   103  }
   104  
   105  // UpdateLabel updates labels of CN store.
   106  func (s *CNState) UpdateLabel(label CNStoreLabel) {
   107  	storeInfo, ok := s.Stores[label.UUID]
   108  	// If the CN store does not exist, we should do nothing and wait for
   109  	// CN heartbeat.
   110  	if !ok {
   111  		return
   112  	}
   113  	storeInfo.Labels = label.Labels
   114  	s.Stores[label.UUID] = storeInfo
   115  }
   116  
   117  // UpdateWorkState updates work state of CN store.
   118  func (s *CNState) UpdateWorkState(state CNWorkState) {
   119  	if state.GetState() == metadata.WorkState_Unknown {
   120  		state.State = metadata.WorkState_Working
   121  	}
   122  	storeInfo, ok := s.Stores[state.UUID]
   123  	// If the CN store does not exist, we should do nothing and wait for
   124  	// CN heartbeat.
   125  	if !ok {
   126  		return
   127  	}
   128  	storeInfo.WorkState = state.State
   129  	s.Stores[state.UUID] = storeInfo
   130  }
   131  
   132  // PatchCNStore updates work state and labels of CN store.
   133  func (s *CNState) PatchCNStore(stateLabel CNStateLabel) {
   134  	if stateLabel.GetState() == metadata.WorkState_Unknown {
   135  		stateLabel.State = metadata.WorkState_Working
   136  	}
   137  	storeInfo, ok := s.Stores[stateLabel.UUID]
   138  	// If the CN store does not exist, we should do nothing and wait for
   139  	// CN heartbeat.
   140  	if !ok {
   141  		return
   142  	}
   143  	storeInfo.WorkState = stateLabel.State
   144  	if stateLabel.Labels != nil {
   145  		storeInfo.Labels = stateLabel.Labels
   146  	}
   147  	s.Stores[stateLabel.UUID] = storeInfo
   148  }
   149  
   150  // NewTNState creates a new DNState.
   151  func NewTNState() TNState {
   152  	return TNState{
   153  		Stores: make(map[string]TNStoreInfo),
   154  	}
   155  }
   156  
   157  // Update applies the incoming DNStoreHeartbeat into HAKeeper. Tick is the
   158  // current tick of the HAKeeper which is used as the timestamp of the heartbeat.
   159  func (s *TNState) Update(hb TNStoreHeartbeat, tick uint64) {
   160  	storeInfo, ok := s.Stores[hb.UUID]
   161  	if !ok {
   162  		storeInfo = TNStoreInfo{}
   163  	}
   164  	storeInfo.Tick = tick
   165  	storeInfo.Shards = hb.Shards
   166  	storeInfo.ServiceAddress = hb.ServiceAddress
   167  	storeInfo.LogtailServerAddress = hb.LogtailServerAddress
   168  	storeInfo.LockServiceAddress = hb.LockServiceAddress
   169  	storeInfo.TaskServiceCreated = hb.TaskServiceCreated
   170  	if hb.ConfigData != nil {
   171  		storeInfo.ConfigData = hb.ConfigData
   172  	}
   173  	storeInfo.QueryAddress = hb.QueryAddress
   174  	s.Stores[hb.UUID] = storeInfo
   175  }
   176  
   177  // NewLogState creates a new LogState.
   178  func NewLogState() LogState {
   179  	return LogState{
   180  		Shards: make(map[uint64]LogShardInfo),
   181  		Stores: make(map[string]LogStoreInfo),
   182  	}
   183  }
   184  
   185  // Update applies the incoming heartbeat message to the LogState with the
   186  // specified tick used as the timestamp.
   187  func (s *LogState) Update(hb LogStoreHeartbeat, tick uint64) {
   188  	s.updateStores(hb, tick)
   189  	s.updateShards(hb)
   190  }
   191  
   192  func (s *LogState) updateStores(hb LogStoreHeartbeat, tick uint64) {
   193  	storeInfo, ok := s.Stores[hb.UUID]
   194  	if !ok {
   195  		storeInfo = LogStoreInfo{}
   196  	}
   197  	storeInfo.Tick = tick
   198  	storeInfo.RaftAddress = hb.RaftAddress
   199  	storeInfo.ServiceAddress = hb.ServiceAddress
   200  	storeInfo.GossipAddress = hb.GossipAddress
   201  	storeInfo.Replicas = hb.Replicas
   202  	storeInfo.TaskServiceCreated = hb.TaskServiceCreated
   203  	if hb.ConfigData != nil {
   204  		storeInfo.ConfigData = hb.ConfigData
   205  	}
   206  	s.Stores[hb.UUID] = storeInfo
   207  }
   208  
   209  func (s *LogState) updateShards(hb LogStoreHeartbeat) {
   210  	for _, incoming := range hb.Replicas {
   211  		recorded, ok := s.Shards[incoming.ShardID]
   212  		if !ok {
   213  			recorded = LogShardInfo{
   214  				ShardID:  incoming.ShardID,
   215  				Replicas: make(map[uint64]string),
   216  			}
   217  		}
   218  
   219  		if incoming.Epoch > recorded.Epoch {
   220  			recorded.Epoch = incoming.Epoch
   221  			recorded.Replicas = incoming.Replicas
   222  		} else if incoming.Epoch == recorded.Epoch && incoming.Epoch > 0 {
   223  			if !reflect.DeepEqual(recorded.Replicas, incoming.Replicas) {
   224  				panic(fmt.Sprintf("inconsistent replicas, recorded: %+v, incoming: %+v",
   225  					recorded, incoming))
   226  			}
   227  		}
   228  
   229  		if incoming.Term > recorded.Term && incoming.LeaderID != NoLeader {
   230  			recorded.Term = incoming.Term
   231  			recorded.LeaderID = incoming.LeaderID
   232  		}
   233  
   234  		s.Shards[incoming.ShardID] = recorded
   235  	}
   236  }
   237  
   238  // LogString returns "ServiceType/ConfigChangeType UUID RepUuid:RepShardID:RepID InitialMembers".
   239  // Do not add CN's StartTaskRunner info to log string, because there has user and password.
   240  func (m *ScheduleCommand) LogString() string {
   241  	c := func(s string) string {
   242  		return s
   243  	}
   244  
   245  	serviceType := map[ServiceType]string{
   246  		LogService:   "L",
   247  		TNService:    "D",
   248  		CNService:    "C",
   249  		ProxyService: "P",
   250  	}[m.ServiceType]
   251  
   252  	target := c(m.UUID)
   253  	if m.ShutdownStore != nil {
   254  		return fmt.Sprintf("%s/shutdown %s", serviceType, target)
   255  	}
   256  	if m.CreateTaskService != nil {
   257  		return fmt.Sprintf("%s/CreateTask %s", serviceType, target)
   258  	}
   259  	if m.DeleteCNStore != nil {
   260  		return fmt.Sprintf("%s/DeleteCNStore %s", serviceType, target)
   261  	}
   262  	if m.JoinGossipCluster != nil {
   263  		return fmt.Sprintf("%s/JoinGossipCluster %s", serviceType, target)
   264  	}
   265  	if m.DeleteProxyStore != nil {
   266  		return fmt.Sprintf("%s/DeleteProxyStore %s", serviceType, target)
   267  	}
   268  	if m.ConfigChange == nil {
   269  		return fmt.Sprintf("%s/unknown command %s", serviceType, m.String())
   270  	}
   271  
   272  	configChangeType := "Unknown"
   273  	if m.ConfigChange != nil {
   274  		configChangeType = map[ConfigChangeType]string{
   275  			AddReplica:    "Add",
   276  			RemoveReplica: "Remove",
   277  			StartReplica:  "Start",
   278  			StopReplica:   "Stop",
   279  			KillZombie:    "Kill",
   280  		}[m.ConfigChange.ChangeType]
   281  	}
   282  
   283  	replica := c(m.ConfigChange.Replica.UUID)
   284  	s := fmt.Sprintf("%s/%s %s %s:%d:%d:%d",
   285  		serviceType, configChangeType, target, replica,
   286  		m.ConfigChange.Replica.ShardID,
   287  		m.ConfigChange.Replica.ReplicaID,
   288  		m.ConfigChange.Replica.Epoch)
   289  
   290  	if len(m.ConfigChange.InitialMembers) != 0 {
   291  		initMembers := make([]string, 0, len(m.ConfigChange.InitialMembers))
   292  		for repId, uuid := range m.ConfigChange.InitialMembers {
   293  			initMembers = append(initMembers, fmt.Sprintf("%d:%s", repId, c(uuid)))
   294  		}
   295  		sort.Strings(initMembers)
   296  		s += fmt.Sprintf(" %v", initMembers)
   297  	}
   298  
   299  	return s
   300  }
   301  
   302  // NewProxyState creates a new ProxyState.
   303  func NewProxyState() ProxyState {
   304  	return ProxyState{
   305  		Stores: make(map[string]ProxyStore),
   306  	}
   307  }
   308  
   309  func (s *ProxyState) Update(hb ProxyHeartbeat, tick uint64) {
   310  	storeInfo, ok := s.Stores[hb.UUID]
   311  	if !ok {
   312  		storeInfo = ProxyStore{}
   313  	}
   314  	storeInfo.UUID = hb.UUID
   315  	storeInfo.Tick = tick
   316  	storeInfo.ListenAddress = hb.ListenAddress
   317  	if hb.ConfigData != nil {
   318  		storeInfo.ConfigData = hb.ConfigData
   319  	}
   320  	s.Stores[hb.UUID] = storeInfo
   321  }