go.etcd.io/etcd@v3.3.27+incompatible/raft/log_unstable.go (about) 1 // Copyright 2015 The etcd Authors 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 raft 16 17 import pb "github.com/coreos/etcd/raft/raftpb" 18 19 // unstable.entries[i] has raft log position i+unstable.offset. 20 // Note that unstable.offset may be less than the highest log 21 // position in storage; this means that the next write to storage 22 // might need to truncate the log before persisting unstable.entries. 23 type unstable struct { 24 // the incoming unstable snapshot, if any. 25 snapshot *pb.Snapshot 26 // all entries that have not yet been written to storage. 27 entries []pb.Entry 28 offset uint64 29 30 logger Logger 31 } 32 33 // maybeFirstIndex returns the index of the first possible entry in entries 34 // if it has a snapshot. 35 func (u *unstable) maybeFirstIndex() (uint64, bool) { 36 if u.snapshot != nil { 37 return u.snapshot.Metadata.Index + 1, true 38 } 39 return 0, false 40 } 41 42 // maybeLastIndex returns the last index if it has at least one 43 // unstable entry or snapshot. 44 func (u *unstable) maybeLastIndex() (uint64, bool) { 45 if l := len(u.entries); l != 0 { 46 return u.offset + uint64(l) - 1, true 47 } 48 if u.snapshot != nil { 49 return u.snapshot.Metadata.Index, true 50 } 51 return 0, false 52 } 53 54 // maybeTerm returns the term of the entry at index i, if there 55 // is any. 56 func (u *unstable) maybeTerm(i uint64) (uint64, bool) { 57 if i < u.offset { 58 if u.snapshot == nil { 59 return 0, false 60 } 61 if u.snapshot.Metadata.Index == i { 62 return u.snapshot.Metadata.Term, true 63 } 64 return 0, false 65 } 66 67 last, ok := u.maybeLastIndex() 68 if !ok { 69 return 0, false 70 } 71 if i > last { 72 return 0, false 73 } 74 return u.entries[i-u.offset].Term, true 75 } 76 77 func (u *unstable) stableTo(i, t uint64) { 78 gt, ok := u.maybeTerm(i) 79 if !ok { 80 return 81 } 82 // if i < offset, term is matched with the snapshot 83 // only update the unstable entries if term is matched with 84 // an unstable entry. 85 if gt == t && i >= u.offset { 86 u.entries = u.entries[i+1-u.offset:] 87 u.offset = i + 1 88 u.shrinkEntriesArray() 89 } 90 } 91 92 // shrinkEntriesArray discards the underlying array used by the entries slice 93 // if most of it isn't being used. This avoids holding references to a bunch of 94 // potentially large entries that aren't needed anymore. Simply clearing the 95 // entries wouldn't be safe because clients might still be using them. 96 func (u *unstable) shrinkEntriesArray() { 97 // We replace the array if we're using less than half of the space in 98 // it. This number is fairly arbitrary, chosen as an attempt to balance 99 // memory usage vs number of allocations. It could probably be improved 100 // with some focused tuning. 101 const lenMultiple = 2 102 if len(u.entries) == 0 { 103 u.entries = nil 104 } else if len(u.entries)*lenMultiple < cap(u.entries) { 105 newEntries := make([]pb.Entry, len(u.entries)) 106 copy(newEntries, u.entries) 107 u.entries = newEntries 108 } 109 } 110 111 func (u *unstable) stableSnapTo(i uint64) { 112 if u.snapshot != nil && u.snapshot.Metadata.Index == i { 113 u.snapshot = nil 114 } 115 } 116 117 func (u *unstable) restore(s pb.Snapshot) { 118 u.offset = s.Metadata.Index + 1 119 u.entries = nil 120 u.snapshot = &s 121 } 122 123 func (u *unstable) truncateAndAppend(ents []pb.Entry) { 124 after := ents[0].Index 125 switch { 126 case after == u.offset+uint64(len(u.entries)): 127 // after is the next index in the u.entries 128 // directly append 129 u.entries = append(u.entries, ents...) 130 case after <= u.offset: 131 u.logger.Infof("replace the unstable entries from index %d", after) 132 // The log is being truncated to before our current offset 133 // portion, so set the offset and replace the entries 134 u.offset = after 135 u.entries = ents 136 default: 137 // truncate to after and copy to u.entries 138 // then append 139 u.logger.Infof("truncate the unstable entries before index %d", after) 140 u.entries = append([]pb.Entry{}, u.slice(u.offset, after)...) 141 u.entries = append(u.entries, ents...) 142 } 143 } 144 145 func (u *unstable) slice(lo uint64, hi uint64) []pb.Entry { 146 u.mustCheckOutOfBounds(lo, hi) 147 return u.entries[lo-u.offset : hi-u.offset] 148 } 149 150 // u.offset <= lo <= hi <= u.offset+len(u.offset) 151 func (u *unstable) mustCheckOutOfBounds(lo, hi uint64) { 152 if lo > hi { 153 u.logger.Panicf("invalid unstable.slice %d > %d", lo, hi) 154 } 155 upper := u.offset + uint64(len(u.entries)) 156 if lo < u.offset || hi > upper { 157 u.logger.Panicf("unstable.slice[%d,%d) out of bound [%d,%d]", lo, hi, u.offset, upper) 158 } 159 }