From 8f45b6f9fbb034017e8c6a9c87b99ac2a48554fb Mon Sep 17 00:00:00 2001 From: zijiren <84728412+zijiren233@users.noreply.github.com> Date: Mon, 3 Mar 2025 14:42:20 +0800 Subject: [PATCH] feat: async clean log (#5434) * chore: unify the start index to page * feat: async clean log * feat: to limit offset utils --- service/aiproxy/common/config/config.go | 12 ++++- service/aiproxy/controller/channel.go | 4 +- service/aiproxy/controller/group.go | 4 +- service/aiproxy/controller/modelconfig.go | 4 +- service/aiproxy/controller/token.go | 8 ++-- service/aiproxy/controller/utils.go | 10 ----- service/aiproxy/main.go | 36 ++++++++++++--- service/aiproxy/model/channel.go | 10 +++-- service/aiproxy/model/consumeerr.go | 8 +--- service/aiproxy/model/group.go | 11 ++--- service/aiproxy/model/log.go | 53 ++++++++++++----------- service/aiproxy/model/modelconfig.go | 10 +++-- service/aiproxy/model/option.go | 10 +++-- service/aiproxy/model/token.go | 10 +++-- service/aiproxy/model/utils.go | 13 ++++++ 15 files changed, 126 insertions(+), 77 deletions(-) diff --git a/service/aiproxy/common/config/config.go b/service/aiproxy/common/config/config.go index 7e0e212f3..636390414 100644 --- a/service/aiproxy/common/config/config.go +++ b/service/aiproxy/common/config/config.go @@ -23,10 +23,11 @@ var ( var ( disableServe atomic.Bool + logStorageHours int64 = 0 // default 0 means no limit saveAllLogDetail atomic.Bool logDetailRequestBodyMaxSize int64 = 128 * 1024 // 128KB logDetailResponseBodyMaxSize int64 = 128 * 1024 // 128KB - logDetailStorageHours int64 = 3 * 24 + logDetailStorageHours int64 = 3 * 24 // 3 days internalToken atomic.Value ) @@ -99,6 +100,15 @@ func SetTimeoutWithModelType(timeout map[int]int64) { timeoutWithModelType.Store(timeout) } +func GetLogStorageHours() int64 { + return atomic.LoadInt64(&logStorageHours) +} + +func SetLogStorageHours(hours int64) { + hours = env.Int64("LOG_STORAGE_HOURS", hours) + atomic.StoreInt64(&logStorageHours, hours) +} + func GetLogDetailStorageHours() int64 { return atomic.LoadInt64(&logDetailStorageHours) } diff --git a/service/aiproxy/controller/channel.go b/service/aiproxy/controller/channel.go index 23c7e1646..1335f4092 100644 --- a/service/aiproxy/controller/channel.go +++ b/service/aiproxy/controller/channel.go @@ -33,7 +33,7 @@ func GetChannels(c *gin.Context) { channelType, _ := strconv.Atoi(c.Query("channel_type")) baseURL := c.Query("base_url") order := c.Query("order") - channels, total, err := model.GetChannels(page*perPage, perPage, id, name, key, channelType, baseURL, order) + channels, total, err := model.GetChannels(page, perPage, id, name, key, channelType, baseURL, order) if err != nil { middleware.ErrorResponse(c, http.StatusOK, err.Error()) return @@ -86,7 +86,7 @@ func SearchChannels(c *gin.Context) { channelType, _ := strconv.Atoi(c.Query("channel_type")) baseURL := c.Query("base_url") order := c.Query("order") - channels, total, err := model.SearchChannels(keyword, page*perPage, perPage, id, name, key, channelType, baseURL, order) + channels, total, err := model.SearchChannels(keyword, page, perPage, id, name, key, channelType, baseURL, order) if err != nil { middleware.ErrorResponse(c, http.StatusOK, err.Error()) return diff --git a/service/aiproxy/controller/group.go b/service/aiproxy/controller/group.go index 8442e30fb..84931912b 100644 --- a/service/aiproxy/controller/group.go +++ b/service/aiproxy/controller/group.go @@ -32,7 +32,7 @@ func (g *GroupResponse) MarshalJSON() ([]byte, error) { func GetGroups(c *gin.Context) { page, perPage := parsePageParams(c) order := c.DefaultQuery("order", "") - groups, total, err := model.GetGroups(page*perPage, perPage, order, false) + groups, total, err := model.GetGroups(page, perPage, order, false) if err != nil { middleware.ErrorResponse(c, http.StatusOK, err.Error()) return @@ -56,7 +56,7 @@ func SearchGroups(c *gin.Context) { page, perPage := parsePageParams(c) order := c.DefaultQuery("order", "") status, _ := strconv.Atoi(c.Query("status")) - groups, total, err := model.SearchGroup(keyword, page*perPage, perPage, order, status) + groups, total, err := model.SearchGroup(keyword, page, perPage, order, status) if err != nil { middleware.ErrorResponse(c, http.StatusOK, err.Error()) return diff --git a/service/aiproxy/controller/modelconfig.go b/service/aiproxy/controller/modelconfig.go index 9925800d6..32ba6a2fd 100644 --- a/service/aiproxy/controller/modelconfig.go +++ b/service/aiproxy/controller/modelconfig.go @@ -11,7 +11,7 @@ import ( func GetModelConfigs(c *gin.Context) { page, perPage := parsePageParams(c) _model := c.Query("model") - configs, total, err := model.GetModelConfigs(page*perPage, perPage, _model) + configs, total, err := model.GetModelConfigs(page, perPage, _model) if err != nil { middleware.ErrorResponse(c, http.StatusOK, err.Error()) return @@ -55,7 +55,7 @@ func SearchModelConfigs(c *gin.Context) { page, perPage := parsePageParams(c) _model := c.Query("model") owner := c.Query("owner") - configs, total, err := model.SearchModelConfigs(keyword, page*perPage, perPage, _model, model.ModelOwner(owner)) + configs, total, err := model.SearchModelConfigs(keyword, page, perPage, _model, model.ModelOwner(owner)) if err != nil { middleware.ErrorResponse(c, http.StatusOK, err.Error()) return diff --git a/service/aiproxy/controller/token.go b/service/aiproxy/controller/token.go index a3dbdc773..4b9aece2e 100644 --- a/service/aiproxy/controller/token.go +++ b/service/aiproxy/controller/token.go @@ -111,7 +111,7 @@ func GetTokens(c *gin.Context) { order := c.Query("order") status, _ := strconv.Atoi(c.Query("status")) - tokens, total, err := model.GetTokens(group, page*perPage, perPage, order, status) + tokens, total, err := model.GetTokens(group, page, perPage, order, status) if err != nil { middleware.ErrorResponse(c, http.StatusOK, err.Error()) return @@ -134,7 +134,7 @@ func GetGroupTokens(c *gin.Context) { order := c.Query("order") status, _ := strconv.Atoi(c.Query("status")) - tokens, total, err := model.GetTokens(group, page*perPage, perPage, order, status) + tokens, total, err := model.GetTokens(group, page, perPage, order, status) if err != nil { middleware.ErrorResponse(c, http.StatusOK, err.Error()) return @@ -155,7 +155,7 @@ func SearchTokens(c *gin.Context) { status, _ := strconv.Atoi(c.Query("status")) group := c.Query("group") - tokens, total, err := model.SearchTokens(group, keyword, page*perPage, perPage, order, status, name, key) + tokens, total, err := model.SearchTokens(group, keyword, page, perPage, order, status, name, key) if err != nil { middleware.ErrorResponse(c, http.StatusOK, err.Error()) return @@ -181,7 +181,7 @@ func SearchGroupTokens(c *gin.Context) { key := c.Query("key") status, _ := strconv.Atoi(c.Query("status")) - tokens, total, err := model.SearchTokens(group, keyword, page*perPage, perPage, order, status, name, key) + tokens, total, err := model.SearchTokens(group, keyword, page, perPage, order, status, name, key) if err != nil { middleware.ErrorResponse(c, http.StatusOK, err.Error()) return diff --git a/service/aiproxy/controller/utils.go b/service/aiproxy/controller/utils.go index f66c5f313..f24260559 100644 --- a/service/aiproxy/controller/utils.go +++ b/service/aiproxy/controller/utils.go @@ -8,16 +8,6 @@ import ( func parsePageParams(c *gin.Context) (page, perPage int) { page, _ = strconv.Atoi(c.Query("p")) - page-- - if page < 0 { - page = 0 - } - perPage, _ = strconv.Atoi(c.Query("per_page")) - if perPage <= 0 { - perPage = 10 - } else if perPage > 100 { - perPage = 100 - } return } diff --git a/service/aiproxy/main.go b/service/aiproxy/main.go index ec9a973a8..ef0735f45 100644 --- a/service/aiproxy/main.go +++ b/service/aiproxy/main.go @@ -2,6 +2,7 @@ package main import ( "context" + "errors" "fmt" stdlog "log" "net/http" @@ -134,13 +135,36 @@ func setupHTTPServer() (*http.Server, *gin.Engine) { }, server } -func autoTestBannedModels() { +func autoTestBannedModels(ctx context.Context) { log.Info("auto test banned models start") ticker := time.NewTicker(time.Second * 15) defer ticker.Stop() - for range ticker.C { - controller.AutoTestBannedModels() + for { + select { + case <-ctx.Done(): + return + case <-ticker.C: + controller.AutoTestBannedModels() + } + } +} + +func cleanLog(ctx context.Context) { + log.Info("clean log start") + ticker := time.NewTicker(time.Minute * 15) + defer ticker.Stop() + + for { + select { + case <-ctx.Done(): + return + case <-ticker.C: + err := model.CleanLog() + if err != nil { + log.Errorf("clean log failed: %s", err) + } + } } } @@ -165,12 +189,14 @@ func main() { go func() { log.Infof("server started on http://localhost:%s", srv.Addr[1:]) - if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { + if err := srv.ListenAndServe(); err != nil && + !errors.Is(err, http.ErrServerClosed) { log.Fatal("failed to start HTTP server: " + err.Error()) } }() - go autoTestBannedModels() + go autoTestBannedModels(ctx) + go cleanLog(ctx) <-ctx.Done() diff --git a/service/aiproxy/model/channel.go b/service/aiproxy/model/channel.go index e6a1da8f6..6f89a2e82 100644 --- a/service/aiproxy/model/channel.go +++ b/service/aiproxy/model/channel.go @@ -152,7 +152,7 @@ func GetAllChannels() (channels []*Channel, err error) { return channels, err } -func GetChannels(startIdx int, num int, id int, name string, key string, channelType int, baseURL string, order string) (channels []*Channel, total int64, err error) { +func GetChannels(page int, perPage int, id int, name string, key string, channelType int, baseURL string, order string) (channels []*Channel, total int64, err error) { tx := DB.Model(&Channel{}) if id != 0 { tx = tx.Where("id = ?", id) @@ -176,11 +176,12 @@ func GetChannels(startIdx int, num int, id int, name string, key string, channel if total <= 0 { return nil, 0, nil } - err = tx.Order(getChannelOrder(order)).Limit(num).Offset(startIdx).Find(&channels).Error + limit, offset := toLimitOffset(page, perPage) + err = tx.Order(getChannelOrder(order)).Limit(limit).Offset(offset).Find(&channels).Error return channels, total, err } -func SearchChannels(keyword string, startIdx int, num int, id int, name string, key string, channelType int, baseURL string, order string) (channels []*Channel, total int64, err error) { +func SearchChannels(keyword string, page int, perPage int, id int, name string, key string, channelType int, baseURL string, order string) (channels []*Channel, total int64, err error) { tx := DB.Model(&Channel{}) // Handle exact match conditions for non-zero values @@ -257,7 +258,8 @@ func SearchChannels(keyword string, startIdx int, num int, id int, name string, if total <= 0 { return nil, 0, nil } - err = tx.Order(getChannelOrder(order)).Limit(num).Offset(startIdx).Find(&channels).Error + limit, offset := toLimitOffset(page, perPage) + err = tx.Order(getChannelOrder(order)).Limit(limit).Offset(offset).Find(&channels).Error return channels, total, err } diff --git a/service/aiproxy/model/consumeerr.go b/service/aiproxy/model/consumeerr.go index 9843b7393..b8a8c06f9 100644 --- a/service/aiproxy/model/consumeerr.go +++ b/service/aiproxy/model/consumeerr.go @@ -138,12 +138,8 @@ func SearchConsumeError(keyword string, requestID string, group string, tokenNam return nil, 0, nil } - page-- - if page < 0 { - page = 0 - } - var errors []*ConsumeError - err = tx.Order(getLogOrder(order)).Limit(perPage).Offset(page * perPage).Find(&errors).Error + limit, offset := toLimitOffset(page, perPage) + err = tx.Order(getLogOrder(order)).Limit(limit).Offset(offset).Find(&errors).Error return errors, total, err } diff --git a/service/aiproxy/model/group.go b/service/aiproxy/model/group.go index f6922381b..83e581afe 100644 --- a/service/aiproxy/model/group.go +++ b/service/aiproxy/model/group.go @@ -55,7 +55,7 @@ func getGroupOrder(order string) string { } } -func GetGroups(startIdx int, num int, order string, onlyDisabled bool) (groups []*Group, total int64, err error) { +func GetGroups(page int, perPage int, order string, onlyDisabled bool) (groups []*Group, total int64, err error) { tx := DB.Model(&Group{}) if onlyDisabled { tx = tx.Where("status = ?", GroupStatusDisabled) @@ -69,8 +69,8 @@ func GetGroups(startIdx int, num int, order string, onlyDisabled bool) (groups [ if total <= 0 { return nil, 0, nil } - - err = tx.Order(getGroupOrder(order)).Limit(num).Offset(startIdx).Find(&groups).Error + limit, offset := toLimitOffset(page, perPage) + err = tx.Order(getGroupOrder(order)).Limit(limit).Offset(offset).Find(&groups).Error return groups, total, err } @@ -242,7 +242,7 @@ func UpdateGroupStatus(id string, status int) (err error) { return HandleUpdateResult(result, ErrGroupNotFound) } -func SearchGroup(keyword string, startIdx int, num int, order string, status int) (groups []*Group, total int64, err error) { +func SearchGroup(keyword string, page int, perPage int, order string, status int) (groups []*Group, total int64, err error) { tx := DB.Model(&Group{}) if status != 0 { tx = tx.Where("status = ?", status) @@ -259,7 +259,8 @@ func SearchGroup(keyword string, startIdx int, num int, order string, status int if total <= 0 { return nil, 0, nil } - err = tx.Order(getGroupOrder(order)).Limit(num).Offset(startIdx).Find(&groups).Error + limit, offset := toLimitOffset(page, perPage) + err = tx.Order(getGroupOrder(order)).Limit(limit).Offset(offset).Find(&groups).Error return groups, total, err } diff --git a/service/aiproxy/model/log.go b/service/aiproxy/model/log.go index 9d1f13567..50ffa70cb 100644 --- a/service/aiproxy/model/log.go +++ b/service/aiproxy/model/log.go @@ -12,7 +12,6 @@ import ( "github.com/labring/sealos/service/aiproxy/common" "github.com/labring/sealos/service/aiproxy/common/config" "github.com/shopspring/decimal" - log "github.com/sirupsen/logrus" "golang.org/x/sync/errgroup" "gorm.io/gorm" ) @@ -204,7 +203,28 @@ func GetGroupLogDetail(logID int, group string) (*RequestDetail, error) { return &detail, nil } -func cleanRequestDetail() error { +func CleanLog() error { + err := cleanLog() + if err != nil { + return err + } + return cleanLogDetail() +} + +func cleanLog() error { + logStorageHours := config.GetLogStorageHours() + if logStorageHours <= 0 { + return nil + } + return LogDB. + Where( + "created_at < ?", + time.Now().Add(-time.Duration(logStorageHours)*time.Hour), + ). + Delete(&Log{}).Error +} + +func cleanLogDetail() error { detailStorageHours := config.GetLogDetailStorageHours() if detailStorageHours <= 0 { return nil @@ -237,15 +257,6 @@ func RecordConsumeLog( ip string, requestDetail *RequestDetail, ) error { - defer func() { - if requestDetail == nil { - return - } - err := cleanRequestDetail() - if err != nil { - log.Errorf("delete request detail failed: %s", err) - } - }() log := &Log{ RequestID: requestID, RequestAt: requestAt, @@ -404,11 +415,6 @@ func getLogs( }) g.Go(func() error { - page-- - if page < 0 { - page = 0 - } - query := buildGetLogsQuery( group, startTimestamp, @@ -431,10 +437,11 @@ func getLogs( }) } + limit, offset := toLimitOffset(page, perPage) return query. Order(getLogOrder(order)). - Limit(perPage). - Offset(page * perPage). + Limit(limit). + Offset(offset). Find(&logs).Error }) @@ -721,11 +728,6 @@ func searchLogs( }) g.Go(func() error { - page-- - if page < 0 { - page = 0 - } - query := buildSearchLogsQuery( group, keyword, @@ -750,10 +752,11 @@ func searchLogs( }) } + limit, offset := toLimitOffset(page, perPage) return query. Order(getLogOrder(order)). - Limit(perPage). - Offset(page * perPage). + Limit(limit). + Offset(offset). Find(&logs).Error }) diff --git a/service/aiproxy/model/modelconfig.go b/service/aiproxy/model/modelconfig.go index 5727e17f2..32162ed58 100644 --- a/service/aiproxy/model/modelconfig.go +++ b/service/aiproxy/model/modelconfig.go @@ -78,7 +78,7 @@ func (c *ModelConfig) SupportFormats() ([]string, bool) { return GetModelConfigStringSlice(c.Config, ModelConfigSupportFormatsKey) } -func GetModelConfigs(startIdx int, num int, model string) (configs []*ModelConfig, total int64, err error) { +func GetModelConfigs(page int, perPage int, model string) (configs []*ModelConfig, total int64, err error) { tx := DB.Model(&ModelConfig{}) if model != "" { tx = tx.Where("model = ?", model) @@ -90,7 +90,8 @@ func GetModelConfigs(startIdx int, num int, model string) (configs []*ModelConfi if total <= 0 { return nil, 0, nil } - err = tx.Order("created_at desc").Limit(num).Offset(startIdx).Find(&configs).Error + limit, offset := toLimitOffset(page, perPage) + err = tx.Order("created_at desc").Limit(limit).Offset(offset).Find(&configs).Error return configs, total, err } @@ -112,7 +113,7 @@ func GetModelConfig(model string) (*ModelConfig, error) { return config, HandleNotFound(err, ErrModelConfigNotFound) } -func SearchModelConfigs(keyword string, startIdx int, num int, model string, owner ModelOwner) (configs []*ModelConfig, total int64, err error) { +func SearchModelConfigs(keyword string, page int, perPage int, model string, owner ModelOwner) (configs []*ModelConfig, total int64, err error) { tx := DB.Model(&ModelConfig{}).Where("model LIKE ?", "%"+keyword+"%") if model != "" { tx = tx.Where("model = ?", model) @@ -153,7 +154,8 @@ func SearchModelConfigs(keyword string, startIdx int, num int, model string, own if total <= 0 { return nil, 0, nil } - err = tx.Order("created_at desc").Limit(num).Offset(startIdx).Find(&configs).Error + limit, offset := toLimitOffset(page, perPage) + err = tx.Order("created_at desc").Limit(limit).Offset(offset).Find(&configs).Error return configs, total, err } diff --git a/service/aiproxy/model/option.go b/service/aiproxy/model/option.go index 7b557eee0..1d21c3102 100644 --- a/service/aiproxy/model/option.go +++ b/service/aiproxy/model/option.go @@ -56,6 +56,7 @@ func InitOption2DB() error { } func initOptionMap() error { + optionMap["LogStorageHours"] = strconv.FormatInt(config.GetLogStorageHours(), 10) optionMap["LogDetailStorageHours"] = strconv.FormatInt(config.GetLogDetailStorageHours(), 10) optionMap["SaveAllLogDetail"] = strconv.FormatBool(config.GetSaveAllLogDetail()) optionMap["LogDetailRequestBodyMaxSize"] = strconv.FormatInt(config.GetLogDetailRequestBodyMaxSize(), 10) @@ -189,14 +190,17 @@ func updateOption(key string, value string, isInit bool) (err error) { switch key { case "InternalToken": config.SetInternalToken(value) + case "LogStorageHours": + logStorageHours, err := strconv.ParseInt(value, 10, 64) + if err != nil { + return err + } + config.SetLogStorageHours(logStorageHours) case "LogDetailStorageHours": logDetailStorageHours, err := strconv.ParseInt(value, 10, 64) if err != nil { return err } - if logDetailStorageHours < 0 { - return errors.New("log detail storage hours must be greater than 0") - } config.SetLogDetailStorageHours(logDetailStorageHours) case "SaveAllLogDetail": config.SetSaveAllLogDetail(toBool(value)) diff --git a/service/aiproxy/model/token.go b/service/aiproxy/model/token.go index 78f8ff7e3..9f281e001 100644 --- a/service/aiproxy/model/token.go +++ b/service/aiproxy/model/token.go @@ -86,7 +86,7 @@ func InsertToken(token *Token, autoCreateGroup bool) error { return nil } -func GetTokens(group string, startIdx int, num int, order string, status int) (tokens []*Token, total int64, err error) { +func GetTokens(group string, page int, perPage int, order string, status int) (tokens []*Token, total int64, err error) { tx := DB.Model(&Token{}) if group != "" { tx = tx.Where("group_id = ?", group) @@ -104,11 +104,12 @@ func GetTokens(group string, startIdx int, num int, order string, status int) (t if total <= 0 { return nil, 0, nil } - err = tx.Order(getTokenOrder(order)).Limit(num).Offset(startIdx).Find(&tokens).Error + limit, offset := toLimitOffset(page, perPage) + err = tx.Order(getTokenOrder(order)).Limit(limit).Offset(offset).Find(&tokens).Error return tokens, total, err } -func SearchTokens(group string, keyword string, startIdx int, num int, order string, status int, name string, key string) (tokens []*Token, total int64, err error) { +func SearchTokens(group string, keyword string, page int, perPage int, order string, status int, name string, key string) (tokens []*Token, total int64, err error) { tx := DB.Model(&Token{}) if group != "" { tx = tx.Where("group_id = ?", group) @@ -163,7 +164,8 @@ func SearchTokens(group string, keyword string, startIdx int, num int, order str if total <= 0 { return nil, 0, nil } - err = tx.Order(getTokenOrder(order)).Limit(num).Offset(startIdx).Find(&tokens).Error + limit, offset := toLimitOffset(page, perPage) + err = tx.Order(getTokenOrder(order)).Limit(limit).Offset(offset).Find(&tokens).Error return tokens, total, err } diff --git a/service/aiproxy/model/utils.go b/service/aiproxy/model/utils.go index 148454496..49293ae15 100644 --- a/service/aiproxy/model/utils.go +++ b/service/aiproxy/model/utils.go @@ -151,3 +151,16 @@ func String2Int(keyword string) int { } return i } + +func toLimitOffset(page int, perPage int) (limit int, offset int) { + page-- + if page < 0 { + page = 0 + } + if perPage <= 0 { + perPage = 10 + } else if perPage > 100 { + perPage = 100 + } + return perPage, page * perPage +}