Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

V2 #96

Merged
merged 26 commits into from
Jan 4, 2024
Merged

V2 #96

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 25 additions & 15 deletions .github/workflows/main.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -112,30 +112,40 @@ jobs:
- name: Tests with real databases
run: make test.coverage.with_real_db

- name: Code coverage data
run: |
set -x
COVERAGE_TOTAL=`go tool cover -func=coverage.out | grep total | grep -Eo '[0-9]+\.[0-9]+'`
echo "COVERAGE_TOTAL=$COVERAGE_TOTAL" >> $GITHUB_ENV
- uses: jandelgado/[email protected]
with:
outfile: ./coverage.lcov

- name: Coveralls
uses: coverallsapp/github-action@master
uses: coverallsapp/github-action@v2
with:
path-to-lcov: ./coverage.lcov
format: golang
file: ./coverage.out
github-token: ${{ secrets.GITHUB_TOKEN }}

golangci:
name: lint
# https://github.com/golangci/golangci-lint/issues/828
generate-pkgs-matrix:
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
steps:
- name: Fetch Repository
uses: actions/checkout@v4
- id: set-matrix
run: |
DIRECTORIES=$(find . -not -path "*/vendor/*" -type f -name go.mod -exec sh -c 'echo $(dirname {})' \; | jq -R -s -c 'split("\n")[:-1]')
echo "matrix=${DIRECTORIES}" >> $GITHUB_OUTPUT

lint:
needs: generate-pkgs-matrix
runs-on: ubuntu-latest
strategy:
matrix:
modules: ${{fromJson(needs.generate-pkgs-matrix.outputs.matrix)}}
steps:
- uses: actions/setup-go@v3
- name: Fetch Repository
uses: actions/checkout@v4
- uses: actions/setup-go@v4
with:
go-version: ${{ env.GO_TARGET_VERSION }}
- uses: actions/checkout@v3
- name: golangci-lint
uses: golangci/golangci-lint-action@v3
with:
args: --timeout=3m -v
working-directory: ${{ matrix.modules }}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,4 @@ vendor/

# Code coverage
coverage.*
coverage/
16 changes: 5 additions & 11 deletions .golangci.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
linters-settings:
enable-all: true

run:
skip-dirs:
- trm/mock
Expand Down Expand Up @@ -68,9 +65,6 @@ linters:

issues:
exclude-use-default: false
errcheck:
check-type-assertions: true
check-blank: true
exclude:
- ST1000 # ST1000: at least one file in a package should have a package comment
exclude-rules:
Expand All @@ -95,28 +89,28 @@ issues:
linters:
- staticcheck
text: "SA1029: should not use built-in type string as key for value; define your own type to avoid collisions"
- path: .+/context\.go
- path: (.+/)?context\.go
linters:
- ireturn
- path: .+/transaction\.go
- path: (.+/)?transaction\.go
linters:
- ireturn
source: \) Begin\(ctx
- path: .+/settings\.go
- path: (.+/)?settings\.go
linters:
- ireturn
source: "(?:EnrichBy|SetPropagation|SetCtxKey|CtxKey|SetCancelable|SetTimeout)"
- linters:
- gochecknoglobals
source: "DefaultCtxGetter"
- &internal_text
path: ^internal
path: (^trm/internal|^internal)
text: "var-naming: don't use an underscore in package name"
- <<: *internal_text
text: "package-comments: should have a package comment"
- <<: *internal_text
text: "ST1003: should not use underscores in package names"
- path: (_test\.go|^internal)
- path: (_test\.go|^trm/internal|^internal)
linters:
- funlen
- gochecknoglobals
Expand Down
51 changes: 42 additions & 9 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,25 +1,58 @@
CVPKG=go list ./... | grep -v mocks | grep -v internal/
GO_TEST=go test `$(CVPKG)` -race
GO_TEST_WITH_REAL_DB=$(GO_TEST) --tags=with_real_db
COVERAGE_FILE="coverage.out"
DIR=$(PWD)

GO_TEST=cd ./sh && bash ./go.test.sh
GO_TEST_COVERAGE=cd ./sh && bash ./go.test.coverage.sh

GO_TEST_WITH_REAL_DB=--tags=with_real_db

test:
$(GO_TEST)

test.with_real_db:
$(GO_TEST_WITH_REAL_DB)
$(GO_TEST) $(GO_TEST_WITH_REAL_DB)

test.coverage:
$(GO_TEST) -covermode=atomic -coverprofile=$(COVERAGE_FILE)
$(GO_TEST_COVERAGE)

test.coverage.with_real_db:
$(GO_TEST_WITH_REAL_DB) -covermode=atomic -coverprofile=$(COVERAGE_FILE)
$(GO_TEST_COVERAGE) $(GO_TEST_WITH_REAL_DB)

fmt:
go fmt ./...
cd sh && sh ./go.fmt.sh

lint:
golangci-lint run -v --timeout=2m
cd sh && sh ./lint.sh

lint.verbose:
cd sh && sh ./lint.sh -v

lint.cache.clean:
golangci-lint cache clean

generate:
go generate ./...

go.mod.tidy:
cd sh && sh ./go.mod.tidy.sh

go.mod.vendor:
cd sh && sh ./go.mod.vendor.sh

go.work.sync:
go work sync


tag: git.tag tag.pkg

tag.pkg:
cd sh && sh ./tag.pkg.sh $(version)

git.tag: git.tag.create git.tag.push

# 1.0, "v2." added automatically
# make git.tag version="0.0-rc1"
git.tag.create:
cd sh && sh ./git.tag.sh $(version)

git.tag.push:
git push origin --tags
70 changes: 45 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
# Go transaction manager

[![Go Reference](https://pkg.go.dev/badge/github.com/avito-tech/go-transaction-manager.svg)](https://pkg.go.dev/github.com/avito-tech/go-transaction-manager)
[![Go Reference](https://pkg.go.dev/badge/github.com/avito-tech/go-transaction-manager.svg)](https://pkg.go.dev/github.com/avito-tech/go-transaction-manager/trm/v2)
[![Test Status](https://github.com/avito-tech/go-transaction-manager/actions/workflows/main.yaml/badge.svg)](https://github.com/avito-tech/go-transaction-manager/actions?query=branch%3Amain)
[![Coverage Status](https://coveralls.io/repos/github/avito-tech/go-transaction-manager/badge.svg?branch=main)](https://coveralls.io/github/avito-tech/go-transaction-manager?branch=main)
[![Go Report Card](https://goreportcard.com/badge/github.com/avito-tech/go-transaction-manager)](https://goreportcard.com/report/github.com/avito-tech/go-transaction-manager)
[![Go Report Card](https://goreportcard.com/badge/github.com/avito-tech/go-transaction-manager)](https://goreportcard.com/report/github.com/avito-tech/go-transaction-manager/)
[![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)

Transaction manager is an abstraction to coordinate database transaction boundaries.
Expand All @@ -12,45 +12,56 @@ Easiest way to get the perfect repository.

## Supported implementations

* [database/sql](https://pkg.go.dev/database/sql), [docs](https://pkg.go.dev/github.com/avito-tech/go-transaction-manager/sql) (Go 1.13)
* [jmoiron/sqlx](https://github.com/jmoiron/sqlx), [docs](https://pkg.go.dev/github.com/avito-tech/go-transaction-manager/sqlx) (Go 1.13)
* [gorm](https://github.com/go-gorm/gorm), [docs](https://pkg.go.dev/github.com/avito-tech/go-transaction-manager/gorm) (Go 1.16)
* [mongo-go-driver](https://github.com/mongodb/mongo-go-driver), [docs](https://pkg.go.dev/github.com/avito-tech/go-transaction-manager/mongo) (Go 1.13)
* [go-redis/redis](https://github.com/go-redis/redis), [docs](https://pkg.go.dev/github.com/avito-tech/go-transaction-manager/redis) (Go 1.17)
* [pgx_v4](https://github.com/jackc/pgx/tree/v4), [docs](https://pkg.go.dev/github.com/avito-tech/go-transaction-manager/pgxv4) (Go 1.16)
* [pgx_v5](https://github.com/jackc/pgx), [docs](https://pkg.go.dev/github.com/avito-tech/go-transaction-manager/pgxv5) (Go 1.19)
* [database/sql](https://pkg.go.dev/database/sql), [docs](https://pkg.go.dev/github.com/avito-tech/go-transaction-manager/drivers/sql/v2) (
Go 1.13)
* [jmoiron/sqlx](https://github.com/jmoiron/sqlx), [docs](https://pkg.go.dev/github.com/avito-tech/go-transaction-manager/drivers/sqlx/v2) (
Go 1.13)
* [gorm](https://github.com/go-gorm/gorm), [docs](https://pkg.go.dev/github.com/avito-tech/go-transaction-manager/drivers/gorm/v2) (
Go 1.18)
* [mongo-go-driver](https://github.com/mongodb/mongo-go-driver), [docs](https://pkg.go.dev/github.com/avito-tech/go-transaction-manager/drivers/mongo/v2) (
Go 1.13)
* [go-redis/redis](https://github.com/go-redis/redis), [docs](https://pkg.go.dev/github.com/avito-tech/go-transaction-manager/drivers/goredis8/v2) (
Go 1.17)
* [pgx_v4](https://github.com/jackc/pgx/tree/v4), [docs](https://pkg.go.dev/github.com/avito-tech/go-transaction-manager/drivers/pgxv4/v2) (
Go 1.16)
* [pgx_v5](https://github.com/jackc/pgx), [docs](https://pkg.go.dev/github.com/avito-tech/go-transaction-manager/drivers/pgxv5/v2) (
Go 1.19)

## Installation

```bash
go get github.com/avito-tech/go-transaction-manager
go get github.com/avito-tech/go-transaction-manager/trm/v2
```

To install some support database use `go get github.com/avito-tech/go-transaction-manager/drivers/{name}`.

For example `go get github.com/avito-tech/go-transaction-manager/drivers/sqlx/v2`.

### Backwards Compatibility

The library is compatible with the most recent two versions of Go.
Compatibility beyond that is not guaranteed.

## Usage

**To use multiple transactions from different databases**, you need to set CtxKey in [Settings](trm/settings.go) by [WithCtxKey](trm/settings/option.go).
**To use multiple transactions from different databases**, you need to set CtxKey in [Settings](trm/settings.go)
by [WithCtxKey](trm/settings/option.go) ([docs](https://pkg.go.dev/github.com/avito-tech/go-transaction-manager/trm/v2)).

**For nested transactions with different transaction managers**, you need to use [ChainedMW](trm/manager/chain.go) ([docs](https://pkg.go.dev/github.com/avito-tech/go-transaction-manager/trm/manager)).
**For nested transactions with different transaction managers**, you need to use [ChainedMW](trm/manager/chain.go) ([docs](https://pkg.go.dev/github.com/avito-tech/go-transaction-manager/trm/v2/manager)).

**To skip a transaction rollback due to an error, use [ErrSkip](trm/manager.go#L20) or [Skippable](trm/manager.go#L24)**
**To skip a transaction rollback due to an error, use [ErrSkip](manager.go#L20) or [Skippable](manager.go#L24)**

### Explanation of the approach ([English](https://www.youtube.com/watch?v=aRsea6FFAyA), [Russian](https://habr.com/ru/companies/avito/articles/727168/))
### Explanation of the approach [English](https://www.youtube.com/watch?v=aRsea6FFAyA), Russian [article](https://habr.com/ru/companies/avito/articles/727168/) and [youtube](https://www.youtube.com/watch?v=fcdckM5sUxA).

### Examples with an ideal *repository* and nested transactions.

* [database/sql](sql/example_test.go)
* [jmoiron/sqlx](sqlx/example_test.go)
* [gorm](gorm/example_test.go)
* [mongo-go-driver](mongo/example_test.go)
* [go-redis/redis](redis/example_test.go)
* [pgx_v4](pgxv4/example_test.go)
* [pgx_v5](pgxv5/example_test.go)

* [database/sql](drivers/sql/example_test.go)
* [jmoiron/sqlx](drivers/sqlx/example_test.go)
* [gorm](drivers/gorm/example_test.go)
* [mongo-go-driver](drivers/mongo/example_test.go)
* [go-redis/redis](drivers/goredis8/example_test.go)
* [pgx_v4](drivers/pgxv4/example_test.go)
* [pgx_v5](drivers/pgxv5/example_test.go)

Below is an example how to start usage.

Expand All @@ -64,8 +75,8 @@ import (
"github.com/jmoiron/sqlx"
_ "github.com/mattn/go-sqlite3"

trmsqlx "github.com/avito-tech/go-transaction-manager/sqlx"
"github.com/avito-tech/go-transaction-manager/trm/manager"
trmsqlx "github.com/avito-tech/go-transaction-manager/drivers/sqlx/v2"
"github.com/avito-tech/go-transaction-manager/trm/v2/manager"
)

func main() {
Expand All @@ -85,6 +96,7 @@ func main() {
err = trManager.Do(ctx, func(ctx context.Context) error {
checkErr(r.Save(ctx, u))

// example of nested transactions
return trManager.Do(ctx, func(ctx context.Context) error {
u.Username = "new_username"
return r.Save(ctx, u)
Expand Down Expand Up @@ -146,4 +158,12 @@ func (r *repo) Save(ctx context.Context, u *user) error {

## Benchmark

[Comparing](internal/benchmark/with_or_without_trm/README.md) examples with and without trm.
[Comparing](trm/internal/benchmark/with_or_without_trm/README.md) examples with and without trm.

## Contribution

1. To local development sync dependencies use `make go.work.sync`.
2. After finalizing of changes bump up version in all drivers.

* To install all dependencies use `make go.mod.tidy` or `make go.mod.vendor`.
* To run all tests use `make go.test` or `make go.test.with_real_db` for integration tests.
12 changes: 6 additions & 6 deletions redis/context.go → drivers/goredis8/context.go
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
package redis
package goredis8

import (
"context"

"github.com/go-redis/redis/v8"

"github.com/avito-tech/go-transaction-manager/trm"
trmcontext "github.com/avito-tech/go-transaction-manager/trm/context"
"github.com/avito-tech/go-transaction-manager/trm/v2"
trmcontext "github.com/avito-tech/go-transaction-manager/trm/v2/context"
)

// DefaultCtxGetter is the CtxGetter with settings.DefaultCtxKey.
var DefaultCtxGetter = NewCtxGetter(trmcontext.DefaultManager)

// CtxGetter gets redis.Pipeliner from trm.СtxManager by casting trm.Transaction to redis.UniversalClient.
// CtxGetter gets goredis8.Pipeliner from trm.СtxManager by casting trm.Transaction to redis.UniversalClient.
type CtxGetter struct {
ctxManager trm.СtxManager
}
Expand All @@ -22,7 +22,7 @@ func NewCtxGetter(c trm.СtxManager) *CtxGetter {
return &CtxGetter{ctxManager: c}
}

// DefaultTrOrDB returns Cmdable from context.Context or DB(redis.Cmdable) otherwise.
// DefaultTrOrDB returns Cmdable from context.Context or DB(goredis8.Cmdable) otherwise.
func (c *CtxGetter) DefaultTrOrDB(ctx context.Context, db redis.Cmdable) redis.Cmdable {
if tr := c.ctxManager.Default(ctx); tr != nil {
return c.convert(tr)
Expand All @@ -31,7 +31,7 @@ func (c *CtxGetter) DefaultTrOrDB(ctx context.Context, db redis.Cmdable) redis.C
return db
}

// TrOrDB returns Cmdable from context.Context by trm.CtxKey or DB(redis.Cmdable) otherwise.
// TrOrDB returns Cmdable from context.Context by trm.CtxKey or DB(goredis8.Cmdable) otherwise.
func (c *CtxGetter) TrOrDB(ctx context.Context, key trm.CtxKey, db redis.Cmdable) redis.Cmdable {
if tr := c.ctxManager.ByKey(ctx, key); tr != nil {
return c.convert(tr)
Expand Down
11 changes: 6 additions & 5 deletions redis/example_test.go → drivers/goredis8/example_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//go:build with_real_db
// +build with_real_db

package redis_test
package goredis8_test

import (
"context"
Expand All @@ -10,10 +10,11 @@ import (

"github.com/go-redis/redis/v8"

trmredis "github.com/avito-tech/go-transaction-manager/redis"
"github.com/avito-tech/go-transaction-manager/trm"
"github.com/avito-tech/go-transaction-manager/trm/manager"
"github.com/avito-tech/go-transaction-manager/trm/settings"
trmredis "github.com/avito-tech/go-transaction-manager/drivers/goredis8/v2"

"github.com/avito-tech/go-transaction-manager/trm/v2"
"github.com/avito-tech/go-transaction-manager/trm/v2/manager"
"github.com/avito-tech/go-transaction-manager/trm/v2/settings"
)

// Example demonstrates the implementation of the Repository pattern by trm.Manager.
Expand Down
4 changes: 2 additions & 2 deletions redis/factory.go → drivers/goredis8/factory.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package redis
package goredis8

import (
"context"

"github.com/go-redis/redis/v8"

"github.com/avito-tech/go-transaction-manager/trm"
trm "github.com/avito-tech/go-transaction-manager/trm/v2"
)

// NewDefaultFactory creates default trm.Transaction(redis.UniversalClient).
Expand Down
14 changes: 14 additions & 0 deletions drivers/goredis8/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
module github.com/avito-tech/go-transaction-manager/drivers/goredis8/v2

go 1.14

require (
github.com/avito-tech/go-transaction-manager/trm/v2 v2.0.0-rc6
github.com/go-redis/redis/v8 v8.11.5
github.com/go-redis/redismock/v8 v8.11.5
github.com/kr/pretty v0.3.0 // indirect
github.com/rogpeppe/go-internal v1.12.0 // indirect
github.com/stretchr/testify v1.8.2
golang.org/x/net v0.10.0 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
)
Loading
Loading