Documentation
¶
Overview ¶
Package stdent provides re-usable code for using Ent.
Index ¶
- func AttemptFromContext(ctx context.Context) int
- func ContextWithAttempts(ctx context.Context, v int) context.Context
- func ContextWithTx[T Tx](ctx context.Context, tx T) context.Context
- func HasReadPromotion(ctx context.Context) bool
- func NoTestForMaxQueryPlanCosts(ctx context.Context) bool
- func Transact0[T Tx](ctx context.Context, txr *Transactor[T], ...) (err error)
- func Transact1[T Tx, U any](ctx context.Context, txr *Transactor[T], ...) (res U, err error)
- func TransactR[T Tx, I any, O any, IP interface{ ... }, OP interface{ ... }](ctx context.Context, ro, rw *Transactor[T], inp IP, ...) (OP, error)
- func TransactR0[T Tx](ctx context.Context, ro, rw *Transactor[T], ...) error
- func TxFromContext[T Tx](ctx context.Context) T
- func WithNoTestForMaxQueryPlanCosts(ctx context.Context) context.Context
- func WithReadPromotion(ctx context.Context) context.Context
- func WithWriteObserver(ctx context.Context) (context.Context, *atomic.Bool)
- type BeginHookFunc
- type Client
- type Driver
- type DriverOption
- type Option
- type Transactor
- type Tx
- type WTx
- func (tx WTx) Exec(ctx context.Context, query string, args, v any) error
- func (tx WTx) ExecContext(ctx context.Context, query string, args ...any) (sql.Result, error)
- func (tx WTx) Query(ctx context.Context, query string, args, v any) error
- func (tx WTx) QueryContext(ctx context.Context, query string, args ...any) (*sql.Rows, error)
- func (tx WTx) StandardTx() *sql.Tx
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func AttemptFromContext ¶ added in v0.0.50
AttemptFromContext returns which execution attempt it is. Panics if this information is not present.
func ContextWithAttempts ¶ added in v0.0.50
ContextWithAttempts stores which execution attempt it is.
func ContextWithTx ¶ added in v0.0.50
ContextWithTx returns a context with the Tx in it.
func HasReadPromotion ¶ added in v0.0.235
HasReadPromotion reports whether ctx was stamped by WithReadPromotion. Consumers normally don't call this directly — TransactR / TransactR0 consult it on every invocation — but it is exported for tests and for diagnostics (e.g. attaching the decision to a span).
func NoTestForMaxQueryPlanCosts ¶ added in v0.0.91
NoTestForMaxQueryPlanCosts returns whether the cost check is disabled.
func Transact0 ¶ added in v0.0.50
func Transact0[T Tx]( ctx context.Context, txr *Transactor[T], fnc func(ctx context.Context, tx T) error, ) (err error)
Transact0 runs Transact1 but without a value to return.
func Transact1 ¶ added in v0.0.50
func Transact1[T Tx, U any]( ctx context.Context, txr *Transactor[T], fnc func(ctx context.Context, tx T) (U, error), ) (res U, err error)
Transact1 runs fnc in a transaction T derived from the provided Ent client while returning one value of type U. The implementation is taken from the official docs: https://entgo.io/docs/transactions#best-practices. If the context already has a transaction, it runs it in that one.
func TransactR ¶ added in v0.0.235
func TransactR[T Tx, I any, O any, IP interface{ *I }, OP interface{ *O }]( ctx context.Context, ro, rw *Transactor[T], inp IP, fn func(ctx context.Context, tx T, inp IP) (OP, error), ) (OP, error)
TransactR is Transact1 for read-bound callers that may, per invocation, be transparently promoted to the writer to give the caller read-your-writes consistency. The choice is driven by HasReadPromotion on ctx and forwards to Transact1 with the chosen transactor, so every guarantee Transact1 provides (retry-on-serialization-failure, nested-tx reuse, panic rollback) applies unchanged.
Callers that bind their handler to ONE boundary at proto-gen / compile time can still pass both `ro` and `rw` here without leaking the routing policy into generated code or into the handler body.
The inp / fn shape (taking a typed input + returning a typed output) mirrors common request/response transactional helpers.
func TransactR0 ¶ added in v0.0.235
func TransactR0[T Tx]( ctx context.Context, ro, rw *Transactor[T], fn func(ctx context.Context, tx T) error, ) error
TransactR0 is TransactR for the common case where the inner function neither needs a typed input nor returns a typed output; mirrors Transact0 exactly, including all of its safety guarantees, and chooses the transactor the same way TransactR does.
func TxFromContext ¶ added in v0.0.50
TxFromContext will get a transaction of type T from the context or panic.
func WithNoTestForMaxQueryPlanCosts ¶ added in v0.0.91
WithNoTestForMaxQueryPlanCosts allow disabling the plan cost check.
func WithReadPromotion ¶ added in v0.0.235
WithReadPromotion stamps ctx so that a subsequent TransactR or TransactR0 call will open its transaction against the read-write transactor instead of the read-only one. It is the single seam where read-your-writes routing policy plugs in — e.g. an HTTP middleware that observes a signed "write fence" cookie set after a recent write, or any caller that knows it needs the writer for a specific invocation.
The decision is invisible to the developer-implemented handler body: it still receives a single T and has no way to widen its own posture (downstream concerns like per-tenant Postgres roles and transaction mode continue to enforce what the caller may do).
func WithWriteObserver ¶ added in v0.0.235
WithWriteObserver attaches a fresh write observer to ctx and returns both the derived ctx and the same *atomic.Bool that the stdent transact layer will flip on the first successful commit of a non-read-only transaction within ctx's lifetime.
It exists because context.Context only flows downward: a plain bool stamped by Transact1 would be invisible to a caller running above the handler (e.g. an HTTP middleware that wants to react to "a write happened during this request"). The *atomic.Bool sidecar bridges that gap — the middleware keeps the pointer, calls the next handler with the derived ctx, and inspects the bool after the handler returns.
The observer mechanism is intentionally independent from WithReadPromotion: it does not care which transactor the write went through, nor whether the caller participates in ro/rw routing. Any successful commit of a transactor with Transactor.IsReadOnly == false trips it.
Callers that never attach an observer pay only a single ctx lookup per commit (see [noteWriteObserved]), so non-HTTP consumers (Temporal activities, bootstrap, tests) are unaffected.
Re-calling WithWriteObserver on an already-observed ctx attaches a fresh observer that shadows the previous one for descendants of the new ctx; the previously-returned pointer keeps observing only what is reachable through the older ctx.
Types ¶
type BeginHookFunc ¶ added in v0.0.152
type BeginHookFunc = func( ctx context.Context, sql *strings.Builder, tx entdialect.ExecQuerier, ) (*strings.Builder, error)
BeginHookFunc is a function that is called right when the transaction has been setup.
type Driver ¶ added in v0.0.91
type Driver struct {
entdialect.Driver
// contains filtered or unexported fields
}
Driver is an opionated Ent driver that wraps a base driver but only allows interactions with the database to be done through a transaction with specific isolation properties and hooking any sql being executed.
func NewDriver ¶ added in v0.0.91
func NewDriver( base entdialect.Driver, opts ...DriverOption, ) *Driver
NewDriver inits the driver.
func (Driver) BeginTx ¶ added in v0.0.91
BeginTx calls the base driver's method if it's supported and calls our hook. Only snapshot-or-stricter isolation levels are accepted: read-committed, repeatable-read or serializable. Aurora hot standbys only accept repeatable-read or read-committed, so serializable is permitted but should not be used against read-only replicas.
func (Driver) Exec ¶ added in v0.0.91
Exec executes a query that does not return records. For example, in SQL, INSERT or UPDATE. It scans the result into the pointer v. For SQL drivers, it is dialect/sql.Result.
type DriverOption ¶ added in v0.0.91
type DriverOption func(*Driver)
DriverOption configures a Driver.
func BeginHook ¶ added in v0.0.91
func BeginHook(v BeginHookFunc) DriverOption
BeginHook may be called right when the transaction has been setup. This allows injecting custom settings into transaction. For example to facilitate role switching and Row-level security. This can either be performed by extending the sql statement that is already being performed (perferred for simple operations). Or using the transaction concretely.
func DiscourageSequentialScans ¶ added in v0.0.91
func DiscourageSequentialScans() DriverOption
DiscourageSequentialScans will dis-incentivize the query planner to use sequential scans for all transactions. This is mainly useful with the TestForMaxQueryPlanCost option to assert that queries under testing are missing an index.
func TestForMaxQueryPlanCosts ¶ added in v0.0.91
func TestForMaxQueryPlanCosts(maxCost float64) DriverOption
TestForMaxQueryPlanCosts will enable EXPLAIN on every query that is executed with the driver and fail when the cost of the resulting query is above the maximum. Together with the enable_seqscan=OFF it can help test of infefficient queries do to missing indexes.
func TxExecQueryLoggingLevel ¶ added in v0.0.91
func TxExecQueryLoggingLevel(v zapcore.Level) DriverOption
TxExecQueryLoggingLevel configures the level at which transaction's exec and query sql logs are send to the logger.
type Option ¶ added in v0.0.50
type Option func(opts *options)
Option configures a Transactor.
func IsolationLevel ¶ added in v0.0.50
func IsolationLevel(v sql.IsolationLevel) Option
IsolationLevel specifies the isolation level for new transactions.
func SerializationFailureCodes ¶ added in v0.0.50
SerializationFailureCodes configures which PostgreSQL error codes should be considered serialization failures.
func SerializationFailureMaxRetries ¶ added in v0.0.50
SerializationFailureMaxRetries configures the maximum number of retries in case the transacted code encounters a serialization failure.
type Transactor ¶ added in v0.0.50
type Transactor[T Tx] struct { // contains filtered or unexported fields }
Transactor manages transactional operations with retry logic for serialization failures.
func New ¶ added in v0.0.50
func New[T Tx](client Client[T], opts ...Option) *Transactor[T]
New creates a new Transactor with the given client and options.
func (Transactor[T]) IsReadOnly ¶ added in v0.0.190
func (txr Transactor[T]) IsReadOnly() bool
IsReadOnly reports whether the transactor is configured for read-only transactions.
type WTx ¶ added in v0.0.91
type WTx struct {
entdialect.Tx
MaxQueryPlanCosts float64
// contains filtered or unexported fields
}
WTx wraps a Ent transaction to provide us with the ability to hook any sql before it's being executed. In our case we ant to fail tests when the to-be-executed query plan has a cost that is too high.
func (WTx) Exec ¶ added in v0.0.91
Exec executes a query that does not return records. For example, in SQL, INSERT or UPDATE. It scans the result into the pointer v. For SQL drivers, it is dialect/sql.Result.
func (WTx) ExecContext ¶ added in v0.0.115
ExecContext implements a way to execute raw sql.
func (WTx) Query ¶ added in v0.0.91
Query executes a query that returns rows, typically a SELECT in SQL. It scans the result into the pointer v. For SQL drivers, it is *dialect/sql.Rows.
func (WTx) QueryContext ¶ added in v0.0.115
QueryContext implements a way to execute raw sql.
func (WTx) StandardTx ¶ added in v0.0.116
StandardTx returns the sql.Tx instance that this Ent transaction holds. This is useful for code that depends on that interface. It panics if the Ent transaction could not be converted to a *sql.Tx.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package stdenttest is a utility for writing tests on ent transaction.
|
Package stdenttest is a utility for writing tests on ent transaction. |
|
Package stdentwritefence provides an HTTP middleware that gives a client read-your-writes consistency against an ro/rw transactor pair without any server-side state.
|
Package stdentwritefence provides an HTTP middleware that gives a client read-your-writes consistency against an ro/rw transactor pair without any server-side state. |