Files
tasks/common/persistence/sql/sqlplugin/tests/queue_v2.go
T
Prathyush PVandGitHub c1c60dc25b Find last message in DLQ before starting merge operation (#8273)
## What changed?
Add last message id in the DLQ ListQueues response. Use this last
message id in merge DLQ command if it is not provided by the user.

## Why?
tdbg dlq merge command has an optional argument --last-message-id. If
this is not provided, the system workflow created for this task will
merge/purge messages until the DLQ is empty.
This can create a loop that will not end. If the underlying issue that
pushed the messages to DLQ is still around, the newly merged tasks will
again get added to DLQ. The system workflow will proceed to merge this
task again. This can go in an infinite loop.

## How did you test it?
- [x] built
- [ ] run locally and tested manually
- [x] covered by existing tests
- [x] added new unit test(s)
- [ ] added new functional test(s)
2025-09-10 19:34:53 +00:00

713 lines
25 KiB
Go

package tests
import (
"context"
"database/sql"
"errors"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
enumspb "go.temporal.io/api/enums/v1"
"go.temporal.io/api/serviceerror"
persistencespb "go.temporal.io/server/api/persistence/v1"
"go.temporal.io/server/common/log"
"go.temporal.io/server/common/log/tag"
"go.temporal.io/server/common/persistence"
"go.temporal.io/server/common/persistence/persistencetest"
"go.temporal.io/server/common/persistence/serialization"
persistencesql "go.temporal.io/server/common/persistence/sql"
"go.temporal.io/server/common/persistence/sql/sqlplugin"
)
var (
ErrGetLastMessageIdFailed = errors.New("getLastMessageId error")
ErrTxBeginFailed = errors.New("txBegin error")
ErrInsertFailed = errors.New("insert error")
ErrTxRollbackFailed = errors.New("txRollBack err")
ErrTxCommitFailed = errors.New("txCommit err")
ErrRangeSelectFailed = errors.New("rangeSelect err")
ErrSelectMetadataFailed = errors.New("selectFromMetadata err")
ErrInsertMetadataFailed = errors.New("insertMetadataFailed")
ErrRangeDeleteFailed = errors.New("rangeDeleteFailed")
ErrUpdateMetadataFailed = errors.New("updateMetadataFailed")
ErrSelectQueueNames = errors.New("selectQueueNamesFailed")
)
type (
faultyDB struct {
sqlplugin.DB
getLastMessageIdErr error
txBeginErr error
txCommitErr error
insertErr error
txRollbackErr error
rangeSelectError error
selectMetadataError error
insertMetadataError error
rangeDeleteError error
updateMetadataError error
selectQueueNamesError error
commitCalls int
}
faultyTx struct {
db *faultyDB
sqlplugin.Tx
commitCalls *int
}
logRecorder struct {
log.Logger
errMsgs []string
}
)
func (db *faultyDB) BeginTx(ctx context.Context) (sqlplugin.Tx, error) {
if db.txBeginErr != nil {
return nil, db.txBeginErr
}
tx, err := db.DB.BeginTx(ctx)
if err != nil {
return nil, err
}
return &faultyTx{db: db, commitCalls: &db.commitCalls, Tx: tx}, nil
}
func (tx *faultyTx) InsertIntoQueueV2Messages(ctx context.Context, row []sqlplugin.QueueV2MessageRow) (sql.Result, error) {
if tx.db.insertErr != nil {
return nil, tx.db.insertErr
}
return tx.Tx.InsertIntoQueueV2Messages(ctx, row)
}
func (tx *faultyTx) GetLastEnqueuedMessageIDForUpdateV2(ctx context.Context, filter sqlplugin.QueueV2Filter) (int64, error) {
if tx.db.getLastMessageIdErr != nil {
return 0, tx.db.getLastMessageIdErr
}
return tx.Tx.GetLastEnqueuedMessageIDForUpdateV2(ctx, filter)
}
func (db *faultyDB) GetLastEnqueuedMessageIDForUpdateV2(ctx context.Context, filter sqlplugin.QueueV2Filter) (int64, error) {
if db.getLastMessageIdErr != nil {
return 0, db.getLastMessageIdErr
}
return db.DB.GetLastEnqueuedMessageIDForUpdateV2(ctx, filter)
}
func (db *faultyDB) RangeSelectFromQueueV2Messages(ctx context.Context, filter sqlplugin.QueueV2MessagesFilter) ([]sqlplugin.QueueV2MessageRow, error) {
return []sqlplugin.QueueV2MessageRow{}, db.rangeSelectError
}
func (db *faultyDB) SelectFromQueueV2Metadata(ctx context.Context, filter sqlplugin.QueueV2MetadataFilter) (*sqlplugin.QueueV2MetadataRow, error) {
if db.selectMetadataError != nil {
return &sqlplugin.QueueV2MetadataRow{}, db.selectMetadataError
}
return db.DB.SelectFromQueueV2Metadata(ctx, filter)
}
func (db *faultyDB) SelectNameFromQueueV2Metadata(ctx context.Context, filter sqlplugin.QueueV2MetadataTypeFilter) ([]sqlplugin.QueueV2MetadataRow, error) {
if db.selectQueueNamesError != nil {
return nil, db.selectQueueNamesError
}
return db.DB.SelectNameFromQueueV2Metadata(ctx, filter)
}
func (db *faultyDB) SelectFromQueueV2MetadataForUpdate(ctx context.Context, filter sqlplugin.QueueV2MetadataFilter) (*sqlplugin.QueueV2MetadataRow, error) {
if db.selectMetadataError != nil {
return &sqlplugin.QueueV2MetadataRow{}, db.selectMetadataError
}
return db.DB.SelectFromQueueV2MetadataForUpdate(ctx, filter)
}
func (tx *faultyTx) SelectFromQueueV2Metadata(ctx context.Context, filter sqlplugin.QueueV2MetadataFilter) (*sqlplugin.QueueV2MetadataRow, error) {
if tx.db.selectMetadataError != nil {
return &sqlplugin.QueueV2MetadataRow{}, tx.db.selectMetadataError
}
return tx.Tx.SelectFromQueueV2Metadata(ctx, filter)
}
func (tx *faultyTx) SelectFromQueueV2MetadataForUpdate(ctx context.Context, filter sqlplugin.QueueV2MetadataFilter) (*sqlplugin.QueueV2MetadataRow, error) {
if tx.db.selectMetadataError != nil {
return &sqlplugin.QueueV2MetadataRow{}, tx.db.selectMetadataError
}
return tx.Tx.SelectFromQueueV2MetadataForUpdate(ctx, filter)
}
func (db *faultyDB) InsertIntoQueueV2Metadata(ctx context.Context, row *sqlplugin.QueueV2MetadataRow) (sql.Result, error) {
if db.insertMetadataError != nil {
return nil, db.insertMetadataError
}
return db.DB.InsertIntoQueueV2Metadata(ctx, row)
}
func (tx *faultyTx) RangeDeleteFromQueueV2Messages(ctx context.Context, filter sqlplugin.QueueV2MessagesFilter) (sql.Result, error) {
if tx.db.rangeDeleteError != nil {
return nil, tx.db.rangeDeleteError
}
return tx.Tx.RangeDeleteFromQueueV2Messages(ctx, filter)
}
func (tx *faultyTx) UpdateQueueV2Metadata(ctx context.Context, row *sqlplugin.QueueV2MetadataRow) (sql.Result, error) {
if tx.db.updateMetadataError != nil {
return nil, tx.db.updateMetadataError
}
return tx.Tx.UpdateQueueV2Metadata(ctx, row)
}
func (tx *faultyTx) Rollback() error {
if err := tx.Tx.Rollback(); err != nil {
return err
}
return tx.db.txRollbackErr
}
func (tx *faultyTx) Commit() error {
*tx.commitCalls++
if tx.db.txCommitErr != nil {
err := tx.Rollback()
if err != nil {
return err
}
return tx.db.txCommitErr
}
return tx.Tx.Commit()
}
func (l *logRecorder) Error(msg string, _ ...tag.Tag) {
l.errMsgs = append(l.errMsgs, msg)
}
func RunSQLQueueV2TestSuite(t *testing.T, baseDB sqlplugin.DB) {
ctx := context.Background()
t.Run("TestListQueueFailsToGetLastMessageID", func(t *testing.T) {
t.Parallel()
testListQueueFailsToGetLastMessageID(ctx, t, baseDB)
})
t.Run("TestListQueueFailsToExtractQueueMetadata", func(t *testing.T) {
t.Parallel()
testListQueueFailsToExtractQueueMetadata(ctx, t, baseDB)
})
t.Run("GetPartitionFailsForListQueues", func(t *testing.T) {
t.Parallel()
testListQueuesGetPartitionFails(ctx, t, baseDB)
})
t.Run("QueueInsertFails", func(t *testing.T) {
t.Parallel()
testQueueInsertFails(ctx, t, baseDB)
})
t.Run("TxBeginFails", func(t *testing.T) {
t.Parallel()
testBeginTxFails(ctx, t, baseDB)
})
t.Run("TxCommitFails", func(t *testing.T) {
t.Parallel()
testCommitTxFails(ctx, t, baseDB)
})
t.Run("FailedToGetLastMessageIDFromDB", func(t *testing.T) {
t.Parallel()
testGetLastMessageIDFails(ctx, t, baseDB)
})
t.Run("FailedToGetLastMessageIDFromDB", func(t *testing.T) {
t.Parallel()
testRangeSelectFromQueueV2MessagesFails(ctx, t, baseDB)
})
t.Run("InsertIntoQueueV2MetadataFails", func(t *testing.T) {
t.Parallel()
testInsertIntoQueueV2MetadataFails(ctx, t, baseDB)
})
t.Run("GetPartitionFailsForRangeDelete", func(t *testing.T) {
t.Parallel()
testGetPartitionFailsForRangeDelete(ctx, t, baseDB)
})
t.Run("GetLastMessageIDForDeleteFails", func(t *testing.T) {
t.Parallel()
testGetLastMessageIDForDeleteFails(ctx, t, baseDB)
})
t.Run("RangeDeleteMessagesFails", func(t *testing.T) {
t.Parallel()
testRangeDeleteMessagesFails(ctx, t, baseDB)
})
t.Run("RangeDeleteActuallyDeletes", func(t *testing.T) {
t.Parallel()
testRangeDeleteActuallyDeletes(ctx, t, baseDB)
})
t.Run("UpdateMetadataFails", func(t *testing.T) {
t.Parallel()
testUpdateMetadataFails(ctx, t, baseDB)
})
t.Run("InvalidMetadataEncoding", func(t *testing.T) {
t.Parallel()
testInvalidMetadataEncoding(ctx, t, baseDB)
})
t.Run("InvalidMetadataPayload", func(t *testing.T) {
t.Parallel()
testInvalidMetadataPayload(ctx, t, baseDB)
})
t.Run("SelectMetadataFails", func(t *testing.T) {
t.Parallel()
testSelectMetadataFails(ctx, t, baseDB)
})
t.Run("SelectNameFromQueueV2MetadataFails", func(t *testing.T) {
t.Parallel()
testSelectNameFromQueueV2MetadataFails(ctx, t, baseDB)
})
t.Run("SelectNameFromQueueV2NegativeToken", func(t *testing.T) {
t.Parallel()
testSelectNameFromQueueV2NegativeToken(ctx, t, baseDB)
})
}
func testQueueInsertFails(ctx context.Context, t *testing.T, baseDB sqlplugin.DB) {
queueType := persistence.QueueTypeHistoryNormal
queueName := "test-queue-" + t.Name()
db := &faultyDB{
DB: baseDB,
insertErr: ErrInsertFailed,
txRollbackErr: ErrTxRollbackFailed,
}
logger := &logRecorder{Logger: log.NewTestLogger()}
q := persistencesql.NewQueueV2(db, logger)
_, err := q.CreateQueue(ctx, &persistence.InternalCreateQueueRequest{
QueueType: queueType,
QueueName: queueName,
})
require.NoError(t, err)
_, err = persistencetest.EnqueueMessage(context.Background(), q, queueType, queueName)
require.Error(t, err)
assert.ErrorContains(t, err, "insert error")
require.Len(t, logger.errMsgs, 1)
assert.Contains(t, logger.errMsgs[0], "transaction rollback error")
assert.Equal(t, db.commitCalls, 0)
}
func testCommitTxFails(ctx context.Context, t *testing.T, baseDB sqlplugin.DB) {
queueType := persistence.QueueTypeHistoryNormal
queueName := "test-queue-" + t.Name()
db := &faultyDB{
DB: baseDB,
txCommitErr: ErrTxCommitFailed,
}
logger := &logRecorder{Logger: log.NewTestLogger()}
q := persistencesql.NewQueueV2(db, logger)
_, err := q.CreateQueue(ctx, &persistence.InternalCreateQueueRequest{
QueueType: queueType,
QueueName: queueName,
})
require.NoError(t, err)
_, err = persistencetest.EnqueueMessage(context.Background(), q, queueType, queueName)
require.Error(t, err)
assert.ErrorContains(t, err, "EnqueueMessage failed")
assert.Equal(t, db.commitCalls, 1)
}
func testBeginTxFails(ctx context.Context, t *testing.T, baseDB sqlplugin.DB) {
queueType := persistence.QueueTypeHistoryNormal
queueName := "test-queue-" + t.Name()
db := &faultyDB{
DB: baseDB,
txBeginErr: ErrTxBeginFailed,
}
logger := &logRecorder{Logger: log.NewTestLogger()}
q := persistencesql.NewQueueV2(db, logger)
_, err := q.CreateQueue(ctx, &persistence.InternalCreateQueueRequest{
QueueType: queueType,
QueueName: queueName,
})
require.NoError(t, err)
_, err = persistencetest.EnqueueMessage(context.Background(), q, queueType, queueName)
require.Error(t, err)
assert.ErrorContains(t, err, "txBegin error")
assert.Equal(t, db.commitCalls, 0)
}
func testGetLastMessageIDFails(ctx context.Context, t *testing.T, baseDB sqlplugin.DB) {
queueType := persistence.QueueTypeHistoryNormal
queueName := "test-queue-" + t.Name()
db := &faultyDB{
DB: baseDB,
getLastMessageIdErr: ErrGetLastMessageIdFailed,
txRollbackErr: ErrTxRollbackFailed,
}
logger := &logRecorder{Logger: log.NewTestLogger()}
q := persistencesql.NewQueueV2(db, logger)
_, err := q.CreateQueue(ctx, &persistence.InternalCreateQueueRequest{
QueueType: queueType,
QueueName: queueName,
})
require.NoError(t, err)
_, err = persistencetest.EnqueueMessage(context.Background(), q, queueType, queueName)
require.Error(t, err)
assert.ErrorContains(t, err, "failed to get last messageId")
assert.Equal(t, db.commitCalls, 0)
}
func testRangeSelectFromQueueV2MessagesFails(ctx context.Context, t *testing.T, baseDB sqlplugin.DB) {
queueType := persistence.QueueTypeHistoryNormal
queueName := "test-queue-" + t.Name()
db := &faultyDB{
DB: baseDB,
rangeSelectError: ErrRangeSelectFailed,
}
logger := &logRecorder{Logger: log.NewTestLogger()}
q := persistencesql.NewQueueV2(db, logger)
_, err := q.CreateQueue(ctx, &persistence.InternalCreateQueueRequest{
QueueType: queueType,
QueueName: queueName,
})
require.NoError(t, err)
_, err = q.ReadMessages(context.Background(), &persistence.InternalReadMessagesRequest{
QueueType: queueType,
QueueName: queueName,
PageSize: 1,
NextPageToken: nil,
})
require.Error(t, err)
assert.ErrorContains(t, err, "RangeSelectFromQueueV2Messages operation failed")
}
func testInsertIntoQueueV2MetadataFails(ctx context.Context, t *testing.T, baseDB sqlplugin.DB) {
queueType := persistence.QueueTypeHistoryNormal
queueName := "test-queue-" + t.Name()
db := &faultyDB{
DB: baseDB,
insertMetadataError: ErrInsertMetadataFailed,
}
logger := &logRecorder{Logger: log.NewTestLogger()}
q := persistencesql.NewQueueV2(db, logger)
_, err := q.CreateQueue(ctx, &persistence.InternalCreateQueueRequest{
QueueType: queueType,
QueueName: queueName,
})
require.Error(t, err)
assert.ErrorContains(t, err, "InsertIntoQueueV2Metadata operation failed")
}
func testGetPartitionFailsForRangeDelete(ctx context.Context, t *testing.T, baseDB sqlplugin.DB) {
queueType := persistence.QueueTypeHistoryNormal
queueName := "test-queue-" + t.Name()
logger := &logRecorder{Logger: log.NewTestLogger()}
q := persistencesql.NewQueueV2(baseDB, logger)
queuePB := persistencespb.Queue{
Partitions: map[int32]*persistencespb.QueuePartition{
0: {},
1: {},
},
}
bytes, _ := queuePB.Marshal()
row := sqlplugin.QueueV2MetadataRow{
QueueType: queueType,
QueueName: queueName,
MetadataPayload: bytes,
MetadataEncoding: enumspb.ENCODING_TYPE_PROTO3.String(),
}
_, err := baseDB.InsertIntoQueueV2Metadata(ctx, &row)
require.NoError(t, err)
_, err = q.RangeDeleteMessages(context.Background(), &persistence.InternalRangeDeleteMessagesRequest{
QueueType: persistence.QueueTypeHistoryNormal,
QueueName: "test-queue-" + t.Name(),
InclusiveMaxMessageMetadata: persistence.MessageMetadata{ID: 0},
})
assert.Error(t, err)
assert.ErrorContains(t, err, "partitions")
}
func testGetLastMessageIDForDeleteFails(ctx context.Context, t *testing.T, baseDB sqlplugin.DB) {
queueType := persistence.QueueTypeHistoryNormal
queueName := "test-queue-" + t.Name()
db := &faultyDB{
DB: baseDB,
getLastMessageIdErr: ErrGetLastMessageIdFailed,
}
logger := &logRecorder{Logger: log.NewTestLogger()}
q := persistencesql.NewQueueV2(db, logger)
_, err := q.CreateQueue(ctx, &persistence.InternalCreateQueueRequest{
QueueType: queueType,
QueueName: queueName,
})
require.NoError(t, err)
_, err = q.RangeDeleteMessages(context.Background(), &persistence.InternalRangeDeleteMessagesRequest{
QueueType: persistence.QueueTypeHistoryNormal,
QueueName: "test-queue-" + t.Name(),
InclusiveMaxMessageMetadata: persistence.MessageMetadata{ID: 0},
})
assert.Error(t, err)
assert.ErrorContains(t, err, "getLastMessageId error")
}
func testRangeDeleteMessagesFails(ctx context.Context, t *testing.T, baseDB sqlplugin.DB) {
queueType := persistence.QueueTypeHistoryNormal
queueName := "test-queue-" + t.Name()
db := &faultyDB{
DB: baseDB,
rangeDeleteError: ErrRangeDeleteFailed,
}
logger := &logRecorder{Logger: log.NewTestLogger()}
q := persistencesql.NewQueueV2(db, logger)
_, err := q.CreateQueue(ctx, &persistence.InternalCreateQueueRequest{
QueueType: queueType,
QueueName: queueName,
})
require.NoError(t, err)
persistencetest.EnqueueMessagesForDelete(t, q, queueName, queueType)
_, err = q.RangeDeleteMessages(context.Background(), &persistence.InternalRangeDeleteMessagesRequest{
QueueType: queueType,
QueueName: queueName,
InclusiveMaxMessageMetadata: persistence.MessageMetadata{ID: 0},
})
assert.Error(t, err)
assert.ErrorContains(t, err, "rangeDeleteFailed")
}
func testUpdateMetadataFails(ctx context.Context, t *testing.T, baseDB sqlplugin.DB) {
queueType := persistence.QueueTypeHistoryNormal
queueName := "test-queue-" + t.Name()
db := &faultyDB{
DB: baseDB,
updateMetadataError: ErrUpdateMetadataFailed,
}
logger := &logRecorder{Logger: log.NewTestLogger()}
q := persistencesql.NewQueueV2(db, logger)
_, err := q.CreateQueue(ctx, &persistence.InternalCreateQueueRequest{
QueueType: queueType,
QueueName: queueName,
})
require.NoError(t, err)
persistencetest.EnqueueMessagesForDelete(t, q, queueName, queueType)
_, err = q.RangeDeleteMessages(context.Background(), &persistence.InternalRangeDeleteMessagesRequest{
QueueType: queueType,
QueueName: queueName,
InclusiveMaxMessageMetadata: persistence.MessageMetadata{ID: 0},
})
assert.Error(t, err)
assert.ErrorContains(t, err, "updateMetadataFailed")
}
func testSelectMetadataFails(ctx context.Context, t *testing.T, baseDB sqlplugin.DB) {
queueType := persistence.QueueTypeHistoryNormal
queueName := "test-queue-" + t.Name()
db := &faultyDB{
DB: baseDB,
selectMetadataError: ErrSelectMetadataFailed,
}
logger := &logRecorder{Logger: log.NewTestLogger()}
q := persistencesql.NewQueueV2(db, logger)
_, err := q.ReadMessages(ctx, &persistence.InternalReadMessagesRequest{
QueueType: queueType,
QueueName: queueName,
PageSize: 10,
})
assert.Error(t, err)
assert.ErrorContains(t, err, ErrSelectMetadataFailed.Error())
_, err = persistencetest.EnqueueMessage(context.Background(), q, queueType, queueName)
assert.Error(t, err)
assert.ErrorContains(t, err, ErrSelectMetadataFailed.Error())
_, err = q.RangeDeleteMessages(context.Background(), &persistence.InternalRangeDeleteMessagesRequest{
QueueType: queueType,
QueueName: queueName,
InclusiveMaxMessageMetadata: persistence.MessageMetadata{ID: 0},
})
assert.Error(t, err)
assert.ErrorAs(t, err, new(*serviceerror.Unavailable))
}
func testInvalidMetadataPayload(ctx context.Context, t *testing.T, baseDB sqlplugin.DB) {
queueType := persistence.QueueTypeHistoryNormal
queueName := "test-queue-" + t.Name()
logger := &logRecorder{Logger: log.NewTestLogger()}
q := persistencesql.NewQueueV2(baseDB, logger)
row := sqlplugin.QueueV2MetadataRow{
QueueType: queueType,
QueueName: queueName,
MetadataPayload: []byte("invalid_payload"),
MetadataEncoding: enumspb.ENCODING_TYPE_PROTO3.String(),
}
_, err := baseDB.InsertIntoQueueV2Metadata(ctx, &row)
require.NoError(t, err)
_, err = q.ReadMessages(context.Background(), &persistence.InternalReadMessagesRequest{
QueueType: queueType,
QueueName: queueName,
PageSize: 10,
})
assert.Error(t, err)
assert.ErrorAs(t, err, new(*serialization.DeserializationError))
}
func testInvalidMetadataEncoding(ctx context.Context, t *testing.T, baseDB sqlplugin.DB) {
queueType := persistence.QueueTypeHistoryNormal
queueName := "test-queue-" + t.Name()
logger := &logRecorder{Logger: log.NewTestLogger()}
q := persistencesql.NewQueueV2(baseDB, logger)
row := sqlplugin.QueueV2MetadataRow{
QueueType: queueType,
QueueName: queueName,
MetadataPayload: []byte("test"),
MetadataEncoding: "invalid_encoding",
}
_, err := baseDB.InsertIntoQueueV2Metadata(ctx, &row)
require.NoError(t, err)
_, err = q.ReadMessages(context.Background(), &persistence.InternalReadMessagesRequest{
QueueType: queueType,
QueueName: queueName,
PageSize: 10,
})
assert.Error(t, err)
assert.ErrorAs(t, err, new(*serialization.UnknownEncodingTypeError))
_, err = persistencetest.EnqueueMessage(context.Background(), q, queueType, queueName)
assert.Error(t, err)
assert.ErrorAs(t, err, new(*serialization.UnknownEncodingTypeError))
_, err = q.RangeDeleteMessages(context.Background(), &persistence.InternalRangeDeleteMessagesRequest{
QueueType: queueType,
QueueName: queueName,
InclusiveMaxMessageMetadata: persistence.MessageMetadata{ID: 0},
})
assert.Error(t, err)
assert.ErrorAs(t, err, new(*serviceerror.Unavailable))
}
func testRangeDeleteActuallyDeletes(ctx context.Context, t *testing.T, db sqlplugin.DB) {
queueKey := persistencetest.GetQueueKey(t)
queueType := persistence.QueueTypeHistoryNormal
q := persistencesql.NewQueueV2(db, log.NewTestLogger())
_, err := q.CreateQueue(ctx, &persistence.InternalCreateQueueRequest{
QueueType: queueType,
QueueName: queueKey.GetQueueName(),
})
require.NoError(t, err)
for i := 0; i < 3; i++ {
_, err = persistencetest.EnqueueMessage(context.Background(), q, queueType, queueKey.GetQueueName())
require.NoError(t, err)
}
resp, err := q.RangeDeleteMessages(context.Background(), &persistence.InternalRangeDeleteMessagesRequest{
QueueType: queueType,
QueueName: queueKey.GetQueueName(),
InclusiveMaxMessageMetadata: persistence.MessageMetadata{ID: persistence.FirstQueueMessageID + 2},
})
require.NoError(t, err)
assert.Equal(t, int64(3), resp.MessagesDeleted)
result, err := q.ReadMessages(ctx, &persistence.InternalReadMessagesRequest{
QueueType: queueType,
QueueName: queueKey.GetQueueName(),
PageSize: 10,
})
require.NoError(t, err)
assert.Empty(t, result.Messages)
messages, err := db.RangeSelectFromQueueV2Messages(ctx, sqlplugin.QueueV2MessagesFilter{
QueueType: queueType,
QueueName: queueKey.GetQueueName(),
MinMessageID: 0,
MaxMessageID: 100,
PageSize: 10,
})
require.NoError(t, err)
if assert.Len(t, messages, 1) {
assert.Equal(t, int64(persistence.FirstQueueMessageID+2), messages[0].MessageID)
}
response, err := persistencetest.EnqueueMessage(context.Background(), q, queueType, queueKey.GetQueueName())
require.NoError(t, err)
assert.Equal(t, int64(persistence.FirstQueueMessageID+3), response.Metadata.ID)
}
func testSelectNameFromQueueV2MetadataFails(ctx context.Context, t *testing.T, baseDB sqlplugin.DB) {
queueType := persistence.QueueTypeHistoryDLQ
db := &faultyDB{
DB: baseDB,
selectQueueNamesError: ErrSelectQueueNames,
}
logger := &logRecorder{Logger: log.NewTestLogger()}
q := persistencesql.NewQueueV2(db, logger)
_, err := q.ListQueues(ctx, &persistence.InternalListQueuesRequest{
QueueType: queueType,
PageSize: 10,
NextPageToken: nil,
})
assert.Error(t, err)
assert.ErrorContains(t, err, "SelectNameFromQueueV2Metadata operation failed")
}
func testSelectNameFromQueueV2NegativeToken(ctx context.Context, t *testing.T, baseDB sqlplugin.DB) {
queueType := persistence.QueueTypeHistoryDLQ
logger := &logRecorder{Logger: log.NewTestLogger()}
q := persistencesql.NewQueueV2(baseDB, logger)
_, err := q.ListQueues(ctx, &persistence.InternalListQueuesRequest{
QueueType: queueType,
PageSize: 1,
NextPageToken: persistence.GetNextPageTokenForListQueues(-1),
})
require.Error(t, err)
require.ErrorIs(t, err, persistence.ErrNegativeListQueuesOffset)
}
func testListQueuesGetPartitionFails(ctx context.Context, t *testing.T, baseDB sqlplugin.DB) {
// Using a different QueueType to prevent this test from failing because of queues created in previous tests.
queueType := persistence.QueueV2Type(4)
queueName := "test-queue-" + t.Name()
logger := &logRecorder{Logger: log.NewTestLogger()}
q := persistencesql.NewQueueV2(baseDB, logger)
queuePB := persistencespb.Queue{
Partitions: map[int32]*persistencespb.QueuePartition{
0: {},
1: {},
},
}
bytes, _ := queuePB.Marshal()
row := sqlplugin.QueueV2MetadataRow{
QueueType: queueType,
QueueName: queueName,
MetadataPayload: bytes,
MetadataEncoding: enumspb.ENCODING_TYPE_PROTO3.String(),
}
_, err := baseDB.InsertIntoQueueV2Metadata(ctx, &row)
require.NoError(t, err)
_, err = persistencetest.EnqueueMessage(context.Background(), q, queueType, queueName)
require.NoError(t, err)
_, err = q.ListQueues(context.Background(), &persistence.InternalListQueuesRequest{
QueueType: queueType,
PageSize: 100,
})
assert.Error(t, err)
assert.ErrorContains(t, err, "partitions")
}
func testListQueueFailsToGetLastMessageID(ctx context.Context, t *testing.T, baseDB sqlplugin.DB) {
// Using a different QueueType to prevent this test from failing because of queues created in previous tests.
queueType := persistence.QueueV2Type(5)
queueName := "test-queue-" + t.Name()
db := &faultyDB{
DB: baseDB,
getLastMessageIdErr: ErrGetLastMessageIdFailed,
}
logger := &logRecorder{Logger: log.NewTestLogger()}
q := persistencesql.NewQueueV2(db, logger)
_, err := q.CreateQueue(ctx, &persistence.InternalCreateQueueRequest{
QueueType: queueType,
QueueName: queueName,
})
assert.NoError(t, err)
_, err = q.ListQueues(ctx, &persistence.InternalListQueuesRequest{
QueueType: queueType,
PageSize: 100,
})
assert.Error(t, err)
assert.ErrorContains(t, err, ErrGetLastMessageIdFailed.Error())
}
func testListQueueFailsToExtractQueueMetadata(ctx context.Context, t *testing.T, baseDB sqlplugin.DB) {
// Using a different QueueType to prevent this test from failing because of queues created in previous tests.
queueType := persistence.QueueV2Type(6)
queueName := "test-queue-" + t.Name()
q := persistencesql.NewQueueV2(baseDB, log.NewTestLogger())
row := sqlplugin.QueueV2MetadataRow{
QueueType: queueType,
QueueName: queueName,
MetadataPayload: []byte("test"),
MetadataEncoding: "invalid_encoding",
}
_, err := baseDB.InsertIntoQueueV2Metadata(ctx, &row)
assert.NoError(t, err)
_, err = persistencetest.EnqueueMessage(context.Background(), q, queueType, queueName)
assert.Error(t, err)
assert.ErrorAs(t, err, new(*serialization.UnknownEncodingTypeError))
}