In Go Language We have developed an API for client global search in Onboarding based on Client Id, Pan Number and Mobile Number which creates the connection to the database on each request and closes the connection after performing required query operations. Which results in overall 3 requests to the database server. The API is used in JIFFY APP and Neuron Web App which has a comparatively high user base i.e more than 4 Lakhs, which results in high frequency of requests to the API ultimately resulting in increase in load on the database server. To overcome the above mentioned challenge we have done some research to optimize the API performance and effective database utilization where we underwent the connection pool mechanism which can be implemented.
We have done some changes and initiated the database connection pool at the time of booting the Go application, which means we are establishing the specific set of connections to the database server with following configuration :-
func OnboardingConnectionPool() {
log.Info("OnboardingConnectionPool Initiated: ")
dbDriver := os.Getenv("DB_DRIVER")
dbName := os.Getenv("DB_NAME")
dbUser := os.Getenv("DB_USERNAME")
dbPassword := os.Getenv("DB_PASSWORD")
dbTcp := "@tcp(" + os.Getenv("DB_HOST") + ":" + os.Getenv("DB_PORT") + ")/"
db, err := gorm.Open(dbDriver, dbUser+":"+dbPassword+dbTcp+dbName+"?charset=utf8&parseTime=True")
DB_MAX_IDLE_CONN, _ := strconv.Atoi(os.Getenv("DB_MAX_IDLE_CONN"))
DB_MAX_OPEN_CONN, _ := strconv.Atoi(os.Getenv("DB_MAX_OPEN_CONN"))
DB_MAX_IDLE_TIME, _ := strconv.Atoi(os.Getenv("DB_MAX_IDLE_TIME"))
DB_MAX_LIFE_TIME, _ := strconv.Atoi(os.Getenv("DB_MAX_LIFE_TIME"))
// Max Ideal Connection
db.DB().SetMaxIdleConns(DB_MAX_IDLE_CONN)
// Max Open Connection
db.DB().SetMaxOpenConns(DB_MAX_OPEN_CONN)
// Idle Connection Timeout
db.DB().SetConnMaxIdleTime(time.Duration(DB_MAX_IDLE_TIME) * time.Second)
// Connection Lifetime
db.DB().SetConnMaxLifetime(time.Duration(DB_MAX_LIFE_TIME) * time.Second)
log.Info("@OnboardingConnectionPool MYSQL MAX Open Connections: ",
// This is for analyzing the stats after setting a connection
db.DB().Stats().MaxOpenConnections)
log.Info("@OnboardingConnectionPool MYSQL Open Connections: ",
db.DB().Stats().OpenConnections)
log.Info("@OnboardingConnectionPool MYSQL InUse Connections: ",
db.DB().Stats().InUse)
log.Info("@OnboardingConnectionPool MYSQL Idle Connections: ", db.DB().Stats().Idle)
onboardingDB = db
if err != nil {
log.Error("OnboardingConnectionPool Error: ", err)
}
}
And Initiating DB pool when application boots, as follows
func main() {
err := godotenv.Load(".env")
if err != nil {
log.Error("Error loading .env file")
}
router := Routes.SetupRouter()
**Controllers.OnboardingConnectionPool()**
router.Static("/assets", "./assets")
router.Run(":" + os.Getenv("APP_PORT"))
}
K6 Load testing results :-
Virtual Users :- 10 Time Interval :- 10s Success % :- 93%
Failure rate increases as the open / idle connections to the database server increases and results in not serving the future requests received on the server.
K6 Load testing results :- Virtual Users :- 10 Time Interval :- 10s Success % :- 100%
This helps in fulfilling the requests until the database server is shut down by any external actions. And the application will rebuild the connection pool with a fixed number of connections and start serving again.