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