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  }