2024-08-11 14:29:42 +00:00
|
|
|
package adapter
|
|
|
|
|
|
|
|
import (
|
|
|
|
"database/sql"
|
|
|
|
"fmt"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
2024-08-15 07:10:49 +00:00
|
|
|
/*
|
|
|
|
DBAdapter
|
2024-08-17 16:53:58 +00:00
|
|
|
- Automatically creates connections for each database URL.
|
|
|
|
- Executes queries on the specified database.
|
|
|
|
- Closes connections older than ConnectionLiveTime
|
2024-08-15 07:10:49 +00:00
|
|
|
*/
|
2024-08-11 14:29:42 +00:00
|
|
|
type DBAdapter struct {
|
2024-08-12 13:47:06 +00:00
|
|
|
Type string
|
2024-08-17 16:53:58 +00:00
|
|
|
DbInit []string
|
2024-08-12 13:47:06 +00:00
|
|
|
MaxAttempts int
|
|
|
|
ConnectionLiveTime int
|
|
|
|
dbs *DBMap
|
|
|
|
}
|
|
|
|
|
|
|
|
type DBMap struct {
|
|
|
|
Connections map[string]*sql.DB
|
|
|
|
ConnectionAttempts map[string]int
|
|
|
|
ConnectionTime map[string]int64
|
2024-08-11 14:29:42 +00:00
|
|
|
}
|
|
|
|
|
2024-08-17 16:53:58 +00:00
|
|
|
func (a *DBAdapter) AfterOpen(sql string) {
|
|
|
|
a.DbInit = append(a.DbInit, sql)
|
|
|
|
}
|
|
|
|
|
2024-08-11 14:29:42 +00:00
|
|
|
func (a *DBAdapter) Open(url string) (*sql.DB, error) {
|
2024-08-12 13:47:06 +00:00
|
|
|
defer a.CleanUp()
|
|
|
|
|
2024-08-11 14:29:42 +00:00
|
|
|
if a.MaxAttempts == 0 {
|
|
|
|
a.MaxAttempts = 6
|
|
|
|
}
|
2024-08-12 13:53:12 +00:00
|
|
|
|
2024-08-12 13:47:06 +00:00
|
|
|
if a.ConnectionLiveTime == 0 {
|
|
|
|
a.ConnectionLiveTime = 120
|
|
|
|
}
|
2024-08-12 13:53:12 +00:00
|
|
|
|
2024-08-12 13:47:06 +00:00
|
|
|
if a.dbs == nil {
|
|
|
|
a.dbs = &DBMap{
|
|
|
|
Connections: make(map[string]*sql.DB),
|
|
|
|
ConnectionAttempts: make(map[string]int),
|
|
|
|
ConnectionTime: make(map[string]int64),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
connections := a.dbs.Connections
|
|
|
|
attempts := a.dbs.ConnectionAttempts
|
|
|
|
lastHits := a.dbs.ConnectionTime
|
|
|
|
|
|
|
|
lastHits[url] = time.Now().Unix()
|
|
|
|
if _, ok := connections[url]; !ok {
|
|
|
|
connections[url], _ = sql.Open(a.Type, url)
|
2024-08-11 14:29:42 +00:00
|
|
|
} else {
|
2024-08-12 13:47:06 +00:00
|
|
|
err := connections[url].Ping()
|
2024-08-11 14:29:42 +00:00
|
|
|
if err != nil {
|
2024-08-12 13:47:06 +00:00
|
|
|
connections[url] = nil
|
|
|
|
attempts[url]++
|
2024-08-11 14:29:42 +00:00
|
|
|
time.Sleep(time.Duration(5) * time.Second)
|
2024-08-12 13:47:06 +00:00
|
|
|
if attempts[url] > a.MaxAttempts {
|
|
|
|
return nil, fmt.Errorf(
|
|
|
|
`failed to connect to "%s", after %v attempts`,
|
|
|
|
url,
|
|
|
|
a.MaxAttempts,
|
|
|
|
)
|
2024-08-11 14:29:42 +00:00
|
|
|
}
|
|
|
|
return a.Open(url)
|
|
|
|
}
|
|
|
|
}
|
2024-08-12 13:53:12 +00:00
|
|
|
|
|
|
|
attempts[url] = 0
|
2024-08-17 16:53:58 +00:00
|
|
|
for _, sql := range a.DbInit {
|
|
|
|
connections[url].Exec(sql)
|
|
|
|
}
|
2024-08-12 13:53:12 +00:00
|
|
|
return connections[url], nil
|
2024-08-11 14:29:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (a *DBAdapter) GetDB(url string) *sql.DB {
|
2024-08-12 13:47:06 +00:00
|
|
|
if a.dbs == nil {
|
2024-08-11 14:29:42 +00:00
|
|
|
return nil
|
|
|
|
}
|
2024-08-12 13:47:06 +00:00
|
|
|
return a.dbs.Connections[url]
|
2024-08-11 14:29:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (a *DBAdapter) Close() {
|
2024-08-12 13:53:12 +00:00
|
|
|
for url, db := range a.dbs.Connections {
|
2024-08-11 14:29:42 +00:00
|
|
|
db.Close()
|
2024-08-12 13:53:12 +00:00
|
|
|
delete(a.dbs.Connections, url)
|
|
|
|
delete(a.dbs.ConnectionAttempts, url)
|
|
|
|
delete(a.dbs.ConnectionTime, url)
|
2024-08-11 14:29:42 +00:00
|
|
|
}
|
2024-08-12 13:47:06 +00:00
|
|
|
a.dbs = nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *DBAdapter) CleanUp() {
|
|
|
|
if a.dbs == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
lastHits := a.dbs.ConnectionTime
|
|
|
|
liveTime := a.ConnectionLiveTime
|
|
|
|
for url, db := range a.dbs.Connections {
|
|
|
|
if time.Now().Unix()-lastHits[url] > int64(liveTime) {
|
|
|
|
db.Close()
|
|
|
|
delete(a.dbs.Connections, url)
|
|
|
|
delete(a.dbs.ConnectionAttempts, url)
|
|
|
|
delete(a.dbs.ConnectionTime, url)
|
|
|
|
}
|
|
|
|
}
|
2024-08-11 14:29:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (a *DBAdapter) Query(req Query) (*sql.Rows, error) {
|
|
|
|
db, err := a.Open(req.Db)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2024-08-15 15:52:21 +00:00
|
|
|
if req.Transaction {
|
|
|
|
tx, _ := db.Begin()
|
|
|
|
rows, err := tx.Query(req.Expression, req.Data...)
|
2024-08-17 16:53:58 +00:00
|
|
|
if err != nil {
|
|
|
|
tx.Rollback()
|
|
|
|
return nil, err
|
|
|
|
}
|
2024-08-15 15:52:21 +00:00
|
|
|
tx.Commit()
|
|
|
|
return rows, err
|
|
|
|
}
|
2024-08-11 14:29:42 +00:00
|
|
|
sfmt, err := db.Prepare(req.Expression)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return sfmt.Query(req.Data...)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *DBAdapter) Exec(req Query) (sql.Result, error) {
|
|
|
|
db, err := a.Open(req.Db)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2024-08-15 15:52:21 +00:00
|
|
|
if req.Transaction {
|
|
|
|
tx, _ := db.Begin()
|
|
|
|
result, err := tx.Exec(req.Expression, req.Data...)
|
2024-08-17 16:53:58 +00:00
|
|
|
if err != nil {
|
|
|
|
tx.Rollback()
|
|
|
|
return nil, err
|
|
|
|
}
|
2024-08-15 15:52:21 +00:00
|
|
|
tx.Commit()
|
|
|
|
return result, err
|
|
|
|
}
|
2024-08-11 14:29:42 +00:00
|
|
|
sfmt, err := db.Prepare(req.Expression)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return sfmt.Exec(req.Data...)
|
|
|
|
}
|