Skip to content

Commit b8e0d8d

Browse files
committed
Simple benchmark utility
1 parent d192d7b commit b8e0d8d

File tree

9 files changed

+487
-155
lines changed

9 files changed

+487
-155
lines changed

backend/redis/redis_test.go

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,6 @@ const (
2121
password = "RedisPassw0rd"
2222
)
2323

24-
func Benchmark_RedisBackend(b *testing.B) {
25-
if testing.Short() {
26-
b.Skip()
27-
}
28-
29-
client := getClient()
30-
setup := getCreateBackend(client, true)
31-
32-
test.SimpleWorkflowBenchmark(b, setup, nil)
33-
}
34-
3524
func Test_RedisBackend(t *testing.T) {
3625
if testing.Short() {
3726
t.Skip()

backend/test/bench.go

Lines changed: 0 additions & 144 deletions
This file was deleted.

bench/README.md

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# Simple benchmark utility
2+
3+
The benchmark runs a "workflow tree" made up of root, mid and leaf workflows. The root workflows start mid workflows, which in turn start leaf workflows. The leaf workflows execute activities.
4+
5+
Options to influence the number of workflows, sub-workflows, and activities:
6+
7+
- `-runs` Number of root workflows to start
8+
- `-fanout` Number of child "mid" workflows to execute per root/mid workflow
9+
- `-leaffanout` Number of leaf workflows to execute per mid workflow
10+
- `-depth` Depth of mid workflows
11+
12+
```
13+
┌──────┐ ──────┐
14+
│ Root │ │
15+
└──┬───┘ │
16+
│ │
17+
fanout ────► ┌──────┴───────┐ │
18+
│ │ │
19+
┌──┴──┐ ┌──┴──┐ │ depth
20+
│ Mid │ │ Mid │ │
21+
└──┬──┘ └──┬──┘ │
22+
│ │ │
23+
leaffanout ───► ┌─────┴──┐ ┌──┴─────┐ │
24+
│ │ │ │ │
25+
┌───┴┐ ┌──┴─┐ ┌──┴─┐ ┌──┴─┐ │
26+
│Leaf│ │Leaf│ │Leaf│ │Leaf│ │
27+
└────┘ └────┘ └────┘ └────┘ ───┘
28+
29+
┌────────────┐ ┌───┐
30+
│ Activities │ ... │ │
31+
└────────────┘ └───┘
32+
```
33+
34+
## Run
35+
36+
```shell
37+
$ go run .
38+
```
39+
40+
### Help
41+
42+
```shell
43+
$ go run .h -h
44+
-activities int
45+
Number of activities to execute per leaf workflow (default 2)
46+
-backend string
47+
Backend to use. Supported backends are:
48+
- redis
49+
- mysql
50+
- sqlite
51+
(default "redis")
52+
-cachesize int
53+
Size of the workflow executor cache (default 128)
54+
-depth int
55+
Depth of mid workflows (default 2)
56+
-fanout int
57+
Number of child workflows to execute per root/mid workflow (default 2)
58+
-format string
59+
Output format. Supported formats are:
60+
- text
61+
- csv
62+
(default "text")
63+
-leaffanout int
64+
Number of leaf workflows to execute per mid workflow (default 2)
65+
-resultsize int
66+
Size of activity result payload in bytes (default 100)
67+
-runs int
68+
Number of root workflows to start (default 1)
69+
-scenario string
70+
Scenario to run. Support scenarios are:
71+
- basic
72+
(default "basic")
73+
-timeout duration
74+
Timeout for the benchmark run (default 30s)
75+
```

bench/bench.sqlite-shm

64 KB
Binary file not shown.

bench/bench.sqlite-wal

19 MB
Binary file not shown.

bench/main.go

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"database/sql"
6+
"flag"
7+
"fmt"
8+
"log"
9+
"os"
10+
"sync"
11+
"time"
12+
13+
"github.com/cschleiden/go-workflows/backend"
14+
"github.com/cschleiden/go-workflows/backend/mysql"
15+
"github.com/cschleiden/go-workflows/backend/redis"
16+
"github.com/cschleiden/go-workflows/backend/sqlite"
17+
"github.com/cschleiden/go-workflows/client"
18+
"github.com/cschleiden/go-workflows/worker"
19+
redisv8 "github.com/go-redis/redis/v8"
20+
)
21+
22+
var b = flag.String("backend", "redis", "Backend to use. Supported backends are:\n- redis\n- mysql\n- sqlite\n")
23+
var timeout = flag.Duration("timeout", time.Second*30, "Timeout for the benchmark run")
24+
var scenario = flag.String("scenario", "basic", "Scenario to run. Support scenarios are:\n- basic\n")
25+
var runs = flag.Int("runs", 1, "Number of root workflows to start")
26+
var depth = flag.Int("depth", 2, "Depth of mid workflows")
27+
var fanOut = flag.Int("fanout", 2, "Number of child workflows to execute per root/mid workflow")
28+
var leafFanOut = flag.Int("leaffanout", 2, "Number of leaf workflows to execute per mid workflow")
29+
var activities = flag.Int("activities", 2, "Number of activities to execute per leaf workflow")
30+
var resultSize = flag.Int("resultsize", 100, "Size of activity result payload in bytes")
31+
var format = flag.String("format", "text", "Output format. Supported formats are:\n- text\n- csv\n")
32+
var cacheSize = flag.Int("cachesize", 128, "Size of the workflow executor cache")
33+
34+
func main() {
35+
flag.Parse()
36+
37+
ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(*timeout).Add(time.Second*5))
38+
defer cancel()
39+
40+
mm := newMemMetrics()
41+
ba := getBackend(*b, backend.WithLogger(&nullLogger{}), backend.WithMetrics(mm))
42+
43+
wo := worker.DefaultWorkerOptions
44+
wo.WorkflowExecutorCacheSize = *cacheSize
45+
w := worker.New(ba, &wo)
46+
47+
w.RegisterWorkflow(Root)
48+
w.RegisterWorkflow(Mid)
49+
w.RegisterWorkflow(Leaf)
50+
w.RegisterActivity(Activity)
51+
52+
if err := w.Start(ctx); err != nil {
53+
panic(err)
54+
}
55+
56+
c := client.New(ba)
57+
58+
start := time.Now()
59+
wg := sync.WaitGroup{}
60+
for i := 0; i < *runs; i++ {
61+
i, err := c.CreateWorkflowInstance(ctx, client.WorkflowInstanceOptions{
62+
InstanceID: fmt.Sprintf("root-%d", i),
63+
}, Root, &MidInput{
64+
FanOut: *fanOut,
65+
Depth: *depth,
66+
67+
LeafFanOut: *leafFanOut,
68+
69+
Activities: *activities,
70+
PayloadSizeBytes: *resultSize,
71+
})
72+
if err != nil {
73+
panic(err)
74+
}
75+
76+
wg.Add(1)
77+
go func() {
78+
defer wg.Done()
79+
err := c.WaitForWorkflowInstance(ctx, i, *timeout)
80+
if err != nil {
81+
panic(fmt.Errorf("Workflow instance %s failed: %w", i.InstanceID, err))
82+
}
83+
}()
84+
}
85+
86+
wg.Wait()
87+
88+
end := time.Now()
89+
90+
switch *format {
91+
case "text":
92+
log.Println("Ran", *runs, "root workflows in", end.Sub(start).Seconds(), "seconds")
93+
mm.Print()
94+
95+
case "csv":
96+
fmt.Printf(
97+
"%s,%v,%s,%d,%d,%d,%d,%d,%d\n",
98+
*b, end.Sub(start).Seconds(), *scenario, *runs, *depth, *fanOut, *leafFanOut, *activities, *resultSize)
99+
}
100+
}
101+
102+
func getBackend(b string, opt ...backend.BackendOption) backend.Backend {
103+
switch b {
104+
case "memory":
105+
return sqlite.NewInMemoryBackend(opt...)
106+
107+
case "sqlite":
108+
os.Remove("bench.sqlite")
109+
110+
return sqlite.NewSqliteBackend("bench.sqlite", opt...)
111+
112+
case "mysql":
113+
db, err := sql.Open("mysql", fmt.Sprintf("%s:%s@/?parseTime=true&interpolateParams=true", "root", "root"))
114+
if err != nil {
115+
panic(err)
116+
}
117+
118+
if _, err := db.Exec("DROP DATABASE IF EXISTS bench"); err != nil {
119+
panic(fmt.Errorf("dropping database: %w", err))
120+
}
121+
122+
if _, err := db.Exec("CREATE DATABASE bench"); err != nil {
123+
panic(fmt.Errorf("creating database: %w", err))
124+
}
125+
126+
if err := db.Close(); err != nil {
127+
panic(err)
128+
}
129+
130+
return mysql.NewMysqlBackend("localhost", 3306, "root", "root", "bench", opt...)
131+
132+
case "redis":
133+
rclient := redisv8.NewUniversalClient(&redisv8.UniversalOptions{
134+
Addrs: []string{"localhost:6379"},
135+
Username: "",
136+
Password: "RedisPassw0rd",
137+
DB: 0,
138+
WriteTimeout: time.Second * 30,
139+
ReadTimeout: time.Second * 30,
140+
})
141+
142+
rclient.FlushAll(context.Background()).Result()
143+
144+
b, err := redis.NewRedisBackend(rclient, redis.WithBackendOptions(opt...))
145+
if err != nil {
146+
panic(err)
147+
}
148+
149+
return b
150+
151+
default:
152+
panic("unknown backend " + b)
153+
}
154+
}

0 commit comments

Comments
 (0)