feat(platform): expose structured capacity metrics
This commit is contained in:
@@ -9,17 +9,24 @@ import (
|
||||
)
|
||||
|
||||
type CapacityCheckResult struct {
|
||||
Status string `json:"status"`
|
||||
ActiveConnections int64 `json:"active_connections"`
|
||||
KafkaLag float64 `json:"kafka_lag"`
|
||||
Findings []string `json:"findings"`
|
||||
Status string `json:"status"`
|
||||
CapacityMetrics CapacityMetrics `json:"capacity_metrics"`
|
||||
Findings []string `json:"findings"`
|
||||
}
|
||||
|
||||
type capacityCheckReport struct {
|
||||
Status string `json:"status"`
|
||||
Totals struct {
|
||||
ActiveConnections int64 `json:"active_connections"`
|
||||
KafkaLag float64 `json:"kafka_lag"`
|
||||
ActiveConnections int64 `json:"active_connections"`
|
||||
KafkaLag float64 `json:"kafka_lag"`
|
||||
BridgeConsumerPending float64 `json:"bridge_consumer_pending"`
|
||||
BridgeAckPending float64 `json:"bridge_ack_pending"`
|
||||
BridgeBatchPendingMessages float64 `json:"bridge_batch_pending_messages"`
|
||||
FastWriterConsumerPending float64 `json:"fast_writer_consumer_pending"`
|
||||
FastWriterAckPending float64 `json:"fast_writer_ack_pending"`
|
||||
FastWriterBatchPending float64 `json:"fast_writer_batch_pending_messages"`
|
||||
HistoryBatchPending float64 `json:"history_batch_pending_messages"`
|
||||
HistoryRowsPending float64 `json:"history_rows_pending"`
|
||||
} `json:"totals"`
|
||||
Findings []string `json:"findings"`
|
||||
}
|
||||
@@ -45,9 +52,19 @@ func (c *CapacityCheckCommand) CheckCapacity(ctx context.Context) (CapacityCheck
|
||||
return CapacityCheckResult{}, jsonErr
|
||||
}
|
||||
return CapacityCheckResult{
|
||||
Status: report.Status,
|
||||
ActiveConnections: report.Totals.ActiveConnections,
|
||||
KafkaLag: report.Totals.KafkaLag,
|
||||
Findings: report.Findings,
|
||||
Status: report.Status,
|
||||
CapacityMetrics: CapacityMetrics{
|
||||
ActiveConnections: int(report.Totals.ActiveConnections),
|
||||
KafkaLag: int(report.Totals.KafkaLag),
|
||||
BridgeConsumerPending: int(report.Totals.BridgeConsumerPending),
|
||||
BridgeAckPending: int(report.Totals.BridgeAckPending),
|
||||
BridgeBatchPendingMessages: int(report.Totals.BridgeBatchPendingMessages),
|
||||
FastWriterConsumerPending: int(report.Totals.FastWriterConsumerPending),
|
||||
FastWriterAckPending: int(report.Totals.FastWriterAckPending),
|
||||
FastWriterBatchPending: int(report.Totals.FastWriterBatchPending),
|
||||
HistoryBatchPending: int(report.Totals.HistoryBatchPending),
|
||||
HistoryRowsPending: int(report.Totals.HistoryRowsPending),
|
||||
},
|
||||
Findings: report.Findings,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -672,12 +672,27 @@ func bucketStats(values map[string]int) []QualityBucketStat {
|
||||
func (m *MockStore) OpsHealth(context.Context) (OpsHealth, error) {
|
||||
summary, _ := m.DashboardSummary(context.Background())
|
||||
redisOnlineKeys := 92
|
||||
activeConnections := 120000
|
||||
capacityMetrics := CapacityMetrics{
|
||||
ActiveConnections: activeConnections,
|
||||
KafkaLag: 0,
|
||||
BridgeConsumerPending: 0,
|
||||
BridgeAckPending: 0,
|
||||
BridgeBatchPendingMessages: 0,
|
||||
FastWriterConsumerPending: 0,
|
||||
FastWriterAckPending: 0,
|
||||
FastWriterBatchPending: 0,
|
||||
HistoryBatchPending: 0,
|
||||
HistoryRowsPending: 0,
|
||||
}
|
||||
return OpsHealth{
|
||||
LinkHealth: summary.LinkHealth,
|
||||
KafkaLag: summary.KafkaLag,
|
||||
RedisOnlineKeys: &redisOnlineKeys,
|
||||
TDengineWritable: true,
|
||||
MySQLWritable: true,
|
||||
LinkHealth: summary.LinkHealth,
|
||||
KafkaLag: summary.KafkaLag,
|
||||
ActiveConnections: &activeConnections,
|
||||
CapacityMetrics: capacityMetrics,
|
||||
RedisOnlineKeys: &redisOnlineKeys,
|
||||
TDengineWritable: true,
|
||||
MySQLWritable: true,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -359,14 +359,28 @@ type QualityPriorityIssue struct {
|
||||
}
|
||||
|
||||
type OpsHealth struct {
|
||||
LinkHealth []LinkHealth `json:"linkHealth"`
|
||||
KafkaLag *int `json:"kafkaLag"`
|
||||
ActiveConnections *int `json:"activeConnections"`
|
||||
CapacityFindings []string `json:"capacityFindings"`
|
||||
RedisOnlineKeys *int `json:"redisOnlineKeys"`
|
||||
TDengineWritable bool `json:"tdengineWritable"`
|
||||
MySQLWritable bool `json:"mysqlWritable"`
|
||||
Runtime RuntimeInfo `json:"runtime"`
|
||||
LinkHealth []LinkHealth `json:"linkHealth"`
|
||||
KafkaLag *int `json:"kafkaLag"`
|
||||
ActiveConnections *int `json:"activeConnections"`
|
||||
CapacityMetrics CapacityMetrics `json:"capacityMetrics"`
|
||||
CapacityFindings []string `json:"capacityFindings"`
|
||||
RedisOnlineKeys *int `json:"redisOnlineKeys"`
|
||||
TDengineWritable bool `json:"tdengineWritable"`
|
||||
MySQLWritable bool `json:"mysqlWritable"`
|
||||
Runtime RuntimeInfo `json:"runtime"`
|
||||
}
|
||||
|
||||
type CapacityMetrics struct {
|
||||
ActiveConnections int `json:"activeConnections"`
|
||||
KafkaLag int `json:"kafkaLag"`
|
||||
BridgeConsumerPending int `json:"bridgeConsumerPending"`
|
||||
BridgeAckPending int `json:"bridgeAckPending"`
|
||||
BridgeBatchPendingMessages int `json:"bridgeBatchPendingMessages"`
|
||||
FastWriterConsumerPending int `json:"fastWriterConsumerPending"`
|
||||
FastWriterAckPending int `json:"fastWriterAckPending"`
|
||||
FastWriterBatchPending int `json:"fastWriterBatchPending"`
|
||||
HistoryBatchPending int `json:"historyBatchPending"`
|
||||
HistoryRowsPending int `json:"historyRowsPending"`
|
||||
}
|
||||
|
||||
type RuntimeInfo struct {
|
||||
|
||||
@@ -929,7 +929,7 @@ func (s *ProductionStore) OpsHealth(ctx context.Context) (OpsHealth, error) {
|
||||
locationHealth := s.mysqlTableReadHealth(ctx, "vehicle_realtime_location", "读取实时位置表正常")
|
||||
tdengineHealth := s.tdengineRawFrameHealth(ctx)
|
||||
redisHealth, redisOnlineKeys := s.redisOnlineKeyHealth(ctx)
|
||||
capacityHealth, kafkaLag, activeConnections, capacityFindings := s.capacityCheckHealth(ctx)
|
||||
capacityHealth, kafkaLag, activeConnections, capacityMetrics, capacityFindings := s.capacityCheckHealth(ctx)
|
||||
mysqlWritable := mysqlStatus == "ok" && snapshotHealth.Status == "ok" && locationHealth.Status == "ok"
|
||||
return OpsHealth{
|
||||
LinkHealth: []LinkHealth{
|
||||
@@ -942,6 +942,7 @@ func (s *ProductionStore) OpsHealth(ctx context.Context) (OpsHealth, error) {
|
||||
},
|
||||
KafkaLag: kafkaLag,
|
||||
ActiveConnections: activeConnections,
|
||||
CapacityMetrics: capacityMetrics,
|
||||
CapacityFindings: capacityFindings,
|
||||
RedisOnlineKeys: redisOnlineKeys,
|
||||
TDengineWritable: tdengineHealth.Status == "ok",
|
||||
@@ -949,19 +950,20 @@ func (s *ProductionStore) OpsHealth(ctx context.Context) (OpsHealth, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *ProductionStore) capacityCheckHealth(ctx context.Context) (LinkHealth, *int, *int, []string) {
|
||||
func (s *ProductionStore) capacityCheckHealth(ctx context.Context) (LinkHealth, *int, *int, CapacityMetrics, []string) {
|
||||
health := LinkHealth{Name: "Capacity check", Status: "warning", Detail: "平台暂未接入 capacity-check"}
|
||||
if s.capacityCheck == nil {
|
||||
return health, nil, nil, []string{}
|
||||
return health, nil, nil, CapacityMetrics{}, []string{}
|
||||
}
|
||||
result, err := s.capacityCheck.CheckCapacity(ctx)
|
||||
if err != nil {
|
||||
health.Status = "error"
|
||||
health.Detail = err.Error()
|
||||
return health, nil, nil, []string{}
|
||||
return health, nil, nil, CapacityMetrics{}, []string{}
|
||||
}
|
||||
kafkaLag := int(result.KafkaLag)
|
||||
activeConnections := int(result.ActiveConnections)
|
||||
capacityMetrics := result.CapacityMetrics
|
||||
kafkaLag := capacityMetrics.KafkaLag
|
||||
activeConnections := capacityMetrics.ActiveConnections
|
||||
detail := "active_connections=" + strconv.Itoa(activeConnections) + ", kafka_lag=" + strconv.Itoa(kafkaLag)
|
||||
if len(result.Findings) > 0 {
|
||||
detail += ", findings=" + strings.Join(result.Findings, "; ")
|
||||
@@ -972,7 +974,7 @@ func (s *ProductionStore) capacityCheckHealth(ctx context.Context) (LinkHealth,
|
||||
health.Status = "warning"
|
||||
}
|
||||
health.Detail = detail
|
||||
return health, &kafkaLag, &activeConnections, append([]string{}, result.Findings...)
|
||||
return health, &kafkaLag, &activeConnections, capacityMetrics, append([]string{}, result.Findings...)
|
||||
}
|
||||
|
||||
func (s *ProductionStore) redisOnlineKeyHealth(ctx context.Context) (LinkHealth, *int) {
|
||||
|
||||
@@ -213,10 +213,14 @@ func TestOpsHealthUsesCapacityCheckProbeForKafkaLag(t *testing.T) {
|
||||
store := &ProductionStore{
|
||||
db: db,
|
||||
capacityCheck: fakeCapacityCheckProbe{result: CapacityCheckResult{
|
||||
Status: "degraded",
|
||||
ActiveConnections: 120000,
|
||||
KafkaLag: 42,
|
||||
Findings: []string{"kafka lag 42"},
|
||||
Status: "degraded",
|
||||
CapacityMetrics: CapacityMetrics{
|
||||
ActiveConnections: 120000,
|
||||
KafkaLag: 42,
|
||||
BridgeConsumerPending: 1024,
|
||||
BridgeAckPending: 2,
|
||||
},
|
||||
Findings: []string{"kafka lag 42"},
|
||||
}},
|
||||
}
|
||||
|
||||
@@ -230,6 +234,9 @@ func TestOpsHealthUsesCapacityCheckProbeForKafkaLag(t *testing.T) {
|
||||
if health.ActiveConnections == nil || *health.ActiveConnections != 120000 {
|
||||
t.Fatalf("OpsHealth should expose capacity-check active connections, got %+v", health.ActiveConnections)
|
||||
}
|
||||
if health.CapacityMetrics.BridgeConsumerPending != 1024 || health.CapacityMetrics.BridgeAckPending != 2 {
|
||||
t.Fatalf("OpsHealth should expose structured capacity metrics, got %+v", health.CapacityMetrics)
|
||||
}
|
||||
if len(health.CapacityFindings) != 1 || health.CapacityFindings[0] != "kafka lag 42" {
|
||||
t.Fatalf("OpsHealth should expose structured capacity findings, got %+v", health.CapacityFindings)
|
||||
}
|
||||
@@ -254,9 +261,11 @@ func TestOpsHealthReturnsEmptyCapacityFindingsArray(t *testing.T) {
|
||||
store := &ProductionStore{
|
||||
db: db,
|
||||
capacityCheck: fakeCapacityCheckProbe{result: CapacityCheckResult{
|
||||
Status: "ok",
|
||||
ActiveConnections: 254,
|
||||
KafkaLag: 0,
|
||||
Status: "ok",
|
||||
CapacityMetrics: CapacityMetrics{
|
||||
ActiveConnections: 254,
|
||||
KafkaLag: 0,
|
||||
},
|
||||
}},
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user