mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-10-04 23:16:37 +08:00
后端与全仓代码质量清理(golangci 扩展集 · 测试质量 · 并发安全 · 文档同步)
代码质量全量清理,零行为变化:golangci 扩展集 13 类 linter(gosec/modernize/perfsprint/canonicalheader/usestdlibvars/wastedassign/intrange/errorlint/forcetypeassert/recvcheck/exhaustive/unparam)全量修复,测试代码质量(testifylint/thelper/usetesting)25→0,frpc 进程生命周期真 bug(进程组击杀)、全仓 go test -race 6 类数据竞争(含 1 个生产竞争)、SPDX license 头补齐 131 文件、前端测试套件 next-intl 迁移后 44 失败→全绿、过期 swagger 文档重新生成、pnpm-workspace 构建审批。 Experiments: #2-#17, #18, #20, #21, #23 Metric: total_issues 108 → 8 (-92.6%)
This commit is contained in:
@@ -6,6 +6,7 @@ package analytics
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
@@ -15,7 +16,7 @@ import (
|
||||
|
||||
func userAccessLogConn() error {
|
||||
if db.ChConn == nil {
|
||||
return fmt.Errorf("clickhouse native connection is not initialized")
|
||||
return errors.New("clickhouse native connection is not initialized")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ func GetDailyTrend(ctx context.Context, days int) ([]DailyTrend, error) {
|
||||
defer func() { _ = rows.Close() }()
|
||||
|
||||
trendMap := make(map[string]uint64, days)
|
||||
for i := 0; i < days; i++ {
|
||||
for i := range days {
|
||||
dateStr := time.Now().AddDate(0, 0, -i).Format("2006-01-02")
|
||||
trendMap[dateStr] = 0
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ package analytics
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
db "github.com/Rain-kl/Wavelet/internal/infra/persistence"
|
||||
@@ -17,7 +18,7 @@ func BatchInsert(ctx context.Context, logs []analyticsmodel.UserAccessLog) error
|
||||
return nil
|
||||
}
|
||||
if db.ChConn == nil {
|
||||
return fmt.Errorf("clickhouse connection is not initialized")
|
||||
return errors.New("clickhouse connection is not initialized")
|
||||
}
|
||||
|
||||
batch, err := db.ChConn.PrepareBatch(ctx, analyticsmodel.UserAccessLog{}.BatchInsertSQL())
|
||||
|
||||
@@ -5,6 +5,7 @@ package analytics
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/Rain-kl/Wavelet/internal/infra/config"
|
||||
@@ -19,7 +20,7 @@ type ClickHouseOperationalStats = analyticsmodel.ClickHouseOperationalStats
|
||||
// GetClickHouseOperationalStats returns operational metrics for the configured database.
|
||||
func GetClickHouseOperationalStats(ctx context.Context) (*ClickHouseOperationalStats, error) {
|
||||
if db.ChConn == nil {
|
||||
return nil, fmt.Errorf("clickhouse native connection is not initialized")
|
||||
return nil, errors.New("clickhouse native connection is not initialized")
|
||||
}
|
||||
database := config.Config.ClickHouse.Database
|
||||
stats := &ClickHouseOperationalStats{Database: database}
|
||||
|
||||
@@ -5,6 +5,7 @@ package analytics
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -19,7 +20,7 @@ type NodeAccessLogRegionCount = analyticsmodel.NodeAccessLogRegionCount
|
||||
|
||||
func nodeAccessLogConn() (driver.Conn, error) {
|
||||
if db.ChConn == nil {
|
||||
return nil, fmt.Errorf("clickhouse connection is not initialized")
|
||||
return nil, errors.New("clickhouse connection is not initialized")
|
||||
}
|
||||
return db.ChConn, nil
|
||||
}
|
||||
|
||||
@@ -50,8 +50,8 @@ func TestBatchInsertNodeAccessLogs_UsesModelBatchSQL(t *testing.T) {
|
||||
require.Len(t, mockBatch.rows, 1)
|
||||
assert.Equal(t, "node-a", mockBatch.rows[0][1])
|
||||
require.Len(t, mockBatch.rows[0], 14)
|
||||
assert.Equal(t, "", mockBatch.rows[0][7]) // user_agent
|
||||
assert.Equal(t, "", mockBatch.rows[0][8]) // cache_status
|
||||
assert.Empty(t, mockBatch.rows[0][7]) // user_agent
|
||||
assert.Empty(t, mockBatch.rows[0][8]) // cache_status
|
||||
assert.Equal(t, uint64(2048), mockBatch.rows[0][10]) // bytes_sent
|
||||
assert.Equal(t, uint64(0), mockBatch.rows[0][11]) // request_length
|
||||
assert.Equal(t, uint32(0), mockBatch.rows[0][12]) // request_time_ms
|
||||
|
||||
@@ -5,6 +5,7 @@ package analytics
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -20,7 +21,7 @@ func BatchInsertNodeAccessLogs(ctx context.Context, logs []analyticsmodel.NodeAc
|
||||
return nil
|
||||
}
|
||||
if db.ChConn == nil {
|
||||
return fmt.Errorf("clickhouse connection is not initialized")
|
||||
return errors.New("clickhouse connection is not initialized")
|
||||
}
|
||||
|
||||
batch, err := db.ChConn.PrepareBatch(ctx, analyticsmodel.NodeAccessLog{}.BatchInsertSQL())
|
||||
|
||||
@@ -5,8 +5,9 @@ package analytics
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
"slices"
|
||||
"time"
|
||||
|
||||
"github.com/ClickHouse/clickhouse-go/v2/lib/driver"
|
||||
@@ -16,7 +17,7 @@ import (
|
||||
|
||||
func observabilityConn() (driver.Conn, error) {
|
||||
if db.ChConn == nil {
|
||||
return nil, fmt.Errorf("clickhouse connection is not initialized")
|
||||
return nil, errors.New("clickhouse connection is not initialized")
|
||||
}
|
||||
return db.ChConn, nil
|
||||
}
|
||||
@@ -367,7 +368,7 @@ func mergeNodeMetricHourlyPreferRollup(rollup, raw []NodeMetricHourly) []NodeMet
|
||||
}
|
||||
result := make([]NodeMetricHourly, 0, len(order))
|
||||
// Keep chronological order of first-seen keys; re-sort by hour for stability.
|
||||
sort.Slice(order, func(i, j int) bool { return order[i] < order[j] })
|
||||
slices.Sort(order)
|
||||
for _, key := range order {
|
||||
result = append(result, byHour[key])
|
||||
}
|
||||
|
||||
@@ -55,8 +55,8 @@ func TestListNodeMetricHourly_PrefersRollup(t *testing.T) {
|
||||
rows, err := ListNodeMetricHourly(ctx, NodeObservabilityFilter{Since: since})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, rows, 1)
|
||||
assert.Equal(t, 42.5, rows[0].AverageCPUUsagePercent)
|
||||
assert.Equal(t, 60.0, rows[0].AverageMemoryUsagePercent)
|
||||
assert.InDelta(t, 42.5, rows[0].AverageCPUUsagePercent, 1e-9)
|
||||
assert.InDelta(t, 60.0, rows[0].AverageMemoryUsagePercent, 1e-9)
|
||||
assert.Equal(t, int64(100), rows[0].NetworkRxBytes)
|
||||
assert.Equal(t, 2, rows[0].ReportedNodes)
|
||||
require.Len(t, mock.queries, 1)
|
||||
@@ -92,10 +92,10 @@ func TestListNodeMetricHourly_MergesRawGapsWithPartialRollup(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.Len(t, rows, 2)
|
||||
assert.Equal(t, rawHour, rows[0].Hour)
|
||||
assert.Equal(t, 12.0, rows[0].AverageCPUUsagePercent)
|
||||
assert.InDelta(t, 12.0, rows[0].AverageCPUUsagePercent, 1e-9)
|
||||
// Overlapping hour prefers rollup (99) over raw (50).
|
||||
assert.Equal(t, rollupHour, rows[1].Hour)
|
||||
assert.Equal(t, 99.0, rows[1].AverageCPUUsagePercent)
|
||||
assert.InDelta(t, 99.0, rows[1].AverageCPUUsagePercent, 1e-9)
|
||||
require.GreaterOrEqual(t, len(mock.queries), 2)
|
||||
assert.Contains(t, mock.queries[1], "lagInFrame")
|
||||
}
|
||||
@@ -112,9 +112,9 @@ func TestMergeNodeMetricHourlyPreferRollup(t *testing.T) {
|
||||
)
|
||||
require.Len(t, merged, 2)
|
||||
assert.Equal(t, h1, merged[0].Hour)
|
||||
assert.Equal(t, 10.0, merged[0].AverageCPUUsagePercent)
|
||||
assert.InDelta(t, 10.0, merged[0].AverageCPUUsagePercent, 1e-9)
|
||||
assert.Equal(t, h2, merged[1].Hour)
|
||||
assert.Equal(t, 80.0, merged[1].AverageCPUUsagePercent)
|
||||
assert.InDelta(t, 80.0, merged[1].AverageCPUUsagePercent, 1e-9)
|
||||
}
|
||||
|
||||
func TestHourlyRollupCoversWindow(t *testing.T) {
|
||||
@@ -147,7 +147,7 @@ func TestListNodeMetricHourly_FallsBackToRawOnRollupError(t *testing.T) {
|
||||
rows, err := ListNodeMetricHourly(ctx, NodeObservabilityFilter{})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, rows, 1)
|
||||
assert.Equal(t, 10.0, rows[0].AverageCPUUsagePercent)
|
||||
assert.InDelta(t, 10.0, rows[0].AverageCPUUsagePercent, 1e-9)
|
||||
assert.Equal(t, int64(3), rows[0].DiskReadBytes)
|
||||
require.GreaterOrEqual(t, len(mock.queries), 2)
|
||||
assert.Contains(t, mock.queries[0], nodeMetricCapacityHourlyTableName())
|
||||
|
||||
@@ -5,6 +5,7 @@ package analytics
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -30,7 +31,7 @@ func BatchInsertNodeMetricSnapshots(ctx context.Context, snapshots []analyticsmo
|
||||
return nil
|
||||
}
|
||||
if db.ChConn == nil {
|
||||
return fmt.Errorf("clickhouse connection is not initialized")
|
||||
return errors.New("clickhouse connection is not initialized")
|
||||
}
|
||||
|
||||
batch, err := db.ChConn.PrepareBatch(ctx, analyticsmodel.NodeMetricSnapshot{}.BatchInsertSQL())
|
||||
@@ -102,7 +103,7 @@ func BatchInsertNodeEdgeHealth(ctx context.Context, rows []analyticsmodel.NodeEd
|
||||
return nil
|
||||
}
|
||||
if db.ChConn == nil {
|
||||
return fmt.Errorf("clickhouse connection is not initialized")
|
||||
return errors.New("clickhouse connection is not initialized")
|
||||
}
|
||||
batch, err := db.ChConn.PrepareBatch(ctx, analyticsmodel.NodeEdgeHealth{}.BatchInsertSQL())
|
||||
if err != nil {
|
||||
@@ -160,7 +161,7 @@ func BatchInsertNodeObsFrps(ctx context.Context, observations []analyticsmodel.N
|
||||
return nil
|
||||
}
|
||||
if db.ChConn == nil {
|
||||
return fmt.Errorf("clickhouse connection is not initialized")
|
||||
return errors.New("clickhouse connection is not initialized")
|
||||
}
|
||||
|
||||
batch, err := db.ChConn.PrepareBatch(ctx, analyticsmodel.NodeObsFrps{}.BatchInsertSQL())
|
||||
@@ -223,7 +224,7 @@ func BatchInsertNodeObsFrpc(ctx context.Context, observations []analyticsmodel.N
|
||||
return nil
|
||||
}
|
||||
if db.ChConn == nil {
|
||||
return fmt.Errorf("clickhouse connection is not initialized")
|
||||
return errors.New("clickhouse connection is not initialized")
|
||||
}
|
||||
|
||||
batch, err := db.ChConn.PrepareBatch(ctx, analyticsmodel.NodeObsFrpc{}.BatchInsertSQL())
|
||||
|
||||
@@ -48,6 +48,7 @@ var (
|
||||
authSourceListenerOnce sync.Once
|
||||
authSourceListenerCtx context.Context
|
||||
authSourceListenerCancel context.CancelFunc
|
||||
authSourceListenerDone chan struct{}
|
||||
)
|
||||
|
||||
func cloneAuthSources(sources []model.AuthSource) []model.AuthSource {
|
||||
@@ -116,15 +117,19 @@ func ensureAuthSourceCacheListener() {
|
||||
|
||||
func startAuthSourceCacheInvalidationListener() {
|
||||
authSourceListenerCtx, authSourceListenerCancel = context.WithCancel(context.Background())
|
||||
authSourceListenerDone = make(chan struct{})
|
||||
|
||||
go func() {
|
||||
pubsub := db.Redis.Subscribe(authSourceListenerCtx, authSourceInvalidationChannel)
|
||||
listenerCtx := authSourceListenerCtx
|
||||
defer close(authSourceListenerDone)
|
||||
|
||||
pubsub := db.Redis.Subscribe(listenerCtx, authSourceInvalidationChannel)
|
||||
defer func() {
|
||||
_ = pubsub.Close()
|
||||
}()
|
||||
|
||||
go func() {
|
||||
<-authSourceListenerCtx.Done()
|
||||
<-listenerCtx.Done()
|
||||
_ = pubsub.Close()
|
||||
}()
|
||||
|
||||
@@ -257,7 +262,11 @@ func InvalidateAuthSourceCache(ctx context.Context) error {
|
||||
func StopAuthSourceCacheListener() {
|
||||
if authSourceListenerCancel != nil {
|
||||
authSourceListenerCancel()
|
||||
if authSourceListenerDone != nil {
|
||||
<-authSourceListenerDone
|
||||
}
|
||||
authSourceListenerCancel = nil
|
||||
authSourceListenerDone = nil
|
||||
}
|
||||
authSourceListenerOnce = sync.Once{}
|
||||
}
|
||||
|
||||
@@ -1536,7 +1536,7 @@ func (s *userAccessLogGormStore) GetDailyTrend(ctx context.Context, days int) ([
|
||||
counts[r.Date] = r.Cnt
|
||||
}
|
||||
out := make([]analyticsmodel.DailyTrend, 0, days)
|
||||
for i := 0; i < days; i++ {
|
||||
for i := range days {
|
||||
d := start.AddDate(0, 0, i).Format("2006-01-02")
|
||||
out = append(out, analyticsmodel.DailyTrend{Date: d, Count: counts[d]})
|
||||
}
|
||||
|
||||
@@ -418,14 +418,8 @@ func openFlareAccessLogPaginateBounds(total int, page int, pageSize int) (int, i
|
||||
if pageSize <= 0 {
|
||||
return 0, total
|
||||
}
|
||||
start := page * pageSize
|
||||
if start > total {
|
||||
start = total
|
||||
}
|
||||
end := start + pageSize
|
||||
if end > total {
|
||||
end = total
|
||||
}
|
||||
start := min(page*pageSize, total)
|
||||
end := min(start+pageSize, total)
|
||||
return start, end
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// Copyright 2026 Arctel.net
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package repository
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
// Copyright 2026 Arctel.net
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"gorm.io/gorm"
|
||||
|
||||
@@ -124,7 +126,7 @@ func ListZoneDomainsByIDs(ctx context.Context, domainIDs []uint) ([]model.ZoneDo
|
||||
for _, id := range domainIDs {
|
||||
domain, ok := byID[id]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("one or more zone domains do not exist")
|
||||
return nil, errors.New("one or more zone domains do not exist")
|
||||
}
|
||||
ordered = append(ordered, domain)
|
||||
}
|
||||
|
||||
@@ -90,6 +90,7 @@ var (
|
||||
systemConfigListenerOnce sync.Once
|
||||
systemConfigListenerCtx context.Context
|
||||
systemConfigListenerCancel context.CancelFunc
|
||||
systemConfigListenerDone chan struct{}
|
||||
)
|
||||
|
||||
func ensureSystemConfigCacheListener() {
|
||||
@@ -102,15 +103,19 @@ func startSystemConfigCacheInvalidationListener() {
|
||||
}
|
||||
|
||||
systemConfigListenerCtx, systemConfigListenerCancel = context.WithCancel(context.Background())
|
||||
systemConfigListenerDone = make(chan struct{})
|
||||
|
||||
go func() {
|
||||
pubsub := db.Redis.Subscribe(systemConfigListenerCtx, SystemConfigBroadcastChannel)
|
||||
listenerCtx := systemConfigListenerCtx
|
||||
defer close(systemConfigListenerDone)
|
||||
|
||||
pubsub := db.Redis.Subscribe(listenerCtx, SystemConfigBroadcastChannel)
|
||||
defer func() {
|
||||
_ = pubsub.Close()
|
||||
}()
|
||||
|
||||
go func() {
|
||||
<-systemConfigListenerCtx.Done()
|
||||
<-listenerCtx.Done()
|
||||
_ = pubsub.Close()
|
||||
}()
|
||||
|
||||
@@ -135,7 +140,11 @@ func startSystemConfigCacheInvalidationListener() {
|
||||
func StopSystemConfigCacheListener() {
|
||||
if systemConfigListenerCancel != nil {
|
||||
systemConfigListenerCancel()
|
||||
if systemConfigListenerDone != nil {
|
||||
<-systemConfigListenerDone
|
||||
}
|
||||
systemConfigListenerCancel = nil
|
||||
systemConfigListenerDone = nil
|
||||
}
|
||||
systemConfigListenerOnce = sync.Once{}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// Copyright 2026 Arctel.net
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package repository
|
||||
|
||||
import (
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
)
|
||||
|
||||
func setupTaskExecutionTestEnvironment(t *testing.T) func() {
|
||||
t.Helper()
|
||||
sqliteDB, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{
|
||||
DisableForeignKeyConstraintWhenMigrating: true,
|
||||
})
|
||||
@@ -323,7 +324,7 @@ func TestAppendTaskExecutionLogNonExistent(t *testing.T) {
|
||||
|
||||
// Redis 缓冲不依赖数据库记录是否已经创建。
|
||||
err := AppendTaskExecutionLog(ctx, "nonexistent_task", "测试日志")
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = FlushTaskExecutionLog(ctx, "nonexistent_task")
|
||||
assert.Error(t, err)
|
||||
@@ -454,7 +455,7 @@ func TestListTaskExecutionsDefaultPaging(t *testing.T) {
|
||||
items, total, err := ListTaskExecutions(ctx, model.ListTaskExecutionsRequest{})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, int64(0), total)
|
||||
assert.Len(t, items, 0)
|
||||
assert.Empty(t, items)
|
||||
}
|
||||
|
||||
func TestCleanupTaskExecutionLogs(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user