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 }