mirror of
https://github.com/hanzoai/tasks.git
synced 2026-10-04 02:04:03 +00:00
## 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)
713 lines
25 KiB
Go
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))
|
|
}
|