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  }