⏱️ धडा 10 — Rate limits आणि caching: काउंटरवरची रांग
📍 तुम्ही इथे आहात: 12 पैकी धडा 10 · मागे: lesson-09-versioning · पुढे: lesson-11-beyond-rest
📦 या ब्रँचमध्ये काय आहे
धडे 01–09, आणि त्यासोबत सगळे एकदम आले की काय होते:
rate limits (429, Retry-After), caching (ETag,
If-None-Match, Cache-Control), timeouts आणि jitter सह
backoff — आणि हे सगळे करणारा एक सभ्य client. खरी file:
- api/client.py — सभ्य client, 40 ओळींपेक्षा कमी
🧒 5 वर्षांच्या मुलाला समजावल्यासारखे
गर्दीच्या दिवशी तीन गोष्टी फ्रंट ऑफिस शांत ठेवतात:
- रांग ⏱️ — प्रत्येक पाहुणा 10
सेकंदात 20 slips देऊ शकतो. 21 व्या slip ला
429 Too Many Requestsशिक्का आणि एक टीप मिळते:Retry-After: 10. ओरडून कोणीही office रिकामे करू शकत नाही. - प्रत 🧾 — तुम्ही एखादा विद्यार्थी आणता तेव्हा शिक्क्यावर एक
ETagअसतो, उत्तराचा ठसा (fingerprint). पुढच्या वेळी तुम्ही "बदलले असेल तरच" (If-None-Match: "0e9a…") जोडता आणि, ते बदलले नसेल तर, clerk body शिवाय304 Not Modifiedउत्तर देतो: तुमची प्रत अजून चांगली आहे.Cache-Control: max-age=30याच्याही पुढे जाते: 30 सेकंद, विचारूसुद्धा नका. - सभ्य पाहुणा 😌 — कधीच कायम वाट पाहत नाही (timeout 3 s);
429सांगितल्यावर किंवा office बिघडलेले असताना (5xx), दार ठोकत बसण्याऐवजी दर वेळी जास्त, थोड्या randomness सह थांबतो (backoff + jitter: 1 s, 2 s, 4 s …); काही प्रयत्नांनंतर हार मानतो.
🗺️ आकृती
sequenceDiagram
participant C as 😌 polite client
participant S as 🏢 school-api
C->>S: 1 GET /v1/students/2
S-->>C: 2 200 · ETag "0e9a…" · Cache-Control: max-age=30
C->>S: 3 GET /v1/students/2 If-None-Match: "0e9a…"
S-->>C: 4 304 Not Modified (no body — your copy is good)
C->>S: 5 slip #21 within 10 s
S-->>C: 6 429 rate_limited · Retry-After: 10
Note over C: sleep Retry-After (+ jitter), retry, give up after 4 tries
❓ काय
- Rate limiting: प्रत्येक client साठी (IP, key, किंवा user) एका कालखंडासह एक
बादली (bucket); ती रिकामी झाली की
429+Retry-After. खरी फाटके token buckets वापरतात आणि servers दरम्यान स्थिती वाटून घेतात (धडा 12). API key नुसार मर्यादा IP नुसार मर्यादेपेक्षा सरस (अनेक users एकच NAT वापरतात). - ETag / If-None-Match: validation caching — एक स्वस्त फेरी,
body नाही. Cache-Control: max-age: expiration caching — तेवढा वेळ
शून्य फेऱ्या.
private= फक्त client cache करू शकतो;public= CDNs आणि gateways सुद्धा करू शकतात. - Timeouts: प्रत्येक call ला एक असतो (connect + read). Timeout नसणे म्हणजे कायम वाट पाहत बसलेला thread.
- Jitter सह backoff:
sleep(min(cap, base × 2ⁿ) + random). Jitter हजारो clients ना एकाच वेळी एकसाथ retry करण्यापासून थांबवतो ("thundering herd").Retry-Afterअसेल तर ते पाळा. - Retry budget: काही प्रयत्न, मग खरा error — अनंत retries म्हणजे outage वाढवणारे यंत्र.
🤔 का
कारण मर्यादा नसलेले API सगळ्यांसाठी बंद पडण्यापासून फक्त एका bug च्या अंतरावर असते, आणि timeouts व backoff नसलेला client हाच तो bug असतो. Caching हाच धडा दुसऱ्या बाजूने: सर्वात स्वस्त slip म्हणजे जी कधी दिलीच जात नाही. या तिन्ही सवयी मिळून counter वर "resilient" चा बहुतेक अर्थ होतो.
🔧 कसे (या repo मध्ये)
rate_limited() (प्रत्येक client IP साठी bucket, 10 s मध्ये RATE_LIMIT, 429 +
retry_after_seconds), etag_of() + do_GET मधली If-None-Match तपासणी,
आणि प्रत्येक read वर Cache-Control. api/client.py client चा
अर्धा भाग दाखवते: timeout, स्वतःच्या cache मधून If-None-Match, 429
आणि 5xx वर backoff, 4 चा retry budget.
🧪 करून पाहा
B=http://127.0.0.1:8080/v1
T=$(curl -si $B/students/2 | tr -d '\r' | awk -F': ' '/^ETag/{print $2}'); echo "etag $T"
curl -si $B/students/2 -H "If-None-Match: $T" | sed -n '1p' # 304
for i in $(seq 1 25); do curl -s -o /dev/null -w '%{http_code} ' $B/health; done; echo # 200 ×20 then 429
curl -s $B/health # the 429 body: retry_after_seconds
sleep 10; python3 api/client.py # 200, then 304 from cache, then 200s
RATE_LIMIT=3 python3 api/school_api.py 8081 & sleep 1; for i in 1 2 3 4 5; do curl -s -o /dev/null -w '%{http_code} ' http://127.0.0.1:8081/v1/health; done; echo
✅ तपासा — तुम्हाला काय दिसायला हवे
Body शिवाय 304; वीस 200 मग 429; retry_after_seconds: 10 असलेला JSON error. client.py एक 200, मग स्वतःच्या cache मधून दिलेला 304, मग तीन 200 छापते — आणि bucket रिकामी असताना तुम्ही तो चालवला तर वाढत जाणाऱ्या आणि थांबणाऱ्या 429 → sleeping 1.7s (attempt 1) ओळी. Port 8081 वर RATE_LIMIT=3 सह, चौथा call 429 असतो.
🏁 तुम्ही आत्ताच काय सिद्ध केले
गर्दीच्या लोंढ्यातही counter न्याय्य राहतो, न बदललेल्या उत्तराला body चा खर्च लागत नाही, आणि सभ्य client जाणूनबुजून थांबतो, मागे सरकतो (back off) आणि हार मानतो.
⚠️ नेहमीच्या चुका
- बाहेर जाणाऱ्या calls वर timeout नसणे — "सगळे अडकले" चे जुने-जाणते कारण
- jitter शिवाय retry करणे — प्रत्येक client त्याच सेकंदाला retry करतो
4xxretry करणे (429सोडून)- फक्त IP नुसार rate limiting — NAT मागचे एक संपूर्ण office एकाच user सारखे मर्यादित होते
- वैयक्तिक data वर
Cache-Control: public— सामायिक cache एका user चे पान दुसऱ्याला देतो
🏭 प्रत्यक्ष वापरात हे का महत्त्वाचे: AWS API Gateway, Kubernetes Ingress controllers आणि प्रत्येक CDN नेमके हेच headers आणि codes लागू करतात; सभ्य-client चे नियम प्रत्येक SDK मध्ये आधीच बांधलेले असतात. ते इथे एकदा शिका, सगळीकडे ओळखा.
⏭️ पुढे
REST हा एकमेव आकार नाही. GraphQL, gRPC, webhooks आणि events — आणि जेव्हा counter तुम्हाला परत call करतो.
git checkout lesson-11-beyond-rest