feat(ops): add usage_logs partition status to ops dashboard

Add partition management integration to the smart ops system:
- Backend: Add GetUsageLogsPartitionStatus endpoint in OpsHandler
- Backend: Add partition query methods in OpsRepository
- Backend: Add UsageLogsPartitionStatus type in OpsService
- Frontend: Add OpsPartitionStatusCard component
- Frontend: Add partition status display in OpsDashboard
- i18n: Add Chinese and English translations

The partition status card shows:
- Whether usage_logs is partitioned
- Current row count vs threshold (100K)
- Partition count (if partitioned)
- Warning message when partitioning is recommended

This allows administrators to monitor partition status directly
from the ops dashboard without checking server logs.
This commit is contained in:
User
2026-04-16 23:16:17 +08:00
parent eb5adbbae5
commit 60d15d2ba4
10 changed files with 409 additions and 1 deletions

View File

@@ -1526,3 +1526,78 @@ func opsNullInt16(v *int16) any {
}
return sql.NullInt64{Int64: int64(*v), Valid: true}
}
// ==================== Usage Logs Partition Management ====================
// IsUsageLogsPartitioned checks if usage_logs table is partitioned.
func (r *opsRepository) IsUsageLogsPartitioned(ctx context.Context) (bool, error) {
if r == nil || r.db == nil {
return false, fmt.Errorf("nil ops repository")
}
var isPartitioned bool
err := r.db.QueryRowContext(ctx, `
SELECT EXISTS(
SELECT 1
FROM pg_partitioned_table pt
JOIN pg_class c ON c.oid = pt.partrelid
WHERE c.relname = 'usage_logs'
)
`).Scan(&isPartitioned)
if err != nil {
return false, fmt.Errorf("check usage_logs partitioned: %w", err)
}
return isPartitioned, nil
}
// GetUsageLogsRowCount returns the approximate row count of usage_logs table.
func (r *opsRepository) GetUsageLogsRowCount(ctx context.Context) (int64, error) {
if r == nil || r.db == nil {
return 0, fmt.Errorf("nil ops repository")
}
var rowCount int64
// Use pg_class.reltuples for fast approximate count (avoid slow COUNT(*) on large tables)
err := r.db.QueryRowContext(ctx, `
SELECT COALESCE(
(SELECT reltuples::bigint FROM pg_class WHERE relname = 'usage_logs'),
0
)
`).Scan(&rowCount)
if err != nil {
// Fallback to actual COUNT if pg_class estimate fails
err = r.db.QueryRowContext(ctx, `SELECT COUNT(*) FROM usage_logs`).Scan(&rowCount)
if err != nil {
return 0, fmt.Errorf("get usage_logs row count: %w", err)
}
}
return rowCount, nil
}
// GetUsageLogsPartitionCount returns the number of partitions for usage_logs table.
func (r *opsRepository) GetUsageLogsPartitionCount(ctx context.Context) (int, error) {
if r == nil || r.db == nil {
return 0, fmt.Errorf("nil ops repository")
}
var count int
err := r.db.QueryRowContext(ctx, `
SELECT COUNT(*)
FROM pg_class c
WHERE c.relkind = 'r'
AND c.relname LIKE 'usage_logs_%'
AND EXISTS (
SELECT 1 FROM pg_inherits i
WHERE i.inhrelid = c.oid
AND EXISTS (
SELECT 1 FROM pg_class parent
WHERE parent.oid = i.inhparent
AND parent.relname = 'usage_logs'
)
)
`).Scan(&count)
if err != nil {
return 0, fmt.Errorf("get usage_logs partition count: %w", err)
}
return count, nil
}