@@ -81,12 +81,15 @@ def get_pending_retries(limit: int = 100) -> list:
8181 limit: Maximum number of items to retrieve
8282
8383 Returns:
84- List of retry items (oldest first)
84+ List of tuples: (original_json_string, parsed_dict) (oldest first)
85+ The original JSON string is needed for removal from Redis
8586 """
8687 try :
8788 # Get oldest items first (FIFO)
8889 items = redis_client .zrange (RETRY_QUEUE_KEY , 0 , limit - 1 )
89- return [json .loads (item ) for item in items ]
90+ # Return both original JSON string AND parsed dict
91+ # This ensures we can use the exact key for removal
92+ return [(item .decode () if isinstance (item , bytes ) else item , json .loads (item )) for item in items ]
9093 except Exception as e :
9194 logger .error (f"Failed to get pending retries: { e } " )
9295 return []
@@ -98,15 +101,16 @@ def remove_from_retry_queue(stroke_id: str, retry_item_json: str) -> None:
98101
99102 Args:
100103 stroke_id: Stroke identifier
101- retry_item_json: The JSON string of the retry item (used as Redis key )
102- MUST be the same JSON string used when adding (with sort_keys=True)
104+ retry_item_json: The ORIGINAL JSON string from Redis (not reconstructed! )
105+ This must be the exact string that was used as the Redis key
103106 """
104107 try :
105108 result = redis_client .zrem (RETRY_QUEUE_KEY , retry_item_json )
106109 if result > 0 :
107- logger .info (f"Removed stroke { stroke_id } from retry queue (success) " )
110+ logger .info (f"Removed stroke { stroke_id } from retry queue" )
108111 else :
109- logger .warning (f"Stroke { stroke_id } not found in retry queue (may have been already removed)" )
112+ logger .error (f"Stroke { stroke_id } not found in retry queue key mismatch! Queue will not shrink!" )
113+ logger .error (f"This indicates a bug in JSON serialization consistency" )
110114 except Exception as e :
111115 logger .error (f"Failed to remove stroke { stroke_id } from retry queue: { e } " )
112116
@@ -162,7 +166,7 @@ def process_retry_queue(max_items: int = 50) -> Dict[str, int]:
162166
163167 logger .info (f"Processing { len (pending_items )} pending GraphQL retries" )
164168
165- for item in pending_items :
169+ for original_json , item in pending_items :
166170 stroke_id = item .get ("stroke_id" )
167171 asset_data = item .get ("asset_data" )
168172
@@ -175,7 +179,7 @@ def process_retry_queue(max_items: int = 50) -> Dict[str, int]:
175179 attempts = get_retry_attempts (stroke_id )
176180 if attempts >= MAX_RETRY_ATTEMPTS :
177181 logger .error (f"Stroke { stroke_id } exceeded max retry attempts ({ MAX_RETRY_ATTEMPTS } ), removing from queue" )
178- remove_from_retry_queue (stroke_id , json . dumps ( item ) )
182+ remove_from_retry_queue (stroke_id , original_json )
179183 stats ["skipped" ] += 1
180184 continue
181185
@@ -193,9 +197,9 @@ def process_retry_queue(max_items: int = 50) -> Dict[str, int]:
193197 txn_id = commit_transaction_via_graphql (prep )
194198 logger .info (f"RETRY SUCCESS: Stroke { stroke_id } committed to ResilientDB: { txn_id } " )
195199
196- # Remove from queue on success - use deterministic JSON serialization
197- retry_item_json = json . dumps ( item , sort_keys = True )
198- remove_from_retry_queue (stroke_id , retry_item_json )
200+ # Use original JSON string as Redis key for removal
201+ # This ensures exact match with the key that was stored
202+ remove_from_retry_queue (stroke_id , original_json )
199203 stats ["success" ] += 1
200204
201205 except Exception as e :
0 commit comments