avito-tech / go-transaction-manager Goto Github PK
View Code? Open in Web Editor NEWTransaction manager for GoLang
License: MIT License
Transaction manager for GoLang
License: MIT License
redis and postgres runs on all go versions, but they shoud be run only for last version to test with real db.
The isolation level is mainly in the RDBMS.
NoSQL DBs have variations which are not the same as in RDBMS.
Hello.
For now this github repository contains main functionality and drivers/plugins for different databases.
Do you think about separated repositories?
One with main functionality, and another for drivers/plugins (each for plugin: 1 for pgx, 1 for redis etc).
Currently library supports postgres through sqlx, but not pgx.
Hello, I've wanted to add support for https://github.com/dgraph-io/badger, but most recent badger/v4 uses go 1.19, and trm is 1.13 due to backward compatibility. So I have a few questions
Add processing of https://github.com/go-redis/redis
There is a problem, go-redis open and close transaction inside closure.
To solve it, we can start goroutine for closure to getting transaction and stop it after Commit or Rollback of Transaction.
Processing cases when:
Gorm currently has a transaction management feature, but the code for many operations looks overloaded.
We have a service (UserService) to work with the user. When debiting a user's account, we must:
If we get an error on one of these, we must roll back. Code samples:
Implementation transaction service
cat internal/service/transaction.go
package service
...
type TransactionService struct {
repository *repository.TransactionRepository
}
...
func (s *TransactionService) Create(
transactionCreate *dto.TransactionCreate, tx ...*gorm.DB,
) (*entity.Transaction, error) {
return s.repository.Create(
&entity.Transaction{
Type: transactionCreate.Type,
Amount: transactionCreate.Amount,
UserID: transactionCreate.UserID,
OrderID: transactionCreate.OrderID,
ServiceID: transactionCreate.ServiceID,
}, tx...,
)
}
Implementation user service
cat internal/service/user.go
package service
...
type UserService struct {
repository *repository.UserRepository
transactionService *TransactionService
}
...
func (s *UserService) WithdrawUserBalance(
userID uuid.UUID, userWithdrawal *dto.UserWithdrawal, tx ...*gorm.DB,
) (*entity.User, error) {
var user *entity.User
err := s.repository.Transaction(func(tx *gorm.DB) (err error) {
user, err = s.FindOne(userID, tx)
if err != nil {
return
}
user, err = s.repository.WithdrawUserBalance(
user, userWithdrawal.Amount, tx,
)
if err != nil {
return
}
_, err = s.transactionService.Create(&dto.TransactionCreate{
Type: "withdrawal",
Amount: userWithdrawal.Amount,
UserID: userID,
OrderID: userWithdrawal.OrderID,
ServiceID: userWithdrawal.ServiceID,
}, tx)
if err != nil {
return
}
return
}, tx...)
return user, err
}
Implementation transaction repository
cat internal/repository/transaction.go
package repository
...
type TransactionRepository struct {
db *gorm.DB
}
...
func (r *TransactionRepository) txDefault(tx ...*gorm.DB) *gorm.DB {
if len(tx) < 1 {
return r.db
}
return tx[0]
}
func (r *TransactionRepository) Create(
transaction *entity.Transaction, tx ...*gorm.DB,
) (*entity.Transaction, error) {
err := r.txDefault(tx...).Create(transaction).Error
if err != nil {
return nil, err
}
return transaction, nil
}
Implementation user repository
cat internal/repository/user.go
package repository
...
type UserRepository struct {
db *gorm.DB
}
...
func (r *UserRepository) Transaction(
fn func(*gorm.DB) error, tx ...*gorm.DB,
) error {
return r.txDefault(tx...).Transaction(fn)
}
func (r *UserRepository) txDefault(tx ...*gorm.DB) *gorm.DB {
if len(tx) < 1 {
return r.db
}
return tx[0]
}
...
func (r *UserRepository) WithdrawUserBalance(
user *entity.User, amount uint, tx ...*gorm.DB,
) (*entity.User, error) {
if user.Balance < amount || user.Balance == 0 {
return nil, errors.New("user balance is not enough")
}
user.Balance -= amount
err := r.txDefault(tx...).Save(user).Error
if err != nil {
return nil, err
}
return user, nil
}
Using the standard gorm transaction manager forces you to add transaction management logic on both the service and the repository level, and the code nesting when opening a transaction is not good enough.
parallel starting and finishing transactions in different DBs
pgx closes transaction on error.
Then the lib returns missing error of "commit unexpectedly resulted in rollback".
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.