github.com/matrixorigin/matrixone@v1.2.0/pkg/logservice/rsm.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  	"encoding/binary"
    19  	"io"
    20  
    21  	sm "github.com/lni/dragonboat/v4/statemachine"
    22  	pb "github.com/matrixorigin/matrixone/pkg/pb/logservice"
    23  )
    24  
    25  // XXX WHY BIG ENDIAN?
    26  var (
    27  	binaryEnc = binary.BigEndian
    28  )
    29  
    30  const (
    31  	firstLogShardID uint64 = 1
    32  	headerSize             = pb.HeaderSize
    33  )
    34  
    35  // used to indicate query types
    36  type leaseHolderIDQuery struct{}
    37  type indexQuery struct{}
    38  type truncatedLsnQuery struct{}
    39  type leaseHistoryQuery struct{ lsn uint64 }
    40  
    41  func getAppendCmd(cmd []byte, replicaID uint64) []byte {
    42  	if len(cmd) < headerSize+8 {
    43  		panic("cmd too small")
    44  	}
    45  	binaryEnc.PutUint32(cmd, uint32(pb.UserEntryUpdate))
    46  	binaryEnc.PutUint64(cmd[headerSize:], replicaID)
    47  	return cmd
    48  }
    49  
    50  func parseCmdTag(cmd []byte) pb.UpdateType {
    51  	return pb.UpdateType(binaryEnc.Uint32(cmd))
    52  }
    53  
    54  func parseTruncatedLsn(cmd []byte) uint64 {
    55  	return binaryEnc.Uint64(cmd[headerSize:])
    56  }
    57  
    58  func parseLeaseHolderID(cmd []byte) uint64 {
    59  	return binaryEnc.Uint64(cmd[headerSize:])
    60  }
    61  
    62  func parseTsoUpdateCmd(cmd []byte) uint64 {
    63  	return binaryEnc.Uint64(cmd[headerSize:])
    64  }
    65  
    66  func getSetLeaseHolderCmd(leaseHolderID uint64) []byte {
    67  	cmd := make([]byte, headerSize+8)
    68  	binaryEnc.PutUint32(cmd, uint32(pb.LeaseHolderIDUpdate))
    69  	binaryEnc.PutUint64(cmd[headerSize:], leaseHolderID)
    70  	return cmd
    71  }
    72  
    73  func getSetTruncatedLsnCmd(lsn uint64) []byte {
    74  	cmd := make([]byte, headerSize+8)
    75  	binaryEnc.PutUint32(cmd, uint32(pb.TruncateLSNUpdate))
    76  	binaryEnc.PutUint64(cmd[headerSize:], lsn)
    77  	return cmd
    78  }
    79  
    80  func getTsoUpdateCmd(count uint64) []byte {
    81  	cmd := make([]byte, headerSize+8)
    82  	binaryEnc.PutUint32(cmd, uint32(pb.TSOUpdate))
    83  	binaryEnc.PutUint64(cmd[headerSize:], count)
    84  	return cmd
    85  }
    86  
    87  type stateMachine struct {
    88  	shardID   uint64
    89  	replicaID uint64
    90  	state     pb.RSMState
    91  }
    92  
    93  var _ sm.IStateMachine = (*stateMachine)(nil)
    94  
    95  func newStateMachine(shardID uint64, replicaID uint64) sm.IStateMachine {
    96  	state := pb.RSMState{
    97  		Tso:          1,
    98  		LeaseHistory: make(map[uint64]uint64),
    99  	}
   100  	return &stateMachine{
   101  		shardID:   shardID,
   102  		replicaID: replicaID,
   103  		state:     state,
   104  	}
   105  }
   106  
   107  func (s *stateMachine) truncateLeaseHistory(lsn uint64) {
   108  	_, lsn = s.getLeaseHistory(lsn)
   109  	for key := range s.state.LeaseHistory {
   110  		if key < lsn {
   111  			delete(s.state.LeaseHistory, key)
   112  		}
   113  	}
   114  }
   115  
   116  func (s *stateMachine) getLeaseHistory(lsn uint64) (uint64, uint64) {
   117  	max := uint64(0)
   118  	lease := uint64(0)
   119  	for key, val := range s.state.LeaseHistory {
   120  		if key >= lsn {
   121  			continue
   122  		}
   123  		if key > max {
   124  			max = key
   125  			lease = val
   126  		}
   127  	}
   128  	return lease, max
   129  }
   130  
   131  func (s *stateMachine) handleSetLeaseHolderID(cmd []byte) sm.Result {
   132  	s.state.LeaseHolderID = parseLeaseHolderID(cmd)
   133  	s.state.LeaseHistory[s.state.Index] = s.state.LeaseHolderID
   134  	return sm.Result{}
   135  }
   136  
   137  func (s *stateMachine) handleTruncateLsn(cmd []byte) sm.Result {
   138  	lsn := parseTruncatedLsn(cmd)
   139  	if lsn > s.state.TruncatedLsn {
   140  		s.state.TruncatedLsn = lsn
   141  		s.truncateLeaseHistory(lsn)
   142  		return sm.Result{}
   143  	}
   144  	return sm.Result{Value: s.state.TruncatedLsn}
   145  }
   146  
   147  // handleUserUpdate returns an empty sm.Result on success or it returns a
   148  // sm.Result value with the Value field set to the current leaseholder ID
   149  // to indicate rejection by mismatched leaseholder ID.
   150  func (s *stateMachine) handleUserUpdate(cmd []byte) sm.Result {
   151  	if s.state.LeaseHolderID != parseLeaseHolderID(cmd) {
   152  		data := make([]byte, 8)
   153  		binaryEnc.PutUint64(data, s.state.LeaseHolderID)
   154  		return sm.Result{Data: data}
   155  	}
   156  	return sm.Result{Value: s.state.Index}
   157  }
   158  
   159  func (s *stateMachine) handleTsoUpdate(cmd []byte) sm.Result {
   160  	count := parseTsoUpdateCmd(cmd)
   161  	result := sm.Result{Value: s.state.Tso}
   162  	s.state.Tso += count
   163  	return result
   164  }
   165  
   166  func (s *stateMachine) Close() error {
   167  	return nil
   168  }
   169  
   170  func (s *stateMachine) Update(e sm.Entry) (sm.Result, error) {
   171  	cmd := e.Cmd
   172  	s.state.Index = e.Index
   173  
   174  	switch parseCmdTag(cmd) {
   175  	case pb.LeaseHolderIDUpdate:
   176  		return s.handleSetLeaseHolderID(cmd), nil
   177  	case pb.TruncateLSNUpdate:
   178  		return s.handleTruncateLsn(cmd), nil
   179  	case pb.UserEntryUpdate:
   180  		return s.handleUserUpdate(cmd), nil
   181  	case pb.TSOUpdate:
   182  		return s.handleTsoUpdate(cmd), nil
   183  	default:
   184  		panic("unknown entry type")
   185  	}
   186  }
   187  
   188  func (s *stateMachine) Lookup(query interface{}) (interface{}, error) {
   189  	if _, ok := query.(indexQuery); ok {
   190  		return s.state.Index, nil
   191  	} else if _, ok := query.(leaseHolderIDQuery); ok {
   192  		return s.state.LeaseHolderID, nil
   193  	} else if _, ok := query.(truncatedLsnQuery); ok {
   194  		return s.state.TruncatedLsn, nil
   195  	} else if v, ok := query.(leaseHistoryQuery); ok {
   196  		lease, _ := s.getLeaseHistory(v.lsn)
   197  		return lease, nil
   198  	}
   199  	panic("unknown lookup command type")
   200  }
   201  
   202  func (s *stateMachine) SaveSnapshot(w io.Writer,
   203  	_ sm.ISnapshotFileCollection, _ <-chan struct{}) error {
   204  	// FIXME: memory recycling when necessary
   205  	data := make([]byte, s.state.Size())
   206  	n, err := s.state.MarshalToSizedBuffer(data)
   207  	if err != nil {
   208  		return err
   209  	}
   210  	_, err = w.Write(data[:n])
   211  	return err
   212  }
   213  
   214  func (s *stateMachine) RecoverFromSnapshot(r io.Reader,
   215  	_ []sm.SnapshotFile, _ <-chan struct{}) error {
   216  	data, err := io.ReadAll(r)
   217  	if err != nil {
   218  		return err
   219  	}
   220  	return s.state.Unmarshal(data)
   221  }