diff --git a/internal/db/models/db_node_initializer.go b/internal/db/models/db_node_initializer.go index 50aa87a6..5ed9939e 100644 --- a/internal/db/models/db_node_initializer.go +++ b/internal/db/models/db_node_initializer.go @@ -2,6 +2,7 @@ package models import ( "fmt" + dbutils "github.com/TeaOSLab/EdgeAPI/internal/db/utils" "github.com/TeaOSLab/EdgeAPI/internal/goman" "github.com/TeaOSLab/EdgeAPI/internal/remotelogs" "github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs" @@ -29,8 +30,9 @@ var httpAccessLogDAOMapping = map[int64]*HTTPAccessLogDAOWrapper{} // dbNodeId = // HTTPAccessLogDAOWrapper HTTP访问日志DAO type HTTPAccessLogDAOWrapper struct { - DAO *HTTPAccessLogDAO - NodeId int64 + DAO *HTTPAccessLogDAO + NodeId int64 + IsLocal bool } func init() { @@ -195,7 +197,7 @@ func (this *DBNodeInitializer) loop() error { continue } - daoObject := dbs.DAOObject{ + var daoObject = dbs.DAOObject{ Instance: db, DB: node.Name + "(id:" + strconv.Itoa(int(node.Id)) + ")", Table: tableDef.Name, @@ -210,12 +212,13 @@ func (this *DBNodeInitializer) loop() error { accessLogLocker.Lock() accessLogDBMapping[nodeId] = db - dao := &HTTPAccessLogDAO{ + var dao = &HTTPAccessLogDAO{ DAOObject: daoObject, } httpAccessLogDAOMapping[nodeId] = &HTTPAccessLogDAOWrapper{ - DAO: dao, - NodeId: nodeId, + DAO: dao, + NodeId: nodeId, + IsLocal: dbutils.IsLocalAddr(node.Host), } accessLogLocker.Unlock() } diff --git a/internal/db/models/http_access_log_dao.go b/internal/db/models/http_access_log_dao.go index c87f764a..c04f9618 100644 --- a/internal/db/models/http_access_log_dao.go +++ b/internal/db/models/http_access_log_dao.go @@ -161,16 +161,27 @@ func (this *HTTPAccessLogDAO) DumpAccessLogsFromQueue(size int) (hasMore bool, e size = 100 } + if len(oldAccessLogQueue) == 0 && len(accessLogQueue) == 0 { + return false, nil + } + var dao = randomHTTPAccessLogDAO() if dao == nil { dao = &HTTPAccessLogDAOWrapper{ DAO: SharedHTTPAccessLogDAO, NodeId: 0, } - } - if len(oldAccessLogQueue) == 0 && len(accessLogQueue) == 0 { - return false, nil + // 检查本地数据库空间 + if dbutils.IsLocalDatabase && !dbutils.HasFreeSpace { + return false, errors.New("dump accesslog failed: there is no enough space left for database (" + dbutils.LocalDatabaseDataDir + ")") + } + } else if dao.IsLocal { + // 检查本地数据库空间 + // 我们假定本地只能安装一个数据库,访问日志中的数据库和当前API连接的数据库一致 + if !dbutils.HasFreeSpace { + return true, errors.New("dump accesslog failed: there is no enough space left for database (" + dbutils.LocalDatabaseDataDir + ")") + } } // 开始事务 diff --git a/internal/db/utils/disk.go b/internal/db/utils/disk.go new file mode 100644 index 00000000..90ae6649 --- /dev/null +++ b/internal/db/utils/disk.go @@ -0,0 +1,73 @@ +// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn . + +package dbutils + +import ( + "github.com/TeaOSLab/EdgeAPI/internal/goman" + "github.com/go-sql-driver/mysql" + "github.com/iwind/TeaGo/dbs" + "golang.org/x/sys/unix" + "time" +) + +const minFreeSpaceGB = 3 + +var HasFreeSpace = true +var IsLocalDatabase = false +var LocalDatabaseDataDir = "" + +func init() { + var ticker = time.NewTicker(5 * time.Minute) + + dbs.OnReadyDone(func() { + goman.New(func() { + for range ticker.C { + HasFreeSpace = CheckHasFreeSpace() + } + }) + }) +} + +// CheckHasFreeSpace 检查当前数据库是否有剩余空间 +func CheckHasFreeSpace() bool { + db, _ := dbs.Default() + if db == nil { + return false + } + + config, _ := db.Config() + if config == nil { + return false + } + + dsnConfig, _ := mysql.ParseDSN(config.Dsn) + if dsnConfig == nil { + return false + } + + if IsLocalAddr(dsnConfig.Addr) { + IsLocalDatabase = true + + // only for local database + one, err := db.FindOne("SHOW VARIABLES WHERE variable_name='datadir'") + if err != nil || len(one) == 0 { + return true + } + + var dir = one.GetString("Value") + if len(dir) == 0 { + return true + } + LocalDatabaseDataDir = dir + + var stat unix.Statfs_t + err = unix.Statfs(dir, &stat) + if err != nil { + return true + } + + var availableSpace = (stat.Bavail * uint64(stat.Bsize)) / (1 << 30) // GB + return availableSpace > minFreeSpaceGB + } + return true +} diff --git a/internal/db/utils/disk_test.go b/internal/db/utils/disk_test.go new file mode 100644 index 00000000..72697694 --- /dev/null +++ b/internal/db/utils/disk_test.go @@ -0,0 +1,14 @@ +// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn . + +package dbutils_test + +import ( + dbutils "github.com/TeaOSLab/EdgeAPI/internal/db/utils" + _ "github.com/iwind/TeaGo/bootstrap" + "testing" +) + +func TestHasFreeSpace(t *testing.T) { + t.Log(dbutils.CheckHasFreeSpace()) + t.Log(dbutils.LocalDatabaseDataDir) +} diff --git a/internal/db/utils/utils.go b/internal/db/utils/utils.go index d8d2e7c5..686dc21a 100644 --- a/internal/db/utils/utils.go +++ b/internal/db/utils/utils.go @@ -2,6 +2,7 @@ package dbutils import ( "github.com/iwind/TeaGo/dbs" + "net" "strings" ) @@ -69,3 +70,26 @@ func SetGlobalVarMax(db *dbs.DB, variableName string, maxValue int) error { } return nil } + +// IsLocalAddr 是否为本地数据库 +func IsLocalAddr(addr string) bool { + var host = addr + if strings.Contains(addr, ":") { + host, _, _ = net.SplitHostPort(addr) + if len(host) == 0 { + host = addr + } + } + + if host == "127.0.0.1" || host == "::1" || host == "localhost" { + return true + } + + interfaceAddrs, _ := net.InterfaceAddrs() + for _, interfaceAddr := range interfaceAddrs { + if strings.HasPrefix(interfaceAddr.String(), host+"/") { + return true + } + } + return false +} diff --git a/internal/db/utils/utils_test.go b/internal/db/utils/utils_test.go index 5d2acdb1..db1d1b2b 100644 --- a/internal/db/utils/utils_test.go +++ b/internal/db/utils/utils_test.go @@ -4,6 +4,7 @@ package dbutils_test import ( dbutils "github.com/TeaOSLab/EdgeAPI/internal/db/utils" + "github.com/iwind/TeaGo/assert" "testing" ) @@ -12,3 +13,13 @@ func TestQuoteLike(t *testing.T) { t.Log(s + " => " + dbutils.QuoteLike(s)) } } + +func TestIsLocalAddr(t *testing.T) { + var a = assert.NewAssertion(t) + a.IsTrue(dbutils.IsLocalAddr("127.0.0.1")) + a.IsTrue(dbutils.IsLocalAddr("localhost")) + a.IsTrue(dbutils.IsLocalAddr("::1")) + a.IsTrue(dbutils.IsLocalAddr("127.0.0.1:3306")) + a.IsFalse(dbutils.IsLocalAddr("192.168.2.200")) + a.IsFalse(dbutils.IsLocalAddr("192.168.2.200:3306")) +}