Fixing database interaction + adding stored routine calls

This commit is contained in:
POUDEROUX Tom 2025-04-26 14:22:21 +02:00
parent b03e2d2af1
commit c40d035f30
2 changed files with 100 additions and 56 deletions

View File

@ -51,6 +51,7 @@ func Init(router *mux.Router) {
func apiRoot(w http.ResponseWriter, r *http.Request) { func apiRoot(w http.ResponseWriter, r *http.Request) {
tStart := time.Now() tStart := time.Now()
database.ExecuteStoredRoutine("test")
fmt.Fprintf(w, "API ROOT") fmt.Fprintf(w, "API ROOT")
profiler.Add(profiler_API_PROCESS_REQUEST, time.Duration(time.Since(tStart).Microseconds())) profiler.Add(profiler_API_PROCESS_REQUEST, time.Duration(time.Since(tStart).Microseconds()))
} }
@ -66,7 +67,6 @@ func apiAuth(w http.ResponseWriter, r *http.Request) {
func apiAuthLogin(w http.ResponseWriter, r *http.Request) { func apiAuthLogin(w http.ResponseWriter, r *http.Request) {
tStart := time.Now() tStart := time.Now()
fmt.Fprintf(w, "API Auth Login") fmt.Fprintf(w, "API Auth Login")
database.Ping()
profiler.Add(profiler_API_PROCESS_REQUEST, time.Duration(time.Since(tStart).Microseconds())) profiler.Add(profiler_API_PROCESS_REQUEST, time.Duration(time.Since(tStart).Microseconds()))
} }

View File

@ -6,6 +6,7 @@ import (
"database/sql" "database/sql"
"fmt" "fmt"
"log" "log"
"strings"
"time" "time"
_ "github.com/go-sql-driver/mysql" _ "github.com/go-sql-driver/mysql"
@ -14,12 +15,18 @@ import (
const ( const (
profiler_DATABASE_CONNECT string = "Database Connect" profiler_DATABASE_CONNECT string = "Database Connect"
profiler_DATABASE_QUERY string = "Database Query" profiler_DATABASE_QUERY string = "Database Query"
profiler_DATABASE_PING string = "Database Ping"
) )
var db *sql.DB const (
database_ROUTINE_TYPE_PROCEDURE uint8 = 1
database_ROUTINE_TYPE_FUNCTION uint8 = 2
)
var dbPath string
var dbConfig config.Database var dbConfig config.Database
var isInit bool var isInit bool
var storedRoutines map[string]uint8
var db *sql.DB
func Init() { func Init() {
if isInit { if isInit {
@ -27,47 +34,21 @@ func Init() {
} }
dbConfig = config.GetConfig().Database dbConfig = config.GetConfig().Database
dbPath = fmt.Sprintf("%s:%s@(%s:%d)/%s", dbConfig.User, dbConfig.Password, dbConfig.Host, dbConfig.Port, dbConfig.Database)
profiler.Register(profiler_DATABASE_CONNECT, 20, "µs") profiler.Register(profiler_DATABASE_CONNECT, 200, "µs")
profiler.Register(profiler_DATABASE_QUERY, 4000, "µs") profiler.Register(profiler_DATABASE_QUERY, 4000, "µs")
profiler.Register(profiler_DATABASE_PING, 20, "µs")
isInit = true isInit = true
fetchStoredRoutines()
Connect()
var query string = `SHOW GRANTS;`
var rows *sql.Rows = executeQuery(query)
if rows != nil {
defer rows.Close()
for rows.Next() {
var res string
rows.Scan(&res)
fmt.Println(res)
}
} }
} func connect() {
func Terminate() {
isInit = false
if db != nil {
db.Close()
db = nil
}
}
func Connect() {
if !isInit { if !isInit {
return return
} }
if db != nil {
db.Close()
}
var err error var err error
var dbPath string = fmt.Sprintf("%s:%s@(%s:%d)/%s?parseTime=true", dbConfig.User, dbConfig.Password, dbConfig.Host, dbConfig.Port, dbConfig.Database)
log.Printf("Connecting to database %s at %s:%d as %s\n", dbConfig.Database, dbConfig.Host, dbConfig.Port, dbConfig.User) log.Printf("Connecting to database %s at %s:%d as %s\n", dbConfig.Database, dbConfig.Host, dbConfig.Port, dbConfig.User)
tStart := time.Now() tStart := time.Now()
@ -77,82 +58,145 @@ func Connect() {
break break
} }
log.Printf("Error while connecting the database [%d/%d] : %s\n", i+1, dbConfig.RetriesOnError, err) log.Printf("Error while connecting the database [%d/%d] : %s\n", i+1, dbConfig.RetriesOnError, err)
if i+1 == dbConfig.RetriesOnError {
break
}
time.Sleep((time.Duration)(dbConfig.TimeBetweenRetriesMs) * time.Millisecond) time.Sleep((time.Duration)(dbConfig.TimeBetweenRetriesMs) * time.Millisecond)
} }
profiler.Add(profiler_DATABASE_CONNECT, time.Duration(time.Since(tStart).Microseconds()))
if err != nil { if err != nil {
log.Fatalln("Cannot connect to database !") log.Fatalln("Cannot connect to database !")
} }
Ping() db.SetConnMaxLifetime(time.Minute * 1)
db.SetMaxOpenConns(100)
db.SetMaxIdleConns(100)
ping()
profiler.Add(profiler_DATABASE_CONNECT, time.Duration(time.Since(tStart).Microseconds()))
return
} }
func Disconnect() { func ExecuteStoredRoutine(name string, args ...any) {
if !isInit { if !isInit {
return return
} }
if storedRoutines == nil {
fetchStoredRoutines()
}
if db == nil { var exist bool
var routineType uint8
routineType, exist = storedRoutines[name]
if !exist {
return return
} }
db.Close() switch routineType {
} case database_ROUTINE_TYPE_PROCEDURE:
callStoredProcedure(name, args...)
func Ping() { case database_ROUTINE_TYPE_FUNCTION:
if !isInit { callStoredFunction(name, args...)
return }
}
if db == nil {
return
} }
func ping() {
var err error var err error
tStart := time.Now()
for i := range dbConfig.RetriesOnError { for i := range dbConfig.RetriesOnError {
err = db.Ping() err = db.Ping()
if err == nil { if err == nil {
break break
} }
log.Printf("Error while pinging the database [%d/%d] : %s\n", i+1, dbConfig.RetriesOnError, err) log.Printf("Error while pinging the database [%d/%d] : %s\n", i+1, dbConfig.RetriesOnError, err)
if i+1 == dbConfig.RetriesOnError {
break
}
time.Sleep((time.Duration)(dbConfig.TimeBetweenRetriesMs) * time.Millisecond) time.Sleep((time.Duration)(dbConfig.TimeBetweenRetriesMs) * time.Millisecond)
} }
profiler.Add(profiler_DATABASE_PING, time.Duration(time.Since(tStart).Microseconds()))
if err != nil { if err != nil {
log.Fatalln("Cannot ping the database !") log.Fatalln("Cannot ping the database !")
} }
} }
func executeQuery(query string) *sql.Rows { func executeQuery(query string, args ...any) *sql.Rows {
if !isInit { if !isInit {
return nil return nil
} }
if db == nil { if db == nil {
return nil connect()
} }
Ping()
var err error var err error
var rows *sql.Rows var rows *sql.Rows
tStart := time.Now() tStart := time.Now()
for i := range dbConfig.RetriesOnError { for i := range dbConfig.RetriesOnError {
rows, err = db.Query(query) rows, err = db.Query(query, args...)
if err == nil { if err == nil {
break break
} }
log.Printf("Error while querying the database [%d/%d] : %s\n", i+1, dbConfig.RetriesOnError, err) log.Printf("Error while querying the database [%d/%d] : %s\n", i+1, dbConfig.RetriesOnError, err)
if i+1 == dbConfig.RetriesOnError {
break
}
time.Sleep((time.Duration)(dbConfig.TimeBetweenRetriesMs) * time.Millisecond) time.Sleep((time.Duration)(dbConfig.TimeBetweenRetriesMs) * time.Millisecond)
ping()
} }
profiler.Add(profiler_DATABASE_QUERY, time.Duration(time.Since(tStart).Microseconds())) profiler.Add(profiler_DATABASE_QUERY, time.Duration(time.Since(tStart).Microseconds()))
if err != nil { if err != nil {
log.Fatalln("Cannot query the database !") log.Printf("Cannot query the database ! (Query : %s) with args ", query)
log.Fatalln(args...)
} }
return rows return rows
} }
func fetchStoredRoutines() {
storedRoutines = nil
var query string = fmt.Sprintf("SELECT ROUTINE_NAME, ROUTINE_TYPE FROM information_schema.ROUTINES WHERE ROUTINE_SCHEMA=\"%s\";", dbConfig.Database)
var rows *sql.Rows = executeQuery(query)
if rows != nil {
storedRoutines = make(map[string]uint8, 0)
defer rows.Close()
for rows.Next() {
var resName, resType string
rows.Scan(&resName, &resType)
switch resType {
case "PROCEDURE":
storedRoutines[resName] = database_ROUTINE_TYPE_PROCEDURE
case "FUNCTION":
storedRoutines[resName] = database_ROUTINE_TYPE_FUNCTION
}
}
}
fmt.Println(storedRoutines)
}
func callStoredProcedure(name string, args ...any) {
var query string
if len(args) == 0 {
query = fmt.Sprintf("CALL %s()", name)
} else if len(args) == 1 {
query = fmt.Sprintf("CALL %s(?)", name)
} else {
query = strings.Repeat(", ?", len(args)-1)
query = fmt.Sprintf("CALL %s(?%s)", name, query)
}
var rows *sql.Rows = executeQuery(query, args...)
defer rows.Close()
for rows.Next() {
var res string
rows.Scan(&res)
log.Println(res)
}
}
func callStoredFunction(name string, args ...any) {
}