Move the "container updated" message for Buildables that build Diffs outside of the transaction
Summary: Ref T13216. See PHI970. Ref T13054. See some discussion in T13216. When a Harbormaster Buildable object is first created for a Diff, it has no `containerPHID` since the revision has not yet been created. We later (after creating a revision) send the Buildable a message telling it that we've added a container and it should re-link the container object. Currently, we send this message in `applyExternalEffects()`, which runs inside the Differential transaction. If Harbormaster races quickly enough, it can read the `Diff` object before the transaction commits, and not see the container update. Add a `didCommitTransaction()` callback after the transactions commit, then move the message code there instead. Test Plan: - See T13216 for substantial evidence that this change is on the right track. - Before change: added `sleep(15)`, reproduced the issue reliably. - After change: unable to reproduce issue even with `sleep(15)` (the `containerPHID` always populates correctly). Reviewers: amckinley Reviewed By: amckinley Maniphest Tasks: T13216, T13054 Differential Revision: https://secure.phabricator.com/D19807
This commit is contained in:
		@@ -57,6 +57,12 @@ final class DifferentialRevisionUpdateTransaction
 | 
			
		||||
    // Harbormaster. See discussion in T8650.
 | 
			
		||||
    $diff->setRevisionID($object->getID());
 | 
			
		||||
    $diff->save();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public function didCommitTransaction($object, $value) {
 | 
			
		||||
    $editor = $this->getEditor();
 | 
			
		||||
    $diff = $editor->requireDiff($value);
 | 
			
		||||
    $omnipotent = PhabricatorUser::getOmnipotentUser();
 | 
			
		||||
 | 
			
		||||
    // If there are any outstanding buildables for this diff, tell
 | 
			
		||||
    // Harbormaster that their containers need to be updated. This is
 | 
			
		||||
@@ -64,7 +70,7 @@ final class DifferentialRevisionUpdateTransaction
 | 
			
		||||
    // and unit results.
 | 
			
		||||
 | 
			
		||||
    $buildables = id(new HarbormasterBuildableQuery())
 | 
			
		||||
      ->setViewer(PhabricatorUser::getOmnipotentUser())
 | 
			
		||||
      ->setViewer($omnipotent)
 | 
			
		||||
      ->withManualBuildables(false)
 | 
			
		||||
      ->withBuildablePHIDs(array($diff->getPHID()))
 | 
			
		||||
      ->execute();
 | 
			
		||||
 
 | 
			
		||||
@@ -869,6 +869,24 @@ abstract class PhabricatorApplicationTransactionEditor
 | 
			
		||||
    return $xactions;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  final protected function didCommitTransactions(
 | 
			
		||||
    PhabricatorLiskDAO $object,
 | 
			
		||||
    array $xactions) {
 | 
			
		||||
 | 
			
		||||
    foreach ($xactions as $xaction) {
 | 
			
		||||
      $type = $xaction->getTransactionType();
 | 
			
		||||
 | 
			
		||||
      $xtype = $this->getModularTransactionType($type);
 | 
			
		||||
      if (!$xtype) {
 | 
			
		||||
        continue;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      $xtype = clone $xtype;
 | 
			
		||||
      $xtype->setStorage($xaction);
 | 
			
		||||
      $xtype->didCommitTransaction($object, $xaction->getNewValue());
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public function setContentSource(PhabricatorContentSource $content_source) {
 | 
			
		||||
    $this->contentSource = $content_source;
 | 
			
		||||
    return $this;
 | 
			
		||||
@@ -1106,6 +1124,9 @@ abstract class PhabricatorApplicationTransactionEditor
 | 
			
		||||
        $object->saveTransaction();
 | 
			
		||||
        $transaction_open = false;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      $this->didCommitTransactions($object, $xactions);
 | 
			
		||||
 | 
			
		||||
    } catch (Exception $ex) {
 | 
			
		||||
      if ($read_locking) {
 | 
			
		||||
        $object->endReadLocking();
 | 
			
		||||
 
 | 
			
		||||
@@ -35,6 +35,10 @@ abstract class PhabricatorModularTransactionType
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public function didCommitTransaction($object, $value) {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public function getTransactionHasEffect($object, $old, $new) {
 | 
			
		||||
    return ($old !== $new);
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -103,26 +103,26 @@ abstract class PhabricatorWorkerTaskQuery
 | 
			
		||||
    return $this->formatWhereClause($conn, $where);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  protected function buildOrderClause(AphrontDatabaseConnection $conn_r) {
 | 
			
		||||
  protected function buildOrderClause(AphrontDatabaseConnection $conn) {
 | 
			
		||||
    // NOTE: The garbage collector executes this query with a date constraint,
 | 
			
		||||
    // and the query is inefficient if we don't use the same key for ordering.
 | 
			
		||||
    // See T9808 for discussion.
 | 
			
		||||
 | 
			
		||||
    if ($this->dateCreatedBefore) {
 | 
			
		||||
      return qsprintf($conn_r, 'ORDER BY dateCreated DESC, id DESC');
 | 
			
		||||
      return qsprintf($conn, 'ORDER BY dateCreated DESC, id DESC');
 | 
			
		||||
    } else if ($this->dateModifiedSince) {
 | 
			
		||||
      return qsprintf($conn_r, 'ORDER BY dateModified DESC, id DESC');
 | 
			
		||||
      return qsprintf($conn, 'ORDER BY dateModified DESC, id DESC');
 | 
			
		||||
    } else {
 | 
			
		||||
      return qsprintf($conn_r, 'ORDER BY id DESC');
 | 
			
		||||
      return qsprintf($conn, 'ORDER BY id DESC');
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  protected function buildLimitClause(AphrontDatabaseConnection $conn_r) {
 | 
			
		||||
    $clause =  '';
 | 
			
		||||
  protected function buildLimitClause(AphrontDatabaseConnection $conn) {
 | 
			
		||||
    if ($this->limit) {
 | 
			
		||||
      $clause = qsprintf($conn_r, 'LIMIT %d', $this->limit);
 | 
			
		||||
      return qsprintf($conn, 'LIMIT %d', $this->limit);
 | 
			
		||||
    } else {
 | 
			
		||||
      return qsprintf($conn, '');
 | 
			
		||||
    }
 | 
			
		||||
    return $clause;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -145,19 +145,23 @@ final class PhabricatorWorkerTriggerQuery
 | 
			
		||||
    return $triggers;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  protected function buildJoinClause(AphrontDatabaseConnection $conn_r) {
 | 
			
		||||
  protected function buildJoinClause(AphrontDatabaseConnection $conn) {
 | 
			
		||||
    $joins = array();
 | 
			
		||||
 | 
			
		||||
    if (($this->nextEpochMin !== null) ||
 | 
			
		||||
        ($this->nextEpochMax !== null) ||
 | 
			
		||||
        ($this->order == self::ORDER_EXECUTION)) {
 | 
			
		||||
      $joins[] = qsprintf(
 | 
			
		||||
        $conn_r,
 | 
			
		||||
        $conn,
 | 
			
		||||
        'JOIN %T e ON e.triggerID = t.id',
 | 
			
		||||
        id(new PhabricatorWorkerTriggerEvent())->getTableName());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return implode(' ', $joins);
 | 
			
		||||
    if ($joins) {
 | 
			
		||||
      return qsprintf($conn, '%LJ', $joins);
 | 
			
		||||
    } else {
 | 
			
		||||
      return qsprintf($conn, '');
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  protected function buildWhereClause(AphrontDatabaseConnection $conn) {
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user