usage_query_cache.go 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566
  1. package admin
  2. import (
  3. "context"
  4. "time"
  5. "github.com/Wei-Shaw/sub2api/internal/pkg/usagestats"
  6. )
  7. // 与 dashboard 查询缓存同款:30s TTL 进程内缓存,仅服务 /admin/usage/stats 读路径。
  8. var usageStatsCache = newSnapshotCache(30 * time.Second)
  9. type usageStatsCacheKeyData struct {
  10. StartTime string `json:"start_time"`
  11. EndTime string `json:"end_time"`
  12. UserID int64 `json:"user_id"`
  13. APIKeyID int64 `json:"api_key_id"`
  14. AccountID int64 `json:"account_id"`
  15. GroupID int64 `json:"group_id"`
  16. Model string `json:"model"`
  17. BillingMode string `json:"billing_mode"`
  18. RequestType *int16 `json:"request_type"`
  19. Stream *bool `json:"stream"`
  20. NativeCompactionV2 *bool `json:"native_compaction_v2"`
  21. BillingType *int8 `json:"billing_type"`
  22. UpstreamModelMismatch *bool `json:"upstream_model_mismatch"`
  23. }
  24. func usageStatsCacheKey(filters usagestats.UsageLogFilters) string {
  25. start := ""
  26. if filters.StartTime != nil {
  27. start = filters.StartTime.UTC().Format(time.RFC3339)
  28. }
  29. end := ""
  30. if filters.EndTime != nil {
  31. end = filters.EndTime.UTC().Format(time.RFC3339)
  32. }
  33. return mustMarshalDashboardCacheKey(usageStatsCacheKeyData{
  34. StartTime: start,
  35. EndTime: end,
  36. UserID: filters.UserID,
  37. APIKeyID: filters.APIKeyID,
  38. AccountID: filters.AccountID,
  39. GroupID: filters.GroupID,
  40. Model: filters.Model,
  41. BillingMode: filters.BillingMode,
  42. RequestType: filters.RequestType,
  43. Stream: filters.Stream,
  44. NativeCompactionV2: filters.NativeCompactionV2,
  45. BillingType: filters.BillingType,
  46. UpstreamModelMismatch: filters.UpstreamModelMismatch,
  47. })
  48. }
  49. // getStatsCached 命中则返回缓存,未命中则回源 usageService 并写缓存。
  50. func (h *UsageHandler) getStatsCached(ctx context.Context, filters usagestats.UsageLogFilters) (*usagestats.UsageStats, bool, error) {
  51. key := usageStatsCacheKey(filters)
  52. entry, hit, err := usageStatsCache.GetOrLoad(key, func() (any, error) {
  53. return h.usageService.GetStatsWithFilters(ctx, filters)
  54. })
  55. if err != nil {
  56. return nil, hit, err
  57. }
  58. stats, err := snapshotPayloadAs[*usagestats.UsageStats](entry.Payload)
  59. return stats, hit, err
  60. }