|
package sqlite_test |
|
|
|
import ( |
|
"database/sql" |
|
_ "embed" |
|
"fmt" |
|
"math/rand" |
|
"path" |
|
"runtime" |
|
sqlite "sqlite-bench-with-golang" |
|
"strconv" |
|
"testing" |
|
"time" |
|
|
|
_ "github.com/mattn/go-sqlite3" |
|
) |
|
|
|
func TestSQLiteBench(t *testing.T) { |
|
opTimes := 10000 |
|
|
|
queryVals := make([]int, 0) |
|
|
|
for ii := 0; ii < opTimes; ii++ { |
|
randVal := rand.Int31() |
|
queryVals = append(queryVals, int(randVal)) |
|
} |
|
|
|
writeDB, readDB := setupSQLitePool(t) |
|
|
|
insCh := make(chan int) |
|
for ii := 0; ii < opTimes; ii++ { |
|
go func(val int) { |
|
err := writeDB.InsertRecord(val, val) |
|
if err != nil { |
|
panic(err) |
|
} |
|
insCh <- val |
|
}(queryVals[ii]) |
|
} |
|
for ii := 0; ii < opTimes; ii++ { |
|
<-insCh |
|
} |
|
|
|
// shuffle query vals array elements |
|
rand.Shuffle(len(queryVals), func(i, j int) { queryVals[i], queryVals[j] = queryVals[j], queryVals[i] }) |
|
|
|
fmt.Println("records insertion done.") |
|
|
|
THREAD_NUM := 24 |
|
runtime.GOMAXPROCS(THREAD_NUM) |
|
|
|
ch := make(chan [2]int) |
|
|
|
runningThCnt := 0 |
|
allCnt := 0 |
|
commitedCnt := 0 |
|
abotedCnt := 0 |
|
|
|
startTime := time.Now() |
|
for ii := 0; ii < opTimes; ii++ { |
|
// wait last go routines finishes |
|
if ii == opTimes-1 { |
|
for runningThCnt > 0 { |
|
recvRslt := <-ch |
|
allCnt++ |
|
if recvRslt[1] == -1 { |
|
abotedCnt++ |
|
} else { |
|
commitedCnt++ |
|
if recvRslt[0] != recvRslt[1] { |
|
panic("failed to select val: " + strconv.Itoa(int(recvRslt[0]))) |
|
} |
|
} |
|
runningThCnt-- |
|
} |
|
break |
|
} |
|
|
|
// wait for keeping THREAD_NUM * 2 groroutine existing |
|
for runningThCnt >= THREAD_NUM*2 { |
|
recvRslt := <-ch |
|
runningThCnt-- |
|
allCnt++ |
|
if allCnt%500 == 0 { |
|
fmt.Printf(strconv.Itoa(allCnt) + " queries done\n") |
|
} |
|
if recvRslt[1] == -1 { |
|
abotedCnt++ |
|
} else { |
|
commitedCnt++ |
|
if recvRslt[0] != recvRslt[1] { |
|
panic("failed to select val: " + strconv.Itoa(int(recvRslt[0]))) |
|
} |
|
} |
|
} |
|
|
|
go func(queryVal int) { |
|
kv, err_ := readDB.SelectVal(queryVal) |
|
if err_ != nil { |
|
fmt.Println(err_) |
|
ch <- [2]int{queryVal, -1} |
|
return |
|
} |
|
|
|
gotValue := kv.V |
|
ch <- [2]int{queryVal, gotValue} |
|
}(queryVals[ii]) |
|
|
|
runningThCnt++ |
|
} |
|
|
|
fmt.Println("allCnt: " + strconv.Itoa(allCnt)) |
|
fmt.Println("abotedCnt: " + strconv.Itoa(abotedCnt)) |
|
fmt.Println("commitedCnt: " + strconv.Itoa(commitedCnt)) |
|
d := time.Since(startTime) |
|
fmt.Printf("%f qps: elapsed %f sec\n", float32(opTimes)/float32(d.Seconds()), d.Seconds()) |
|
} |
|
|
|
//go:embed sqlite.sql |
|
var sqliteSchema string |
|
|
|
func setupSQLitePool(t *testing.T) (*sqlite.DB, *sqlite.DB) { |
|
t.Helper() |
|
|
|
p := path.Join(t.TempDir(), "benchmark.db") |
|
fmt.Println(p) |
|
|
|
writeDB, err := sql.Open("sqlite3", p+"?_journal=WAL&_timeout=10000&_fk=true&_cache_size=500") |
|
printErrIfNotNil(t, err) |
|
|
|
writeDB.SetMaxOpenConns(1) |
|
|
|
_, err = writeDB.Exec(sqliteSchema) |
|
printErrIfNotNil(t, err) |
|
|
|
newWriteDB := sqlite.NewDB(writeDB) |
|
|
|
readDB, err := sql.Open("sqlite3", p+"?_journal=WAL&_timeout=10000&_fk=true&_cache_size=500") |
|
printErrIfNotNil(t, err) |
|
|
|
newReadDB := sqlite.NewDB(readDB) |
|
|
|
return newWriteDB, newReadDB |
|
} |
|
|
|
func printErrIfNotNil(t *testing.T, err error) { |
|
if err != nil { |
|
t.Fatal("Error is not nil:", err) |
|
} |
|
} |