Skip to content

Commit fe8af37

Browse files
committed
added transactions example for database/sql driver
1 parent 18d177b commit fe8af37

File tree

2 files changed

+207
-0
lines changed

2 files changed

+207
-0
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Using transactions over databas/sql driver for YDB
2+
3+
## Runing code snippet
4+
```bash
5+
go run -ydb="grpcs://endpoint/?database=database"
6+
```
7+
8+
Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"database/sql"
6+
"flag"
7+
"fmt"
8+
"os"
9+
"strings"
10+
11+
ydb "github.com/ydb-platform/ydb-go-sdk/v3"
12+
"github.com/ydb-platform/ydb-go-sdk/v3/retry"
13+
)
14+
15+
var dsn string
16+
17+
func init() { //nolint:gochecknoinits
18+
required := []string{"ydb"}
19+
flagSet := flag.NewFlagSet(os.Args[0], flag.ExitOnError)
20+
flagSet.Usage = func() {
21+
out := flagSet.Output()
22+
_, _ = fmt.Fprintf(out, "Usage:\n%s [options]\n", os.Args[0])
23+
_, _ = fmt.Fprintf(out, "\nOptions:\n")
24+
flagSet.PrintDefaults()
25+
}
26+
flagSet.StringVar(&dsn,
27+
"ydb", "",
28+
"YDB connection string",
29+
)
30+
if err := flagSet.Parse(os.Args[1:]); err != nil {
31+
flagSet.Usage()
32+
os.Exit(1)
33+
}
34+
flagSet.Visit(func(f *flag.Flag) {
35+
for i, arg := range required {
36+
if arg == f.Name {
37+
required = append(required[:i], required[i+1:]...)
38+
}
39+
}
40+
})
41+
if len(required) > 0 {
42+
fmt.Printf("\nSome required options not defined: %v\n\n", required)
43+
flagSet.Usage()
44+
os.Exit(1)
45+
}
46+
}
47+
48+
func main() {
49+
ctx, cancel := context.WithCancel(context.Background())
50+
defer cancel()
51+
nativeDriver, err := ydb.Open(ctx, dsn)
52+
if err != nil {
53+
panic(err)
54+
}
55+
defer nativeDriver.Close(ctx)
56+
57+
connector, err := ydb.Connector(nativeDriver,
58+
ydb.WithAutoDeclare(),
59+
ydb.WithQueryService(true),
60+
)
61+
if err != nil {
62+
panic(err)
63+
}
64+
65+
db := sql.OpenDB(connector)
66+
defer db.Close()
67+
68+
if words, err := txWithoutRetries(ctx, db); err != nil {
69+
panic(err)
70+
} else {
71+
fmt.Printf("SUCCESS: %q\n", strings.Join(words, " "))
72+
}
73+
74+
if words, err := txWithRetries(ctx, db); err != nil {
75+
panic(err)
76+
} else {
77+
fmt.Printf("SUCCESS: %q\n", strings.Join(words, " "))
78+
}
79+
}
80+
81+
func txWithoutRetries(ctx context.Context, db *sql.DB) (words []string, _ error) {
82+
tx, err := db.BeginTx(ctx, &sql.TxOptions{
83+
Isolation: sql.LevelDefault,
84+
ReadOnly: false,
85+
})
86+
if err != nil {
87+
return nil, err
88+
}
89+
defer tx.Rollback()
90+
91+
row := tx.QueryRowContext(ctx, "SELECT 'execute';")
92+
93+
var word string
94+
err = row.Scan(&word)
95+
if err != nil {
96+
return nil, err
97+
}
98+
99+
words = append(words, word)
100+
101+
rows, err := tx.QueryContext(ctx, `
102+
SELECT w, ord FROM (
103+
SELECT $word1 AS w, 1 AS ord
104+
UNION
105+
SELECT $word2 AS w, 2 AS ord
106+
UNION
107+
SELECT 'without'u AS w, 3 AS ord
108+
UNION
109+
SELECT $word3 AS w, 4 AS ord
110+
)
111+
ORDER BY ord;
112+
`,
113+
sql.Named("word1", "in"),
114+
sql.Named("word2", "transaction"),
115+
sql.Named("word3", "retries"),
116+
)
117+
if err != nil {
118+
return nil, err
119+
}
120+
121+
for rows.Next() {
122+
var (
123+
word string
124+
ord int
125+
)
126+
if err = rows.Scan(&word, &ord); err != nil {
127+
return nil, err
128+
}
129+
words = append(words, word)
130+
}
131+
132+
if err = rows.Err(); err != nil {
133+
return nil, err
134+
}
135+
136+
if err = tx.Commit(); err != nil {
137+
return nil, err
138+
}
139+
140+
return words, nil
141+
}
142+
143+
func txWithRetries(ctx context.Context, db *sql.DB) (words []string, _ error) {
144+
err := retry.DoTx(ctx, db, func(ctx context.Context, tx *sql.Tx) error {
145+
words = words[:0] // empty for new retry attempt
146+
147+
row := tx.QueryRowContext(ctx, "SELECT 'execute';")
148+
149+
var word string
150+
err := row.Scan(&word)
151+
if err != nil {
152+
return err
153+
}
154+
155+
words = append(words, word)
156+
157+
rows, err := tx.QueryContext(ctx, `
158+
SELECT w, ord FROM (
159+
SELECT $word1 AS w, 1 AS ord
160+
UNION
161+
SELECT $word2 AS w, 2 AS ord
162+
UNION
163+
SELECT 'with'u AS w, 3 AS ord
164+
UNION
165+
SELECT $word3 AS w, 4 AS ord
166+
UNION
167+
SELECT 'using'u AS w, 5 AS ord
168+
UNION
169+
SELECT 'retry.DoTx'u AS w, 6 AS ord
170+
)
171+
ORDER BY ord;
172+
`,
173+
sql.Named("word1", "in"),
174+
sql.Named("word2", "transaction"),
175+
sql.Named("word3", "retries"),
176+
)
177+
if err != nil {
178+
return err
179+
}
180+
181+
for rows.Next() {
182+
var (
183+
word string
184+
ord int
185+
)
186+
if err = rows.Scan(&word, &ord); err != nil {
187+
return err
188+
}
189+
words = append(words, word)
190+
}
191+
192+
return rows.Err()
193+
})
194+
if err != nil {
195+
return nil, err
196+
}
197+
198+
return words, nil
199+
}

0 commit comments

Comments
 (0)