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