transactor

package module
v0.0.0-...-8e010cd Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 8, 2022 License: Apache-2.0 Imports: 5 Imported by: 0

README

Let's imagine we have MyService where transactional behavior is required and 2 repositories OrderRepository and PaymentRepository which must create order and payment consistently within single transaction, so if either first or second fail - no data is created at all and transaction is rolled back.

Firstly, for object which requires transactional behavior you can define property with transactor interface, so now trx can accept any transactor no matter if it is postgres or mongo or anything else:

type transactor interface {
	WithinTransaction(ctx context.Context, txFn func(context.Context) error) error
}

type MyService struct {
	trx transactor
}

Then, your database accessors (repositories in our case) must be embedded with concrete transaction runner interface, in current example - PgxWithinTransactionRunner. To run query you call Runner method of transaction runner (it is available because of embedding) and run query. Please, pay attention on OrderRepository method CreateOrder below:

type Order struct {
	// Order fields
}

type Payment struct {
	// Payment fields
}

type OrderRepository struct {
	pgx_transactor.PgxWithinTransactionRunner
}

func NewOrderRepository(r pgx_transactor.PgxWithinTransactionRunner) *OrderRepository {
	return &OrderRepository{PgxWithinTransactionRunner: r}
}

func (r *OrderRepository) CreateOrder(ctx context.Context, order *Order) error {
	// queryRunner can be used now to perform any pgx related functionality available in interface `PgxQueryRunner` 
	// it returns either `pgx.Tx` or `pgxpool.Pool` both implements `PgxQueryRunner` interface
	queryRunner := r.Runner(ctx)
	// ...
	
	// so, example of running INSERT query
	q := "INSERT ..."
	args := ...
	r.Runner(ctx).Exec(ctx, q, args...)
}

type PaymentRepository struct {
	pgx_transactor.PgxWithinTransactionRunner
}

func NewPaymentRepository(r pgx_transactor.PgxWithinTransactionRunner) *PaymentRepository {
	return &PaymentRepository{PgxWithinTransactionRunner: r}
}

func (r *PaymentRepository) CreatePayment(ctx context.Context, p *model.Payment) error { 
	// pretty the same concept as for CreateOrder of OrderRepository 
	//...
}

Now, you can use repositories in combination with transactor in MyService:

type MyService struct {
	trx transactor // in this example will be maintained via pgx_transactor.NewPgxTransactor(...)
	orderRps OrderRepository // order repository added
	paymentRps PaymentRepository // payment repository added
}

// ...
// omitting NewMyService
// ...

// new method CreateOrderWithItems is added
func (s *MyService) CreateOrderAndPayment(ctx context.Context, order *Order, payment *Payment) error {
	// WithinTransaction injects pgx.Tx into ctx, so txCtx is already with injected transaction
	err := s.trx.WithinTransaction(ctx, func (txCtx context.Context) error {
		var inTxErr error

		// txCtx has pgx.Tx injected - Runner in repository will return pgx.Tx
		inTxErr = s.orderRps.CreateOrder(txCtx, order)
		if inTxErr != nil {
			// if error - transaction is rolled back
			return inTxErr
		}
		
		// txCtx has pgx.Tx injected - Runner in repository will return pgx.Tx
		inTxErr = s.paymentRps.CreatePayment(txCtx, payment)
		if inTxErr != nil {
			// if error - transaction is rolled back 
			return inTxErr
		}
		
		// transaction is committed if returned error is nil
		return nil
	})
}

As you can see from example above, transaction is shared between 2 repositories through context where it is injected

Documentation

Overview

Package transactor contains logic for postgres pgx transactional behavior

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type PgxQueryRunner

type PgxQueryRunner interface {
	Exec(ctx context.Context, sql string, arguments ...interface{}) (pgconn.CommandTag, error)
	Query(ctx context.Context, sql string, optionsAndArgs ...interface{}) (pgx.Rows, error)
	QueryRow(ctx context.Context, sql string, optionsAndArgs ...interface{}) pgx.Row
	Begin(context.Context) (pgx.Tx, error)
	SendBatch(context.Context, *pgx.Batch) pgx.BatchResults
	CopyFrom(context.Context, pgx.Identifier, []string, pgx.CopyFromSource) (int64, error)
}

PgxQueryRunner represents query runner behavior

type PgxTransactor

type PgxTransactor interface {
	WithinTransaction(ctx context.Context, txFn func(context.Context) error) error
	WithinTransactionWithOptions(ctx context.Context, txFn func(context.Context) error, opts pgx.TxOptions) error
}

PgxTransactor represents pgx transactor behavior

func NewPgxTransactor

func NewPgxTransactor(p *pgxpool.Pool) PgxTransactor

NewPgxTransactor builds new PgxTransactor

type PgxWithinTransactionRunner

type PgxWithinTransactionRunner interface {
	Runner(ctx context.Context) PgxQueryRunner
}

PgxWithinTransactionRunner represents query runner retriever for pgx

func NewPgxWithinTransactionRunner

func NewPgxWithinTransactionRunner(p *pgxpool.Pool) PgxWithinTransactionRunner

NewPgxWithinTransactionRunner builds new PgxWithinTransactionRunner

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL