Merge pull request #10 from 30x/XAPID-1089
Add periodic DB Conn Info.
diff --git a/README.md b/README.md
index e3519f2..7a4d0cd 100644
--- a/README.md
+++ b/README.md
@@ -52,3 +52,8 @@
## Running Tests
go test $(glide novendor)
+
+## apid.Data() service
+This service provides the primitives to perform SQL operations on the database. It also provides the
+provision to alter DB connection pool settings via ConfigDBMaxConns, ConfigDBIdleConns andonfigDBConnsTimeout configuration parameters. They currently are defaulted to 1000, 1000 and 120 seconds respectively.
+More details on this can be found at https://golang.org/pkg/database/sql
diff --git a/api/api.go b/api/api.go
index 32a28aa..d378de1 100644
--- a/api/api.go
+++ b/api/api.go
@@ -27,10 +27,16 @@
)
const (
- configAPIListen = "api_listen"
- configExpVarPath = "api_expvar_path"
- configReadyPath = "api_ready"
- configHealthyPath = "api_healthy"
+ configAPIListen = "api_listen"
+ configExpVarPath = "api_expvar_path"
+ configReadyPath = "api_ready"
+ configHealthyPath = "api_healthy"
+ ConfigDBMaxConns = "db_config_max_conns"
+ ConfigDBIdleConns = "db_config_idle_conns"
+ ConfigDBConnsTimeout = "db_config_conns_timeout_seconds"
+ dbDefaultMaxConnsLimit = 1000
+ dbDefaultIdleConnsLimit = 1000
+ dbMaxConnTimeoutLimit = 120
)
var log apid.LogService
@@ -45,6 +51,10 @@
config.SetDefault(configReadyPath, "/ready")
config.SetDefault(configHealthyPath, "/healthy")
+ config.SetDefault(ConfigDBMaxConns, dbDefaultMaxConnsLimit)
+ config.SetDefault(ConfigDBIdleConns, dbDefaultIdleConnsLimit)
+ config.SetDefault(ConfigDBConnsTimeout, dbMaxConnTimeoutLimit)
+
listen := config.GetString(configAPIListen)
h, p, err := net.SplitHostPort(listen)
if err != nil {
diff --git a/data/data.go b/data/data.go
index 0c24d65..7fed43d 100644
--- a/data/data.go
+++ b/data/data.go
@@ -17,23 +17,27 @@
import (
"database/sql"
"fmt"
+ "github.com/30x/apid-core"
+ "github.com/30x/apid-core/api"
+ "github.com/30x/apid-core/data/wrap"
+ "github.com/30x/apid-core/logger"
+ "github.com/Sirupsen/logrus"
+ "github.com/mattn/go-sqlite3"
"os"
"path"
"runtime"
+ "strings"
"sync"
-
- "github.com/30x/apid-core"
- "github.com/30x/apid-core/data/wrap"
- "github.com/mattn/go-sqlite3"
+ "time"
)
const (
- configDataDriverKey = "data_driver"
- configDataSourceKey = "data_source"
- configDataPathKey = "data_path"
-
- commonDBID = "common"
- commonDBVersion = "base"
+ configDataDriverKey = "data_driver"
+ configDataSourceKey = "data_source"
+ configDataPathKey = "data_path"
+ statCollectionInterval = 10
+ commonDBID = "common"
+ commonDBVersion = "base"
defaultTraceLevel = "warn"
)
@@ -41,7 +45,12 @@
var log, dbTraceLog apid.LogService
var config apid.ConfigService
-var dbMap = make(map[string]*sql.DB)
+type dbMapInfo struct {
+ db *sql.DB
+ closed chan bool
+}
+
+var dbMap = make(map[string]*dbMapInfo)
var dbMapSync sync.RWMutex
func CreateDataService() apid.DataService {
@@ -97,12 +106,17 @@
dbMapSync.Lock()
defer dbMapSync.Unlock()
- db := dbMap[versionedID]
- if db != nil {
- dbMap[versionedID] = nil
+ dbm := dbMap[versionedID]
+ if dbm != nil && dbm.db != nil {
+ if strings.EqualFold(config.GetString(logger.ConfigLevel), logrus.DebugLevel.String()) {
+ dbm.closed <- true
+ }
log.Warn("SETTING FINALIZER")
finalizer := Delete(versionedID)
- runtime.SetFinalizer(db, finalizer)
+ runtime.SetFinalizer(dbm.db, finalizer)
+ dbMap[versionedID] = nil
+ } else {
+ log.Error("Cannot find DB handle for ver {%s} to release", version)
}
return
@@ -110,23 +124,19 @@
func (d *dataService) dbVersionForID(id, version string) (db *sql.DB, err error) {
+ var stoplogchan chan bool
versionedID := VersionedDBID(id, version)
dbMapSync.RLock()
- db = dbMap[versionedID]
+ dbm := dbMap[versionedID]
dbMapSync.RUnlock()
- if db != nil {
- return
+ if dbm != nil && dbm.db != nil {
+ return dbm.db, nil
}
dbMapSync.Lock()
defer dbMapSync.Unlock()
- db = dbMap[versionedID]
- if db != nil {
- return
- }
-
dataPath := DBPath(versionedID)
if err = os.MkdirAll(path.Dir(dataPath), 0700); err != nil {
@@ -171,8 +181,16 @@
log.Errorf("error enabling foreign_keys: %s", err)
return
}
+ if strings.EqualFold(config.GetString(logger.ConfigLevel),
+ logrus.DebugLevel.String()) {
+ stoplogchan = logDBInfo(versionedID, db)
+ }
- dbMap[versionedID] = db
+ db.SetMaxOpenConns(config.GetInt(api.ConfigDBMaxConns))
+ db.SetMaxIdleConns(config.GetInt(api.ConfigDBIdleConns))
+ db.SetConnMaxLifetime(time.Duration(config.GetInt(api.ConfigDBConnsTimeout)) * time.Second)
+ dbInfo := dbMapInfo{db: db, closed: stoplogchan}
+ dbMap[versionedID] = &dbInfo
return
}
@@ -200,3 +218,20 @@
relativeDataPath := config.GetString(configDataPathKey)
return path.Join(storagePath, relativeDataPath, id, "sqlite3")
}
+
+func logDBInfo(versionedId string, db *sql.DB) chan bool {
+ stop := make(chan bool)
+ go func() {
+ for {
+ select {
+ case <-time.After(time.Duration(statCollectionInterval * time.Second)):
+ log.Debugf("Current number of open DB connections for ver {%s} is {%d}",
+ versionedId, db.Stats().OpenConnections)
+ case <-stop:
+ log.Debugf("Stop DB conn. logging for ver {%s}", versionedId)
+ return
+ }
+ }
+ }()
+ return stop
+}
diff --git a/logger/logger.go b/logger/logger.go
index 39f6e8b..7c16b17 100644
--- a/logger/logger.go
+++ b/logger/logger.go
@@ -24,7 +24,7 @@
)
const (
- configLevel = "log_level"
+ ConfigLevel = "log_level"
defaultLevel = logrus.ErrorLevel
@@ -42,10 +42,10 @@
func Base() apid.LogService {
if std == nil {
config = apid.Config()
- config.SetDefault(configLevel, defaultLevel.String())
- logLevel := config.GetString(configLevel)
+ config.SetDefault(ConfigLevel, defaultLevel.String())
+ logLevel := config.GetString(ConfigLevel)
fmt.Printf("Base log level: %s\n", logLevel)
- std = NewLogger(configLevel, logLevel)
+ std = NewLogger(ConfigLevel, logLevel)
}
return std
}
@@ -62,7 +62,7 @@
// note: config module xx log level using config var: xx_log_level = "debug"
func (l *logger) ForModule(name string) apid.LogService {
- configKey := fmt.Sprintf("%s_%s", name, configLevel)
+ configKey := fmt.Sprintf("%s_%s", name, ConfigLevel)
log := NewLogger(configKey, config.GetString(configKey)).WithField(moduleField, name)
std.Debugf("created logger '%s' at level %s", name, log.(loggerPlus).Level())
return log