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  }