From 9573e06a09323121fe7cb79779c81e0640ea051c Mon Sep 17 00:00:00 2001 From: Roman Dmitrienko Date: Tue, 24 Dec 2024 11:59:21 +0300 Subject: [PATCH] fix: serialize concurrent transactions when running tests with txdb --- db/db.go | 14 ++++++++++++++ tests/db/test_db.go | 4 ++++ transactions/transactions_service.go | 10 +++++----- 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/db/db.go b/db/db.go index 97b106cb..2dd03728 100644 --- a/db/db.go +++ b/db/db.go @@ -13,6 +13,8 @@ import ( "github.com/getAlby/hub/logger" ) +var SerializeTransactions = false + type Config struct { URI string LogQueries bool @@ -156,3 +158,15 @@ func Stop(db *gorm.DB) error { func IsPostgresURI(uri string) bool { return strings.HasPrefix(uri, "postgresql://") } + +var txSerializer = make(chan struct{}, 1) + +func RunTransaction(db *gorm.DB, txFunc func(tx *gorm.DB) error) error { + if SerializeTransactions { + txSerializer <- struct{}{} + defer func() { + <-txSerializer + }() + } + return db.Transaction(txFunc) +} diff --git a/tests/db/test_db.go b/tests/db/test_db.go index 70c6a814..e6c79d44 100644 --- a/tests/db/test_db.go +++ b/tests/db/test_db.go @@ -18,6 +18,10 @@ func init() { uri := GetTestDatabaseURI() if db.IsPostgresURI(uri) { txdb.Register(testDriver, "pgx", uri) + + // txdb fails when transactions are run concurrently in goroutines + // (concurrent begins/commits/rollback confuse its checkpoints logic). + db.SerializeTransactions = true } } } diff --git a/transactions/transactions_service.go b/transactions/transactions_service.go index ec087908..e1d4d6cc 100644 --- a/transactions/transactions_service.go +++ b/transactions/transactions_service.go @@ -225,7 +225,7 @@ func (svc *transactionsService) SendPaymentSync(ctx context.Context, payReq stri paymentAmount = *amountMsat } - err = svc.db.Transaction(func(tx *gorm.DB) error { + err = db.RunTransaction(svc.db, func(tx *gorm.DB) error { var existingSettledTransaction db.Transaction if tx.Limit(1).Find(&existingSettledTransaction, &db.Transaction{ Type: constants.TRANSACTION_TYPE_OUTGOING, @@ -294,7 +294,7 @@ func (svc *transactionsService) SendPaymentSync(ctx context.Context, payReq stri } // As the LNClient did not return a timeout error, we assume the payment definitely failed - svc.db.Transaction(func(tx *gorm.DB) error { + db.RunTransaction(svc.db, func(tx *gorm.DB) error { return svc.markPaymentFailed(tx, &dbTransaction, err.Error()) }) @@ -303,7 +303,7 @@ func (svc *transactionsService) SendPaymentSync(ctx context.Context, payReq stri // the payment definitely succeeded var settledTransaction *db.Transaction - err = svc.db.Transaction(func(tx *gorm.DB) error { + err = db.RunTransaction(svc.db, func(tx *gorm.DB) error { settledTransaction, err = svc.markTransactionSettled(tx, &dbTransaction, response.Preimage, response.Fee, selfPayment) return err }) @@ -352,7 +352,7 @@ func (svc *transactionsService) SendKeysend(ctx context.Context, amount uint64, selfPayment := destination == lnClient.GetPubkey() - err = svc.db.Transaction(func(tx *gorm.DB) error { + err = db.RunTransaction(svc.db, func(tx *gorm.DB) error { err := svc.validateCanPay(tx, appId, amount, "") if err != nil { return err @@ -464,7 +464,7 @@ func (svc *transactionsService) SendKeysend(ctx context.Context, amount uint64, // the payment definitely succeeded var settledTransaction *db.Transaction - err = svc.db.Transaction(func(tx *gorm.DB) error { + err = db.RunTransaction(svc.db, func(tx *gorm.DB) error { settledTransaction, err = svc.markTransactionSettled(tx, &dbTransaction, preimage, payKeysendResponse.Fee, selfPayment) return err })