github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/kv/kvserver/replica_application_cmd_buf.go (about) 1 // Copyright 2019 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 package kvserver 12 13 import ( 14 "sync" 15 16 "github.com/cockroachdb/cockroach/pkg/kv/kvserver/apply" 17 ) 18 19 // replica_application_*.go files provide concrete implementations of 20 // the interfaces defined in the storage/apply package: 21 // 22 // replica_application_state_machine.go -> apply.StateMachine 23 // replica_application_decoder.go -> apply.Decoder 24 // replica_application_cmd.go -> apply.Command (and variants) 25 // replica_application_cmd_buf.go -> apply.CommandIterator (and variants) 26 // replica_application_cmd_buf.go -> apply.CommandList (and variants) 27 // 28 // These allow Replica to interface with the storage/apply package. 29 30 // replicatedCmdBufNodeSize is the size of arrays in an replicatedCmdBufBufNode. 31 const replicatedCmdBufNodeSize = 8 32 33 // replicatedCmdBufSliceFreeListSize is the size of the replicatedCmdBufSlice 34 // free list. The size has been tuned for the maximum number of iterators that 35 // the storage/apply package uses at once. If this size is insufficient then the 36 // freelist will panic to ensure that regressions are loud. 37 const replicatedCmdBufSliceFreeListSize = 3 38 39 // replicatedCmdBuf is an allocation-efficient buffer used during the 40 // application of raft entries. Initialization occurs lazily upon the first 41 // call to allocate but used replicatedCmdBuf objects should be released 42 // explicitly with the clear() method to release the allocated buffers back 43 // to the pool. 44 type replicatedCmdBuf struct { 45 len int32 46 head, tail *replicatedCmdBufNode 47 free replicatedCmdBufSliceFreeList 48 } 49 50 // replicatedCmdBufNode is a linked-list element in an replicatedCmdBufBuf. 51 type replicatedCmdBufNode struct { 52 len int32 53 buf [replicatedCmdBufNodeSize]replicatedCmd 54 next *replicatedCmdBufNode 55 } 56 57 var replicatedCmdBufBufNodeSyncPool = sync.Pool{ 58 New: func() interface{} { return new(replicatedCmdBufNode) }, 59 } 60 61 // allocate extends the length of buf by one and returns the newly added 62 // element. If this is the first call to allocate it will initialize buf. 63 // After a buf is initialized it should be explicitly destroyed. 64 func (buf *replicatedCmdBuf) allocate() *replicatedCmd { 65 if buf.tail == nil { // lazy initialization 66 n := replicatedCmdBufBufNodeSyncPool.Get().(*replicatedCmdBufNode) 67 buf.head, buf.tail = n, n 68 } 69 if buf.tail.len == replicatedCmdBufNodeSize { 70 newTail := replicatedCmdBufBufNodeSyncPool.Get().(*replicatedCmdBufNode) 71 buf.tail.next = newTail 72 buf.tail = newTail 73 } 74 ret := &buf.tail.buf[buf.tail.len] 75 buf.tail.len++ 76 buf.len++ 77 return ret 78 } 79 80 // truncate clears all of the entries currently in a buffer and returns any 81 // allocated buffers to the pool. 82 func (buf *replicatedCmdBuf) clear() { 83 for buf.head != nil { 84 buf.len -= buf.head.len 85 oldHead := buf.head 86 newHead := oldHead.next 87 buf.head = newHead 88 *oldHead = replicatedCmdBufNode{} 89 replicatedCmdBufBufNodeSyncPool.Put(oldHead) 90 } 91 *buf = replicatedCmdBuf{} 92 } 93 94 // newIter returns a pointer to a new uninitialized iterator. The iterator 95 // should be closed when no longer in use. 96 func (buf *replicatedCmdBuf) newIter() *replicatedCmdBufSlice { 97 return buf.free.get() 98 } 99 100 // replicatedCmdBufPtr is a pointer into a replicatedCmdBuf. 101 type replicatedCmdBufPtr struct { 102 idx int32 103 buf *replicatedCmdBuf 104 node *replicatedCmdBufNode 105 } 106 107 func (ptr *replicatedCmdBufPtr) valid() bool { 108 return ptr.idx < ptr.buf.len 109 } 110 111 func (ptr *replicatedCmdBufPtr) cur() *replicatedCmd { 112 return &ptr.node.buf[ptr.idx%replicatedCmdBufNodeSize] 113 } 114 115 func (ptr *replicatedCmdBufPtr) next() { 116 ptr.idx++ 117 if !ptr.valid() { 118 return 119 } 120 if ptr.idx%replicatedCmdBufNodeSize == 0 { 121 ptr.node = ptr.node.next 122 } 123 } 124 125 // replicatedCmdBufSlice iterates through the entries in a replicatedCmdBufBuf. 126 type replicatedCmdBufSlice struct { 127 head, tail replicatedCmdBufPtr 128 } 129 130 // init initializes the slice over the entries in replicatedCmdBuf. 131 func (it *replicatedCmdBufSlice) init(buf *replicatedCmdBuf) { 132 *it = replicatedCmdBufSlice{ 133 head: replicatedCmdBufPtr{idx: 0, buf: buf, node: buf.head}, 134 tail: replicatedCmdBufPtr{idx: buf.len, buf: buf, node: buf.tail}, 135 } 136 } 137 138 // initEmpty initializes the slice with a length of 0 and pointing at 139 // the head of the replicatedCmdBuf. 140 func (it *replicatedCmdBufSlice) initEmpty(buf *replicatedCmdBuf) { 141 *it = replicatedCmdBufSlice{ 142 head: replicatedCmdBufPtr{idx: 0, buf: buf, node: buf.head}, 143 tail: replicatedCmdBufPtr{idx: 0, buf: buf, node: buf.head}, 144 } 145 } 146 147 // len returns the length of the slice. 148 func (it *replicatedCmdBufSlice) len() int { 149 return int(it.tail.idx - it.head.idx) 150 } 151 152 // Valid implements the apply.CommandIteratorBase interface. 153 func (it *replicatedCmdBufSlice) Valid() bool { 154 return it.len() > 0 155 } 156 157 // Next implements the apply.CommandIteratorBase interface. 158 func (it *replicatedCmdBufSlice) Next() { 159 it.head.next() 160 } 161 162 // cur and its variants implement the apply.{Checked,Applied}CommandIterator interface. 163 func (it *replicatedCmdBufSlice) cur() *replicatedCmd { return it.head.cur() } 164 func (it *replicatedCmdBufSlice) Cur() apply.Command { return it.head.cur() } 165 func (it *replicatedCmdBufSlice) CurChecked() apply.CheckedCommand { return it.head.cur() } 166 func (it *replicatedCmdBufSlice) CurApplied() apply.AppliedCommand { return it.head.cur() } 167 168 // append and its variants implement the apply.{Checked,Applied}CommandList interface. 169 func (it *replicatedCmdBufSlice) append(cmd *replicatedCmd) { 170 cur := it.tail.cur() 171 if cur == cmd { 172 // Avoid the copy. 173 } else { 174 *cur = *cmd 175 } 176 it.tail.next() 177 } 178 func (it *replicatedCmdBufSlice) Append(cmd apply.Command) { it.append(cmd.(*replicatedCmd)) } 179 func (it *replicatedCmdBufSlice) AppendChecked(cmd apply.CheckedCommand) { 180 it.append(cmd.(*replicatedCmd)) 181 } 182 func (it *replicatedCmdBufSlice) AppendApplied(cmd apply.AppliedCommand) { 183 it.append(cmd.(*replicatedCmd)) 184 } 185 186 // newList and its variants implement the apply.{Checked}CommandIterator interface. 187 func (it *replicatedCmdBufSlice) newList() *replicatedCmdBufSlice { 188 it2 := it.head.buf.newIter() 189 it2.initEmpty(it.head.buf) 190 return it2 191 } 192 func (it *replicatedCmdBufSlice) NewList() apply.CommandList { return it.newList() } 193 func (it *replicatedCmdBufSlice) NewCheckedList() apply.CheckedCommandList { return it.newList() } 194 func (it *replicatedCmdBufSlice) NewAppliedList() apply.AppliedCommandList { return it.newList() } 195 196 // Close implements the apply.CommandIteratorBase interface. 197 func (it *replicatedCmdBufSlice) Close() { 198 it.head.buf.free.put(it) 199 } 200 201 // replicatedCmdBufSliceFreeList is a free list of replicatedCmdBufSlice 202 // objects that is used to avoid memory allocations for short-lived 203 // replicatedCmdBufSlice objects that require heap allocation. 204 type replicatedCmdBufSliceFreeList struct { 205 iters [replicatedCmdBufSliceFreeListSize]replicatedCmdBufSlice 206 inUse [replicatedCmdBufSliceFreeListSize]bool 207 } 208 209 func (f *replicatedCmdBufSliceFreeList) put(it *replicatedCmdBufSlice) { 210 *it = replicatedCmdBufSlice{} 211 for i := range f.iters { 212 if &f.iters[i] == it { 213 f.inUse[i] = false 214 return 215 } 216 } 217 } 218 219 func (f *replicatedCmdBufSliceFreeList) get() *replicatedCmdBufSlice { 220 for i, inUse := range f.inUse { 221 if !inUse { 222 f.inUse[i] = true 223 return &f.iters[i] 224 } 225 } 226 panic("replicatedCmdBufSliceFreeList has no free elements. Is " + 227 "replicatedCmdBufSliceFreeListSize tuned properly? Are we " + 228 "leaking iterators by not calling Close?") 229 }