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 }