github.com/mdaxf/iac@v0.0.0-20240519030858-58a061660378/databases/dbconn.go (about)

     1  // Copyright 2023 IAC. All Rights Reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package dbconn
    16  
    17  import (
    18  	"database/sql"
    19  	"fmt"
    20  	"sync"
    21  	"time"
    22  
    23  	//	"github.com/mdaxf/iac/com"
    24  	"github.com/mdaxf/iac/logger"
    25  
    26  	_ "github.com/denisenkom/go-mssqldb"
    27  	_ "github.com/go-sql-driver/mysql"
    28  )
    29  
    30  // DB is the interface for database connection
    31  var DB *sql.DB
    32  var monitoring = false
    33  var connectionerr error
    34  
    35  // ConnectDB is the function to connect to database
    36  // DatabaseType: mysql
    37  // DatabaseConnection: user:password@tcp(localhost:3306)/mydb
    38  // DatabaseName: iac
    39  
    40  var (
    41  	/*
    42  		mysql,sqlserver, goracle
    43  	*/
    44  	DatabaseType = "mysql"
    45  
    46  	/*
    47  		user:password@tcp(localhost:3306)/mydb
    48  		server=%s;port=%d;user id=%s;password=%s;database=%s
    49  	*/
    50  	//DatabaseConnection = "server=xxx;user id=xx;password=xxx;database=xxx"  //sqlserver
    51  	DatabaseConnection = "user:iacf12345678@tcp(localhost:3306)/iac"
    52  	MaxIdleConns       = 5
    53  	MaxOpenConns       = 10
    54  	once               sync.Once
    55  	err                error
    56  )
    57  
    58  // ConnectDB establishes a connection to the database.
    59  // It returns an error if the connection fails.
    60  
    61  func ConnectDB() error {
    62  	// Function execution logging
    63  	connectionerr = nil
    64  	iLog := logger.Log{ModuleName: logger.Framework, User: "System", ControllerName: "Database"}
    65  	startTime := time.Now()
    66  	defer func() {
    67  		elapsed := time.Since(startTime)
    68  		iLog.PerformanceWithDuration("database.ConnectDB", elapsed)
    69  	}()
    70  
    71  	// Recover from any panics and log the error
    72  	defer func() {
    73  		if err := recover(); err != nil {
    74  			iLog.Error(fmt.Sprintf("ConnectDB defer error: %s", err))
    75  			connectionerr = err.(error)
    76  			return
    77  			//	ctx.JSON(http.StatusBadRequest, gin.H{"error": err})
    78  		}
    79  	}()
    80  	/*
    81  		dbconn := &com.DBConn{
    82  			DBType:       DatabaseType,
    83  			DBConnection: DatabaseConnection,
    84  			DBName:       "",
    85  			MaxIdleConns: MaxIdleConns,
    86  			MaxOpenConns: MaxOpenConns,
    87  		}
    88  
    89  		mydbconn := &DBConn{
    90  			DBType:       DatabaseType,
    91  			DBConnection: DatabaseConnection,
    92  			DBName:       "",
    93  			DB:           nil,
    94  			MaxIdleConns: MaxIdleConns,
    95  			MaxOpenConns: MaxOpenConns,
    96  		}
    97  
    98  		err := mydbconn.Connect()
    99  		if err != nil {
   100  			iLog.Error(fmt.Sprintf("initialize Database (MySQL) error: %s", err.Error()))
   101  			return err
   102  		}
   103  
   104  		DB = mydbconn.DB
   105  		dbconn.DB = DB
   106  		iLog.Debug(fmt.Sprintf("Connect to database:%v", DB))
   107  		com.IACDBConn = dbconn
   108  
   109  		return nil */
   110  
   111  	// Log the database connection details
   112  	iLog.Info(fmt.Sprintf("Connect Database: %s %s", DatabaseType, DatabaseConnection))
   113  
   114  	// Establish the database connection if it hasn't been done before
   115  	once.Do(func() {
   116  		DB, err = sql.Open(DatabaseType, DatabaseConnection)
   117  		if err != nil {
   118  			iLog.Error(fmt.Sprintf("Connect Database Error: %v", err))
   119  			connectionerr = err
   120  			return
   121  		}
   122  		DB.SetMaxIdleConns(MaxIdleConns)
   123  		DB.SetMaxOpenConns(MaxOpenConns)
   124  	})
   125  
   126  	if monitoring == false {
   127  		go func() {
   128  			monitorAndReconnectMySQL()
   129  		}()
   130  	}
   131  
   132  	return nil
   133  
   134  }
   135  
   136  // DBPing pings the database to check if it is still alive.
   137  // It returns an error if the ping fails.
   138  
   139  func DBPing() error {
   140  	iLog := logger.Log{ModuleName: logger.Framework, User: "System", ControllerName: "Database"}
   141  	startTime := time.Now()
   142  	defer func() {
   143  		elapsed := time.Since(startTime)
   144  		iLog.PerformanceWithDuration("database.DBPing", elapsed)
   145  	}()
   146  
   147  	defer func() {
   148  		if err := recover(); err != nil {
   149  			iLog.Error(fmt.Sprintf("DBPing defer error: %s", err))
   150  			//	ctx.JSON(http.StatusBadRequest, gin.H{"error": err})
   151  		}
   152  	}()
   153  
   154  	return DB.Ping()
   155  
   156  }
   157  
   158  func monitorAndReconnectMySQL() {
   159  	// Function execution logging
   160  	iLog := logger.Log{ModuleName: logger.Framework, User: "System", ControllerName: "Database.monitorAndReconnectMySQL"}
   161  	startTime := time.Now()
   162  	defer func() {
   163  		elapsed := time.Since(startTime)
   164  		iLog.PerformanceWithDuration("database.monitorAndReconnectMySQL", elapsed)
   165  	}()
   166  
   167  	// Recover from any panics and log the error
   168  	defer func() {
   169  		if err := recover(); err != nil {
   170  			iLog.Error(fmt.Sprintf("monitorAndReconnectMySQL defer error: %s", err))
   171  			//	ctx.JSON(http.StatusBadRequest, gin.H{"error": err})
   172  		}
   173  	}()
   174  	monitoring = true
   175  	for {
   176  		err := DB.Ping()
   177  		if err != nil {
   178  			iLog.Error(fmt.Sprintf("MySQL connection lost, reconnecting..."))
   179  
   180  			ConnectDB()
   181  
   182  			if connectionerr != nil {
   183  				iLog.Error(fmt.Sprintf("Failed to reconnect to MySQL:%v", connectionerr))
   184  				time.Sleep(60 * time.Second) // Wait before retrying
   185  				continue
   186  			} else {
   187  				time.Sleep(5 * 60 * time.Second)
   188  				iLog.Debug(fmt.Sprintf("MySQL reconnected successfully"))
   189  				continue
   190  			}
   191  		} else {
   192  			time.Sleep(5 * 60 * time.Second) // Check connection every 60 seconds
   193  			continue
   194  		}
   195  	}
   196  
   197  }