(Carbon::now()->millisecond(0)->addSeconds((36 * 60 * 60) + $randomOffset)); $this->scheduledTasksRepository->persist($bounceTask); $this->scheduledTasksRepository->flush(); } } } private function startProgress(ScheduledTaskEntity $task): void { $task->setInProgress(true); $this->scheduledTasksRepository->flush(); } private function stopProgress(ScheduledTaskEntity $task): void { // if task is not managed by entity manager, it's already deleted and detached // it can be deleted in self::processSending method if (!$this->entityManager->contains($task)) { return; } $task->setInProgress(false); $this->scheduledTasksRepository->flush(); } private function isTimeout(ScheduledTaskEntity $task): bool { $currentTime = Carbon::now()->millisecond(0); $updatedAt = new Carbon($task->getUpdatedAt()); if ($updatedAt->diffInSeconds($currentTime, false) > $this->getExecutionLimit()) { return true; } return false; } private function getExecutionLimit(): int { return $this->cronHelper->getDaemonExecutionLimit() * 3; } private function deleteTaskIfNewsletterDoesNotExist(ScheduledTaskEntity $task) { $queue = $task->getSendingQueue(); $newsletter = $queue ? $queue->getNewsletter() : null; if ($newsletter !== null) { return; } $this->deleteTask($task); } private function deleteTask(ScheduledTaskEntity $task) { $this->loggerFactory->getLogger(LoggerFactory::TOPIC_NEWSLETTERS)->info( 'delete task in sending queue', ['task_id' => $task->getId()] ); $queue = $task->getSendingQueue(); if ($queue) { $this->sendingQueuesRepository->remove($queue); } $this->scheduledTaskSubscribersRepository->deleteByScheduledTask($task); $this->scheduledTasksRepository->remove($task); $this->scheduledTasksRepository->flush(); } private function endSending(ScheduledTaskEntity $task, NewsletterEntity $newsletter): void { // We should handle all transitions into these states in the processSending method and end processing there or we throw an exception // This might theoretically happen when multiple cron workers are running in parallel which we don't support and try to prevent $unexpectedStates = [ ScheduledTaskEntity::STATUS_PAUSED, ScheduledTaskEntity::STATUS_INVALID, ScheduledTaskEntity::STATUS_SCHEDULED, ]; if (in_array($task->getStatus(), $unexpectedStates)) { $this->loggerFactory->getLogger(LoggerFactory::TOPIC_NEWSLETTERS)->error( 'Sending task reached end of processing in sending queue worker in an unexpected state.', ['task_id' => $task->getId(), 'status' => $task->getStatus()] ); return; } // The task is running but there is no one to send to. // This may happen when we send to all but the execution is interrupted (e.g. by PHP time limit) and we don't update the task status // or if we trigger sending to a newsletter without any subscriber (e.g. scheduled for long time but all were deleted) // Lets set status to completed and update the queue counts if ($task->getStatus() === null && $this->scheduledTaskSubscribersRepository->countUnprocessed($task) === 0) { $task->setStatus(ScheduledTaskEntity::STATUS_COMPLETED); $queue = $task->getSendingQueue(); if ($queue) { $this->sendingQueuesRepository->updateCounts($queue); } $this->scheduledTasksRepository->flush(); } // Task is completed let's do all the stuff for the completed task if ($task->getStatus() === ScheduledTaskEntity::STATUS_COMPLETED) { $this->loggerFactory->getLogger(LoggerFactory::TOPIC_NEWSLETTERS)->info( 'completed newsletter sending', ['newsletter_id' => $newsletter->getId(), 'task_id' => $task->getId()] ); $queue = $task->getSendingQueue(); if ( $queue && !NewsletterReplayMetadata::isLatestNewsletterReplayMeta($queue->getMeta()) && $this->timeZoneCampaignScheduler && $this->timeZoneCampaignScheduler->isTimeZoneQueue($queue) && $this->timeZoneCampaignScheduler->hasIncompleteCampaignQueues($queue) ) { return; } if ($queue && NewsletterReplayMetadata::isLatestNewsletterReplayMeta($queue->getMeta())) { return; } $this->newsletterTask->markNewsletterAsSent($newsletter); $this->statsNotificationsScheduler->schedule($newsletter); } } }