public function execute()
 {
     if (!$this->limit) {
         throw new Exception('You must setLimit() when leasing tasks.');
     }
     $task_table = new PhabricatorWorkerActiveTask();
     $taskdata_table = new PhabricatorWorkerTaskData();
     $lease_ownership_name = $this->getLeaseOwnershipName();
     $conn_w = $task_table->establishConnection('w');
     // Try to satisfy the request from new, unleased tasks first. If we don't
     // find enough tasks, try tasks with expired leases (i.e., tasks which have
     // previously failed).
     $phases = array(self::PHASE_UNLEASED, self::PHASE_EXPIRED);
     $limit = $this->limit;
     $leased = 0;
     foreach ($phases as $phase) {
         // NOTE: If we issue `UPDATE ... WHERE ... ORDER BY id ASC`, the query
         // goes very, very slowly. The `ORDER BY` triggers this, although we get
         // the same apparent results without it. Without the ORDER BY, binary
         // read slaves complain that the query isn't repeatable. To avoid both
         // problems, do a SELECT and then an UPDATE.
         $rows = queryfx_all($conn_w, 'SELECT id, leaseOwner FROM %T %Q %Q %Q', $task_table->getTableName(), $this->buildWhereClause($conn_w, $phase), $this->buildOrderClause($conn_w, $phase), $this->buildLimitClause($conn_w, $limit - $leased));
         // NOTE: Sometimes, we'll race with another worker and they'll grab
         // this task before we do. We could reduce how often this happens by
         // selecting more tasks than we need, then shuffling them and trying
         // to lock only the number we're actually after. However, the amount
         // of time workers spend here should be very small relative to their
         // total runtime, so keep it simple for the moment.
         if ($rows) {
             queryfx($conn_w, 'UPDATE %T task
         SET leaseOwner = %s, leaseExpires = UNIX_TIMESTAMP() + %d
         %Q', $task_table->getTableName(), $lease_ownership_name, self::getDefaultLeaseDuration(), $this->buildUpdateWhereClause($conn_w, $phase, $rows));
             $leased += $conn_w->getAffectedRows();
             if ($leased == $limit) {
                 break;
             }
         }
     }
     if (!$leased) {
         return array();
     }
     $data = queryfx_all($conn_w, 'SELECT task.*, taskdata.data _taskData, UNIX_TIMESTAMP() _serverTime
     FROM %T task LEFT JOIN %T taskdata
       ON taskdata.id = task.dataID
     WHERE leaseOwner = %s AND leaseExpires > UNIX_TIMESTAMP()
     %Q %Q', $task_table->getTableName(), $taskdata_table->getTableName(), $lease_ownership_name, $this->buildOrderClause($conn_w, $phase), $this->buildLimitClause($conn_w, $limit));
     $tasks = $task_table->loadAllFromArray($data);
     $tasks = mpull($tasks, null, 'getID');
     foreach ($data as $row) {
         $tasks[$row['id']]->setServerTime($row['_serverTime']);
         if ($row['_taskData']) {
             $task_data = json_decode($row['_taskData'], true);
         } else {
             $task_data = null;
         }
         $tasks[$row['id']]->setData($task_data);
     }
     return $tasks;
 }
 public function delete()
 {
     $this->openTransaction();
     if ($this->getDataID()) {
         $conn_w = $this->establishConnection('w');
         $data_table = new PhabricatorWorkerTaskData();
         queryfx($conn_w, 'DELETE FROM %T WHERE id = %d', $data_table->getTableName(), $this->getDataID());
     }
     $result = parent::delete();
     $this->saveTransaction();
     return $result;
 }
 public function forceSaveWithoutLease()
 {
     $is_new = !$this->getID();
     if ($is_new) {
         $this->failureCount = 0;
     }
     if ($is_new && $this->getData() !== null) {
         $data = new PhabricatorWorkerTaskData();
         $data->setData($this->getData());
         $data->save();
         $this->setDataID($data->getID());
     }
     return parent::save();
 }
 protected function collectGarbage()
 {
     $table = new PhabricatorWorkerArchiveTask();
     $data_table = new PhabricatorWorkerTaskData();
     $conn_w = $table->establishConnection('w');
     $tasks = id(new PhabricatorWorkerArchiveTaskQuery())->withDateCreatedBefore($this->getGarbageEpoch())->setLimit(100)->execute();
     if (!$tasks) {
         return false;
     }
     $data_ids = array_filter(mpull($tasks, 'getDataID'));
     $task_ids = mpull($tasks, 'getID');
     $table->openTransaction();
     if ($data_ids) {
         queryfx($conn_w, 'DELETE FROM %T WHERE id IN (%Ld)', $data_table->getTableName(), $data_ids);
     }
     queryfx($conn_w, 'DELETE FROM %T WHERE id IN (%Ld)', $table->getTableName(), $task_ids);
     $table->saveTransaction();
     return count($task_ids) == 100;
 }
Exemplo n.º 5
0
 public function save()
 {
     if ($this->leaseOwner) {
         $current_server_time = $this->serverTime + (time() - $this->localTime);
         if ($current_server_time >= $this->leaseExpires) {
             throw new Exception("Trying to update task after lease expiration!");
         }
     }
     $is_new = !$this->getID();
     if ($is_new) {
         $this->failureCount = 0;
     }
     if ($is_new && $this->data) {
         $data = new PhabricatorWorkerTaskData();
         $data->setData($this->data);
         $data->save();
         $this->setDataID($data->getID());
     }
     return parent::save();
 }
 public function collectGarbage()
 {
     $key = 'gcdaemon.ttl.task-archive';
     $ttl = PhabricatorEnv::getEnvConfig($key);
     if ($ttl <= 0) {
         return false;
     }
     $table = new PhabricatorWorkerArchiveTask();
     $data_table = new PhabricatorWorkerTaskData();
     $conn_w = $table->establishConnection('w');
     $rows = queryfx_all($conn_w, 'SELECT id, dataID FROM %T WHERE dateCreated < %d LIMIT 100', $table->getTableName(), time() - $ttl);
     if (!$rows) {
         return false;
     }
     $data_ids = array_filter(ipull($rows, 'dataID'));
     $task_ids = ipull($rows, 'id');
     $table->openTransaction();
     if ($data_ids) {
         queryfx($conn_w, 'DELETE FROM %T WHERE id IN (%Ld)', $data_table->getTableName(), $data_ids);
     }
     queryfx($conn_w, 'DELETE FROM %T WHERE id IN (%Ld)', $table->getTableName(), $task_ids);
     $table->saveTransaction();
     return count($task_ids) == 100;
 }
 public function collectGarbage()
 {
     $key = 'gcdaemon.ttl.task-archive';
     $ttl = PhabricatorEnv::getEnvConfig($key);
     if ($ttl <= 0) {
         return false;
     }
     $table = new PhabricatorWorkerArchiveTask();
     $data_table = new PhabricatorWorkerTaskData();
     $conn_w = $table->establishConnection('w');
     $tasks = id(new PhabricatorWorkerArchiveTaskQuery())->withDateCreatedBefore(time() - $ttl)->execute();
     if (!$tasks) {
         return false;
     }
     $data_ids = array_filter(mpull($tasks, 'getDataID'));
     $task_ids = mpull($tasks, 'getID');
     $table->openTransaction();
     if ($data_ids) {
         queryfx($conn_w, 'DELETE FROM %T WHERE id IN (%Ld)', $data_table->getTableName(), $data_ids);
     }
     queryfx($conn_w, 'DELETE FROM %T WHERE id IN (%Ld)', $table->getTableName(), $task_ids);
     $table->saveTransaction();
     return count($task_ids) == 100;
 }
 public function run()
 {
     $lease_ownership_name = $this->getLeaseOwnershipName();
     $task_table = new PhabricatorWorkerTask();
     $taskdata_table = new PhabricatorWorkerTaskData();
     $sleep = 0;
     do {
         $conn_w = $task_table->establishConnection('w');
         queryfx($conn_w, 'UPDATE %T SET leaseOwner = %s, leaseExpires = UNIX_TIMESTAMP() + 15
       WHERE leaseOwner IS NULL LIMIT 1', $task_table->getTableName(), $lease_ownership_name);
         $rows = $conn_w->getAffectedRows();
         if (!$rows) {
             $rows = queryfx($conn_w, 'UPDATE %T SET leaseOwner = %s, leaseExpires = UNIX_TIMESTAMP() + 15
         WHERE leaseExpires < UNIX_TIMESTAMP() LIMIT 1', $task_table->getTableName(), $lease_ownership_name);
             $rows = $conn_w->getAffectedRows();
         }
         if ($rows) {
             $data = queryfx_all($conn_w, 'SELECT task.*, taskdata.data _taskData, UNIX_TIMESTAMP() _serverTime
         FROM %T task LEFT JOIN %T taskdata
           ON taskdata.id = task.dataID
         WHERE leaseOwner = %s AND leaseExpires > UNIX_TIMESTAMP()
         LIMIT 1', $task_table->getTableName(), $taskdata_table->getTableName(), $lease_ownership_name);
             $tasks = $task_table->loadAllFromArray($data);
             $tasks = mpull($tasks, null, 'getID');
             $task_data = array();
             foreach ($data as $row) {
                 $tasks[$row['id']]->setServerTime($row['_serverTime']);
                 if ($row['_taskData']) {
                     $task_data[$row['id']] = json_decode($row['_taskData'], true);
                 } else {
                     $task_data[$row['id']] = null;
                 }
             }
             foreach ($tasks as $task) {
                 // TODO: We should detect if we acquired a task with an expired lease
                 // and log about it / bump up failure count.
                 // TODO: We should detect if we acquired a task with an excessive
                 // failure count and fail it permanently.
                 $data = idx($task_data, $task->getID());
                 $class = $task->getTaskClass();
                 try {
                     PhutilSymbolLoader::loadClass($class);
                     if (!is_subclass_of($class, 'PhabricatorWorker')) {
                         throw new Exception("Task class '{$class}' does not extend PhabricatorWorker.");
                     }
                     $worker = newv($class, array($data));
                     $lease = $worker->getRequiredLeaseTime();
                     if ($lease !== null) {
                         $task->setLeaseDuration($lease);
                     }
                     $worker->executeTask();
                     $task->delete();
                     if ($data !== null) {
                         queryfx($conn_w, 'DELETE FROM %T WHERE id = %d', $taskdata_table->getTableName(), $task->getDataID());
                     }
                 } catch (Exception $ex) {
                     $task->setFailureCount($task->getFailureCount() + 1);
                     $task->save();
                     throw $ex;
                 }
             }
             $sleep = 0;
         } else {
             $sleep = min($sleep + 1, 30);
         }
         $this->sleep($sleep);
     } while (true);
 }