Do not connect to the shared Redis: CACHE_URL stays empty, own Redis is an open decision
The owner decided (2026-10-07) that we do not connect to anything other teams run on the box, Redis included. CACHE_URL was emptied on the server and our keys were removed from the shared Redis. - settings: drop KEY_PREFIX / CACHE_KEY_PREFIX (it only existed because the Redis was shared); comments say CACHE_URL is intentionally empty and ResilientRedisCache is ready for our own Redis. - config/cache.py docstring and tests updated accordingly (11 tests, still run against a local redis-server). - CLAUDE.md: new server rule 8 (touch only our own files and service), status block, historical line that said "db1 is free" now says not to connect. README, operations, handbook, security doc (item 9 reopened as an open decision) say the same. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,10 +1,14 @@
|
||||
"""
|
||||
RedisCache که با قطعیی Redis درخواستها را خراب نمیکند، ولی بیصدا هم نیست.
|
||||
|
||||
چرا: از ۲۰۲۶-۱۰-۰۷ سقفِ نرخ، کشِ برنامهی درسی، کشِ جوابِ دستیار و شمارندههای ورود روی Redisِ مشترکِ
|
||||
ماشیناند. `django.core.cache.backends.redis.RedisCache` با هر خطای شبکه استثنا میدهد و چون
|
||||
DRF برای هر درخواستِ throttleدار کش را میخواند، قطعیِ Redis یعنی ۵۰۰ برای تقریباً همهی API.
|
||||
Redis مالِ ما نیست و ما کنترلی روی بالا بودنش نداریم.
|
||||
وضعیت: فقط وقتی فعال است که `CACHE_URL` ست باشد. روی سرور (۲۰۲۶-۱۰-۰۷) ست نیست و کش `LocMemCache`
|
||||
است: Redisِ آن ماشین مالِ تیمهای دیگر است و عمداً به آن وصل نمیشویم. این کلاس برای Redisِ اختصاصیِ
|
||||
خودمان نوشته و آزموده شده است.
|
||||
|
||||
چرا: سقفِ نرخ، کشِ برنامهی درسی، کشِ جوابِ دستیار و شمارندههای ورود از کش میخوانند.
|
||||
`django.core.cache.backends.redis.RedisCache` با هر خطای شبکه استثنا میدهد و چون DRF برای هر
|
||||
درخواستِ throttleدار کش را میخواند، قطعیِ Redis یعنی ۵۰۰ برای تقریباً همهی API (هر هفت تستِ
|
||||
`config/tests_cache_resilience.py` روی کشِ معمولی میشکند).
|
||||
|
||||
رفتار هنگامِ خطا (همه با `logger.error`، حداکثر هر ۳۰ ثانیه یک بار برای هر پروسه تا لاگ پر نشود؛
|
||||
و `/api/health` در همان حال ۵۰۳ میدهد چون پروبِ کش شکست میخورد):
|
||||
|
||||
@@ -353,12 +353,11 @@ CACHE_URL = os.environ.get('CACHE_URL', '')
|
||||
if CACHE_URL:
|
||||
CACHES = {
|
||||
'default': {
|
||||
# ResilientRedisCache: قطعیِ Redisِ مشترکِ ماشین API را ۵۰۰ نمیکند (config/cache.py شرح میدهد
|
||||
# چه چیزی در عوضش اعمال نمیشود). KEY_PREFIX چون Redis بینِ چند اپ مشترک است و کلیدِ ما با
|
||||
# هیچ کلیدِ دیگری قاطی نشود؛ مقدارش یک انتخابِ نامگذاری است و عوضکردنش فقط کشِ فعلی را خالی میکند.
|
||||
# ResilientRedisCache: قطعیِ Redis API را ۵۰۰ نمیکند (config/cache.py شرح میدهد چه چیزی در
|
||||
# عوضش اعمال نمیشود). روی سرور فعلاً CACHE_URL خالی است: Redisِ روی آن ماشین مالِ تیمهای
|
||||
# دیگر است و عمداً به آن وصل نمیشویم؛ این مسیر برای Redisِ اختصاصیِ خودمان آماده است.
|
||||
'BACKEND': 'config.cache.ResilientRedisCache',
|
||||
'LOCATION': CACHE_URL,
|
||||
'KEY_PREFIX': os.environ.get('CACHE_KEY_PREFIX', 'tadrisino-mathapp'),
|
||||
# بدونِ timeout، Redisِ گیرکرده هر درخواست را تا پایانِ timeoutِ gunicorn (۱۲۰ ثانیه) نگه میدارد.
|
||||
'OPTIONS': {'socket_connect_timeout': 1, 'socket_timeout': 1},
|
||||
}
|
||||
|
||||
@@ -24,12 +24,9 @@ from . import cache as cache_module
|
||||
BACKEND = 'config.cache.ResilientRedisCache'
|
||||
|
||||
|
||||
def _caches(location, prefix=None):
|
||||
conf = {'BACKEND': BACKEND, 'LOCATION': location,
|
||||
'OPTIONS': {'socket_connect_timeout': 1, 'socket_timeout': 1}}
|
||||
if prefix:
|
||||
conf['KEY_PREFIX'] = prefix
|
||||
return {'default': conf}
|
||||
def _caches(location):
|
||||
return {'default': {'BACKEND': BACKEND, 'LOCATION': location,
|
||||
'OPTIONS': {'socket_connect_timeout': 1, 'socket_timeout': 1}}}
|
||||
|
||||
|
||||
class _Quiet:
|
||||
@@ -141,9 +138,9 @@ class RealRedisTests(_Quiet, TestCase):
|
||||
self.proc.terminate()
|
||||
self.proc.wait(timeout=5)
|
||||
|
||||
def test_normal_behaviour_and_key_prefix(self):
|
||||
def test_normal_behaviour_and_keys_have_a_ttl(self):
|
||||
import redis
|
||||
with override_settings(CACHES=_caches(f'redis://127.0.0.1:{self.port}/1', prefix='tadrisino-test')):
|
||||
with override_settings(CACHES=_caches(f'redis://127.0.0.1:{self.port}/1')):
|
||||
c = caches['default']
|
||||
c.set('k', {'a': 1}, 30)
|
||||
self.assertEqual(c.get('k'), {'a': 1})
|
||||
@@ -153,17 +150,16 @@ class RealRedisTests(_Quiet, TestCase):
|
||||
with self.assertRaises(ValueError):
|
||||
c.incr('missing')
|
||||
raw = redis.Redis(host='127.0.0.1', port=self.port, db=1)
|
||||
self.assertTrue(all(b'tadrisino-test' in k for k in raw.keys('*')))
|
||||
self.assertEqual(redis.Redis(host='127.0.0.1', port=self.port, db=0).dbsize(), 0) # دیتابیسِ دیگران دست نخورد
|
||||
self.assertEqual(redis.Redis(host='127.0.0.1', port=self.port, db=0).dbsize(), 0) # فقط دیتابیسِ خودمان
|
||||
self.assertGreater(raw.ttl(raw.keys('*k')[0]), 0) # TTL گذاشته شده؛ کلیدِ بیانقضا نمیماند
|
||||
|
||||
def test_throttle_is_shared_and_health_is_ok(self):
|
||||
with override_settings(CACHES=_caches(f'redis://127.0.0.1:{self.port}/1', prefix='t')):
|
||||
with override_settings(CACHES=_caches(f'redis://127.0.0.1:{self.port}/1')):
|
||||
response = APIClient().get('/api/health')
|
||||
self.assertIs(response.json()['checks']['cache']['ok'], True)
|
||||
|
||||
def test_redis_dying_mid_flight_and_coming_back_without_restarting_the_app(self):
|
||||
with override_settings(CACHES=_caches(f'redis://127.0.0.1:{self.port}/1', prefix='t')):
|
||||
with override_settings(CACHES=_caches(f'redis://127.0.0.1:{self.port}/1')):
|
||||
c = caches['default']
|
||||
c.set('k', 'before', 60)
|
||||
self.assertEqual(c.get('k'), 'before')
|
||||
|
||||
Reference in New Issue
Block a user