Add a Benchmark comparing us with the sqlx package

This commit is contained in:
Vinícius Garcia 2021-01-22 21:25:14 -03:00
parent f655576bb3
commit e1e711dc91
6 changed files with 265 additions and 1 deletions

View File

@ -3,9 +3,14 @@ path=./...
GOPATH=$(shell go env GOPATH) GOPATH=$(shell go env GOPATH)
TIME=1s
test: setup test: setup
$(GOPATH)/bin/richgo test $(path) $(args) $(GOPATH)/bin/richgo test $(path) $(args)
bench:
go test -bench=. -benchtime=$(TIME)
lint: setup lint: setup
@$(GOPATH)/bin/golint -set_exit_status -min_confidence 0.9 $(path) $(args) @$(GOPATH)/bin/golint -set_exit_status -min_confidence 0.9 $(path) $(args)
@go vet $(path) $(args) @go vet $(path) $(args)

View File

@ -21,6 +21,8 @@ Currently we only support 2 Drivers:
### Why KissORM? ### Why KissORM?
> Note: If you want numbers see our Benchmark section below
KissORM was created to fill a hole between the complexity KissORM was created to fill a hole between the complexity
we find in the tools I've seen so far, namely: we find in the tools I've seen so far, namely:
@ -200,6 +202,26 @@ This library has a few helper functions for helping your tests:
If you want to see examples (we have examples for all the public functions) just If you want to see examples (we have examples for all the public functions) just
read the example tests available on our [example service](./examples/example_service) read the example tests available on our [example service](./examples/example_service)
### Benchmark Comparison
The benchmark is not bad, as far the code is in average as fast as sqlx:
```bash
$ make bench TIME=3s
go test -bench=. -benchtime=3s
goos: linux
goarch: amd64
pkg: github.com/vingarcia/kissorm
BenchmarkInsert/kissorm-setup/insert-one-4 4306 880132 ns/op
BenchmarkInsert/sqlx-setup/insert-one-4 4573 792488 ns/op
BenchmarkQuery/kissorm-setup/single-row-4 10000 315328 ns/op
BenchmarkQuery/kissorm-setup/multiple-rows-4 9288 388538 ns/op
BenchmarkQuery/sqlx-setup/single-row-4 10000 323424 ns/op
BenchmarkQuery/sqlx-setup/multiple-rows-4 10000 338570 ns/op
PASS
ok github.com/vingarcia/kissorm 21.740s
```
### TODO List ### TODO List
- Allow the ID field to have a different name - Allow the ID field to have a different name

232
benchmark_test.go Normal file
View File

@ -0,0 +1,232 @@
package kissorm_test
import (
"context"
"database/sql"
"fmt"
"strconv"
"testing"
"github.com/jmoiron/sqlx"
_ "github.com/lib/pq"
"github.com/vingarcia/kissorm"
)
func BenchmarkInsert(b *testing.B) {
ctx := context.Background()
driver := "postgres"
connStr := "host=localhost port=5432 user=postgres password=postgres dbname=kissorm sslmode=disable"
kissormDB, err := kissorm.New(driver, connStr, 1, "users")
if err != nil {
b.FailNow()
}
type User struct {
ID int `kissorm:"id" db:"id"`
Name string `kissorm:"name" db:"name"`
Age int `kissorm:"age" db:"age"`
}
b.Run("kissorm-setup", func(b *testing.B) {
err := recreateTable(connStr)
if err != nil {
b.Fatalf("error creating table: %s", err.Error())
}
b.Run("insert-one", func(b *testing.B) {
for i := 0; i < b.N; i++ {
err := kissormDB.Insert(ctx, &User{
Name: strconv.Itoa(i),
Age: i,
})
if err != nil {
b.Fatalf("insert error: %s", err.Error())
}
}
})
})
sqlxDB, err := sqlx.Open(driver, connStr)
sqlxDB.SetMaxOpenConns(1)
b.Run("sqlx-setup", func(b *testing.B) {
err := recreateTable(connStr)
if err != nil {
b.Fatalf("error creating table: %s", err.Error())
}
query := `INSERT INTO users(name, age) VALUES (:name, :age) RETURNING id`
b.Run("insert-one", func(b *testing.B) {
for i := 0; i < b.N; i++ {
user := User{
Name: strconv.Itoa(i),
Age: i,
}
rows, err := sqlxDB.NamedQuery(query, user)
if err != nil {
b.Fatalf("insert error: %s", err.Error())
}
if !rows.Next() {
b.Fatalf("missing id from inserted record")
}
rows.Scan(&user.ID)
err = rows.Close()
if err != nil {
b.Fatalf("error closing rows")
}
}
})
})
}
func BenchmarkQuery(b *testing.B) {
ctx := context.Background()
driver := "postgres"
connStr := "host=localhost port=5432 user=postgres password=postgres dbname=kissorm sslmode=disable"
kissormDB, err := kissorm.New(driver, connStr, 1, "users")
if err != nil {
b.FailNow()
}
type User struct {
ID int `kissorm:"id" db:"id"`
Name string `kissorm:"name" db:"name"`
Age int `kissorm:"age" db:"age"`
}
b.Run("kissorm-setup", func(b *testing.B) {
err := recreateTable(connStr)
if err != nil {
b.Fatalf("error creating table: %s", err.Error())
}
err = insertUsers(connStr, 100)
if err != nil {
b.Fatalf("error inserting users: %s", err.Error())
}
b.Run("single-row", func(b *testing.B) {
for i := 0; i < b.N; i++ {
var user User
err := kissormDB.QueryOne(ctx, &user, `SELECT * FROM users OFFSET $1 LIMIT 1`, i%100)
if err != nil {
b.Fatalf("query error: %s", err.Error())
}
}
})
b.Run("multiple-rows", func(b *testing.B) {
for i := 0; i < b.N; i++ {
var users []User
err := kissormDB.Query(ctx, &users, `SELECT * FROM users OFFSET $1 LIMIT 10`, i%90)
if err != nil {
b.Fatalf("query error: %s", err.Error())
}
if len(users) < 10 {
b.Fatalf("expected 10 scanned users, but got: %d", len(users))
}
}
})
})
sqlxDB, err := sqlx.Open(driver, connStr)
sqlxDB.SetMaxOpenConns(1)
b.Run("sqlx-setup", func(b *testing.B) {
err := recreateTable(connStr)
if err != nil {
b.Fatalf("error creating table: %s", err.Error())
}
err = insertUsers(connStr, 100)
if err != nil {
b.Fatalf("error inserting users: %s", err.Error())
}
b.Run("single-row", func(b *testing.B) {
for i := 0; i < b.N; i++ {
var user User
rows, err := sqlxDB.Queryx(`SELECT * FROM users OFFSET $1 LIMIT 1`, i%100)
if err != nil {
b.Fatalf("insert error: %s", err.Error())
}
if !rows.Next() {
b.Fatalf("missing user from inserted record, offset: %d", i%100)
}
rows.StructScan(&user)
err = rows.Close()
if err != nil {
b.Fatalf("error closing rows")
}
}
})
b.Run("multiple-rows", func(b *testing.B) {
for i := 0; i < b.N; i++ {
var users []User
rows, err := sqlxDB.Queryx(`SELECT * FROM users OFFSET $1 LIMIT 10`, i%90)
if err != nil {
b.Fatalf("insert error: %s", err.Error())
}
for j := 0; j < 10; j++ {
if !rows.Next() {
b.Fatalf("missing user from inserted record, offset: %d", i%100)
}
var user User
rows.StructScan(&user)
users = append(users, user)
}
if len(users) < 10 {
b.Fatalf("expected 10 scanned users, but got: %d", len(users))
}
err = rows.Close()
if err != nil {
b.Fatalf("error closing rows")
}
}
})
})
}
func recreateTable(connStr string) error {
db, err := sql.Open("postgres", connStr)
if err != nil {
return err
}
defer db.Close()
db.Exec(`DROP TABLE users`)
_, err = db.Exec(`CREATE TABLE users (
id serial PRIMARY KEY,
age INT,
name VARCHAR(50)
)`)
if err != nil {
return fmt.Errorf("failed to create new users table: %s", err.Error())
}
return nil
}
func insertUsers(connStr string, numUsers int) error {
db, err := sql.Open("postgres", connStr)
if err != nil {
return err
}
defer db.Close()
for i := 0; i < numUsers; i++ {
_, err = db.Exec(`INSERT INTO users (name, age) VALUES ($1, $2)`, strconv.Itoa(i), i)
if err != nil {
return fmt.Errorf("failed to insert new user: %s", err.Error())
}
}
return nil
}

1
go.mod
View File

@ -5,6 +5,7 @@ go 1.14
require ( require (
github.com/ditointernet/go-assert v0.0.0-20200120164340-9e13125a7018 github.com/ditointernet/go-assert v0.0.0-20200120164340-9e13125a7018
github.com/golang/mock v1.4.4 github.com/golang/mock v1.4.4
github.com/jmoiron/sqlx v1.2.0
github.com/lib/pq v1.1.1 github.com/lib/pq v1.1.1
github.com/mattn/go-sqlite3 v1.14.6 github.com/mattn/go-sqlite3 v1.14.6
github.com/pkg/errors v0.9.1 github.com/pkg/errors v0.9.1

5
go.sum
View File

@ -4,11 +4,16 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/ditointernet/go-assert v0.0.0-20200120164340-9e13125a7018 h1:QsFkVafcKOaZoAB4WcyUHdkPbwh+VYwZgYJb/rU6EIM= github.com/ditointernet/go-assert v0.0.0-20200120164340-9e13125a7018 h1:QsFkVafcKOaZoAB4WcyUHdkPbwh+VYwZgYJb/rU6EIM=
github.com/ditointernet/go-assert v0.0.0-20200120164340-9e13125a7018/go.mod h1:5C3SWkut69TSdkerzRDxXMRM5x73PGWNcRLe/xKjXhs= github.com/ditointernet/go-assert v0.0.0-20200120164340-9e13125a7018/go.mod h1:5C3SWkut69TSdkerzRDxXMRM5x73PGWNcRLe/xKjXhs=
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc= github.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc=
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/jmoiron/sqlx v1.2.0 h1:41Ip0zITnmWNR/vHV+S4m+VoUivnWY5E4OJfLZjCJMA=
github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.1.1 h1:sJZmqHoEaY7f+NPP8pgLB/WxulyR3fewgCM2qaSlBb4= github.com/lib/pq v1.1.1 h1:sJZmqHoEaY7f+NPP8pgLB/WxulyR3fewgCM2qaSlBb4=
github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg= github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg=
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=

View File

@ -844,7 +844,6 @@ func TestTransaction(t *testing.T) {
_ = c.Insert(ctx, &u2) _ = c.Insert(ctx, &u2)
err = c.Transaction(ctx, func(db ORMProvider) error { err = c.Transaction(ctx, func(db ORMProvider) error {
fmt.Printf("received db client: %#v\n", db)
err = db.Insert(ctx, &User{Name: "User3"}) err = db.Insert(ctx, &User{Name: "User3"})
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
err = db.Insert(ctx, &User{Name: "User4"}) err = db.Insert(ctx, &User{Name: "User4"})