github.com/ydb-platform/ydb-go-sdk/v3@v3.57.0/internal/topic/topicwriterinternal/queue.go (about) 1 package topicwriterinternal 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "sort" 8 9 "github.com/ydb-platform/ydb-go-sdk/v3/internal/empty" 10 "github.com/ydb-platform/ydb-go-sdk/v3/internal/grpcwrapper/rawtopic/rawtopicwriter" 11 "github.com/ydb-platform/ydb-go-sdk/v3/internal/xerrors" 12 "github.com/ydb-platform/ydb-go-sdk/v3/internal/xsync" 13 ) 14 15 var ( 16 errCloseClosedMessageQueue = xerrors.Wrap(errors.New("ydb: close closed message queue")) 17 errAckOnClosedMessageQueue = xerrors.Wrap(errors.New("ydb: ack on closed message queue")) 18 errGetMessageFromClosedQueue = xerrors.Wrap(errors.New("ydb: get message from closed message queue")) 19 errAddUnorderedMessages = xerrors.Wrap(errors.New("ydb: add unordered messages")) 20 errAckUnexpectedMessage = xerrors.Wrap(errors.New("ydb: ack unexpected message")) 21 ) 22 23 const ( 24 intSize = 32 << (^uint(0) >> 63) // copy from math package for use in go <= 1.16 25 maxInt = 1<<(intSize-1) - 1 // copy from math package for use in go <= 1.16 26 minInt = -1 << (intSize - 1) // copy from math package for use in go <= 1.16 27 28 minPositiveIndexWhichOrderLessThenNegative = maxInt / 2 29 ) 30 31 type messageQueue struct { 32 OnAckReceived func(count int) 33 34 hasNewMessages empty.Chan 35 closedErr error 36 acksReceivedEvent xsync.EventBroadcast 37 38 m xsync.RWMutex 39 closed bool 40 closedChan empty.Chan 41 lastWrittenIndex int 42 lastSentIndex int 43 lastSeqNo int64 44 45 messagesByOrder map[int]messageWithDataContent 46 seqNoToOrderID map[int64]int 47 } 48 49 func newMessageQueue() messageQueue { 50 return messageQueue{ 51 messagesByOrder: make(map[int]messageWithDataContent), 52 seqNoToOrderID: make(map[int64]int), 53 hasNewMessages: make(empty.Chan, 1), 54 closedChan: make(empty.Chan), 55 lastSeqNo: -1, 56 } 57 } 58 59 func (q *messageQueue) AddMessages(messages []messageWithDataContent) error { 60 _, err := q.addMessages(messages, false) 61 62 return err 63 } 64 65 func (q *messageQueue) AddMessagesWithWaiter(messages []messageWithDataContent) ( 66 waiter MessageQueueAckWaiter, 67 err error, 68 ) { 69 return q.addMessages(messages, true) 70 } 71 72 func (q *messageQueue) addMessages(messages []messageWithDataContent, needWaiter bool) ( 73 waiter MessageQueueAckWaiter, 74 err error, 75 ) { 76 q.m.Lock() 77 defer q.m.Unlock() 78 79 if q.closed { 80 return waiter, xerrors.WithStackTrace(fmt.Errorf("ydb: add message to closed message queue: %w", q.closedErr)) 81 } 82 83 if err := q.checkNewMessagesBeforeAddNeedLock(messages); err != nil { 84 return waiter, err 85 } 86 87 for i := range messages { 88 messageIndex := q.addMessageNeedLock(messages[i]) 89 90 if needWaiter { 91 waiter.AddWaitIndex(messageIndex) 92 } 93 } 94 95 q.notifyNewMessages() 96 97 return waiter, nil 98 } 99 100 func (q *messageQueue) notifyNewMessages() { 101 select { 102 case q.hasNewMessages <- empty.Struct{}: 103 // pass 104 default: 105 } 106 } 107 108 func (q *messageQueue) checkNewMessagesBeforeAddNeedLock(messages []messageWithDataContent) error { 109 if len(messages) == 0 { 110 return nil 111 } 112 113 checkedSeqNo := q.lastSeqNo 114 for i := range messages { 115 if messages[i].SeqNo <= checkedSeqNo { 116 return xerrors.WithStackTrace(errAddUnorderedMessages) 117 } 118 checkedSeqNo = messages[i].SeqNo 119 } 120 121 return nil 122 } 123 124 func (q *messageQueue) addMessageNeedLock( 125 mess messageWithDataContent, //nolint:gocritic 126 ) (messageIndex int) { 127 q.lastWrittenIndex++ 128 messageIndex = q.lastWrittenIndex 129 130 if messageIndex == minInt { 131 q.ensureNoSmallIntIndexes() 132 } 133 134 if _, ok := q.messagesByOrder[messageIndex]; ok { 135 panic(fmt.Errorf("ydb: bad internal state os message queue - already exists with index: %v", messageIndex)) 136 } 137 138 q.messagesByOrder[messageIndex] = mess 139 q.seqNoToOrderID[mess.SeqNo] = messageIndex 140 q.lastSeqNo = mess.SeqNo 141 142 return messageIndex 143 } 144 145 func (q *messageQueue) AcksReceived(acks []rawtopicwriter.WriteAck) error { 146 ackReceivedCounter := 0 147 q.m.Lock() 148 defer func() { 149 q.m.Unlock() 150 151 if q.OnAckReceived != nil { 152 q.OnAckReceived(ackReceivedCounter) 153 } 154 }() 155 if q.closed { 156 return xerrors.WithStackTrace(errAckOnClosedMessageQueue) 157 } 158 159 for i := range acks { 160 if err := q.ackReceivedNeedLock(acks[i].SeqNo); err != nil { 161 return err 162 } 163 ackReceivedCounter++ 164 } 165 166 q.acksReceivedEvent.Broadcast() 167 168 return nil 169 } 170 171 func (q *messageQueue) ackReceivedNeedLock(seqNo int64) error { 172 orderID, ok := q.seqNoToOrderID[seqNo] 173 if !ok { 174 return xerrors.WithStackTrace(errAckUnexpectedMessage) 175 } 176 177 delete(q.seqNoToOrderID, seqNo) 178 delete(q.messagesByOrder, orderID) 179 180 return nil 181 } 182 183 func (q *messageQueue) Close(err error) error { 184 isFirstTimeClosed := false 185 q.m.Lock() 186 defer func() { 187 q.m.Unlock() 188 189 // release all 190 if isFirstTimeClosed && q.OnAckReceived != nil { 191 q.OnAckReceived(len(q.seqNoToOrderID)) 192 } 193 }() 194 195 if q.closed { 196 return xerrors.WithStackTrace(errCloseClosedMessageQueue) 197 } 198 isFirstTimeClosed = true 199 200 q.closed = true 201 q.closedErr = err 202 close(q.closedChan) 203 204 return nil 205 } 206 207 func (q *messageQueue) ensureNoSmallIntIndexes() { 208 for k := range q.messagesByOrder { 209 if k >= 0 && k < minPositiveIndexWhichOrderLessThenNegative { 210 panic("ydb: message queue has bad state - overflow or has very old element") 211 } 212 } 213 } 214 215 // GetMessagesForSend one or more messages for send 216 // it blocked until context cancelled of have least one message for send 217 func (q *messageQueue) GetMessagesForSend(ctx context.Context) ([]messageWithDataContent, error) { 218 if err := ctx.Err(); err != nil { 219 return nil, err 220 } 221 var closed bool 222 q.m.WithLock(func() { 223 closed = q.closed 224 }) 225 if closed { 226 return nil, xerrors.WithStackTrace(errGetMessageFromClosedQueue) 227 } 228 229 for { 230 res := q.getMessagesForSendWithLock() 231 if len(res) != 0 { 232 return res, nil 233 } 234 235 select { 236 case <-ctx.Done(): 237 return nil, xerrors.WithStackTrace(ctx.Err()) 238 case <-q.hasNewMessages: 239 // pass 240 case <-q.closedChan: 241 return nil, xerrors.WithStackTrace(fmt.Errorf("ydb: message queue closed with: %w", q.closedErr)) 242 } 243 } 244 } 245 246 func (q *messageQueue) ResetSentProgress() { 247 q.m.Lock() 248 defer q.m.Unlock() 249 250 minKey := q.lastWrittenIndex 251 for k := range q.messagesByOrder { 252 if isFirstCycledIndexLess(k, minKey) { 253 minKey = k 254 } 255 } 256 257 q.lastSentIndex = minKey - 1 258 q.notifyNewMessages() 259 } 260 261 func (q *messageQueue) getMessagesForSendWithLock() []messageWithDataContent { 262 q.m.Lock() 263 defer q.m.Unlock() 264 265 if q.lastWrittenIndex == q.lastSentIndex { 266 return nil 267 } 268 269 var res []messageWithDataContent 270 271 // use "!=" stop instead of "<" - for work with negative indexes after overflow 272 for q.lastWrittenIndex != q.lastSentIndex { 273 q.lastSentIndex++ 274 275 // msg may be unexisted if it already has ack from server 276 // pass 277 if msg, ok := q.messagesByOrder[q.lastSentIndex]; ok { 278 res = append(res, msg) 279 } 280 } 281 282 return res 283 } 284 285 func (q *messageQueue) Wait(ctx context.Context, waiter MessageQueueAckWaiter) error { 286 if err := ctx.Err(); err != nil { 287 return err 288 } 289 290 ctxDone := ctx.Done() 291 for { 292 ackReceived := q.acksReceivedEvent.Waiter() 293 294 hasWaited := false 295 q.m.WithRLock(func() { 296 for len(waiter.sequenseNumbers) > 0 { 297 checkMessageIndex := waiter.sequenseNumbers[0] 298 if _, ok := q.messagesByOrder[checkMessageIndex]; ok { 299 hasWaited = true 300 301 return 302 } 303 waiter.sequenseNumbers = waiter.sequenseNumbers[1:] 304 } 305 }) 306 307 if !hasWaited { 308 return nil 309 } 310 311 select { 312 case <-ctxDone: 313 return ctx.Err() 314 case <-q.closedChan: 315 return q.closedErr 316 case <-ackReceived.Done(): 317 // pass next iteration 318 } 319 } 320 } 321 322 type MessageQueueAckWaiter struct { 323 sequenseNumbers []int 324 } 325 326 func (m *MessageQueueAckWaiter) AddWaitIndex(index int) { 327 m.sequenseNumbers = append(m.sequenseNumbers, index) 328 } 329 330 // sortMessageQueueIndexes deprecated 331 func sortMessageQueueIndexes(keys []int) { 332 sort.Ints(keys) 333 // check index overflow 334 if len(keys) > 0 && keys[0] < 0 && keys[len(keys)-1] > 0 { 335 sort.Slice(keys, func(i, k int) bool { 336 return isFirstCycledIndexLess(keys[i], keys[k]) 337 }) 338 } 339 } 340 341 func isFirstCycledIndexLess(first, second int) bool { 342 switch { 343 case first > minPositiveIndexWhichOrderLessThenNegative && second < 0: 344 return true 345 case first < 0 && second > minPositiveIndexWhichOrderLessThenNegative: 346 return false 347 default: 348 return first < second 349 } 350 }