public function setExecutionStatusForTask($execution_status, Reverb_ProcessQueue_Model_Task_Interface $taskObject, $status_message = null) { $task_id = $taskObject->getId(); if (empty($task_id)) { // TODO Some logging here return 0; } if ($taskObject->isStatusValid($execution_status)) { $update_bind_array = array('status' => $execution_status); if (!is_null($status_message)) { $update_bind_array['status_message'] = $status_message; } $task_id = $taskObject->getId(); $where_conditions_array = array('task_id=?' => $task_id); $rows_updated = $this->_getWriteAdapter()->update($this->getMainTable(), $update_bind_array, $where_conditions_array); return $rows_updated; } // TODO Log error in this case return 0; }
/** * Executes the following: * - Attempts to update task object's row as processing * - If successful, Begins a database transaction * - Attempts to select that row for update * - If successful, attempts to execute method callback defined in row, returning a result object * - Updates the task object based on the resulting object * - Commits the database transaction * * @param Reverb_ProcessQueue_Model_Task_Interface $processQueueTaskObject */ public function processQueueTask(Reverb_ProcessQueue_Model_Task_Interface $processQueueTaskObject) { try { $able_to_lock_for_processing = $processQueueTaskObject->attemptUpdatingRowAsProcessing(); if (!$able_to_lock_for_processing) { // Assume another thread of execution is already processing this task return; } } catch (Exception $e) { $error_message = $this->__(self::EXCEPTION_UPDATE_AS_PROCESSING, $processQueueTaskObject->getId(), $e->getMessage()); $this->_logError($error_message); return; } // At this point, start transaction and lock row for update to ensure exclusive access $taskResourceSingleton = $processQueueTaskObject->getResource(); $taskResourceSingleton->beginTransaction(); try { $selected = $processQueueTaskObject->selectForUpdate(); if (!$selected) { // Assume another thread has already locked this task object's row, although this shouldn't happen $taskResourceSingleton->rollBack(); $error_message = $this->__(self::ERROR_FAILED_TO_SELECT_FOR_UPDATE, $processQueueTaskObject->getId()); $this->_logError($error_message); return; } } catch (Exception $e) { $taskResourceSingleton->rollBack(); $error_message = $this->__(self::EXCEPTION_SELECT_FOR_UPDATE, $processQueueTaskObject->getId(), $e->getMessage()); $this->_logError($error_message); return; } try { $taskExecutionResult = $processQueueTaskObject->executeTask(); } catch (Exception $e) { $taskResourceSingleton->rollBack(); $error_message = $this->__(self::EXCEPTION_EXECUTING_TASK, $processQueueTaskObject->getId(), $e->getMessage()); $processQueueTaskObject->setTaskAsErrored($error_message); $this->_logError($error_message); return; } try { $processQueueTaskObject->actOnTaskResult($taskExecutionResult); } catch (Exception $e) { // At this point, we would assume that the task has been performed successfully since executeTask() did not // throw any exceptions. As such, log the exception but commit the transaction. Even if this leaves a row // in the PROCESSING state, it's better than leaving parts of the database out of sync with external resources $error_message = $this->__(self::EXCEPTION_ACTING_ON_TASK_RESULT, $processQueueTaskObject->getId(), $e->getMessage()); $this->_logError($error_message); } try { $taskResourceSingleton->commit(); } catch (Exception $e) { // If an exception occurs here, rollback $taskResourceSingleton->rollback(); $processQueueTaskObject->setTaskAsErrored(); $error_message = $this->__(self::EXCEPTION_COMMITTING_TRANSACTION, $processQueueTaskObject->getId(), $e->getMessage()); $this->_logError($error_message); } }