antigravity_internal500_penalty.go 3.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. package service
  2. import (
  3. "context"
  4. "fmt"
  5. "log/slog"
  6. "net/http"
  7. "time"
  8. "github.com/tidwall/gjson"
  9. )
  10. // INTERNAL 500 渐进惩罚:连续多轮全部返回特定 500 错误时的惩罚时长
  11. const (
  12. internal500PenaltyTier1Duration = 30 * time.Minute // 第 1 轮:临时不可调度 30 分钟
  13. internal500PenaltyTier2Duration = 2 * time.Hour // 第 2 轮:临时不可调度 2 小时
  14. internal500PenaltyTier3Threshold = 3 // 第 3+ 轮:永久禁用
  15. )
  16. // isAntigravityInternalServerError 检测特定的 INTERNAL 500 错误
  17. // 必须同时匹配 error.code==500, error.message=="Internal error encountered.", error.status=="INTERNAL"
  18. func isAntigravityInternalServerError(statusCode int, body []byte) bool {
  19. if statusCode != http.StatusInternalServerError {
  20. return false
  21. }
  22. return gjson.GetBytes(body, "error.code").Int() == 500 &&
  23. gjson.GetBytes(body, "error.message").String() == "Internal error encountered." &&
  24. gjson.GetBytes(body, "error.status").String() == "INTERNAL"
  25. }
  26. // applyInternal500Penalty 根据连续 INTERNAL 500 轮次数应用渐进惩罚
  27. // count=1: temp_unschedulable 10 分钟
  28. // count=2: temp_unschedulable 10 小时
  29. // count>=3: SetError 永久禁用
  30. func (s *AntigravityGatewayService) applyInternal500Penalty(
  31. ctx context.Context, prefix string, account *Account, count int64,
  32. ) {
  33. switch {
  34. case count >= int64(internal500PenaltyTier3Threshold):
  35. reason := fmt.Sprintf("INTERNAL 500 consecutive failures: %d rounds", count)
  36. if err := s.accountRepo.SetError(ctx, account.ID, reason); err != nil {
  37. slog.Error("internal500_set_error_failed", "account_id", account.ID, "error", err)
  38. return
  39. }
  40. slog.Warn("internal500_account_disabled",
  41. "account_id", account.ID, "account_name", account.Name, "consecutive_count", count)
  42. case count == 2:
  43. until := time.Now().Add(internal500PenaltyTier2Duration)
  44. reason := fmt.Sprintf("INTERNAL 500 x%d (temp unsched %v)", count, internal500PenaltyTier2Duration)
  45. if err := s.accountRepo.SetTempUnschedulable(ctx, account.ID, until, reason); err != nil {
  46. slog.Error("internal500_temp_unsched_failed", "account_id", account.ID, "error", err)
  47. return
  48. }
  49. slog.Warn("internal500_temp_unschedulable",
  50. "account_id", account.ID, "account_name", account.Name,
  51. "duration", internal500PenaltyTier2Duration, "consecutive_count", count)
  52. case count == 1:
  53. until := time.Now().Add(internal500PenaltyTier1Duration)
  54. reason := fmt.Sprintf("INTERNAL 500 x%d (temp unsched %v)", count, internal500PenaltyTier1Duration)
  55. if err := s.accountRepo.SetTempUnschedulable(ctx, account.ID, until, reason); err != nil {
  56. slog.Error("internal500_temp_unsched_failed", "account_id", account.ID, "error", err)
  57. return
  58. }
  59. slog.Info("internal500_temp_unschedulable",
  60. "account_id", account.ID, "account_name", account.Name,
  61. "duration", internal500PenaltyTier1Duration, "consecutive_count", count)
  62. }
  63. }
  64. // handleInternal500RetryExhausted 处理 INTERNAL 500 重试耗尽:递增计数器并应用惩罚
  65. func (s *AntigravityGatewayService) handleInternal500RetryExhausted(
  66. ctx context.Context, prefix string, account *Account,
  67. ) {
  68. if s.internal500Cache == nil {
  69. return
  70. }
  71. count, err := s.internal500Cache.IncrementInternal500Count(ctx, account.ID)
  72. if err != nil {
  73. slog.Error("internal500_counter_increment_failed",
  74. "prefix", prefix, "account_id", account.ID, "error", err)
  75. return
  76. }
  77. s.applyInternal500Penalty(ctx, prefix, account, count)
  78. }
  79. // resetInternal500Counter 成功响应时清零 INTERNAL 500 计数器
  80. func (s *AntigravityGatewayService) resetInternal500Counter(
  81. ctx context.Context, prefix string, accountID int64,
  82. ) {
  83. if s.internal500Cache == nil {
  84. return
  85. }
  86. if err := s.internal500Cache.ResetInternal500Count(ctx, accountID); err != nil {
  87. slog.Error("internal500_counter_reset_failed",
  88. "prefix", prefix, "account_id", accountID, "error", err)
  89. }
  90. }