fix(ops): preserve zero-valued system metrics

This commit is contained in:
wucm667
2026-09-04 18:51:24 +08:00
parent b1748c4ea9
commit 9ddf69698d
2 changed files with 92 additions and 19 deletions
+33 -19
View File
@@ -114,37 +114,37 @@ INSERT INTO ops_system_metrics (
opsNullFloat64(input.QPS),
opsNullFloat64(input.TPS),
opsNullInt(input.DurationP50Ms),
opsNullInt(input.DurationP90Ms),
opsNullInt(input.DurationP95Ms),
opsNullInt(input.DurationP99Ms),
opsNullMetricInt(input.DurationP50Ms),
opsNullMetricInt(input.DurationP90Ms),
opsNullMetricInt(input.DurationP95Ms),
opsNullMetricInt(input.DurationP99Ms),
opsNullFloat64(input.DurationAvgMs),
opsNullInt(input.DurationMaxMs),
opsNullMetricInt(input.DurationMaxMs),
opsNullInt(input.TTFTP50Ms),
opsNullInt(input.TTFTP90Ms),
opsNullInt(input.TTFTP95Ms),
opsNullInt(input.TTFTP99Ms),
opsNullMetricInt(input.TTFTP50Ms),
opsNullMetricInt(input.TTFTP90Ms),
opsNullMetricInt(input.TTFTP95Ms),
opsNullMetricInt(input.TTFTP99Ms),
opsNullFloat64(input.TTFTAvgMs),
opsNullInt(input.TTFTMaxMs),
opsNullMetricInt(input.TTFTMaxMs),
opsNullFloat64(input.CPUUsagePercent),
opsNullInt(input.MemoryUsedMB),
opsNullInt(input.MemoryTotalMB),
opsNullMetricInt64(input.MemoryUsedMB),
opsNullMetricInt64(input.MemoryTotalMB),
opsNullFloat64(input.MemoryUsagePercent),
opsNullBool(input.DBOK),
opsNullBool(input.RedisOK),
opsNullInt(input.RedisConnTotal),
opsNullInt(input.RedisConnIdle),
opsNullMetricInt(input.RedisConnTotal),
opsNullMetricInt(input.RedisConnIdle),
opsNullInt(input.DBConnActive),
opsNullInt(input.DBConnIdle),
opsNullInt(input.DBConnWaiting),
opsNullMetricInt(input.DBConnActive),
opsNullMetricInt(input.DBConnIdle),
opsNullMetricInt(input.DBConnWaiting),
opsNullInt(input.GoroutineCount),
opsNullInt(input.ConcurrencyQueueDepth),
opsNullMetricInt(input.GoroutineCount),
opsNullMetricInt(input.ConcurrencyQueueDepth),
)
return err
}
@@ -437,6 +437,20 @@ func opsNullFloat64(v *float64) any {
return sql.NullFloat64{Float64: *v, Valid: true}
}
func opsNullMetricInt(v *int) any {
if v == nil {
return sql.NullInt64{}
}
return sql.NullInt64{Int64: int64(*v), Valid: true}
}
func opsNullMetricInt64(v *int64) any {
if v == nil {
return sql.NullInt64{}
}
return sql.NullInt64{Int64: *v, Valid: true}
}
func opsNullTime(v *time.Time) any {
if v == nil || v.IsZero() {
return sql.NullTime{}
@@ -0,0 +1,59 @@
package repository
import (
"context"
"database/sql/driver"
"testing"
"time"
"github.com/DATA-DOG/go-sqlmock"
"github.com/Wei-Shaw/sub2api/internal/service"
)
func TestInsertSystemMetricsNullableIntegerMetrics(t *testing.T) {
createdAt := time.Date(2026, time.September, 4, 12, 0, 0, 0, time.UTC)
zero := 0
tests := []struct {
name string
dbConnActive *int
wantDBActive driver.Value
}{
{name: "explicit zero is preserved", dbConnActive: &zero, wantDBActive: int64(0)},
{name: "unavailable metric remains null", dbConnActive: nil, wantDBActive: nil},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
db, mock, err := sqlmock.New()
if err != nil {
t.Fatalf("sqlmock.New: %v", err)
}
t.Cleanup(func() { _ = db.Close() })
args := make([]driver.Value, 40)
args[0] = createdAt
args[1] = int64(1)
for i := 4; i <= 12; i++ {
args[i] = int64(0)
}
args[35] = tt.wantDBActive
mock.ExpectExec("INSERT INTO ops_system_metrics").
WithArgs(args...).
WillReturnResult(sqlmock.NewResult(1, 1))
repo := &opsRepository{db: db}
err = repo.InsertSystemMetrics(context.Background(), &service.OpsInsertSystemMetricsInput{
CreatedAt: createdAt,
DBConnActive: tt.dbConnActive,
})
if err != nil {
t.Fatalf("InsertSystemMetrics: %v", err)
}
if err := mock.ExpectationsWereMet(); err != nil {
t.Fatalf("unmet SQL expectations: %v", err)
}
})
}
}