Skip to content
Merged

Stc #11

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion common/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ var (

CriticalRateLimitEnable bool
CriticalRateLimitNum = 20
CriticalRateLimitDuration int64 = 20 * 60
CriticalRateLimitDuration int64 = 60

UploadRateLimitNum = 10
UploadRateLimitDuration int64 = 60
Expand Down
2 changes: 1 addition & 1 deletion common/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ func InitEnv() {

CriticalRateLimitEnable = GetEnvOrDefaultBool("CRITICAL_RATE_LIMIT_ENABLE", true)
CriticalRateLimitNum = GetEnvOrDefault("CRITICAL_RATE_LIMIT", 20)
CriticalRateLimitDuration = int64(GetEnvOrDefault("CRITICAL_RATE_LIMIT_DURATION", 20*60))
CriticalRateLimitDuration = int64(GetEnvOrDefault("CRITICAL_RATE_LIMIT_DURATION", 60))

SearchRateLimitEnable = GetEnvOrDefaultBool("SEARCH_RATE_LIMIT_ENABLE", true)
SearchRateLimitNum = GetEnvOrDefault("SEARCH_RATE_LIMIT", 10)
Expand Down
20 changes: 12 additions & 8 deletions middleware/rate-limit.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,14 @@ var defNext = func(c *gin.Context) {
c.Next()
}

func abortRateLimited(c *gin.Context) {
c.JSON(http.StatusTooManyRequests, gin.H{
"success": false,
"message": "请求过于频繁,请稍后再试",
})
c.Abort()
}

func redisRateLimiter(c *gin.Context, maxRequestNum int, duration int64, mark string) {
ctx := context.Background()
rdb := common.RDB
Expand Down Expand Up @@ -53,8 +61,7 @@ func redisRateLimiter(c *gin.Context, maxRequestNum int, duration int64, mark st
// See: https://stackoverflow.com/questions/50970900/why-is-time-since-returning-negative-durations-on-windows
if int64(nowTime.Sub(oldTime).Seconds()) < duration {
rdb.Expire(ctx, key, common.RateLimitKeyExpirationDuration)
c.Status(http.StatusTooManyRequests)
c.Abort()
abortRateLimited(c)
return
} else {
rdb.LPush(ctx, key, time.Now().Format(timeFormat))
Expand All @@ -67,8 +74,7 @@ func redisRateLimiter(c *gin.Context, maxRequestNum int, duration int64, mark st
func memoryRateLimiter(c *gin.Context, maxRequestNum int, duration int64, mark string) {
key := mark + c.ClientIP()
if !inMemoryRateLimiter.Request(key, maxRequestNum, duration) {
c.Status(http.StatusTooManyRequests)
c.Abort()
abortRateLimited(c)
return
}
}
Expand Down Expand Up @@ -143,8 +149,7 @@ func userRateLimitFactory(maxRequestNum int, duration int64, mark string) func(c
}
key := fmt.Sprintf("%s:user:%d", mark, userId)
if !inMemoryRateLimiter.Request(key, maxRequestNum, duration) {
c.Status(http.StatusTooManyRequests)
c.Abort()
abortRateLimited(c)
return
}
}
Expand Down Expand Up @@ -184,8 +189,7 @@ func userRedisRateLimiter(c *gin.Context, maxRequestNum int, duration int64, key
}
if int64(nowTime.Sub(oldTime).Seconds()) < duration {
rdb.Expire(ctx, key, common.RateLimitKeyExpirationDuration)
c.Status(http.StatusTooManyRequests)
c.Abort()
abortRateLimited(c)
return
} else {
rdb.LPush(ctx, key, time.Now().Format(timeFormat))
Expand Down
27 changes: 24 additions & 3 deletions model/log.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ type Log struct {
Ip string `json:"ip" gorm:"index;default:''"`
RequestId string `json:"request_id,omitempty" gorm:"type:varchar(64);index:idx_logs_request_id;default:''"`
Other string `json:"other"`
Balance int `json:"balance" gorm:"-"`
Balance int `json:"balance" gorm:"default:0"`
BalanceValid bool `json:"-" gorm:"default:false"`
}

// don't use iota, avoid change log type value
Expand Down Expand Up @@ -87,7 +88,7 @@ func AttachLogBalances(logs []*Log) {
}
balances := make(map[int]int)
for _, log := range logs {
if log == nil || log.UserId == 0 {
if log == nil || log.UserId == 0 || log.BalanceValid {
continue
}
if _, ok := balances[log.UserId]; ok {
Expand All @@ -105,7 +106,7 @@ func AttachLogBalances(logs []*Log) {
balances[log.UserId] = quota - newerDelta
}
for _, log := range logs {
if log == nil || log.UserId == 0 {
if log == nil || log.UserId == 0 || log.BalanceValid {
continue
}
balance, ok := balances[log.UserId]
Expand All @@ -117,6 +118,20 @@ func AttachLogBalances(logs []*Log) {
}
}

func attachCurrentBalanceSnapshot(log *Log) {
if log == nil || log.UserId == 0 {
return
}
quota, err := GetUserQuota(log.UserId, true)
if err != nil {
common.SysLog(fmt.Sprintf("failed to attach log balance snapshot for user %d: %v", log.UserId, err))
return
}
quota += pendingBatchUpdateValue(BatchUpdateTypeUserQuota, log.UserId)
log.Balance = quota
log.BalanceValid = true
}

func GetLogByTokenId(tokenId int) (logs []*Log, err error) {
err = LOG_DB.Model(&Log{}).Where("token_id = ?", tokenId).Order("id desc").Limit(common.MaxRecentItems).Find(&logs).Error
formatUserLogs(logs, 0)
Expand All @@ -135,6 +150,7 @@ func RecordLog(userId int, logType int, content string) {
Type: logType,
Content: content,
}
attachCurrentBalanceSnapshot(log)
err := LOG_DB.Create(log).Error
if err != nil {
common.SysLog("failed to record log: " + err.Error())
Expand All @@ -154,6 +170,7 @@ func RecordLogWithAdminInfo(userId int, logType int, content string, adminInfo m
Type: logType,
Content: content,
}
attachCurrentBalanceSnapshot(log)
if len(adminInfo) > 0 {
other := map[string]interface{}{
"admin_info": adminInfo,
Expand Down Expand Up @@ -187,6 +204,7 @@ func RecordTopupLog(userId int, content string, callerIp string, paymentMethod s
Ip: callerIp,
Other: common.MapToJsonStr(other),
}
attachCurrentBalanceSnapshot(log)
err := LOG_DB.Create(log).Error
if err != nil {
common.SysLog("failed to record topup log: " + err.Error())
Expand Down Expand Up @@ -231,6 +249,7 @@ func RecordErrorLog(c *gin.Context, userId int, channelId int, modelName string,
RequestId: requestId,
Other: otherStr,
}
attachCurrentBalanceSnapshot(log)
err := LOG_DB.Create(log).Error
if err != nil {
logger.LogError(c, "failed to record log: "+err.Error())
Expand Down Expand Up @@ -292,6 +311,7 @@ func RecordConsumeLog(c *gin.Context, userId int, params RecordConsumeLogParams)
RequestId: requestId,
Other: otherStr,
}
attachCurrentBalanceSnapshot(log)
err := LOG_DB.Create(log).Error
if err != nil {
logger.LogError(c, "failed to record log: "+err.Error())
Expand Down Expand Up @@ -340,6 +360,7 @@ func RecordTaskBillingLog(params RecordTaskBillingLogParams) {
Group: params.Group,
Other: common.MapToJsonStr(params.Other),
}
attachCurrentBalanceSnapshot(log)
err := LOG_DB.Create(log).Error
if err != nil {
common.SysLog("failed to record task billing log: " + err.Error())
Expand Down
9 changes: 9 additions & 0 deletions model/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,15 @@ func addNewRecord(type_ int, id int, value int) {
}
}

func pendingBatchUpdateValue(type_ int, id int) int {
if !common.BatchUpdateEnabled {
return 0
}
batchUpdateLocks[type_].Lock()
defer batchUpdateLocks[type_].Unlock()
return batchUpdateStores[type_][id]
}

func batchUpdate() {
// check if there's any data to update
hasData := false
Expand Down
6 changes: 2 additions & 4 deletions service/billing_session.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,10 +119,8 @@ func (s *BillingSession) Refund(c *gin.Context) {
common.SysLog("error refunding token quota: " + err.Error())
}
}
// 3) 退还用户已用额度(tokenConsumed已经是折扣后的值)
if tokenConsumed > 0 {
model.UpdateUserUsedQuotaAndRequestCount(s.relayInfo.UserId, -tokenConsumed)
}
// 提交阶段失败不会写消费日志,也不会增加 users.used_quota。
// 这里只回滚预扣的资金和令牌额度;异步任务已生成消费日志后的失败退款由 RefundTaskQuota 回退 used_quota。
})
}

Expand Down
10 changes: 5 additions & 5 deletions service/task_billing.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,9 +165,9 @@ func RefundTaskQuota(ctx context.Context, task *model.Task, reason string) {
// 2. 退还令牌额度
taskAdjustTokenQuota(ctx, task, -quota)

//3. 更新用户和渠道的使用额度
//model.UpdateUserUsedQuotaAndRequestCount(task.UserId, -quota)
//model.UpdateChannelUsedQuota(task.ChannelId, -quota)
// 3. 更新用户和渠道的使用额度
model.UpdateUserUsedQuotaAndRequestCount(task.UserId, -quota)
model.UpdateChannelUsedQuota(task.ChannelId, -quota)

// 4. 记录日志
other := taskBillingOther(task)
Expand Down Expand Up @@ -255,8 +255,8 @@ func RecalculateTaskQuota(ctx context.Context, task *model.Task, actualQuota int
} else {
logType = model.LogTypeRefund
logQuota = -quotaDelta
//model.UpdateUserUsedQuotaAndRequestCount(task.UserId, -quotaDelta)
//model.UpdateChannelUsedQuota(task.ChannelId, -quotaDelta)
model.UpdateUserUsedQuotaAndRequestCount(task.UserId, quotaDelta)
model.UpdateChannelUsedQuota(task.ChannelId, quotaDelta)
}
other := taskBillingOther(task)
other["task_id"] = task.TaskID
Expand Down