2022-10-09 09:25:28 +02:00
package main
import (
"database/sql"
"sync"
"time"
2022-10-18 23:46:47 +02:00
"github.com/google/uuid"
log "github.com/sirupsen/logrus"
2022-10-09 09:25:28 +02:00
)
// Database to store a single connection to the database and its statistics
type Database struct {
2022-10-18 23:46:47 +02:00
ID uuid . UUID
2022-10-09 09:25:28 +02:00
db * sql . DB
driver string
dsn string
query string
reconnect bool
2022-10-18 23:46:47 +02:00
queries float64 // Total number of queries
queriesLock * sync . Mutex
queriesDuration time . Duration // Time spent executing queries
queriesDurationLock * sync . Mutex
2022-10-09 09:25:28 +02:00
}
// NewDatabase creates a single connection to the database then returns the struct
func NewDatabase ( driver string , dsn string , query string , reconnect bool ) ( * Database , error ) {
database := & Database {
driver : driver ,
dsn : dsn ,
query : query ,
reconnect : reconnect ,
}
err := database . connect ( )
if err != nil {
return nil , err
}
2022-10-18 23:46:47 +02:00
database . ID = uuid . New ( )
database . queriesLock = & sync . Mutex { }
database . queriesDurationLock = & sync . Mutex { }
2022-10-09 09:25:28 +02:00
return database , nil
}
func ( d * Database ) connect ( ) error {
db , err := sql . Open ( d . driver , d . dsn )
if err != nil {
return err
}
if err = db . Ping ( ) ; err != nil {
return err
}
d . db = db
return nil
}
func ( d * Database ) disconnect ( ) error {
if d . db != nil {
return d . db . Close ( )
}
return nil
}
// Run to perform benchmark on a single connection for a duration
func ( d * Database ) Run ( duration time . Duration , wg * sync . WaitGroup ) error {
defer wg . Done ( )
defer d . disconnect ( )
// Run single benchmark
start := time . Now ( )
end := start . Add ( duration )
for {
if end . Before ( time . Now ( ) ) {
break
}
2022-10-18 23:46:47 +02:00
queryStart := time . Now ( )
2022-10-09 09:25:28 +02:00
result , err := d . db . Query ( d . query )
if err != nil {
log . Fatalf ( "Failed query the database: %v" , err )
}
if err = result . Close ( ) ; err != nil {
log . Fatalf ( "Failed to close query: %v" , err )
}
2022-10-18 23:46:47 +02:00
queryDuration := time . Now ( ) . Sub ( queryStart )
// Update statistics
d . queriesLock . Lock ( )
2022-10-09 09:25:28 +02:00
d . queries ++
2022-10-18 23:46:47 +02:00
d . queriesLock . Unlock ( )
d . queriesDurationLock . Lock ( )
d . queriesDuration = d . queriesDuration + queryDuration
d . queriesDurationLock . Unlock ( )
// Print debug statistics
log . Debugf ( "Connection %s: Number of queries: %.f | Query duration: %s | Running sum of queries duration: %s" , d . ID , d . queries , queryDuration , d . queriesDuration )
2022-10-09 09:25:28 +02:00
if d . reconnect {
if err = d . disconnect ( ) ; err != nil {
return err
}
if err = d . connect ( ) ; err != nil {
return err
}
}
}
return nil
}
// Queries returns the number of performed queries in the benchmark
func ( d * Database ) Queries ( ) float64 {
return d . queries
}
2022-10-18 23:46:47 +02:00
// AverageQueryTime returns the average query execution time in milliseconds
func ( d * Database ) AverageQueryTime ( ) time . Duration {
return d . queriesDuration / time . Duration ( d . queries )
}