Update: 2026-07-30 02:27:45
This commit is contained in:
@@ -48,14 +48,19 @@ if ($batch['total_images'] == 0) {
|
||||
json_error('لا يمكن إنهاء دفعة فارغة', 400);
|
||||
}
|
||||
|
||||
// 2. Mark as processing
|
||||
// 2. Mark as processing - atomically, so two concurrent finalize calls cannot
|
||||
// both start a background worker for the same batch.
|
||||
$stmt = $db->prepare("
|
||||
UPDATE invoice_batches
|
||||
SET status = 'processing', updated_at = NOW()
|
||||
WHERE id = ?
|
||||
UPDATE invoice_batches
|
||||
SET status = 'processing', updated_at = NOW()
|
||||
WHERE id = ? AND status = 'uploading'
|
||||
");
|
||||
$stmt->execute([$batchId]);
|
||||
|
||||
if ($stmt->rowCount() !== 1) {
|
||||
json_error('تم إنهاء هذه الدفعة مسبقاً', 400);
|
||||
}
|
||||
|
||||
// 3. Send response IMMEDIATELY to mobile app
|
||||
// We manually build the response instead of using json_success() because it calls exit()
|
||||
$responsePayload = json_encode([
|
||||
@@ -117,7 +122,16 @@ $bgLog = function(string $msg) {
|
||||
$bgLog("Background processing started for batch: $batchId");
|
||||
|
||||
try {
|
||||
$queueStmt = $db->prepare("SELECT id FROM invoice_processing_queue WHERE batch_id = ? AND status = 'pending' ORDER BY created_at ASC");
|
||||
// processQueueItem() claims each row atomically, so it is safe for the cron
|
||||
// worker to be walking the same batch at the same time - whoever claims a row
|
||||
// first owns it and the other simply skips it.
|
||||
$queueStmt = $db->prepare("
|
||||
SELECT id FROM invoice_processing_queue
|
||||
WHERE batch_id = ?
|
||||
AND status = 'pending'
|
||||
AND attempts < COALESCE(max_attempts, 3)
|
||||
ORDER BY image_order ASC, created_at ASC
|
||||
");
|
||||
$queueStmt->execute([$batchId]);
|
||||
$items = $queueStmt->fetchAll(\PDO::FETCH_COLUMN);
|
||||
|
||||
@@ -127,12 +141,16 @@ try {
|
||||
$bgLog("Processing queue item: $queueId");
|
||||
try {
|
||||
$success = InvoiceProcessor::processQueueItem((int)$queueId);
|
||||
$bgLog("Queue item $queueId: " . ($success ? "SUCCESS" : "FAILED"));
|
||||
$bgLog("Queue item $queueId: " . ($success ? "SUCCESS" : "FAILED/SKIPPED"));
|
||||
} catch (\Throwable $e) {
|
||||
$bgLog("Queue item $queueId EXCEPTION: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// Final sweep: if every item ended up terminal, close the batch here rather
|
||||
// than waiting up to a minute for the cron to notice.
|
||||
InvoiceProcessor::checkBatchCompletion($batchId);
|
||||
|
||||
$bgLog("Background processing finished for batch: $batchId");
|
||||
|
||||
} catch (\Throwable $e) {
|
||||
|
||||
Reference in New Issue
Block a user