Skip to content

Commit 3daa81e

Browse files
committed
✨ add transaction handler
1 parent 275a5c8 commit 3daa81e

File tree

4 files changed

+151
-2
lines changed

4 files changed

+151
-2
lines changed

README.md

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,4 +139,72 @@ func main() {
139139
}
140140
}
141141

142+
```
143+
144+
## redis with transaction operation
145+
146+
```golang
147+
package main
148+
149+
import (
150+
"context"
151+
"fmt"
152+
"log/slog"
153+
"os"
154+
155+
"github.com/leetcode-golang-classroom/golang-sample-with-redis/internal/config"
156+
"github.com/leetcode-golang-classroom/golang-sample-with-redis/internal/logger"
157+
myredis "github.com/leetcode-golang-classroom/golang-sample-with-redis/internal/redis"
158+
"github.com/leetcode-golang-classroom/golang-sample-with-redis/internal/util"
159+
"github.com/redis/go-redis/v9"
160+
)
161+
162+
func main() {
163+
jsonLogger := slog.New(slog.NewJSONHandler(
164+
os.Stdout, &slog.HandlerOptions{
165+
AddSource: true,
166+
},
167+
))
168+
ctx := logger.CtxWithLogger(context.Background(), jsonLogger)
169+
config.Init(ctx)
170+
redisURL := config.AppCfg.RedisUrl
171+
172+
rdb, err := myredis.New(redisURL)
173+
if err != nil {
174+
util.FailOnError(ctx, err, fmt.Sprintf("failed to connect to %s\n", redisURL))
175+
}
176+
defer rdb.Close()
177+
_, err = rdb.Ping(ctx)
178+
if err != nil {
179+
util.FailOnError(ctx, err, fmt.Sprintf("failed to ping to %s\n", redisURL))
180+
}
181+
// try 10 times
182+
for i := 0; i < 10; i++ {
183+
err = rdb.Watch(ctx, func(tx *redis.Tx) error {
184+
pipe := tx.Pipeline()
185+
err = pipe.IncrBy(ctx, "p1", 100).Err()
186+
if err != nil {
187+
return err
188+
}
189+
err = pipe.DecrBy(ctx, "p0", 100).Err()
190+
if err != nil {
191+
return err
192+
}
193+
_, err = pipe.Exec(ctx)
194+
return err
195+
}, "p0")
196+
197+
if err == nil {
198+
jsonLogger.Info("transaction execution success")
199+
break
200+
} else if err == redis.TxFailedErr {
201+
jsonLogger.Info("transaction execution failed", slog.Int("i", i))
202+
continue
203+
} else {
204+
util.FailOnError(ctx, err, "failed")
205+
}
206+
}
207+
208+
}
209+
142210
```

cmd/pipelined-sample/main.go

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"log/slog"
7+
"os"
8+
9+
"github.com/leetcode-golang-classroom/golang-sample-with-redis/internal/config"
10+
"github.com/leetcode-golang-classroom/golang-sample-with-redis/internal/logger"
11+
myredis "github.com/leetcode-golang-classroom/golang-sample-with-redis/internal/redis"
12+
"github.com/leetcode-golang-classroom/golang-sample-with-redis/internal/util"
13+
"github.com/redis/go-redis/v9"
14+
)
15+
16+
func main() {
17+
jsonLogger := slog.New(slog.NewJSONHandler(
18+
os.Stdout, &slog.HandlerOptions{
19+
AddSource: true,
20+
},
21+
))
22+
ctx := logger.CtxWithLogger(context.Background(), jsonLogger)
23+
config.Init(ctx)
24+
redisURL := config.AppCfg.RedisUrl
25+
26+
rdb, err := myredis.New(redisURL)
27+
if err != nil {
28+
util.FailOnError(ctx, err, fmt.Sprintf("failed to connect to %s\n", redisURL))
29+
}
30+
defer rdb.Close()
31+
_, err = rdb.Ping(ctx)
32+
if err != nil {
33+
util.FailOnError(ctx, err, fmt.Sprintf("failed to ping to %s\n", redisURL))
34+
}
35+
// try 10 times
36+
for i := 0; i < 10; i++ {
37+
err = rdb.Watch(ctx, func(tx *redis.Tx) error {
38+
pipe := tx.Pipeline()
39+
err = pipe.IncrBy(ctx, "p1", 100).Err()
40+
if err != nil {
41+
return err
42+
}
43+
err = pipe.DecrBy(ctx, "p0", 100).Err()
44+
if err != nil {
45+
return err
46+
}
47+
_, err = pipe.Exec(ctx)
48+
return err
49+
}, "p0")
50+
51+
if err == nil {
52+
jsonLogger.Info("transaction execution success")
53+
break
54+
} else if err == redis.TxFailedErr {
55+
jsonLogger.Info("transaction execution failed", slog.Int("i", i))
56+
continue
57+
} else {
58+
util.FailOnError(ctx, err, "failed")
59+
}
60+
}
61+
62+
}

internal/redis/redis.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,12 @@ func (rh *RedisHandler) Get(ctx context.Context, key string) (string, error) {
3737
return result, err
3838
}
3939

40-
// Pipelined -
40+
// Watch
41+
func (rh *RedisHandler) Watch(ctx context.Context, fn func(*redis.Tx) error, keys ...string) error {
42+
return rh.client.Watch(ctx, fn, keys...)
43+
}
44+
45+
// Pipelined - handle pipeline with transaction
4146
func (rh *RedisHandler) Pipelined(ctx context.Context, handler func(pipe redis.Pipeliner) error) ([]redis.Cmder, error) {
4247
return rh.client.Pipelined(ctx, handler)
4348
}

mage-tools/magefile.go

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,11 @@ func Build() error {
2929
if err != nil {
3030
return err
3131
}
32-
return sh.Run("go", "build", "-o", "./bin/pipeline-sample", "./cmd/pipeline-sample/main.go")
32+
err = sh.Run("go", "build", "-o", "./bin/pipeline-sample", "./cmd/pipeline-sample/main.go")
33+
if err != nil {
34+
return err
35+
}
36+
return sh.Run("go", "build", "-o", "./bin/pipelined-sample", "./cmd/pipelined-sample/main.go")
3337
}
3438

3539
// start the basic-sample
@@ -62,6 +66,16 @@ func LaunchPipelineSample() error {
6266
return nil
6367
}
6468

69+
// start the pipelined-sample
70+
func LaunchPipelinedSample() error {
71+
mg.Deps(Build)
72+
err := sh.RunV("./bin/pipelined-sample")
73+
if err != nil {
74+
return err
75+
}
76+
return nil
77+
}
78+
6579
// run the test
6680
func Test() error {
6781
err := sh.RunV("go", "test", "-v", "./...")

0 commit comments

Comments
 (0)