@@ -150,11 +150,22 @@ def is_error_cache_ttl_expired(self) -> bool:
150150 datetime .now () - datetime .fromisoformat (self .get_timestamp ())
151151 ).total_seconds () > error_cache_ttl
152152
153+ def is_computing_stale (self ) -> bool :
154+ """Check if a COMPUTING status is stale (task likely failed or stuck)."""
155+ # Use the same TTL as error cache - if computing takes longer than this,
156+ # it's likely stuck and should be retried
157+ computing_ttl = app .config ["THUMBNAIL_ERROR_CACHE_TTL" ]
158+ return (
159+ datetime .now () - datetime .fromisoformat (self .get_timestamp ())
160+ ).total_seconds () >= computing_ttl
161+
153162 def should_trigger_task (self , force : bool = False ) -> bool :
154163 return (
155164 force
156165 or self .status == StatusValues .PENDING
157166 or (self .status == StatusValues .ERROR and self .is_error_cache_ttl_expired ())
167+ or (self .status == StatusValues .COMPUTING and self .is_computing_stale ())
168+ or (self .status == StatusValues .UPDATED and self ._image is None )
158169 )
159170
160171
@@ -264,10 +275,7 @@ def compute_and_cache( # pylint: disable=too-many-arguments
264275 """
265276 cache_key = cache_key or self .get_cache_key (window_size , thumb_size )
266277 cache_payload = self .get_from_cache_key (cache_key ) or ScreenshotCachePayload ()
267- if (
268- cache_payload .status in [StatusValues .COMPUTING , StatusValues .UPDATED ]
269- and not force
270- ):
278+ if not cache_payload .should_trigger_task (force = force ):
271279 logger .info (
272280 "Skipping compute - already processed for thumbnail: %s" , cache_key
273281 )
@@ -277,7 +285,6 @@ def compute_and_cache( # pylint: disable=too-many-arguments
277285 thumb_size = thumb_size or self .thumb_size
278286 logger .info ("Processing url for thumbnail: %s" , cache_key )
279287 cache_payload .computing ()
280- self .cache .set (cache_key , cache_payload .to_dict ())
281288 image = None
282289 # Assuming all sorts of things can go wrong with Selenium
283290 try :
@@ -295,10 +302,12 @@ def compute_and_cache( # pylint: disable=too-many-arguments
295302 cache_payload .error ()
296303 image = None
297304
305+ # Cache the result (success or error) to avoid immediate retries
298306 if image :
299- logger .info ("Caching thumbnail: %s" , cache_key )
300307 with event_logger .log_context (f"screenshot.cache.{ self .thumbnail_type } " ):
301308 cache_payload .update (image )
309+
310+ logger .info ("Caching thumbnail: %s" , cache_key )
302311 self .cache .set (cache_key , cache_payload .to_dict ())
303312 logger .info ("Updated thumbnail cache; Status: %s" , cache_payload .get_status ())
304313 return
0 commit comments