diff --git a/src/api/api.go b/src/api/api.go index fb4bc35..c55c6b0 100644 --- a/src/api/api.go +++ b/src/api/api.go @@ -51,6 +51,7 @@ func Init(router *mux.Router) { func apiRoot(w http.ResponseWriter, r *http.Request) { tStart := time.Now() + database.ExecuteStoredRoutine("test") fmt.Fprintf(w, "API ROOT") 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) { tStart := time.Now() fmt.Fprintf(w, "API Auth Login") - database.Ping() profiler.Add(profiler_API_PROCESS_REQUEST, time.Duration(time.Since(tStart).Microseconds())) } diff --git a/src/database/database.go b/src/database/database.go index 2600da9..0423a8d 100644 --- a/src/database/database.go +++ b/src/database/database.go @@ -6,6 +6,7 @@ import ( "database/sql" "fmt" "log" + "strings" "time" _ "github.com/go-sql-driver/mysql" @@ -14,12 +15,18 @@ import ( const ( profiler_DATABASE_CONNECT string = "Database Connect" 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 isInit bool +var storedRoutines map[string]uint8 +var db *sql.DB func Init() { if isInit { @@ -27,47 +34,21 @@ func Init() { } 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_PING, 20, "µs") isInit = true - - 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) - } - } - + fetchStoredRoutines() } -func Terminate() { - isInit = false - if db != nil { - db.Close() - db = nil - } -} - -func Connect() { +func connect() { if !isInit { return } - if db != nil { - db.Close() - } - 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) tStart := time.Now() @@ -77,82 +58,145 @@ func Connect() { break } 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) } - profiler.Add(profiler_DATABASE_CONNECT, time.Duration(time.Since(tStart).Microseconds())) if err != nil { 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 { return } + if storedRoutines == nil { + fetchStoredRoutines() + } - if db == nil { + var exist bool + var routineType uint8 + routineType, exist = storedRoutines[name] + if !exist { return } - db.Close() + switch routineType { + case database_ROUTINE_TYPE_PROCEDURE: + callStoredProcedure(name, args...) + case database_ROUTINE_TYPE_FUNCTION: + callStoredFunction(name, args...) + } } -func Ping() { - if !isInit { - return - } - - if db == nil { - return - } - +func ping() { var err error - tStart := time.Now() for i := range dbConfig.RetriesOnError { err = db.Ping() if err == nil { break } 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) } - profiler.Add(profiler_DATABASE_PING, time.Duration(time.Since(tStart).Microseconds())) if err != nil { log.Fatalln("Cannot ping the database !") } } -func executeQuery(query string) *sql.Rows { +func executeQuery(query string, args ...any) *sql.Rows { if !isInit { return nil } - if db == nil { - return nil + connect() } - Ping() - var err error var rows *sql.Rows tStart := time.Now() for i := range dbConfig.RetriesOnError { - rows, err = db.Query(query) + rows, err = db.Query(query, args...) if err == nil { break } 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) + ping() } profiler.Add(profiler_DATABASE_QUERY, time.Duration(time.Since(tStart).Microseconds())) 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 } + +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) { + +}