3939 workflow_config_for_rdp ,
4040)
4141
42- _DEDUP_CALLBACK_SIGN_KEY = "dedup_callback"
43- _DEDUP_CALLBACK_MAX_AGE = 60 * 60 * 96 # 96 hours
42+ DEDUP_CALLBACK_SALT = "dedup_callback"
43+ DEDUP_CALLBACK_MAX_AGE = 60 * 60 * 96 # 96 hours
4444
4545logger = logging .getLogger (__name__ )
4646
@@ -98,7 +98,7 @@ def _build_dedup_callback_url(rdp_id: int, job_id: int) -> str:
9898 """Build an absolute, signed callback URL for the dedup engine to call when dedup finishes."""
9999 token = signing .dumps (
100100 {"rdp_id" : rdp_id , "job_id" : job_id },
101- key = _DEDUP_CALLBACK_SIGN_KEY ,
101+ salt = DEDUP_CALLBACK_SALT ,
102102 )
103103 path = reverse ("dedup_callback" , kwargs = {"signed_token" : token })
104104 base = config .APP_BASE_URL .rstrip ("/" )
@@ -120,21 +120,35 @@ def create_rdp_and_start_dedup_core(job: AsyncJob) -> dict[str, Any]:
120120 if rdp is None :
121121 raise HopePushError ({"errors" : ["RDP: could not claim deduplication set." ], "rdp_id" : rdp_id })
122122
123- job .refresh_from_db ()
123+ awaiting_callback = False
124+ try :
125+ job .refresh_from_db ()
124126
125- callback_url = _build_dedup_callback_url (rdp_id = rdp_id , job_id = job .pk )
126- processor = DedupProcessor (rdp )
127- processor .run (notification_url = callback_url )
127+ callback_url = _build_dedup_callback_url (rdp_id = rdp_id , job_id = job .pk )
128+ processor = DedupProcessor (rdp )
129+ processor .run (notification_url = callback_url )
128130
129- if processor .has_errors :
130- raise HopePushError ({** processor .total , "rdp_id" : rdp_id })
131+ if processor .has_errors :
132+ with transaction .atomic ():
133+ locked = lock_rdp_for_update (pk = rdp_id )
134+ set_rdp_push_status (
135+ rdp = locked ,
136+ status = Rdp .PushStatus .FAILURE ,
137+ hope_rdi_id = "N/A" ,
138+ )
139+ raise HopePushError ({** processor .total , "rdp_id" : rdp_id })
131140
132- with transaction .atomic ():
133- locked = lock_rdp_for_update (pk = rdp_id )
134- locked .status = Rdp .PushStatus .DEDUP_PENDING
135- locked .save (update_fields = ["status" ])
141+ with transaction .atomic ():
142+ locked = lock_rdp_for_update (pk = rdp_id )
143+ locked .status = Rdp .PushStatus .DEDUP_PENDING
144+ locked .save (update_fields = ["status" ])
136145
137- return {** create_result , "dedup_pending" : True }
146+ awaiting_callback = True
147+ return {** create_result , "dedup_pending" : True }
148+ finally :
149+ # Keep the lock only while waiting for the HDE callback; release on any failure.
150+ if not awaiting_callback :
151+ release_rdp_dedup_settings_lock (rdp_id = rdp_id )
138152
139153
140154def create_and_push_rdp_core (job : AsyncJob ) -> dict [str , Any ]:
@@ -170,11 +184,11 @@ def _lock_and_fail_rdp(rdp_id: int, *, reason: str) -> None:
170184 )
171185
172186
173- def _handle_deduplicated (rdp : Rdp , rdp_id : int , findings_count : int ) -> None :
187+ def _handle_deduplicated (rdp : Rdp , rdp_id : int , job_id : int , findings_count : int ) -> None :
174188 try :
175- origin_job = AsyncJob .objects .filter ( rdp_id = rdp_id ). latest ( "id" )
189+ origin_job = AsyncJob .objects .get ( pk = job_id , rdp_id = rdp_id )
176190 except AsyncJob .DoesNotExist :
177- _lock_and_fail_rdp (rdp_id , reason = "missing origin AsyncJob for threshold config" )
191+ _lock_and_fail_rdp (rdp_id , reason = f "missing origin AsyncJob id= { job_id } for threshold config" )
178192 return
179193
180194 max_findings_percent : int = origin_job .config .get ("max_dedup_findings_percent" , 0 )
@@ -233,7 +247,7 @@ def _handle_deduplicated(rdp: Rdp, rdp_id: int, findings_count: int) -> None:
233247 )
234248
235249
236- def dedup_callback_handle (rdp_id : int ) -> None :
250+ def dedup_callback_handle (rdp_id : int , job_id : int ) -> None :
237251 """Handle a dedup engine callback for the given RDP.
238252
239253 Fetches the current deduplication set state from the engine. Only acts on
@@ -265,8 +279,9 @@ def dedup_callback_handle(rdp_id: int) -> None:
265279 return
266280
267281 logger .info (
268- "dedup_callback_handle: rdp_id=%s deduplication_set_id=%s; fetching remote status" ,
282+ "dedup_callback_handle: rdp_id=%s job_id=%s deduplication_set_id=%s; fetching remote status" ,
269283 rdp_id ,
284+ job_id ,
270285 rdp .deduplication_set_id ,
271286 )
272287
@@ -299,7 +314,7 @@ def dedup_callback_handle(rdp_id: int) -> None:
299314 if dedup_state in terminal_failure_states :
300315 _lock_and_fail_rdp (rdp_id , reason = f"dedup engine state { dedup_state } " )
301316 elif dedup_state == DeduplicationSetState .DEDUPLICATED :
302- _handle_deduplicated (rdp , rdp_id , status .findings_count )
317+ _handle_deduplicated (rdp , rdp_id , job_id , status .findings_count )
303318 else :
304319 logger .info (
305320 "dedup_callback_handle: rdp_id=%s intermediate state %s; waiting for next callback" ,
0 commit comments