Skip to content

Commit af1e084

Browse files
refactored retries and open template (#13)
1 parent 167feaf commit af1e084

File tree

285 files changed

+835
-1444
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

285 files changed

+835
-1444
lines changed

dialect/dialect.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,11 @@ func DebugWithContext(d Driver, logger func(context.Context, ...any)) Driver {
8888
return drv
8989
}
9090

91+
// Log returns the log function used by this DebugDriver.
92+
func (d *DebugDriver) Log() func(context.Context, ...any) {
93+
return d.log
94+
}
95+
9196
// Exec logs its params and calls the underlying driver Exec method.
9297
func (d *DebugDriver) Exec(ctx context.Context, query string, args, v any) error {
9398
d.log(ctx, fmt.Sprintf("driver.Exec: query=%v args=%v", query, args))

dialect/sql/driver.go

Lines changed: 56 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,31 +14,75 @@ import (
1414
"strings"
1515

1616
"entgo.io/ent/dialect"
17+
"github.com/ydb-platform/ydb-go-sdk/v3"
1718
)
1819

1920
// Driver is a dialect.Driver implementation for SQL based databases.
2021
type Driver struct {
2122
Conn
22-
dialect string
23+
dialect string
24+
retryExecutor RetryExecutor
2325
}
2426

2527
// NewDriver creates a new Driver with the given Conn and dialect.
26-
func NewDriver(dialect string, c Conn) *Driver {
27-
return &Driver{dialect: dialect, Conn: c}
28+
func NewDriver(
29+
dialect string,
30+
c Conn,
31+
retryExecutor RetryExecutor,
32+
) *Driver {
33+
return &Driver{
34+
Conn: c,
35+
dialect: dialect,
36+
retryExecutor: retryExecutor,
37+
}
2838
}
2939

3040
// Open wraps the database/sql.Open method and returns a dialect.Driver that implements the an ent/dialect.Driver interface.
31-
func Open(dialect, source string) (*Driver, error) {
32-
db, err := sql.Open(dialect, source)
41+
func Open(sqlDialect, dsn string) (*Driver, error) {
42+
var (
43+
db *sql.DB
44+
err error
45+
)
46+
47+
if sqlDialect == dialect.YDB {
48+
nativeDriver, err := ydb.Open(context.Background(), dsn)
49+
if err != nil {
50+
return nil, err
51+
}
52+
53+
conn, err := ydb.Connector(
54+
nativeDriver,
55+
ydb.WithAutoDeclare(),
56+
ydb.WithTablePathPrefix(nativeDriver.Name()),
57+
ydb.WithQueryService(true),
58+
)
59+
if err != nil {
60+
return nil, err
61+
}
62+
63+
db = sql.OpenDB(conn)
64+
} else {
65+
db, err = sql.Open(sqlDialect, dsn)
66+
}
67+
3368
if err != nil {
3469
return nil, err
3570
}
36-
return NewDriver(dialect, Conn{db, dialect}), nil
71+
72+
return NewDriver(
73+
sqlDialect,
74+
Conn{db, sqlDialect},
75+
NewRetryExecutor(sqlDialect, db),
76+
), nil
3777
}
3878

3979
// OpenDB wraps the given database/sql.DB method with a Driver.
40-
func OpenDB(dialect string, db *sql.DB) *Driver {
41-
return NewDriver(dialect, Conn{db, dialect})
80+
func OpenDB(sqlDialect string, db *sql.DB) *Driver {
81+
return NewDriver(
82+
sqlDialect,
83+
Conn{db, sqlDialect},
84+
NewRetryExecutor(sqlDialect, db),
85+
)
4286
}
4387

4488
// DB returns the underlying *sql.DB instance.
@@ -74,6 +118,10 @@ func (d *Driver) BeginTx(ctx context.Context, opts *TxOptions) (dialect.Tx, erro
74118
}, nil
75119
}
76120

121+
func (d *Driver) RetryExecutor() RetryExecutor {
122+
return d.retryExecutor
123+
}
124+
77125
// Close closes the underlying connection.
78126
func (d *Driver) Close() error { return d.DB().Close() }
79127

Lines changed: 44 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@
22
// This source code is licensed under the Apache 2.0 license found
33
// in the LICENSE file in the root directory of this source tree.
44

5-
package sqlgraph
5+
package sql
66

77
import (
88
"context"
9+
"database/sql"
910

1011
"entgo.io/ent/dialect"
1112
)
@@ -31,6 +32,18 @@ type RetryExecutor interface {
3132
) error
3233
}
3334

35+
// NewRetryExecutor creates a new RetryExecutor with the given database connection
36+
func NewRetryExecutor(
37+
sqlDialect string,
38+
db *sql.DB,
39+
) RetryExecutor {
40+
if sqlDialect == dialect.YDB && db != nil {
41+
return &YDBRetryExecutor{db: db}
42+
} else {
43+
return nil
44+
}
45+
}
46+
3447
// RetryExecutorGetter is an optional interface that drivers can implement to provide
3548
// a RetryExecutor for automatic retry handling.
3649
// If a driver implements this interface,
@@ -41,12 +54,37 @@ type RetryExecutorGetter interface {
4154
RetryExecutor() RetryExecutor
4255
}
4356

44-
// getRetryExecutor returns the RetryExecutor for the given driver if available.
45-
func getRetryExecutor(drv dialect.Driver) RetryExecutor {
46-
if reg, ok := drv.(RetryExecutorGetter); ok {
47-
return reg.RetryExecutor()
57+
// GetRetryExecutor returns the RetryExecutor for the given driver if available.
58+
// If the driver is wrapped with a DebugDriver, the returned executor will preserve
59+
// debug logging by wrapping the driver passed to callback functions.
60+
func GetRetryExecutor(drv dialect.Driver) RetryExecutor {
61+
drv, logFn := unwrapDebugDriver(drv)
62+
63+
getter, ok := drv.(RetryExecutorGetter)
64+
if !ok {
65+
return nil
66+
}
67+
68+
executor := getter.RetryExecutor()
69+
if executor == nil {
70+
return nil
71+
}
72+
73+
if logFn != nil {
74+
return &debugRetryExecutor{
75+
RetryExecutor: executor,
76+
log: logFn,
77+
}
78+
}
79+
return executor
80+
}
81+
82+
// unwrapDebugDriver extracts the underlying driver and log function from a DebugDriver.
83+
func unwrapDebugDriver(drv dialect.Driver) (dialect.Driver, func(context.Context, ...any)) {
84+
if debugDriver, ok := drv.(*dialect.DebugDriver); ok {
85+
return debugDriver.Driver, debugDriver.Log()
4886
}
49-
return nil
87+
return drv, nil
5088
}
5189

5290
// RetryConfig holds retry configuration for sqlgraph operations.

dialect/sql/retry_debug.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// Copyright 2019-present Facebook Inc. All rights reserved.
2+
// This source code is licensed under the Apache 2.0 license found
3+
// in the LICENSE file in the root directory of this source tree.
4+
5+
package sql
6+
7+
import (
8+
"context"
9+
10+
"entgo.io/ent/dialect"
11+
)
12+
13+
// debugRetryExecutor wraps a RetryExecutor to preserve debug logging.
14+
type debugRetryExecutor struct {
15+
RetryExecutor
16+
log func(context.Context, ...any)
17+
}
18+
19+
// Do executes the operation with debug logging preserved.
20+
func (d *debugRetryExecutor) Do(
21+
ctx context.Context,
22+
fn func(ctx context.Context, drv dialect.Driver) error,
23+
opts ...any,
24+
) error {
25+
return d.RetryExecutor.Do(
26+
ctx,
27+
func(ctx context.Context, drv dialect.Driver) error {
28+
return fn(ctx, dialect.DebugWithContext(drv, d.log))
29+
},
30+
opts...,
31+
)
32+
}
33+
34+
// DoTx executes the operation within a transaction with debug logging preserved.
35+
func (d *debugRetryExecutor) DoTx(
36+
ctx context.Context,
37+
fn func(ctx context.Context, drv dialect.Driver) error,
38+
opts ...any,
39+
) error {
40+
return d.RetryExecutor.DoTx(
41+
ctx,
42+
func(ctx context.Context, drv dialect.Driver) error {
43+
return fn(ctx, dialect.DebugWithContext(drv, d.log))
44+
},
45+
opts...,
46+
)
47+
}
Lines changed: 22 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,31 +2,25 @@
22
// This source code is licensed under the Apache 2.0 license found
33
// in the LICENSE file in the root directory of this source tree.
44

5-
package ydb
5+
package sql
66

77
import (
88
"context"
99
"database/sql"
1010

1111
"entgo.io/ent/dialect"
12-
entSql "entgo.io/ent/dialect/sql"
1312
"github.com/ydb-platform/ydb-go-sdk/v3/retry"
1413
)
1514

16-
// RetryExecutor implements sqlgraph.RetryExecutor for YDB
17-
type RetryExecutor struct {
15+
// YDBRetryExecutor implements sqlgraph.YDBRetryExecutor for YDB
16+
type YDBRetryExecutor struct {
1817
db *sql.DB
1918
}
2019

21-
// NewRetryExecutor creates a new RetryExecutor with the given database connection
22-
func NewRetryExecutor(db *sql.DB) *RetryExecutor {
23-
return &RetryExecutor{db: db}
24-
}
25-
2620
// Do executes a read-only operation with retry support.
2721
// It uses ydb-go-sdk's retry.Do which handles YDB-specific retryable errors.
2822
// Options should be created using retry.WithIdempotent(), retry.WithLabel(), etc.
29-
func (r *RetryExecutor) Do(
23+
func (r *YDBRetryExecutor) Do(
3024
ctx context.Context,
3125
fn func(ctx context.Context, drv dialect.Driver) error,
3226
opts ...any,
@@ -35,7 +29,7 @@ func (r *RetryExecutor) Do(
3529
ctx,
3630
r.db,
3731
func(ctx context.Context, conn *sql.Conn) error {
38-
return fn(ctx, NewRetryDriver(conn))
32+
return fn(ctx, newConnRetryDriver(conn))
3933
},
4034
retry.WithDoRetryOptions(toRetryOptions(opts)...),
4135
)
@@ -44,7 +38,7 @@ func (r *RetryExecutor) Do(
4438
// DoTx executes the operation within a transaction with retry support.
4539
// It uses ydb-go-sdk's retry.DoTx which handles YDB-specific retryable errors.
4640
// Options should be created using retry.WithIdempotent(), retry.WithLabel(), etc.
47-
func (r *RetryExecutor) DoTx(
41+
func (r *YDBRetryExecutor) DoTx(
4842
ctx context.Context,
4943
fn func(ctx context.Context, drv dialect.Driver) error,
5044
opts ...any,
@@ -53,7 +47,7 @@ func (r *RetryExecutor) DoTx(
5347
ctx,
5448
r.db,
5549
func(ctx context.Context, tx *sql.Tx) error {
56-
return fn(ctx, NewTxRetryDriver(tx))
50+
return fn(ctx, newTxRetryDriver(tx))
5751
},
5852
retry.WithDoTxRetryOptions(toRetryOptions(opts)...),
5953
)
@@ -70,41 +64,41 @@ func toRetryOptions(opts []any) []retry.Option {
7064
return retryOpts
7165
}
7266

73-
// RetryDriver is designed for use only in sqlgraph,
67+
// ydbRetryDriver is designed for use only in sqlgraph,
7468
// specifically - in retry.DoTx callbacks
75-
type RetryDriver struct {
76-
entSql.Conn
69+
type ydbRetryDriver struct {
70+
Conn
7771
}
7872

79-
var _ dialect.Driver = (*RetryDriver)(nil)
73+
var _ dialect.Driver = (*ydbRetryDriver)(nil)
8074

81-
// NewTxRetryDriver creates a new RetryDriver from a transaction.
82-
func NewTxRetryDriver(tx *sql.Tx) *RetryDriver {
83-
return &RetryDriver{
84-
Conn: entSql.Conn{ExecQuerier: tx},
75+
// newConnRetryDriver creates a new RetryDriver from a database connection.
76+
func newConnRetryDriver(conn *sql.Conn) *ydbRetryDriver {
77+
return &ydbRetryDriver{
78+
Conn: Conn{ExecQuerier: conn},
8579
}
8680
}
8781

88-
// NewRetryDriver creates a new RetryDriver from a database connection.
89-
func NewRetryDriver(conn *sql.Conn) *RetryDriver {
90-
return &RetryDriver{
91-
Conn: entSql.Conn{ExecQuerier: conn},
82+
// newTxRetryDriver creates a new RetryDriver from a transaction.
83+
func newTxRetryDriver(tx *sql.Tx) *ydbRetryDriver {
84+
return &ydbRetryDriver{
85+
Conn: Conn{ExecQuerier: tx},
9286
}
9387
}
9488

9589
// sqlgraph creates nested transactions in several methods.
9690
// But YDB doesnt support nested transactions.
9791
// Therefore, this methods returns no-op tx
98-
func (d *RetryDriver) Tx(ctx context.Context) (dialect.Tx, error) {
92+
func (d *ydbRetryDriver) Tx(ctx context.Context) (dialect.Tx, error) {
9993
return dialect.NopTx(d), nil
10094
}
10195

10296
// Close is a no-op for RetryDriver since retry.DoTx manages the transaction lifecycle.
103-
func (d *RetryDriver) Close() error {
97+
func (d *ydbRetryDriver) Close() error {
10498
return nil
10599
}
106100

107101
// Dialect returns the YDB dialect name.
108-
func (d *RetryDriver) Dialect() string {
102+
func (d *ydbRetryDriver) Dialect() string {
109103
return dialect.YDB
110104
}

0 commit comments

Comments
 (0)