From e8cacb57d531137dec3109617e807b30ff5187c9 Mon Sep 17 00:00:00 2001 From: EloquentBrush <147827411+EloquentBrush@users.noreply.github.com> Date: Mon, 11 May 2026 12:49:19 +0300 Subject: [PATCH] fix(feishu): cap _message_text_cache with LRU eviction to prevent unbounded growth _message_text_cache was a plain dict with no size limit. Every unique message_id whose text was fetched (for reply-context lookups) stayed in memory permanently, causing unbounded growth in long-running deployments with active group chats. Replace with an OrderedDict and evict the least-recently-used entry whenever the cache exceeds _FEISHU_MESSAGE_TEXT_CACHE_SIZE (512). Cache hits call move_to_end() to refresh LRU order. Mirrors the identical pattern already used by _pending_processing_reactions in the same class. --- gateway/platforms/feishu.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/gateway/platforms/feishu.py b/gateway/platforms/feishu.py index 10ddbb17d..2366b65ac 100644 --- a/gateway/platforms/feishu.py +++ b/gateway/platforms/feishu.py @@ -240,6 +240,7 @@ _FEISHU_REACTION_FAILURE = "CrossMark" # drain on completion; the cap is a safeguard against unbounded growth from # delete-failures, not a capacity plan. _FEISHU_PROCESSING_REACTION_CACHE_SIZE = 1024 +_FEISHU_MESSAGE_TEXT_CACHE_SIZE = 512 # LRU cap for reply-context message text lookups # QR onboarding constants _ONBOARD_ACCOUNTS_URLS = { @@ -1452,7 +1453,7 @@ class FeishuAdapter(BasePlatformAdapter): self._sent_message_ids_to_chat: Dict[str, str] = {} # message_id → chat_id (for reaction routing) self._sent_message_id_order: List[str] = [] # LRU order for _sent_message_ids_to_chat self._chat_info_cache: Dict[str, Dict[str, Any]] = {} - self._message_text_cache: Dict[str, Optional[str]] = {} + self._message_text_cache: "OrderedDict[str, Optional[str]]" = OrderedDict() self._app_lock_identity: Optional[str] = None self._text_batch_state = FeishuBatchState() self._pending_text_batches = self._text_batch_state.events @@ -3959,6 +3960,7 @@ class FeishuAdapter(BasePlatformAdapter): if not self._client or not message_id: return None if message_id in self._message_text_cache: + self._message_text_cache.move_to_end(message_id) return self._message_text_cache[message_id] try: request = self._build_get_message_request(message_id) @@ -3980,6 +3982,9 @@ class FeishuAdapter(BasePlatformAdapter): mentions=parent_mentions, ) self._message_text_cache[message_id] = text + self._message_text_cache.move_to_end(message_id) + while len(self._message_text_cache) > _FEISHU_MESSAGE_TEXT_CACHE_SIZE: + self._message_text_cache.popitem(last=False) return text except Exception: logger.warning("[Feishu] Failed to fetch parent message %s", message_id, exc_info=True)