go.temporal.io/server@v1.23.0/common/persistence/queue_v2_util.go (about)

     1  // The MIT License
     2  //
     3  // Copyright (c) 2020 Temporal Technologies Inc.  All rights reserved.
     4  //
     5  // Copyright (c) 2020 Uber Technologies, Inc.
     6  //
     7  // Permission is hereby granted, free of charge, to any person obtaining a copy
     8  // of this software and associated documentation files (the "Software"), to deal
     9  // in the Software without restriction, including without limitation the rights
    10  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    11  // copies of the Software, and to permit persons to whom the Software is
    12  // furnished to do so, subject to the following conditions:
    13  //
    14  // The above copyright notice and this permission notice shall be included in
    15  // all copies or substantial portions of the Software.
    16  //
    17  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    18  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    19  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    20  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    21  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    22  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    23  // THE SOFTWARE.
    24  
    25  package persistence
    26  
    27  import (
    28  	"fmt"
    29  
    30  	"go.temporal.io/api/serviceerror"
    31  
    32  	persistencespb "go.temporal.io/server/api/persistence/v1"
    33  )
    34  
    35  const (
    36  	// pageTokenPrefixByte is the first byte of the serialized page token. It's used to ensure that the page token is
    37  	// not empty. Without this, if the last_read_message_id is 0, the serialized page token would be empty, and clients
    38  	// could erroneously assume that there are no more messages beyond the first page. This is purely used to ensure
    39  	// that tokens are non-empty; it is not used to verify that the token is valid like the magic byte in some other
    40  	// protocols.
    41  	pageTokenPrefixByte = 0
    42  )
    43  
    44  func GetNextPageTokenForReadMessages(result []QueueV2Message) []byte {
    45  	if len(result) == 0 {
    46  		return nil
    47  	}
    48  	lastReadMessageID := result[len(result)-1].MetaData.ID
    49  	token := &persistencespb.ReadQueueMessagesNextPageToken{
    50  		LastReadMessageId: lastReadMessageID,
    51  	}
    52  	// This can never fail if you inspect the implementation.
    53  	b, _ := token.Marshal()
    54  
    55  	// See the comment above pageTokenPrefixByte for why we want to do this.
    56  	return append([]byte{pageTokenPrefixByte}, b...)
    57  }
    58  
    59  func GetMinMessageIDToReadForQueueV2(
    60  	queueType QueueV2Type,
    61  	queueName string,
    62  	nextPageToken []byte,
    63  	queue *persistencespb.Queue,
    64  ) (int64, error) {
    65  	if len(nextPageToken) == 0 {
    66  		partition, err := GetPartitionForQueueV2(queueType, queueName, queue)
    67  		if err != nil {
    68  			return 0, err
    69  		}
    70  		return partition.MinMessageId, nil
    71  	}
    72  	var token persistencespb.ReadQueueMessagesNextPageToken
    73  
    74  	// Skip the first byte. See the comment on pageTokenPrefixByte for more details.
    75  	err := token.Unmarshal(nextPageToken[1:])
    76  	if err != nil {
    77  		return 0, fmt.Errorf(
    78  			"%w: %q: %v",
    79  			ErrInvalidReadQueueMessagesNextPageToken,
    80  			nextPageToken,
    81  			err,
    82  		)
    83  	}
    84  	return token.LastReadMessageId + 1, nil
    85  }
    86  
    87  func GetNextPageTokenForListQueues(queueNumber int64) []byte {
    88  	token := &persistencespb.ListQueuesNextPageToken{
    89  		LastReadQueueNumber: queueNumber,
    90  	}
    91  	// This can never fail if you inspect the implementation.
    92  	b, _ := token.Marshal()
    93  
    94  	// See the comment above pageTokenPrefixByte for why we want to do this.
    95  	return append([]byte{pageTokenPrefixByte}, b...)
    96  }
    97  
    98  func GetOffsetForListQueues(
    99  	nextPageToken []byte,
   100  ) (int64, error) {
   101  	if len(nextPageToken) == 0 {
   102  		return 0, nil
   103  	}
   104  	var token persistencespb.ListQueuesNextPageToken
   105  
   106  	// Skip the first byte. See the comment on pageTokenPrefixByte for more details.
   107  	err := token.Unmarshal(nextPageToken[1:])
   108  	if err != nil {
   109  		return 0, fmt.Errorf(
   110  			"%w: %q: %v",
   111  			ErrInvalidListQueuesNextPageToken,
   112  			nextPageToken,
   113  			err,
   114  		)
   115  	}
   116  	return token.LastReadQueueNumber, nil
   117  }
   118  
   119  func GetPartitionForQueueV2(
   120  	queueType QueueV2Type,
   121  	queueName string,
   122  	queue *persistencespb.Queue,
   123  ) (*persistencespb.QueuePartition, error) {
   124  	// Currently, we only have one partition for each queue. However, that might change in the future. If a queue is
   125  	// created with more than 1 partition by a server on a future release, and then that server is downgraded, we
   126  	// will need to handle this case. Since all DLQ tasks are retried infinitely, we just return an error.
   127  	numPartitions := len(queue.Partitions)
   128  	if numPartitions != 1 {
   129  		return nil, serviceerror.NewInternal(
   130  			fmt.Sprintf(
   131  				"queue without single partition detected. queue with type %v and queueName %v has %d partitions, "+
   132  					"but this implementation only supports queues with 1 partition. Did you downgrade your Temporal server?",
   133  				queueType,
   134  				queueName,
   135  				numPartitions,
   136  			),
   137  		)
   138  	}
   139  	partition := queue.Partitions[0]
   140  	return partition, nil
   141  }
   142  
   143  type DeleteRequest struct {
   144  	// LastIDToDeleteInclusive represents the maximum message ID that the user wants to delete, inclusive.
   145  	LastIDToDeleteInclusive int64
   146  	// ExistingMessageRange represents an inclusive range of the minimum message ID and the maximum message ID in the queue.
   147  	ExistingMessageRange InclusiveMessageRange
   148  }
   149  
   150  type InclusiveMessageRange struct {
   151  	MinMessageID int64
   152  	MaxMessageID int64
   153  }
   154  
   155  type DeleteRange struct {
   156  	InclusiveMessageRange
   157  	NewMinMessageID  int64
   158  	MessagesToDelete int64
   159  }
   160  
   161  // GetDeleteRange returns the range of messages to delete, and a boolean indicating whether there is any update to be
   162  // made: meaning either we should delete messages, update the min message ID, or both.
   163  func GetDeleteRange(request DeleteRequest) (DeleteRange, bool) {
   164  	if request.LastIDToDeleteInclusive < request.ExistingMessageRange.MinMessageID {
   165  		// Nothing to delete
   166  		return DeleteRange{}, false
   167  	}
   168  	return DeleteRange{
   169  		InclusiveMessageRange: InclusiveMessageRange{
   170  			MinMessageID: request.ExistingMessageRange.MinMessageID,
   171  			// Never actually delete the last message
   172  			MaxMessageID: min(request.LastIDToDeleteInclusive, request.ExistingMessageRange.MaxMessageID-1),
   173  		},
   174  		NewMinMessageID:  min(request.LastIDToDeleteInclusive, request.ExistingMessageRange.MaxMessageID) + 1,
   175  		MessagesToDelete: min(request.LastIDToDeleteInclusive, request.ExistingMessageRange.MaxMessageID) - request.ExistingMessageRange.MinMessageID + 1,
   176  	}, true
   177  }