Initial commit
This commit is contained in:
93
docs/30_QUOTA_ENFORCEMENT_FLOW1.md
Executable file
93
docs/30_QUOTA_ENFORCEMENT_FLOW1.md
Executable file
@@ -0,0 +1,93 @@
|
||||
# Quota Enforcement — Flow Diagram (rev 2.1)
|
||||
|
||||
> **Scope** – this document explains *how* the free‑tier limits are enforced
|
||||
> inside the scanner service. For policy rationale and legal aspects see
|
||||
> [`33_333_QUOTA_OVERVIEW.md`](33_333_QUOTA_OVERVIEW.md).
|
||||
|
||||
---
|
||||
|
||||
## 0 · Key parameters (rev 2.1)
|
||||
|
||||
| Symbol | Value | Meaning |
|
||||
|--------|-------|---------|
|
||||
| `L_anon` | **{{ quota_anon }}** | Daily ceiling for anonymous users |
|
||||
| `L_jwt` | **{{ quota_token }}** | Daily ceiling for token holders |
|
||||
| `T_warn` | `200` | Soft reminder threshold |
|
||||
| `D_soft` | `5 000 ms` | Delay for *first 30* over‑quota scans |
|
||||
| `D_hard` | `60 000 ms` | Delay for all scans beyond the soft window |
|
||||
|
||||
`L_active` is `L_jwt` if a valid token is present; else `L_anon`.
|
||||
|
||||
---
|
||||
|
||||
## 1 · Sequence diagram
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant C as Client
|
||||
participant API as Scanner API
|
||||
participant REDIS as Redis (quota)
|
||||
C->>API: /scan
|
||||
API->>REDIS: INCR quota:<key>
|
||||
REDIS-->>API: new_count
|
||||
alt new_count ≤ L_active
|
||||
API-->>C: 202 Accepted (no delay)
|
||||
else new_count ≤ L_active + 30
|
||||
API->>C: wait D_soft
|
||||
API-->>C: 202 Accepted
|
||||
else
|
||||
API->>C: wait D_hard
|
||||
API-->>C: 202 Accepted
|
||||
end
|
||||
````
|
||||
|
||||
*Counters auto‑expire **24 h** after first increment (00:00 UTC reset).*
|
||||
|
||||
---
|
||||
|
||||
## 2 · Redis key layout
|
||||
|
||||
| Key pattern | TTL | Description |
|
||||
| ---------------------- | ---- | --------------------------------- |
|
||||
| `quota:ip:<sha256>` | 24 h | Anonymous quota per *hashed* IP |
|
||||
| `quota:tid:<sha256>` | 24 h | Token quota per *hashed* token‑ID |
|
||||
| `quota:ip:<sha256>:ts` | 24 h | First‑seen timestamp (ISO 8601) |
|
||||
|
||||
Keys share a common TTL for efficient mass expiry via `redis-cli --scan`.
|
||||
|
||||
---
|
||||
|
||||
## 3 · Pseudocode (Go‑style)
|
||||
|
||||
```go
|
||||
func gate(key string, limit int) (delay time.Duration) {
|
||||
cnt, _ := rdb.Incr(ctx, key).Result()
|
||||
|
||||
switch {
|
||||
case cnt <= limit:
|
||||
return 0 // under quota
|
||||
case cnt <= limit+30:
|
||||
return 5 * time.Second
|
||||
default:
|
||||
return 60 * time.Second
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
*The middleware applies `time.Sleep(delay)` **before** processing the scan
|
||||
request; it never returns `HTTP 429` under the free tier.*
|
||||
|
||||
---
|
||||
|
||||
## 4 · Metrics & monitoring
|
||||
|
||||
| Metric | PromQL sample | Alert |
|
||||
| ------------------------------ | ------------------------------------------ | --------------------- |
|
||||
| `stella_quota_soft_hits_total` | `increase(...[5m]) > 50` | Many users near limit |
|
||||
| `stella_quota_hard_hits_total` | `rate(...[1h]) > 0.1` | Potential abuse |
|
||||
| Average delay per request | `histogram_quantile(0.95, sum(rate(...)))` | P95 < 1 s expected |
|
||||
|
||||
---
|
||||
|
||||
|
||||
*Generated {{ "now" | date: "%Y‑%m‑%d" }} — values pulled from central constants.*
|
Reference in New Issue
Block a user