I'm having an issue with Gorm / Psql where my database connection get automatically closed.
I never call defer dbInstance.Close()
in main.go (not anymore for now, I've removed it, since that's the only place in my code where I felt the connection could be wrongfully closed) nor was it ever anywhere else.
The way I'm initializing my db is with a "db" package that looks like this:
package db
import (
"fmt"
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/postgres"
)
var DbInstance *gorm.DB
func Init() *gorm.DB {
if DbInstance != nil {
return DbInstance
}
fmt.Println("Opening Db")
db, err := gorm.Open("postgres", "host=localhost port=5432 user=admin dbname=dbname sslmode=disable password=")
if err != nil {
fmt.Println(err)
panic("failed to connect database")
}
return db
}
then I call db.Init()
in my main.go and then only call the db from "db.dbInstance" from the rest of my program.
As I've previously mentioned I used to call defer db.DbInstance.Close()
from main.go but have tried removing it to see if it fixed the issue but it didn't.
What's strange is that the db connection will work for hours and hours in many different calls/function but always end up closing at some point.
From what i understand it should work :
gorm.Open() uses https://golang.org/pkg/database/sql/, which is threadsafe and handles connection pooling for you. Don't call gorm.Open() every request. Call it once when setting up your application, and make sure you use defer db.Close() so connections are cleanly ended when your program exits.
Lastly I need to add that it seems (i'm not 100% sure) that it's closing after I do batch inserts but again the .Close()
function is never called, anywhere in my program.
I'm a bit lost as to what could be happening? Garbage collector (doesn't make sense the var is global)? psql driver closing in the background? Configuration issue?
I'm adding the batch function for reference just in case:
func InsertWithPostGresLimitSizeV2(DB *gorm.DB, array []interface{}) {
if len(array) == 0 {
return
}
numberOfParams := len(DB.NewScope(array[0]).Fields())
// postgres is limited to 65535 params.
maxStructPerBulk := int(65535 / numberOfParams)
currentIndex := 0
if len(array) > maxStructPerBulk {
for len(array) > currentIndex {
if (maxStructPerBulk + currentIndex) < len(array) {
slice := array[currentIndex:(currentIndex + maxStructPerBulk)]
currentIndex += maxStructPerBulk
_, err := DB.BatchInsert(slice)
log.Println(err)
} else {
slice := array[currentIndex:len(array)]
currentIndex = len(array)
_, err := DB.BatchInsert(slice)
log.Println(err)
}
}
} else {
_, err := DB.BatchInsert(array)
log.Println(err)
}
}
func BatchInsert(db *gorm.DB,objArr []interface{}) (int64, error) {
if len(objArr) == 0 {
return 0, errors.New("insert a slice length of 0")
}
mainObj := objArr[0]
mainScope := db.NewScope(mainObj)
mainFields := mainScope.Fields()
quoted := make([]string, 0, len(mainFields))
for i := range mainFields {
if (mainFields[i].IsPrimaryKey && mainFields[i].IsBlank) || (mainFields[i].IsIgnored) {
continue
}
quoted = append(quoted, mainScope.Quote(mainFields[i].DBName))
}
placeholdersArr := make([]string, 0, len(objArr))
for _, obj := range objArr {
scope := db.NewScope(obj)
fields := scope.Fields()
placeholders := make([]string, 0, len(fields))
for i := range fields {
if (fields[i].IsPrimaryKey && fields[i].IsBlank) || (fields[i].IsIgnored) {
continue
}
var vars interface{}
if (fields[i].Name == "CreatedAt" || fields[i].Name == "UpdatedAt") && fields[i].IsBlank {
vars = gorm.NowFunc()
} else {
vars = fields[i].Field.Interface()
}
placeholders = append(placeholders, mainScope.AddToVars(vars))
}
placeholdersStr := "(" + strings.Join(placeholders, ", ") + ")"
placeholdersArr = append(placeholdersArr, placeholdersStr)
mainScope.SQLVars = append(mainScope.SQLVars, scope.SQLVars...)
}
mainScope.Raw(fmt.Sprintf("INSERT INTO %s (%s) VALUES %s",
mainScope.QuotedTableName(),
strings.Join(quoted, ", "),
strings.Join(placeholdersArr, ", "),
))
if err := mainScope.Exec().DB().Error; err != nil {
return 0, err
}
return mainScope.DB().RowsAffected, nil
}
On last thing is that I was thinking of " fixing " the issue by calling my db through but the ping would slow each of my calls:
func getDb() *gorm.DB {
err := DbInstance.DB().Ping()
if err != nil {
fmt.Println("Connection to db closed opening a new one")
return Init()
}
return DbInstance
}