cache_manager module

מנהל Cache מתקדם עם Redis Advanced Cache Manager with Redis

class cache_manager.DynamicTTL[מקור]

Bases: object

ניהול TTL דינמי לפי סוג תוכן וקונטקסט.

הערכים כאן מייצגים TTL בסיסי בשניות עבור סוגי תוכן שכיחים.

BASE_TTL: Dict[str, int] = {'bookmarks': 120, 'collections_detail': 30, 'collections_items': 180, 'collections_list': 60, 'file_content': 3600, 'file_list': 300, 'markdown_render': 1800, 'public_stats': 600, 'search_results': 180, 'settings': 60, 'sticky_summary': 60, 'tags': 300, 'user_stats': 600}
classmethod calculate_ttl(content_type, context=None)[מקור]

חשב TTL בסיסי מוכוון קונטקסט.

מבטיח גבולות בטוחים: מינימום 60 שניות, מקסימום 7200 (שעתיים).

Return type:

int

פרמטרים:
class cache_manager.ActivityBasedTTL[מקור]

Bases: object

התאמת TTL לפי שעות פעילות (best-effort).

classmethod get_activity_multiplier()[מקור]
Return type:

float

classmethod adjust_ttl(base_ttl)[מקור]
Return type:

int

פרמטרים:

base_ttl (int)

cache_manager.build_cache_key(*parts)[מקור]

בניית מפתח cache יעיל ומובנה מהחלקים הנתונים.

  • מסנן חלקים ריקים

  • ממיר לתווים בטוחים (רווחים/סלאשים)

  • מגביל אורך ומוסיף hash קצר במידת הצורך

Return type:

str

פרמטרים:

parts (Any)

class cache_manager.CacheManager[מקור]

Bases: object

מנהל Cache מתקדם עם Redis

__init__()[מקור]
enable_debug_for(seconds)[מקור]

הפעל/הארך חלון דיבאג זמני ללוגים של HIT/MISS/SET.

  • אם seconds <= 0: מכבה דיבאג (debug_until=0)

  • אחרת: מאריך (לא מקצר) את החלון כך שיסתיים לפחות בעוד seconds שניות מהעכשיו

מחזיר את timestamp החדש של debug_until.

Return type:

float

פרמטרים:

seconds (int)

connect()[מקור]

התחברות ל-Redis

get(key)[מקור]

קבלת ערך מה-cache

Return type:

Optional[Any]

פרמטרים:

key (str)

set(key, value, expire_seconds=300)[מקור]

שמירת ערך ב-cache

Return type:

bool

פרמטרים:
set_dynamic(key, value, content_type, context=None)[מקור]

שמירה ב-cache עם TTL דינמי ותיעוד מינימלי במטריקות/לוגים.

Return type:

bool

פרמטרים:
get_with_refresh(key, refresh_func, *, content_type, context=None)[מקור]

קריאה מ-cache; אם חסר – מחשב, שומר דינמית ומחזיר.

Return type:

Any

פרמטרים:
delete(key)[מקור]

מחיקת ערך מה-cache

Return type:

bool

פרמטרים:

key (str)

delete_pattern(pattern)[מקור]

מחיקת כל המפתחות שמתאימים לתבנית, ב-Redis ובפולבק המקומי גם יחד.

מקרה פרטי של delete_patterns() עם דפוס אחד, וכל התיעוד שם חל גם כאן. נשארת כי היא ה-API שרוב הקוראים בריפו משתמשים בו.

Return type:

int

פרמטרים:

pattern (str)

delete_patterns(patterns)[מקור]

מחיקת כל המפתחות שמתאימים ל**אחד** מהדפוסים, בקריאה אחת.

הפולבק המקומי מנוקה תמיד ולפני בדיקת is_enabled: הוא מאוכלס דווקא כש-Redis אינו זמין, ולכן דילוג עליו כאן משאיר נתונים ישנים בזיכרון עד שה-TTL פג — גם אחרי כתיבה שהצליחה.

סריקה נפרדת לכל דפוס, עם ``MATCH`` בצד השרת. גרסת ביניים של הפונקציה הזו הריצה סריקה אחת בלי MATCH וסיננה בפייתון, ונמדדה מול Redis 7 עם 200,000 מפתחות (flask-limiter, סשנים וקאש חולקים אותו מסד): delete_pattern עם דפוס יחיד משך 200,007 מפתחות לתהליך כדי למחוק אחד. יש בריפו עשרות קוראים כאלה, וב-MCP הקריאה רצה על ה-worker היחיד של _WRITE_POOL — כלומר עדכון תיאור אחד חסם כל כתיבה אחרת למשך המשיכה. MATCH אינו מקצר את המעבר של Redis על ה-keyspace, אבל הוא קובע מה חוצה את הרשת ומה מעובד כאן: עם match חוזרים רק המפתחות שמתאימים. מספר ה-roundtrips לתבנית הוא DBSIZE / _SCAN_COUNT, וזה — כפול ה-RTT — מה שקובע את זמן הקריאה בייצור; הנימוק למספר כתוב על הקבוע.

מה כן מאוחד: תקציב הזמן, ה-batch, והספירה. הצורה המקורית קראה ל-delete_pattern בלולאה וכל קריאה קיבלה CACHE_DELETE_PATTERN_BUDGET_SECONDS משלה — עשרה דפוסים יכלו לחסום עד 50 שניות. כאן הדדליין אחד לכל הקריאה.

מפתח שהותאם לעולם אינו נזרק. בצורה הקודמת, כשהתקציב נגמר, ה- batch החלקי — עד 199 מפתחות שכבר עברו התאמה — נזרק בלי DEL, והתוצאה הייתה בדיוק הקאש הישן שהניקוי בא להסיר. כאן ה-batch נשטף לפני כל יציאה, כולל מיצוי תקציב וחריגה; ומיצוי תקציב נרשם ב-WARNING עם מספר הדפוסים שהושלמו, כי ניקוי חלקי שנראה כמו ניקוי מלא הוא הכשל השקט של הפונקציה הזו.

שני סירובים שהם שגיאת קורא, ולכן זורקים ולא נבלעים:

  • מחרוזת במקום רשימה — TypeError. str עומד ב-Sequence[str], ו-[str(p) for p in "file_content:*"] מתפרק לדפוס לכל תו; התו * לבדו מתאים ל**כל** מפתח. טעות של תו אחד בין delete_pattern ל-delete_patterns הייתה מוחקת את המסד כולו, כולל מוני ה-rate-limit.

  • דפוס שמתאים לכל מפתח (*, **, ?* — בלי תו מילולי כלל) — ValueError. ניקוי של כל הקאש עובר דרך clear_all(), שעושה זאת במפורש ובמבוקר, ולא דרך כאן. הקריטריון הוא ”אין תו מילולי בכלל“ ולא ”אין תו מילולי לפני הכוכבית הראשונה“: *:user:{uid}:* ב-invalidate_user_cache() מתחיל בכוכבית ואינו מתאים לכל מפתח — הוא דורש :user:{uid}: — והצורה השנייה הייתה פוסלת אותו.

על הערך המוחזר: המספר סופר רק מה שנמחק בתהליך הזה. שתי מגבלות שהקורא חייב להכיר, כי אף אחת מהן לא משתקפת במספר:

  1. הפולבק המקומי הוא פר-תהליך. עם יותר מ-worker אחד, ניקוי כאן לא נוגע בעותקים של ה-workers האחרים. נתון שאסור להגיש ישן — למשל מספר גרסה — לא יכול להסתמך על הקאש הזה בכלל, בשום מצב.

  2. כש-Redis אינו זמין, 0 אינו מבחין בין ”לא היה מה למחוק“ לבין ”לא יכולתי לגשת“. לכן המצב נרשם ללוג פעם אחת במקום להיבלע.

Return type:

int

פרמטרים:

patterns (Sequence[str])

invalidate_user_cache(user_id)[מקור]

מחיקת כל ה-cache של משתמש ספציפי

Return type:

int

פרמטרים:

user_id (int)

clear_all()[מקור]

ניקוי כל המטמון באופן מבוקר.

  • הפולבק המקומי מנוקה תמיד, גם כש-Redis מושבת. אחרת ”נקה הכל“ משאיר בדיוק את הקאש שפעיל כשאין Redis.

  • אם Redis פעיל – מוחק גם את כל המפתחות שלו באמצעות SCAN+DEL (best-effort).

Return type:

int

ביטול קאש לפי קובץ: תוכן/רינדור/רשימות.

דפוסים נפוצים מעוגנים לאחור בהתאם למפתחות הקיימים בקוד.

אין כאן יציאה מוקדמת על is_enabled: delete_pattern מנקה גם את הפולבק המקומי, שקיים דווקא כש-Redis כבוי. דילוג כאן היה משאיר את הערכים הישנים בזיכרון.

Return type:

int

פרמטרים:
clear_stale(max_scan=1000, ttl_seconds_threshold=60)[מקור]

מחיקת מפתחות שכבר עומדים לפוג (”stale“) בצורה עדינה.

היגיון: - הפולבק המקומי מנוקה תמיד מפגי-תוקף, גם כש-Redis מושבת. - אם Redis מושבת – מחזיר את מה שנוקה מקומית בלבד. - סריקה מדורגת (SCAN) של עד max_scan מפתחות. - מחיקה רק למפתחות עם TTL חיובי קטן מ-ttl_seconds_threshold, או TTL שלילי המציין שאינו קיים. - לא מוחקים מפתחות ללא TTL (ttl == -1) כדי להימנע מפגיעה בקאש ארוך-חיים.

Return type:

int

פרמטרים:
  • max_scan (int)

  • ttl_seconds_threshold (int)

get_stats()[מקור]

סטטיסטיקות cache

Return type:

Dict[str, Any]

cache_manager.dynamic_cache(content_type, key_prefix=None)[מקור]

דקורטור ל-caching דינמי ל-Flask endpoints.

  • בונה מפתח קאש יציב הכולל משתמש/נתיב/פרמטרים

  • שומר רק טיפוסים serializable; עבור Response עם JSON שומר את ה-data בלבד

  • Fail-open: לעולם לא מפיל endpoint על בעיות קאש

Return type:

Callable[[Callable[[ParamSpec(P)], TypeVar(R)]], Callable[[ParamSpec(P)], TypeVar(R)]]

פרמטרים:
  • content_type (str)

  • key_prefix (str | None)

cache_manager.cached(expire_seconds=300, key_prefix='default')[מקור]

דקורטור לcaching פונקציות

פרמטרים:
  • expire_seconds (int)

  • key_prefix (str)

cache_manager.async_cached(expire_seconds=300, key_prefix='default')[מקור]

דקורטור לcaching פונקציות async

פרמטרים:
  • expire_seconds (int)

  • key_prefix (str)