@@ -104,6 +104,12 @@ def _sweep_job_state(task_id):
104104
105105_recovery_state = {'last' : None }
106106
107+ # A row is written BEFORE its RQ job is enqueued (and, for a provider migration,
108+ # committed with the migration itself), so a brand-new row legitimately has no job
109+ # yet. Without this, recovery could revoke a perfectly good alignment microseconds
110+ # before its enqueue landed and run a second one alongside it.
111+ _ENQUEUE_GRACE_SECONDS = 120
112+
107113_ABANDONED_FIRST_SEEN = {}
108114
109115_ABANDONED_CONFIRM_SECONDS = 60
@@ -135,23 +141,28 @@ def _details_full_refresh(details):
135141 return bool (details .get ('full_refresh' )) if isinstance (details , dict ) else False
136142
137143
138- def enqueue_server_alignment (server_id = None , message = None ):
139- """Queue a full-refresh alignment of ONE server (the default when unnamed).
140-
141- Raw connection and a direct enqueue, like ``recover_abandoned_sweeps``, so a
142- caller with no Flask app context - the provider migration runs in an RQ job -
143- can ask for the alignment that rebuilds what a provider swap cannot carry:
144- the server's artist ids and the file paths of the tracks it repointed.
145- Returns the task id, or None when there is no server to align.
146- """
144+ def insert_pending_sweep_row (cur , task_id , message , full_refresh = True ):
147145 import config
146+
147+ details = json .dumps ({
148+ 'message' : message ,
149+ 'status_message' : message ,
150+ 'full_refresh' : bool (full_refresh ),
151+ })
152+ cur .execute (
153+ "INSERT INTO task_status "
154+ "(task_id, task_type, status, progress, details, timestamp, start_time) "
155+ "VALUES (%s, %s, %s, 0, %s, NOW(), %s) "
156+ "ON CONFLICT (task_id) DO NOTHING" ,
157+ (task_id , SWEEP_TASK_TYPE , config .TASK_STATUS_PENDING , details , time .time ()),
158+ )
159+
160+
161+ def enqueue_server_alignment (server_id = None , message = None , task_id = None ):
148162 from app_helper import rq_queue_high
149163
150- task_id = str (uuid .uuid4 ())
164+ task_id = task_id or str (uuid .uuid4 ())
151165 text = message or 'Server alignment queued.'
152- details = json .dumps (
153- {'message' : text , 'status_message' : text , 'full_refresh' : True }
154- )
155166 db = connect_raw ()
156167 db .autocommit = True
157168 try :
@@ -160,13 +171,7 @@ def enqueue_server_alignment(server_id=None, message=None):
160171 return None
161172 cur = db .cursor ()
162173 try :
163- cur .execute (
164- "INSERT INTO task_status "
165- "(task_id, task_type, status, progress, details, timestamp, start_time) "
166- "VALUES (%s, %s, %s, 0, %s, NOW(), %s) "
167- "ON CONFLICT (task_id) DO NOTHING" ,
168- (task_id , SWEEP_TASK_TYPE , config .TASK_STATUS_PENDING , details , time .time ()),
169- )
174+ insert_pending_sweep_row (cur , task_id , text )
170175 finally :
171176 cur .close ()
172177 finally :
@@ -220,9 +225,11 @@ def recover_abandoned_sweeps():
220225 try :
221226 cur .execute (
222227 "SELECT task_id, details FROM task_status WHERE task_type = %s "
223- "AND status NOT IN (%s, %s, %s)" ,
228+ "AND status NOT IN (%s, %s, %s) "
229+ "AND timestamp < NOW() - make_interval(secs => %s)" ,
224230 (SWEEP_TASK_TYPE , config .TASK_STATUS_SUCCESS ,
225- config .TASK_STATUS_FAILURE , config .TASK_STATUS_REVOKED ),
231+ config .TASK_STATUS_FAILURE , config .TASK_STATUS_REVOKED ,
232+ _ENQUEUE_GRACE_SECONDS ),
226233 )
227234 rows = cur .fetchall ()
228235 candidates = [r [0 ] for r in rows ]
@@ -264,16 +271,10 @@ def recover_abandoned_sweeps():
264271 if not revoked_count :
265272 return None
266273 new_task_id = str (uuid .uuid4 ())
267- queued = json .dumps ({
268- 'message' : 'Server alignment queued for all servers.' ,
269- 'full_refresh' : full_refresh ,
270- })
271- cur .execute (
272- "INSERT INTO task_status "
273- "(task_id, task_type, status, progress, details, timestamp, start_time) "
274- "VALUES (%s, %s, %s, 0, %s, NOW(), %s) "
275- "ON CONFLICT (task_id) DO NOTHING" ,
276- (new_task_id , SWEEP_TASK_TYPE , config .TASK_STATUS_PENDING , queued , now ),
274+ insert_pending_sweep_row (
275+ cur , new_task_id ,
276+ 'Server alignment queued for all servers.' ,
277+ full_refresh = full_refresh ,
277278 )
278279 finally :
279280 cur .close ()
0 commit comments