11"""
22Database cleanup service for conversation history records.
33
4- This service handles time -based cleanup operations for conversation history records,
5- removing records older than the retention period (default: 1 day) .
4+ This service handles LRU -based cleanup operations for conversation history records,
5+ removing least recently used sessions when session limits are exceeded .
66"""
77
8- from datetime import UTC , datetime , timedelta
8+ from datetime import UTC , datetime
99
1010from sqlalchemy import Engine , func
1111from sqlmodel import Session , select
1212
13- from mcp_as_a_judge .constants import MAX_TOTAL_SESSIONS , RECORD_RETENTION_DAYS
13+ from mcp_as_a_judge .constants import MAX_TOTAL_SESSIONS
1414from mcp_as_a_judge .db .interface import ConversationRecord
1515from mcp_as_a_judge .logging_config import get_logger
1616
2020
2121class ConversationCleanupService :
2222 """
23- Service for cleaning up old conversation history records.
23+ Service for cleaning up conversation history records.
2424
2525 Implements session-based LRU cleanup strategy:
26- - Maintains max 2000 sessions by removing least recently used sessions
27- - Runs once per day to avoid performance overhead
26+ - Maintains session limit by removing least recently used sessions
27+ - Runs immediately when new sessions are created and limit is exceeded
2828
2929 LRU vs FIFO for Better User Experience:
3030 - LRU (Least Recently Used): Keeps sessions that users are actively using,
@@ -45,53 +45,8 @@ def __init__(self, engine: Engine) -> None:
4545 """
4646 self .engine = engine
4747 self .max_total_sessions = MAX_TOTAL_SESSIONS
48- self .retention_days = RECORD_RETENTION_DAYS
49- self .last_cleanup_time = datetime .now (UTC )
50- self .last_session_cleanup_time = datetime .now (UTC )
5148
52- def cleanup_old_records (self ) -> int :
53- """
54- Remove records older than retention_days.
55- This runs once per day to avoid excessive cleanup operations.
56-
57- Returns:
58- Number of records deleted
59- """
60- # Only run cleanup once per day
61- if (datetime .now (UTC ) - self .last_cleanup_time ).days < 1 :
62- return 0
63-
64- cutoff_date = datetime .now (UTC ) - timedelta (days = self .retention_days )
65-
66- with Session (self .engine ) as session :
67- # Count old records
68- old_count_stmt = select (ConversationRecord ).where (
69- ConversationRecord .timestamp < cutoff_date
70- )
71- old_records = session .exec (old_count_stmt ).all ()
72- old_count = len (old_records )
73-
74- if old_count == 0 :
75- logger .info (
76- f"🧹 Daily cleanup: No records older than { self .retention_days } days"
77- )
78- self .last_cleanup_time = datetime .now (UTC )
79- return 0
80-
81- # Delete old records
82- for record in old_records :
83- session .delete (record )
8449
85- session .commit ()
86-
87- # Reset cleanup tracking
88- self .last_cleanup_time = datetime .now (UTC )
89-
90- logger .info (
91- f"🧹 Daily cleanup: Deleted { old_count } records older than "
92- f"{ self .retention_days } days"
93- )
94- return old_count
9550
9651 def get_session_count (self ) -> int :
9752 """
@@ -178,34 +133,27 @@ def cleanup_excess_sessions(self) -> int:
178133
179134 This implements LRU (Least Recently Used) cleanup strategy:
180135 - Keeps sessions that users are actively using (better UX than FIFO)
181- - Only runs once per day to avoid excessive cleanup operations
182- - During the day, session count can exceed limit
183- (e.g., 5000 sessions is not a memory issue)
184- - Daily cleanup brings it back to the target limit (2000 sessions)
136+ - Runs immediately when session limit is exceeded (no daily restriction)
185137 - Removes entire sessions (all records for those session_ids)
138+ - Called every time a new session is created to maintain session limit
186139
187140 Returns:
188141 Number of records deleted
189142 """
190- # Only run session cleanup once per day
191- if (datetime .now (UTC ) - self .last_session_cleanup_time ).days < 1 :
192- return 0
193-
194143 current_session_count = self .get_session_count ()
195144
196145 if current_session_count <= self .max_total_sessions :
197146 logger .info (
198- f"🧹 Daily session LRU cleanup: { current_session_count } sessions "
147+ f"🧹 Session LRU cleanup: { current_session_count } sessions "
199148 f"(max: { self .max_total_sessions } ) - no cleanup needed"
200149 )
201- self .last_session_cleanup_time = datetime .now (UTC )
202150 return 0
203151
204152 # Calculate how many sessions to remove
205153 sessions_to_remove = current_session_count - self .max_total_sessions
206154
207155 logger .info (
208- f"🧹 Daily session LRU cleanup: { current_session_count } sessions exceeds limit "
156+ f"🧹 Session LRU cleanup: { current_session_count } sessions exceeds limit "
209157 f"({ self .max_total_sessions } ), removing { sessions_to_remove } "
210158 f"least recently used sessions"
211159 )
@@ -215,17 +163,13 @@ def cleanup_excess_sessions(self) -> int:
215163
216164 if not lru_session_ids :
217165 logger .warning ("🧹 No sessions found for LRU cleanup" )
218- self .last_session_cleanup_time = datetime .now (UTC )
219166 return 0
220167
221168 # Delete all records for these sessions
222169 deleted_count = self .delete_sessions (lru_session_ids )
223170
224- # Reset cleanup tracking
225- self .last_session_cleanup_time = datetime .now (UTC )
226-
227171 logger .info (
228- f"✅ Daily session LRU cleanup completed: removed { sessions_to_remove } sessions, "
172+ f"✅ Session LRU cleanup completed: removed { sessions_to_remove } sessions, "
229173 f"deleted { deleted_count } records"
230174 )
231175
0 commit comments