Thiht/transactor
Thiht/transactor has 181 GitHub stars and 7 forks. GitHot measured 0 stars in 24 hours and 0 in 7 days. Last checked 2026-11-12 19:54 UTC. Last push 2026-11-03.
Comment ces chiffres sont mesurés
Transactor is an injectable type making DB transactions seamless.
Progression mesurée des étoiles
- in 24h
- 0
- in 7 days
- 0
- in 15 days
- Historique en cours
- in 1 month
- Historique en cours
Variations nettes sur des périodes qui se chevauchent, pas des totaux quotidiens. Les mesures absentes restent vides. Les étoiles indiquent l’intérêt, pas la qualité.
README.md
Originaltransactor
The transactor pattern is a way to manage transactions seamlessly. You can inject your transactor in your services to make transactions completely transparently.
It relies mostly on the Transactor interface:
type Transactor interface {
WithinTransaction(context.Context, func(context.Context) error) error
IsWithinTransaction(context.Context) bool
}
WithinTransaction starts a new transaction and adds it to the context. Any repository method can then retrieve a transaction from the context or fallback to the initial DB handler. The transaction is committed if the provided function doesn't return an error. It's rollbacked otherwise.
Usage
Installation
go get github.com/Thiht/transactor
The database/sql default implementation (stdlib) is included in the github.com/Thiht/transactor package.
Additional implementations are available in separate modules:
- the
pgximplementation is available ingithub.com/Thiht/transactor/pgx, - the
sqlximplementation is available ingithub.com/Thiht/transactor/sqlx.
The following examples use the stdlib implementation, but the code isn't too different with the other implementations.
Initialize a transactor
This example uses database/sql with the pgx driver, but any database/sql driver can be used.
import stdlibTransactor "github.com/Thiht/transactor/stdlib"
db, _ := sql.Open("pgx", dsn)
transactor, dbGetter := stdlibTransactor.NewTransactor(
db,
stdlibTransactor.NestedTransactionsSavepoints,
)
The currently available strategies for nested transactions with the stdlib implementation are:
- NestedTransactionsSavepoints, an implementation using
SAVEPOINTSand compatible with PostgreSQL, MySQL, MariaDB, and SQLite, - NestedTransactionsOracle, an implementation using Oracle savepoints,
- NestedTransactionsMSSQL, an implementation using Microsoft SQL Server savepoints,
- NestedTransactionsNone, an implementation that prevents using nested transactions.
Use the dbGetter in your repositories
Instead of injecting the *sql.DB handler directly to your repositories, you now have to inject the dbGetter. It will return the appropriate DB handler depending on whether the current execution is in a transaction.
type store struct {
- db *sql.DB
+ dbGetter stdlibTransactor.DBGetter
+ transactor transactor.Transactor
}
func (s store) GetBalance(ctx context.Context, account string) (int, error) {
var balance int
- err := s.db.QueryRowContext(
+ err := s.dbGetter(ctx).QueryRowContext(
ctx,
`SELECT balance FROM accounts WHERE account = $1`,
account,
).Scan(&balance)
return balance, err
}
You can use IsWithinTransaction if you need to implement different behaviours depending on whether a transaction is running.
For example with PostgreSQL, you could add FOR UPDATE conditionally:
func (s store) GetBalance(ctx context.Context, account string) (int, error) {
query := `SELECT balance FROM accounts WHERE account = $1`
if s.transactor.IsWithinTransaction(ctx) {
query += ` FOR UPDATE`
}
// ...
}
IsWithinTransaction is available on each transactor instance (and on the Transactor interface). It returns true only for transactions started by that specific instance, which makes it safe to use when multiple transactor instances are involved (e.g. several databases).
[!WARNING] The package-level
IsWithinTransactionhelper is deprecated: it cannot distinguish between multiple transactor instances and may return incorrect results.
Use the transactor in your services
type service struct {
balanceStore stores.Balance
transactor transactor.Transactor
}
func (s service) IncreaseBalance(ctx context.Context, account string, amount int) error {
return s.transactor.WithinTransaction(ctx, func(ctx context.Context) error {
balance, err := s.balanceStore.GetBalance(ctx, account)
if err != nil {
return err
}
balance += amount
err = s.balanceStore.SetBalance(ctx, account, balance)
if err != nil {
return err
}
return nil
})
}
Thanks to nested transactions support, you can even call your services within a transaction:
type service struct {
balanceStore stores.Balance
transactor transactor.Transactor
}
func (s service) TransferBalance(
ctx context.Context,
fromAccount, toAccount string,
amount int,
) error {
return s.transactor.WithinTransaction(ctx, func(ctx context.Context) error {
err := s.DecreaseBalance(ctx, fromAccount, amount)
if err != nil {
return err
}
err = s.IncreaseBalance(ctx, toAccount, amount)
if err != nil {
return err
}
return nil
})
}
func (s service) IncreaseBalance(ctx context.Context, account string, amount int) error {
return s.transactor.WithinTransaction(ctx, func(ctx context.Context) error {
// ...
})
}
func (s service) DecreaseBalance(ctx context.Context, account string, amount int) error {
return s.transactor.WithinTransaction(ctx, func(ctx context.Context) error {
// ...
})
}
[!WARNING] Transactions are not thread safe, so make sure not to call code making concurrent database access inside
WithinTransaction
Testing
In your tests, you can inject a fake transactor and dbGetter, using NewFakeTransactor:
transactor, dbGetter := stdlibTransactor.NewFakeTransactor(db)
The fake transactor will simply execute its callback function, and the dbGetter will return the provided db handler.
This strategy works because when using this library, you don't have to worry about how transactions are made, just about returning errors appropriately in WithinTransaction.
Explorer des projets similaires
Asabeneh/30-Days-Of-Python
Le défi de programmation 30 Days of Python est un guide pas à pas pour apprendre le langage de programmation Python en 30 jours. Ce défi peut prendre plus de 100 jours. Avancez à votre rythme. Ces vidéos peuvent aussi vous aider : https://www.youtube.com/channel/UC7PNRuno1rzYPb1xLa4yktw
78,618 étoilesgrafana/grafana
Plateforme ouverte et composable d’observabilité et de visualisation de données. Visualise les métriques, les journaux et les traces issus de plusieurs sources, comme Prometheus, Loki, Elasticsearch, InfluxDB, Postgres et bien d’autres.
77,859 étoilesredis/redis
Pour les développeurs qui créent des applications en temps réel pilotées par les données, Redis est le cache, le serveur de structures de données et le moteur de requêtes documentaires et vectorielles préféré, le plus rapide et le plus riche en fonctionnalités.
77,472 étoilesapache/superset
Apache Superset est une plateforme de visualisation et d'exploration de données.
75,495 étoiles