1+ import logging
12from collections .abc import Callable , Iterator
23from functools import partial
34from typing import Any , NamedTuple
5+ from constance import config
46from uuid import UUID , uuid4
57
6- from django .conf import settings
78from django .core import signing
89from django .db import IntegrityError , transaction
910from django .urls import reverse
4344_DEDUP_CALLBACK_SIGN_KEY = "dedup_callback"
4445_DEDUP_CALLBACK_MAX_AGE = 60 * 60 * 96 # 96 hours
4546
47+ logger = logging .getLogger (__name__ )
48+
4649
4750class CloneDeduplicationContext (NamedTuple ):
4851 dedup_source_pk : int
@@ -130,7 +133,7 @@ def _build_dedup_callback_url(rdp_id: int, job_id: int) -> str:
130133 key = _DEDUP_CALLBACK_SIGN_KEY ,
131134 )
132135 path = reverse ("dedup_callback" , kwargs = {"signed_token" : token })
133- base = getattr ( settings , " APP_BASE_URL" , "" ) .rstrip ("/" )
136+ base = config . APP_BASE_URL .rstrip ("/" )
134137 return f"{ base } { path } "
135138
136139
@@ -144,9 +147,10 @@ def create_rdp_and_start_dedup_core(job: AsyncJob) -> dict[str, Any]:
144147 create_result = create_rdp_core (job )
145148 rdp_id = create_result ["rdp_id" ]
146149
147- _check , rdp = claim_rdp_deduplication (rdp_id )
148- if rdp is None :
149- raise HopePushError ({"errors" : ["RDP: could not claim deduplication set." ], "rdp_id" : rdp_id })
150+ with transaction .atomic ():
151+ _check , rdp = claim_rdp_deduplication (rdp_id )
152+ if rdp is None :
153+ raise HopePushError ({"errors" : ["RDP: could not claim deduplication set." ], "rdp_id" : rdp_id })
150154
151155 job .refresh_from_db ()
152156
@@ -179,11 +183,23 @@ def create_and_push_rdp_core(job: AsyncJob) -> dict[str, Any]:
179183 return create_rdp_and_start_dedup_core (job )
180184
181185
182- def _lock_and_fail_rdp (rdp_id : int ) -> None :
186+ def _lock_and_fail_rdp (rdp_id : int , * , reason : str ) -> None :
183187 with transaction .atomic ():
184188 locked = lock_rdp_for_update (pk = rdp_id )
185189 if locked .status == Rdp .PushStatus .DEDUP_PENDING :
186190 set_rdp_push_status (rdp = locked , status = Rdp .PushStatus .FAILURE , hope_rdi_id = "N/A" )
191+ logger .warning (
192+ "dedup_callback_handle: rdp_id=%s marked FAILURE (%s)" ,
193+ rdp_id ,
194+ reason ,
195+ )
196+ else :
197+ logger .info (
198+ "dedup_callback_handle: rdp_id=%s skip FAILURE (%s); status=%s" ,
199+ rdp_id ,
200+ reason ,
201+ locked .status ,
202+ )
187203
188204
189205def _handle_deduplicated (rdp : Rdp , rdp_id : int , findings_count : int ) -> None :
@@ -193,13 +209,31 @@ def _handle_deduplicated(rdp: Rdp, rdp_id: int, findings_count: int) -> None:
193209 total_individuals = qs_individuals_for_rdp (rdp = rdp ).count ()
194210 findings_rate = findings_count / total_individuals * 100 if total_individuals > 0 else float (findings_count )
195211
212+ logger .info (
213+ "dedup_callback_handle: rdp_id=%s DEDUPLICATED findings_count=%s total_individuals=%s "
214+ "findings_rate=%.2f%% max_dedup_findings_percent=%s" ,
215+ rdp_id ,
216+ findings_count ,
217+ total_individuals ,
218+ findings_rate ,
219+ max_findings_percent ,
220+ )
221+
196222 if findings_rate > max_findings_percent :
197- _lock_and_fail_rdp (rdp_id )
223+ _lock_and_fail_rdp (
224+ rdp_id ,
225+ reason = f"findings_rate { findings_rate :.2f} % exceeds threshold { max_findings_percent } %" ,
226+ )
198227 return
199228
200229 with transaction .atomic ():
201230 locked = lock_rdp_for_update (pk = rdp_id )
202231 if locked .status != Rdp .PushStatus .DEDUP_PENDING :
232+ logger .warning (
233+ "dedup_callback_handle: rdp_id=%s skip push queue; status changed to %s" ,
234+ rdp_id ,
235+ locked .status ,
236+ )
203237 return
204238 locked .status = Rdp .PushStatus .PENDING
205239 locked .save (update_fields = ["status" ])
@@ -214,6 +248,11 @@ def _handle_deduplicated(rdp: Rdp, rdp_id: int, findings_count: int) -> None:
214248 config = {"rdp_id" : rdp_id },
215249 )
216250 push_job .queue ()
251+ logger .info (
252+ "dedup_callback_handle: rdp_id=%s status DEDUP_PENDING→PENDING; queued push job_id=%s" ,
253+ rdp_id ,
254+ push_job .pk ,
255+ )
217256
218257
219258def dedup_callback_handle (rdp_id : int ) -> None :
@@ -231,27 +270,64 @@ def dedup_callback_handle(rdp_id: int) -> None:
231270 try :
232271 rdp = rdp_for_push (pk = rdp_id )
233272 except Rdp .DoesNotExist :
273+ logger .warning ("dedup_callback_handle: rdp_id=%s not found" , rdp_id )
234274 return
235275
236276 if rdp .status != Rdp .PushStatus .DEDUP_PENDING :
277+ logger .info (
278+ "dedup_callback_handle: rdp_id=%s skip; status=%s (expected DEDUP_PENDING)" ,
279+ rdp_id ,
280+ rdp .status ,
281+ )
237282 return
238283
239284 if not rdp .deduplication_set_id :
240- _lock_and_fail_rdp (rdp_id )
285+ logger .warning ("dedup_callback_handle: rdp_id=%s missing deduplication_set_id" , rdp_id )
286+ _lock_and_fail_rdp (rdp_id , reason = "missing deduplication_set_id" )
241287 return
242288
289+ logger .info (
290+ "dedup_callback_handle: rdp_id=%s deduplication_set_id=%s; fetching remote status" ,
291+ rdp_id ,
292+ rdp .deduplication_set_id ,
293+ )
294+
243295 status = get_rdp_policy (rdp ).deduplication_status (rdp )
244- if status is None or status .response_status != DedupResponseStatus .OK :
245- # Dedup engine unavailable — leave in DEDUP_PENDING for the next callback.
296+ if status is None :
297+ logger .warning (
298+ "dedup_callback_handle: rdp_id=%s dedup engine returned no status; leaving DEDUP_PENDING" ,
299+ rdp_id ,
300+ )
301+ return
302+
303+ if status .response_status != DedupResponseStatus .OK :
304+ logger .warning (
305+ "dedup_callback_handle: rdp_id=%s dedup engine status unavailable (%s); leaving DEDUP_PENDING" ,
306+ rdp_id ,
307+ status .response_status ,
308+ )
246309 return
247310
248311 dedup_state = status .deduplication_set_status
249312 terminal_failure_states = {DeduplicationSetState .DEDUPLICATION_FAILED , DeduplicationSetState .ENCODING_FAILED }
250313
314+ logger .info (
315+ "dedup_callback_handle: rdp_id=%s remote_state=%s findings_count=%s" ,
316+ rdp_id ,
317+ dedup_state ,
318+ status .findings_count ,
319+ )
320+
251321 if dedup_state in terminal_failure_states :
252- _lock_and_fail_rdp (rdp_id )
322+ _lock_and_fail_rdp (rdp_id , reason = f"dedup engine state { dedup_state } " )
253323 elif dedup_state == DeduplicationSetState .DEDUPLICATED :
254324 _handle_deduplicated (rdp , rdp_id , status .findings_count )
325+ else :
326+ logger .info (
327+ "dedup_callback_handle: rdp_id=%s intermediate state %s; waiting for next callback" ,
328+ rdp_id ,
329+ dedup_state ,
330+ )
255331
256332
257333def claim_rdp_deduplication (rdp_id : int ) -> tuple [ActionCheck , Rdp | None ]:
0 commit comments