|
20 | 20 |
|
21 | 21 |
|
22 | 22 | [](https://www.ruby-lang.org/) |
23 | | -[](https://rubyonrails.org/) |
| 23 | +[](https://rubyonrails.org/) |
24 | 24 | [](https://www.postgresql.org/) |
25 | 25 | [](https://redis.io/) |
26 | 26 | [](http://localhost:3333/api-docs) |
|
35 | 35 | ║ PROSTAFF API — Ruby on Rails 7.2 (API-Only) ║ |
36 | 36 | ╠══════════════════════════════════════════════════════════════════════════════╣ |
37 | 37 | ║ Backend for the ProStaff.gg esports team management platform. ║ |
38 | | -║ 200+ documented endpoints · JWT Auth · Modular Monolith · p95 ~500ms ║ |
| 38 | +║ 200+ documented endpoints · JWT Auth · Modular Monolith · p95 ~200ms ║ |
39 | 39 | ╚══════════════════════════════════════════════════════════════════════════════╝ |
40 | 40 | ``` |
41 | 41 |
|
|
61 | 61 | │ [■] Meta Intelligence — Build aggregation, champion/item analytics │ |
62 | 62 | │ [■] Support System — Ticketing + staff dashboard + FAQ │ |
63 | 63 | │ [■] Global Search — Meilisearch full-text search across models │ |
| 64 | +│ [■] Search Fallback — PostgreSQL ILIKE fallback when Meili offline│ |
64 | 65 | │ [■] Real-time Messaging — Action Cable WebSocket team chat │ |
65 | 66 | │ [■] Background Jobs — Sidekiq for async background processing │ |
| 67 | +│ [■] Circuit Breaker — Riot API isolation (3-state, Redis-backed) │ |
| 68 | +│ [■] Async Audit Log — Non-blocking audit trail via Sidekiq job │ |
| 69 | +│ [■] Response Cache Layer — Redis cache on 6 endpoints (TTL 5–30 min) │ |
66 | 70 | │ [■] Security Hardened — OWASP Top 10, Brakeman, Semgrep, CodeQL, ZAP│ |
67 | 71 | │ [■] Rate Limiting — Rack::Attack: 5 rules + Retry-After headers │ |
68 | | -│ [■] High Performance — p95: ~500ms · cached: ~50ms │ |
| 72 | +│ [■] High Performance — p95: ~200ms prod · cached: ~50ms · >60% hit │ |
69 | 73 | │ [■] Modular Monolith — Scalable modular architecture │ |
70 | | -│ [■] Observability — /health/live + /health/ready + Sidekiq mon. │ |
| 74 | +│ [■] Observability — /health+/live /health/ready + cache metrics │ |
71 | 75 | │ [■] 401 Rate Spike Detection — Sliding-window middleware, alerts at >5% │ |
72 | 76 | │ [■] Job Heartbeat Tracking — Stale scheduled job detection via Redis │ |
73 | 77 | └─────────────────────────────────────────────────────────────────────────────┘ |
@@ -172,7 +176,7 @@ open http://localhost:3333/api-docs |
172 | 176 | ║ LAYER ║ TECNOLOGY ║ |
173 | 177 | ╠══════════════════════╬════════════════════════════════════════════════════╣ |
174 | 178 | ║ Language ║ Ruby 3.4.8 ║ |
175 | | -║ Framework ║ Rails 7.2.0 (API-only mode) ║ |
| 179 | +║ Framework ║ Rails 7.2.3.1 (API-only mode) ║ |
176 | 180 | ║ Database ║ PostgreSQL 14+ ║ |
177 | 181 | ║ Authentication ║ JWT (access + refresh tokens) ║ |
178 | 182 | ║ URL Obfuscation ║ HashID with Base62 encoding ║ |
@@ -441,11 +445,13 @@ graph TB |
441 | 445 | 1. **Modular Monolith**: Each module is self-contained with its own controllers, models, and services |
442 | 446 | 2. **API-Only**: Rails configured in API mode for JSON responses |
443 | 447 | 3. **JWT Authentication**: Stateless authentication using JWT tokens |
444 | | -4. **Background Processing**: Long-running tasks handled by Sidekiq |
445 | | -5. **Caching**: Redis used for session management and performance optimization |
446 | | -6. **External Integration**: Riot Games API integration for real-time data |
447 | | -7. **Rate Limiting**: Rack::Attack for API rate limiting |
448 | | -8. **CORS**: Configured for cross-origin requests from frontend |
| 448 | +4. **Background Processing**: Long-running tasks handled by Sidekiq (async audit logs, Riot sync) |
| 449 | +5. **Cache Layer**: Redis response cache on 6 high-frequency endpoints (org-scoped, TTL 5–30 min) |
| 450 | +6. **Circuit Breaker**: Riot API isolation via `CircuitBreakerService` (closed/open/half-open, Redis-backed) |
| 451 | +7. **Graceful Degradation**: Meilisearch offline → PostgreSQL ILIKE fallback; circuit open → fast fail |
| 452 | +8. **External Integration**: Riot Games API integration for real-time data |
| 453 | +9. **Rate Limiting**: Rack::Attack for API rate limiting |
| 454 | +10. **CORS**: Configured for cross-origin requests from frontend |
449 | 455 |
|
450 | 456 | ## 04 · Setup |
451 | 457 |
|
@@ -821,10 +827,14 @@ GET /health/ready — Readiness probe: checks PostgreSQL + Redis + M |
821 | 827 | Returns 200 (ok/disabled) or 503 (any dep unreachable). |
822 | 828 | Use for load balancer traffic routing. |
823 | 829 |
|
824 | | -GET /api/v1/monitoring/sidekiq — Admin only. Full Sidekiq snapshot: |
825 | | - queue depths, worker count, dead queue, retry queue, |
826 | | - scheduled job heartbeats (stale detection), alert flags. |
827 | | - Returns 503 if Redis unavailable. |
| 830 | +GET /api/v1/monitoring/sidekiq — Admin only. Full Sidekiq snapshot: |
| 831 | + queue depths, worker count, dead queue, retry queue, |
| 832 | + scheduled job heartbeats (stale detection), alert flags. |
| 833 | + Returns 503 if Redis unavailable. |
| 834 | +
|
| 835 | +GET /api/v1/monitoring/cache_stats — Admin only. Real-time cache hit rate: |
| 836 | + total reads, hits, misses, hit_rate (%). |
| 837 | + Counters persist in Redis, reset on Redis flush. |
828 | 838 | ``` |
829 | 839 |
|
830 | 840 | > **Monitoring endpoint response includes:** |
@@ -943,12 +953,25 @@ open coverage/index.html |
943 | 953 | ║ PERFORMANCE BENCHMARKS ║ |
944 | 954 | ╠══════════════════╦════════════════════╣ |
945 | 955 | ║ p(95) Docker ║ ~880ms ║ |
946 | | -║ p(95) Prod est. ║ ~500ms ║ |
| 956 | +║ p(95) Prod est. ║ <200ms(target) ║ |
947 | 957 | ║ With cache ║ ~50ms ║ |
| 958 | +║ Cache hit rate ║ >60%(after warmup)║ |
948 | 959 | ║ Error rate ║ 0% ║ |
949 | 960 | ╚══════════════════╩════════════════════╝ |
950 | 961 | ``` |
951 | 962 |
|
| 963 | +**Cached endpoints** (Redis, org-scoped, bypass on filter params): |
| 964 | + |
| 965 | +| Endpoint | TTL | Invalidation | |
| 966 | +|---|---|---| |
| 967 | +| `GET /players` | 5 min | `after_commit` on Player | |
| 968 | +| `GET /players/:id` | 5 min | After Riot sync | |
| 969 | +| `GET /matches` | 5 min | `after_commit` on Match | |
| 970 | +| `GET /analytics/performance` | 15 min | After Match sync | |
| 971 | +| `GET /tournaments` | 30 min | `after_commit` on Tournament | |
| 972 | + |
| 973 | +All cached responses include `X-Cache-Hit: true/false` header. |
| 974 | + |
952 | 975 | > See [TESTING_GUIDE.md](DOCS/tests/TESTING_GUIDE.md) and [QUICK_START.md](DOCS/setup/QUICK_START.md) |
953 | 976 |
|
954 | 977 | --- |
@@ -1041,6 +1064,10 @@ We take security seriously. If you discover a security vulnerability, please fol |
1041 | 1064 | ```bash |
1042 | 1065 | # Requires admin Bearer token |
1043 | 1066 | curl -H "Authorization: Bearer $TOKEN" https://api.prostaff.gg/api/v1/monitoring/sidekiq |
| 1067 | + |
| 1068 | +# Cache hit rate |
| 1069 | +curl -H "Authorization: Bearer $TOKEN" https://api.prostaff.gg/api/v1/monitoring/cache_stats |
| 1070 | +# { "reads": 4200, "hits": 2730, "misses": 1470, "hit_rate": "65.0%" } |
1044 | 1071 | ``` |
1045 | 1072 |
|
1046 | 1073 | Response shape: |
@@ -1071,6 +1098,28 @@ Response shape: |
1071 | 1098 | | `degraded` | queue > 100, dead > 10, or any scheduled job stale | |
1072 | 1099 | | `critical` | no Sidekiq workers running | |
1073 | 1100 |
|
| 1101 | +### Circuit Breaker — Riot API |
| 1102 | + |
| 1103 | +`CircuitBreakerService` protects the Riot API integration from cascade failures. |
| 1104 | +State persists in Redis (shared across all Puma workers and Sidekiq threads). |
| 1105 | + |
| 1106 | +``` |
| 1107 | +closed (normal) — requests pass through; failure count incremented on error |
| 1108 | +open (tripped) — requests rejected immediately (<100ms); no upstream call |
| 1109 | +half-open (recovery)— one probe request allowed; success closes, failure re-opens |
| 1110 | +``` |
| 1111 | + |
| 1112 | +| Parameter | Default | Env override | |
| 1113 | +|---|---|---| |
| 1114 | +| Failure threshold | 5 consecutive errors | `CIRCUIT_BREAKER_THRESHOLD` | |
| 1115 | +| Recovery timeout | 60 seconds | — | |
| 1116 | + |
| 1117 | +Log events emitted on state transitions: |
| 1118 | +``` |
| 1119 | +[CIRCUIT_BREAKER] Circuit riot_api OPENED after 5 consecutive failures |
| 1120 | +[CIRCUIT_BREAKER] Circuit riot_api CLOSED after recovery |
| 1121 | +``` |
| 1122 | + |
1074 | 1123 | ### 401 Rate Spike Detection |
1075 | 1124 |
|
1076 | 1125 | `Middleware::AuthFailureTracker` counts 401s vs total requests using Redis |
@@ -1212,6 +1261,9 @@ SIDEKIQ_QUEUE_ALERT_THRESHOLD=100 # queue depth → degraded |
1212 | 1261 | SIDEKIQ_DEAD_ALERT_THRESHOLD=10 # dead queue → degraded |
1213 | 1262 | AUTH_TRACKER_THRESHOLD=0.05 # 401 rate spike threshold (5%) |
1214 | 1263 | AUTH_TRACKER_WINDOW=5 # sliding window in minutes |
| 1264 | + |
| 1265 | +# Circuit breaker (optional, defaults shown) |
| 1266 | +CIRCUIT_BREAKER_THRESHOLD=5 # consecutive failures before opening circuit |
1215 | 1267 | ``` |
1216 | 1268 |
|
1217 | 1269 | ### Docker |
|
0 commit comments