Implemented periodic ping to keep connection of transaction alive
This commit is contained in:
parent
c7f668094a
commit
b42b67ff0b
58
main.go
58
main.go
@ -36,7 +36,9 @@ type DB struct {
|
|||||||
// func main() {
|
// func main() {
|
||||||
// db, err := gorm.Open("mysql", "user:password@/dbname?charset=utf8&parseTime=True&loc=Local")
|
// db, err := gorm.Open("mysql", "user:password@/dbname?charset=utf8&parseTime=True&loc=Local")
|
||||||
// }
|
// }
|
||||||
|
//
|
||||||
// GORM has wrapped some drivers, for easier to remember driver's import path, so you could import the mysql driver with
|
// GORM has wrapped some drivers, for easier to remember driver's import path, so you could import the mysql driver with
|
||||||
|
//
|
||||||
// import _ "github.com/jinzhu/gorm/dialects/mysql"
|
// import _ "github.com/jinzhu/gorm/dialects/mysql"
|
||||||
// // import _ "github.com/jinzhu/gorm/dialects/postgres"
|
// // import _ "github.com/jinzhu/gorm/dialects/postgres"
|
||||||
// // import _ "github.com/jinzhu/gorm/dialects/sqlite"
|
// // import _ "github.com/jinzhu/gorm/dialects/sqlite"
|
||||||
@ -121,7 +123,9 @@ func (s *DB) Dialect() Dialect {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Callback return `Callbacks` container, you could add/change/delete callbacks with it
|
// Callback return `Callbacks` container, you could add/change/delete callbacks with it
|
||||||
|
//
|
||||||
// db.Callback().Create().Register("update_created_at", updateCreated)
|
// db.Callback().Create().Register("update_created_at", updateCreated)
|
||||||
|
//
|
||||||
// Refer https://jinzhu.github.io/gorm/development.html#callbacks
|
// Refer https://jinzhu.github.io/gorm/development.html#callbacks
|
||||||
func (s *DB) Callback() *Callback {
|
func (s *DB) Callback() *Callback {
|
||||||
s.parent.callbacks = s.parent.callbacks.clone()
|
s.parent.callbacks = s.parent.callbacks.clone()
|
||||||
@ -224,6 +228,7 @@ func (s *DB) Offset(offset interface{}) *DB {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Order specify order when retrieve records from database, set reorder to `true` to overwrite defined conditions
|
// Order specify order when retrieve records from database, set reorder to `true` to overwrite defined conditions
|
||||||
|
//
|
||||||
// db.Order("name DESC")
|
// db.Order("name DESC")
|
||||||
// db.Order("name DESC", true) // reorder
|
// db.Order("name DESC", true) // reorder
|
||||||
// db.Order(gorm.Expr("name = ? DESC", "first")) // sql expression
|
// db.Order(gorm.Expr("name = ? DESC", "first")) // sql expression
|
||||||
@ -253,12 +258,14 @@ func (s *DB) Having(query interface{}, values ...interface{}) *DB {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Joins specify Joins conditions
|
// Joins specify Joins conditions
|
||||||
|
//
|
||||||
// db.Joins("JOIN emails ON emails.user_id = users.id AND emails.email = ?", "jinzhu@example.org").Find(&user)
|
// db.Joins("JOIN emails ON emails.user_id = users.id AND emails.email = ?", "jinzhu@example.org").Find(&user)
|
||||||
func (s *DB) Joins(query interface{}, args ...interface{}) *DB {
|
func (s *DB) Joins(query interface{}, args ...interface{}) *DB {
|
||||||
return s.clone().search.Joins(query, args...).db
|
return s.clone().search.Joins(query, args...).db
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scopes pass current database connection to arguments `func(*DB) *DB`, which could be used to add conditions dynamically
|
// Scopes pass current database connection to arguments `func(*DB) *DB`, which could be used to add conditions dynamically
|
||||||
|
//
|
||||||
// func AmountGreaterThan1000(db *gorm.DB) *gorm.DB {
|
// func AmountGreaterThan1000(db *gorm.DB) *gorm.DB {
|
||||||
// return db.Where("amount > ?", 1000)
|
// return db.Where("amount > ?", 1000)
|
||||||
// }
|
// }
|
||||||
@ -270,6 +277,7 @@ func (s *DB) Joins(query interface{}, args ...interface{}) *DB {
|
|||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// db.Scopes(AmountGreaterThan1000, OrderStatus([]string{"paid", "shipped"})).Find(&orders)
|
// db.Scopes(AmountGreaterThan1000, OrderStatus([]string{"paid", "shipped"})).Find(&orders)
|
||||||
|
//
|
||||||
// Refer https://jinzhu.github.io/gorm/crud.html#scopes
|
// Refer https://jinzhu.github.io/gorm/crud.html#scopes
|
||||||
func (s *DB) Scopes(funcs ...func(*DB) *DB) *DB {
|
func (s *DB) Scopes(funcs ...func(*DB) *DB) *DB {
|
||||||
for _, f := range funcs {
|
for _, f := range funcs {
|
||||||
@ -356,6 +364,7 @@ func (s *DB) ScanRows(rows *sql.Rows, result interface{}) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Pluck used to query single column from a model as a map
|
// Pluck used to query single column from a model as a map
|
||||||
|
//
|
||||||
// var ages []int64
|
// var ages []int64
|
||||||
// db.Find(&users).Pluck("age", &ages)
|
// db.Find(&users).Pluck("age", &ages)
|
||||||
func (s *DB) Pluck(column string, value interface{}) *DB {
|
func (s *DB) Pluck(column string, value interface{}) *DB {
|
||||||
@ -454,6 +463,7 @@ func (s *DB) Delete(value interface{}, where ...interface{}) *DB {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Raw use raw sql as conditions, won't run it unless invoked by other methods
|
// Raw use raw sql as conditions, won't run it unless invoked by other methods
|
||||||
|
//
|
||||||
// db.Raw("SELECT name, age FROM users WHERE name = ?", 3).Scan(&result)
|
// db.Raw("SELECT name, age FROM users WHERE name = ?", 3).Scan(&result)
|
||||||
func (s *DB) Raw(sql string, values ...interface{}) *DB {
|
func (s *DB) Raw(sql string, values ...interface{}) *DB {
|
||||||
return s.clone().search.Raw(true).Where(sql, values...).db
|
return s.clone().search.Raw(true).Where(sql, values...).db
|
||||||
@ -469,6 +479,7 @@ func (s *DB) Exec(sql string, values ...interface{}) *DB {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Model specify the model you would like to run db operations
|
// Model specify the model you would like to run db operations
|
||||||
|
//
|
||||||
// // update all users's name to `hello`
|
// // update all users's name to `hello`
|
||||||
// db.Model(&User{}).Update("name", "hello")
|
// db.Model(&User{}).Update("name", "hello")
|
||||||
// // if user's primary key is non-blank, will use it as condition, then will only update the user's name to `hello`
|
// // if user's primary key is non-blank, will use it as condition, then will only update the user's name to `hello`
|
||||||
@ -530,13 +541,42 @@ func (s *DB) WrapInTx(f func(tx *DB) error) (err error) {
|
|||||||
if _, ok := s.db.(*sql.Tx); ok {
|
if _, ok := s.db.(*sql.Tx); ok {
|
||||||
// Already in a transaction
|
// Already in a transaction
|
||||||
return f(s)
|
return f(s)
|
||||||
} else {
|
}
|
||||||
|
|
||||||
// Lets start a new transaction
|
// Lets start a new transaction
|
||||||
tx := s.Begin()
|
tx := s.Begin()
|
||||||
if err = tx.Error; err != nil {
|
if err = tx.Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a channel to stop the ping goroutine.
|
||||||
|
stopTxPing := make(chan bool)
|
||||||
|
// Start a goroutine that pings the database connection for a keep-alive.
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
// Stop the goroutine when the stop channel receives a value ..
|
||||||
|
case <-stopTxPing:
|
||||||
|
return
|
||||||
|
// .. otherwise ping the database connection every 10 seconds.
|
||||||
|
case <-time.After(10 * time.Second):
|
||||||
|
err := tx.DB().Ping()
|
||||||
|
if err != nil {
|
||||||
|
tx.AddError(
|
||||||
|
fmt.Errorf(
|
||||||
|
"Could not ping database connection for transaction: %w",
|
||||||
|
err,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
panicked := true
|
panicked := true
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
if panicked || err != nil {
|
if panicked || err != nil {
|
||||||
rollbackErr := tx.Rollback().Error
|
rollbackErr := tx.Rollback().Error
|
||||||
@ -549,13 +589,22 @@ func (s *DB) WrapInTx(f func(tx *DB) error) (err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
err = f(tx)
|
err = f(tx)
|
||||||
|
|
||||||
|
// As soon as the inner stack has returned, stop the ping goroutine. As the transaction will be
|
||||||
|
// only committed after this point, the ping would fail and the goroutine will exit.
|
||||||
|
stopTxPing <- true
|
||||||
|
// Last but not least, close the stop ping channel.
|
||||||
|
close(stopTxPing)
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
err = tx.Commit().Error
|
err = tx.Commit().Error
|
||||||
}
|
}
|
||||||
|
|
||||||
panicked = false
|
panicked = false
|
||||||
return
|
|
||||||
}
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// SkipAssocSave disables saving of associations
|
// SkipAssocSave disables saving of associations
|
||||||
@ -674,6 +723,7 @@ func (s *DB) RemoveIndex(indexName string) *DB {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// AddForeignKey Add foreign key to the given scope, e.g:
|
// AddForeignKey Add foreign key to the given scope, e.g:
|
||||||
|
//
|
||||||
// db.Model(&User{}).AddForeignKey("city_id", "cities(id)", "RESTRICT", "RESTRICT")
|
// db.Model(&User{}).AddForeignKey("city_id", "cities(id)", "RESTRICT", "RESTRICT")
|
||||||
func (s *DB) AddForeignKey(field string, dest string, onDelete string, onUpdate string) *DB {
|
func (s *DB) AddForeignKey(field string, dest string, onDelete string, onUpdate string) *DB {
|
||||||
scope := s.NewScope(s.Value)
|
scope := s.NewScope(s.Value)
|
||||||
@ -682,6 +732,7 @@ func (s *DB) AddForeignKey(field string, dest string, onDelete string, onUpdate
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RemoveForeignKey Remove foreign key from the given scope, e.g:
|
// RemoveForeignKey Remove foreign key from the given scope, e.g:
|
||||||
|
//
|
||||||
// db.Model(&User{}).RemoveForeignKey("city_id", "cities(id)")
|
// db.Model(&User{}).RemoveForeignKey("city_id", "cities(id)")
|
||||||
func (s *DB) RemoveForeignKey(field string, dest string) *DB {
|
func (s *DB) RemoveForeignKey(field string, dest string) *DB {
|
||||||
scope := s.clone().NewScope(s.Value)
|
scope := s.clone().NewScope(s.Value)
|
||||||
@ -712,6 +763,7 @@ func (s *DB) Association(column string) *Association {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Preload preload associations with given conditions
|
// Preload preload associations with given conditions
|
||||||
|
//
|
||||||
// db.Preload("Orders", "state NOT IN (?)", "cancelled").Find(&users)
|
// db.Preload("Orders", "state NOT IN (?)", "cancelled").Find(&users)
|
||||||
func (s *DB) Preload(column string, conditions ...interface{}) *DB {
|
func (s *DB) Preload(column string, conditions ...interface{}) *DB {
|
||||||
return s.clone().search.Preload(column, conditions...).db
|
return s.clone().search.Preload(column, conditions...).db
|
||||||
|
Loading…
x
Reference in New Issue
Block a user