[hw-4] add postgres db

This commit is contained in:
Никита Шубин
2025-06-26 12:08:46 +00:00
parent 3ebaad5558
commit 77ed9fcf85
46 changed files with 1582 additions and 369 deletions

View File

@@ -0,0 +1,56 @@
package postgres
import (
"context"
"github.com/jackc/pgx/v5/pgxpool"
"github.com/pkg/errors"
)
// From https://gitlab.ozon.dev/go/classroom-18/students/week-4-workshop/-/blob/master/internal/infra/postgres/postgres.go
func NewPool(ctx context.Context, dsn string) (*pgxpool.Pool, error) {
config, err := pgxpool.ParseConfig(dsn)
if err != nil {
return nil, errors.Wrap(err, "pgxpool.ParseConfig")
}
pool, err := pgxpool.NewWithConfig(ctx, config)
if err != nil {
return nil, errors.Wrap(err, "pgxpool.NewWithConfig")
}
return pool, nil
}
func NewPools(ctx context.Context, DSNs ...string) ([]*pgxpool.Pool, error) {
pools := make([]*pgxpool.Pool, len(DSNs))
for i, dsn := range DSNs {
cfg, err := pgxpool.ParseConfig(dsn)
if err != nil {
closeOpened(pools[:i])
return nil, errors.Wrap(err, "pgxpool.ParseConfig")
}
pool, err := pgxpool.NewWithConfig(ctx, cfg)
if err != nil {
closeOpened(pools[:i])
return nil, errors.Wrap(err, "pgxpool.NewWithConfig")
}
pools[i] = pool
}
return pools, nil
}
func closeOpened(pools []*pgxpool.Pool) {
for _, p := range pools {
if p != nil {
p.Close()
}
}
}

View File

@@ -0,0 +1,87 @@
package postgres
// From https://gitlab.ozon.dev/go/classroom-18/students/week-4-workshop/-/blob/master/internal/infra/postgres/tx.go
import (
"context"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgxpool"
"github.com/opentracing/opentracing-go"
)
// Tx транзакция.
type Tx pgx.Tx
type txKey struct{}
func ctxWithTx(ctx context.Context, tx pgx.Tx) context.Context {
return context.WithValue(ctx, txKey{}, tx)
}
func TxFromCtx(ctx context.Context) (pgx.Tx, bool) {
tx, ok := ctx.Value(txKey{}).(pgx.Tx)
return tx, ok
}
type TxManager struct {
write *pgxpool.Pool
read *pgxpool.Pool
}
func NewTxManager(write, read *pgxpool.Pool) *TxManager {
return &TxManager{
write: write,
read: read,
}
}
// WithTransaction выполняет fn в транзакции с дефолтным уровнем изоляции.
func (m *TxManager) WriteWithTransaction(ctx context.Context, fn func(ctx context.Context) error) (err error) {
return m.withTx(ctx, m.write, pgx.TxOptions{}, fn)
}
func (m *TxManager) ReadWithTransaction(ctx context.Context, fn func(ctx context.Context) error) (err error) {
return m.withTx(ctx, m.read, pgx.TxOptions{}, fn)
}
// WithTransaction выполняет fn в транзакции с уровнем изоляции RepeatableRead.
func (m *TxManager) WriteWithRepeatableRead(ctx context.Context, fn func(ctx context.Context) error) (err error) {
return m.withTx(ctx, m.write, pgx.TxOptions{IsoLevel: pgx.RepeatableRead}, fn)
}
func (m *TxManager) ReadWithRepeatableRead(ctx context.Context, fn func(ctx context.Context) error) (err error) {
return m.withTx(ctx, m.read, pgx.TxOptions{IsoLevel: pgx.RepeatableRead}, fn)
}
// WithTx выполняет fn в транзакции.
func (m *TxManager) withTx(ctx context.Context, pool *pgxpool.Pool, options pgx.TxOptions, fn func(ctx context.Context) error) (err error) {
var span opentracing.Span
span, ctx = opentracing.StartSpanFromContext(ctx, "Transaction")
defer span.Finish()
tx, err := pool.BeginTx(ctx, options)
if err != nil {
return
}
ctx = ctxWithTx(ctx, tx)
defer func() {
if p := recover(); p != nil {
// a panic occurred, rollback and repanic
_ = tx.Rollback(ctx)
panic(p)
} else if err != nil {
// something went wrong, rollback
_ = tx.Rollback(ctx)
} else {
// all good, commit
err = tx.Commit(ctx)
}
}()
err = fn(ctx)
return
}

View File

@@ -0,0 +1,19 @@
package tools
import (
"fmt"
"math"
)
func SafeCastInt64ToUInt32(num int64) (uint32, error) {
if num < 0 {
return 0, fmt.Errorf("tried casting signed negative number to unsigned number")
}
if num > math.MaxUint32 {
return 0, fmt.Errorf("tried casting larger number than uint32 can store")
}
// the bounds are checked, and cast should be safe.
return uint32(num), nil // #nosec G115
}