Skip to content
Closed
Show file tree
Hide file tree
Changes from 6 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
1 change: 1 addition & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ issues:
exclude-dirs:
- trm/manager/mock
- sql/mock
- drivers/mongov2/internal
exclude-use-default: false
exclude:
- ST1000 # ST1000: at least one file in a package should have a package comment
Expand Down
27 changes: 18 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ Easiest way to get the perfect repository.
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)
* [mongo-go-driver v2](https://github.com/mongodb/mongo-go-driver), [docs](https://pkg.go.dev/github.com/avito-tech/go-transaction-manager/drivers/mongov2/v2) (
Go 1.21)
* [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) (
Expand Down Expand Up @@ -48,14 +50,16 @@ The critical bugs are firstly solved for the most recent two Golang versions and

`go get -u && go mod tidy` helps you.

**Note**: The go-transaction-manager uses some old dependencies to support backwards compatibility for old versions of Go.
**Note**: The go-transaction-manager uses some old dependencies to support backwards compatibility for old versions of
Go.

## Usage

**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/v2/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](manager.go#L20) or [Skippable](manager.go#L24)**

Expand All @@ -67,6 +71,7 @@ by [WithCtxKey](trm/settings/option.go) ([docs](https://pkg.go.dev/github.com/av
* [jmoiron/sqlx](drivers/sqlx/example_test.go)
* [gorm](drivers/gorm/example_test.go)
* [mongo-go-driver](drivers/mongo/example_test.go)
* [mongo-go-driver v2](drivers/mongov2/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)
Expand Down Expand Up @@ -181,13 +186,14 @@ func (r *repo) Save(ctx context.Context, u *user) error {
* To run all tests use `make test` or `make test.with_real_db` for integration tests.

To run database by docker, there is [docker-compose.yaml](trm/drivers/test/docker-compose.yaml).

```bash
docker compose -f trm/drivers/test/docker-compose.yaml up
```

For full GitHub Actions run, you can use [act](https://github.com/nektos/act).

#### Running old go versions
#### Running old go versions

To stop Golang upgrading set environment variable `GOTOOLCHAIN=local` .

Expand All @@ -199,26 +205,29 @@ go1.16 install
Use `-mod=readonly` to prevent `go.mod` modification.

To run tests

```
go1.16 test -race -mod=readonly ./...
```

### How to bump up Golang version in CI/CD

1. Changes in [.github/workflows/main.yaml](.github/workflows/main.yaml).
1. Add all old version of Go in `go-version:` for `tests-units` job.
2. Update `go-version:` on current version of Go for `lint` and `tests-integration` jobs.
1. Add all old version of Go in `go-version:` for `tests-units` job.
2. Update `go-version:` on current version of Go for `lint` and `tests-integration` jobs.
2. Update build tags by replacing `build go1.xx` on new version.


### Resolve problems with old version of dependencies

To build `go.mod` compatible for old version use `go mod tidy -compat=1.13` ([docs](https://go.dev/ref/mod#go-mod-tidy)).
To build `go.mod` compatible for old version use
`go mod tidy -compat=1.13` ([docs](https://go.dev/ref/mod#go-mod-tidy)).

However, `--compat` doesn't always work correct and we need to set some library versions manually.

1. `go get go.uber.org/[email protected]` in [trm](trm), [sql](drivers/sql), [sqlx](drivers/sqlx).
2. `go get github.com/mattn/[email protected]` in [trm](trm), [sql](drivers/sql), [sqlx](drivers/sqlx).
3. `go get github.com/stretchr/[email protected]` in [trm](trm), [sql](drivers/sql), [sqlx](drivers/sqlx), [goredis8](drivers/goredis8), [mongo](drivers/mongo).
4. `go get github.com/jackc/[email protected]` in [pgxv4](drivers/pgxv4). Golang version was bumped up from 1.12 to 1.17 in pgconn v1.14.3.
3. `go get github.com/stretchr/[email protected]`
in [trm](trm), [sql](drivers/sql), [sqlx](drivers/sqlx), [goredis8](drivers/goredis8), [mongo](drivers/mongo).
4. `go get github.com/jackc/[email protected]` in [pgxv4](drivers/pgxv4). Golang version was bumped up from 1.12 to 1.17 in
pgconn v1.14.3.
5. `go get golang.org/x/[email protected]` in [pgxv4](drivers/pgxv4).
50 changes: 50 additions & 0 deletions drivers/mongov2/context.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package mongov2

import (
"context"

"go.mongodb.org/mongo-driver/v2/mongo"

"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 Tr from trm.СtxManager by casting trm.Transaction to Tr.
type CtxGetter struct {
ctxManager trm.СtxManager
}

// NewCtxGetter returns *CtxGetter to get Tr from context.Context.
func NewCtxGetter(c trm.СtxManager) *CtxGetter {
return &CtxGetter{ctxManager: c}
}

// DefaultTrOrDB returns mongo.Session from context.Context or DB(mongo.Session) otherwise.
func (c *CtxGetter) DefaultTrOrDB(ctx context.Context, db *mongo.Session) *mongo.Session {
if tr := c.ctxManager.Default(ctx); tr != nil {
return c.convert(tr)
}

return db
}

// TrOrDB returns mongo.Session from context.Context by trm.CtxKey or DB(mongo.Session) otherwise.
func (c *CtxGetter) TrOrDB(ctx context.Context, key trm.CtxKey, db *mongo.Session) *mongo.Session {
if tr := c.ctxManager.ByKey(ctx, key); tr != nil {
return c.convert(tr)
}

return db
}

func (c *CtxGetter) convert(tr trm.Transaction) *mongo.Session {
if tx, ok := tr.Transaction().(*mongo.Session); ok {
return tx
}

return nil
}
61 changes: 61 additions & 0 deletions drivers/mongov2/context_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
//go:build go1.21

package mongov2

import (
"context"
"testing"

"github.com/avito-tech/go-transaction-manager/drivers/mongov2/v2/internal/mtest"

"github.com/stretchr/testify/require"

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

func TestContext(t *testing.T) {
t.Parallel()

ctx := context.Background()
mt := mtest.New(
t,
mtest.NewOptions().ClientType(mtest.Mock),
)

mt.Run("all", func(mt *mtest.T) {
mt.Parallel()

m := manager.Must(
NewDefaultFactory(mt.Client),
)

err := m.Do(ctx, func(ctx context.Context) error {
tr := DefaultCtxGetter.TrOrDB(ctx, settings.DefaultCtxKey, nil)
require.NotNil(t, tr)

tr = DefaultCtxGetter.DefaultTrOrDB(ctx, nil)
require.NotNil(t, tr)

tr = DefaultCtxGetter.TrOrDB(ctx, "invalid ley", nil)
require.Nil(t, tr)

err := m.Do(ctx, func(ctx context.Context) error {
tr = DefaultCtxGetter.DefaultTrOrDB(ctx, nil)
require.NotNil(t, tr)

tr = DefaultCtxGetter.TrOrDB(ctx, settings.DefaultCtxKey, nil)
require.NotNil(t, tr)

tr = DefaultCtxGetter.TrOrDB(ctx, "invalid ley", nil)
require.Nil(t, tr)

return nil
})

return err
})

require.NoError(t, err)
})
}
24 changes: 24 additions & 0 deletions drivers/mongov2/contract.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package mongov2

import (
"context"

"go.mongodb.org/mongo-driver/v2/mongo/options"
"go.mongodb.org/mongo-driver/v2/mongo/readpref"

"go.mongodb.org/mongo-driver/v2/mongo"
)

//nolint:interfacebloat

Check failure on line 12 in drivers/mongov2/contract.go

View workflow job for this annotation

GitHub Actions / lint (./drivers/mongov2)

directive `//nolint:interfacebloat` is unused for linter "interfacebloat" (nolintlint)
type client interface {
Disconnect(ctx context.Context) error
Ping(ctx context.Context, rp *readpref.ReadPref) error
StartSession(opts ...options.Lister[options.SessionOptions]) (*mongo.Session, error)
Database(name string, opts ...options.Lister[options.DatabaseOptions]) *mongo.Database
ListDatabases(ctx context.Context, filter interface{}, opts ...options.Lister[options.ListDatabasesOptions]) (mongo.ListDatabasesResult, error)
ListDatabaseNames(ctx context.Context, filter interface{}, opts ...options.Lister[options.ListDatabasesOptions]) ([]string, error)
UseSession(ctx context.Context, fn func(context.Context) error) error
UseSessionWithOptions(ctx context.Context, opts *options.SessionOptionsBuilder, fn func(context.Context) error) error
Watch(ctx context.Context, pipeline interface{}, opts ...options.Lister[options.ChangeStreamOptions]) (*mongo.ChangeStream, error)
NumberSessionsInProgress() int
}
107 changes: 107 additions & 0 deletions drivers/mongov2/example_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
//go:build with_real_db

package mongov2_test

import (
"context"
"fmt"

"go.mongodb.org/mongo-driver/v2/bson"
"go.mongodb.org/mongo-driver/v2/mongo"
"go.mongodb.org/mongo-driver/v2/mongo/options"

trmmongo "github.com/avito-tech/go-transaction-manager/drivers/mongov2/v2"
trmcontext "github.com/avito-tech/go-transaction-manager/trm/v2/context"

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

// Example demonstrates the implementation of the Repository pattern by trm.Manager.
func Example() {
ctx := context.Background()

client, err := mongo.Connect(options.Client().
ApplyURI("mongodb://127.0.0.1:27017/?directConnection=true"))
checkErr(err)
defer client.Disconnect(ctx)

collection := client.Database("test").Collection("users")

r := newRepo(collection, trmmongo.DefaultCtxGetter)

u := &user{
ID: 1,
Username: "username",
}

trManager := manager.Must(
trmmongo.NewDefaultFactory(client),
manager.WithCtxManager(trmcontext.DefaultManager),
)

err = trManager.Do(ctx, func(ctx context.Context) error {
if err := r.Save(ctx, u); err != nil {
return err
}

return trManager.Do(ctx, func(ctx context.Context) error {
u.Username = "new_username"

return r.Save(ctx, u)
})
})
checkErr(err)

userFromDB, err := r.GetByID(ctx, u.ID)
checkErr(err)

fmt.Println(userFromDB)

// Output: &{1 new_username}
}

type repo struct {
collection *mongo.Collection
getter *trmmongo.CtxGetter
}

func newRepo(collection *mongo.Collection, c *trmmongo.CtxGetter) *repo {
return &repo{
collection: collection,
getter: c,
}
}

type user struct {
ID int64 `bson:"_id"`
Username string `bson:"username"`
}

func (r *repo) GetByID(ctx context.Context, id int64) (*user, error) {
var result user

err := r.collection.FindOne(ctx, bson.M{"_id": id}).Decode(&result)

return &result, err
}

func (r *repo) Save(ctx context.Context, u *user) error {
if err := r.collection.FindOneAndUpdate(
ctx,
bson.M{"_id": u.ID},
bson.M{"$set": u},
options.FindOneAndUpdate().
SetReturnDocument(options.After).
SetUpsert(true),
).Err(); err != nil {
return err
}

return nil
}

func checkErr(err error, args ...interface{}) {
if err != nil {
panic(fmt.Sprint(append([]interface{}{err}, args...)...))
}
}
16 changes: 16 additions & 0 deletions drivers/mongov2/factory.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package mongov2

import (
"context"

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

// NewDefaultFactory creates default trm.Transaction(mongo.Session).
func NewDefaultFactory(client client) trm.TrFactory {
return func(ctx context.Context, trms trm.Settings) (context.Context, trm.Transaction, error) {
s, _ := trms.(Settings)

return NewTransaction(ctx, s.SessionOpts(), s.TransactionOpts(), client)
}
}
29 changes: 29 additions & 0 deletions drivers/mongov2/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
module github.com/avito-tech/go-transaction-manager/drivers/mongov2/v2

go 1.21

require (
github.com/avito-tech/go-transaction-manager/trm/v2 v2.0.0-rc10
github.com/davecgh/go-spew v1.1.1
github.com/google/go-cmp v0.6.0
github.com/stretchr/testify v1.8.2
go.mongodb.org/mongo-driver/v2 v2.0.0
go.uber.org/goleak v1.3.0
)

require (
github.com/golang/mock v1.6.0 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/klauspost/compress v1.16.7 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
github.com/xdg-go/scram v1.1.2 // indirect
github.com/xdg-go/stringprep v1.0.4 // indirect
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
go.uber.org/atomic v1.7.0 // indirect
go.uber.org/multierr v1.9.0 // indirect
golang.org/x/crypto v0.29.0 // indirect
golang.org/x/sync v0.9.0 // indirect
golang.org/x/text v0.20.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
Loading
Loading