vitess.io/vitess@v0.16.2/go/vt/vttablet/tabletserver/messager/cache.go (about)

     1  /*
     2  Copyright 2019 The Vitess Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package messager
    18  
    19  import (
    20  	"container/heap"
    21  	"sync"
    22  
    23  	"vitess.io/vitess/go/vt/log"
    24  
    25  	"vitess.io/vitess/go/sqltypes"
    26  )
    27  
    28  //_______________________________________________
    29  
    30  // MessageRow represents a message row.
    31  // The first column in Row is always the "id".
    32  type MessageRow struct {
    33  	Priority  int64
    34  	TimeNext  int64
    35  	Epoch     int64
    36  	TimeAcked int64
    37  	Row       []sqltypes.Value
    38  
    39  	// defunct is set if the row was asked to be removed
    40  	// from cache.
    41  	defunct bool
    42  }
    43  
    44  type messageHeap []*MessageRow
    45  
    46  func (mh messageHeap) Len() int {
    47  	return len(mh)
    48  }
    49  
    50  func (mh messageHeap) Less(i, j int) bool {
    51  	// Lower epoch is more important.
    52  	// If epochs match, newer messages are more important.
    53  	return mh[i].Priority < mh[j].Priority ||
    54  		(mh[i].Priority == mh[j].Priority && mh[i].TimeNext > mh[j].TimeNext)
    55  }
    56  
    57  func (mh messageHeap) Swap(i, j int) {
    58  	mh[i], mh[j] = mh[j], mh[i]
    59  }
    60  
    61  func (mh *messageHeap) Push(x any) {
    62  	*mh = append(*mh, x.(*MessageRow))
    63  }
    64  
    65  func (mh *messageHeap) Pop() any {
    66  	old := *mh
    67  	n := len(old)
    68  	x := old[n-1]
    69  	*mh = old[0 : n-1]
    70  	return x
    71  }
    72  
    73  //_______________________________________________
    74  
    75  // cache is the cache for the messager. Messages initially
    76  // start in the sendQueue. When they are popped, they move
    77  // to the inFlight set. They are eventually discarded
    78  // after being successfully sent. Messages can be discarded
    79  // early (while still in the send queue) by any kind of
    80  // update to a message (like an ack). If so, such messages
    81  // are marked as defunct in the cache, and are eventually
    82  // discarded when popped.
    83  type cache struct {
    84  	mu   sync.Mutex
    85  	size int
    86  
    87  	sendQueue messageHeap
    88  	// inQueue is used to efficiently find items in sendQueue.
    89  	// The message id is the key.
    90  	inQueue map[string]*MessageRow
    91  
    92  	// inFlight are messages that are still being sent.
    93  	// They guard from such messages from being added back prematurely.
    94  	// The message id is the key.
    95  	inFlight map[string]bool
    96  }
    97  
    98  // NewMessagerCache creates a new cache.
    99  func newCache(size int) *cache {
   100  	mc := &cache{
   101  		size:     size,
   102  		inQueue:  make(map[string]*MessageRow),
   103  		inFlight: make(map[string]bool),
   104  	}
   105  	return mc
   106  }
   107  
   108  func (mc *cache) IsEmpty() bool {
   109  	mc.mu.Lock()
   110  	defer mc.mu.Unlock()
   111  	return len(mc.sendQueue) == 0
   112  }
   113  
   114  // Clear clears the cache.
   115  func (mc *cache) Clear() {
   116  	log.Infof("messager cache - Clearing cache. Acquiring my lock")
   117  	mc.mu.Lock()
   118  	log.Infof("messager cache - acquired lock")
   119  	defer mc.mu.Unlock()
   120  	mc.sendQueue = nil
   121  	mc.inQueue = make(map[string]*MessageRow)
   122  	mc.inFlight = make(map[string]bool)
   123  	log.Infof("messager cache - cache cleared")
   124  }
   125  
   126  // Add adds a MessageRow to the cache. It returns
   127  // false if the cache is full.
   128  func (mc *cache) Add(mr *MessageRow) bool {
   129  	mc.mu.Lock()
   130  	defer mc.mu.Unlock()
   131  	if len(mc.sendQueue) >= mc.size {
   132  		return false
   133  	}
   134  	id := mr.Row[0].ToString()
   135  	if mc.inFlight[id] {
   136  		return true
   137  	}
   138  	if _, ok := mc.inQueue[id]; ok {
   139  		return true
   140  	}
   141  	heap.Push(&mc.sendQueue, mr)
   142  	mc.inQueue[id] = mr
   143  	return true
   144  }
   145  
   146  // Pop removes the next MessageRow. Once the
   147  // message has been sent, Discard must be called.
   148  // The discard has to happen as a separate operation
   149  // to prevent the poller thread from repopulating the
   150  // message while it's being sent.
   151  // If the Cache is empty Pop returns nil.
   152  func (mc *cache) Pop() *MessageRow {
   153  	mc.mu.Lock()
   154  	defer mc.mu.Unlock()
   155  	for {
   156  		if len(mc.sendQueue) == 0 {
   157  			return nil
   158  		}
   159  		mr := heap.Pop(&mc.sendQueue).(*MessageRow)
   160  		// If message was previously marked as defunct, drop
   161  		// it and continue.
   162  		if mr.defunct {
   163  			continue
   164  		}
   165  		id := mr.Row[0].ToString()
   166  
   167  		// Move the message from inQueue to inFlight.
   168  		delete(mc.inQueue, id)
   169  		mc.inFlight[id] = true
   170  		return mr
   171  	}
   172  }
   173  
   174  // Discard forgets the specified id.
   175  func (mc *cache) Discard(ids []string) {
   176  	mc.mu.Lock()
   177  	defer mc.mu.Unlock()
   178  	for _, id := range ids {
   179  		if mr := mc.inQueue[id]; mr != nil {
   180  			// The row is still in the queue somewhere. Mark
   181  			// it as defunct. It will be "garbage collected" later.
   182  			mr.defunct = true
   183  		}
   184  		delete(mc.inQueue, id)
   185  		delete(mc.inFlight, id)
   186  	}
   187  }
   188  
   189  // Size returns the max size of cache.
   190  func (mc *cache) Size() int {
   191  	mc.mu.Lock()
   192  	defer mc.mu.Unlock()
   193  	return mc.size
   194  }