Page MenuHomePhabricator

Deleting stale `ManiphestTransaction`s
Closed, ResolvedPublic

Description

I have some stale data resulting from a repository being removed and later re-added. Now I have a bunch of "X added a commit: Unknown Object (Commit)" transactions lying around. I am trying to write a script to prune these transactions from the database, but I'm not quite sure how to tackle this... can you point me in the right direction?

{F170343}

Event Timeline

joshuaspence assigned this task to epriestley.
joshuaspence raised the priority of this task from to High.
joshuaspence updated the task description. (Show Details)
joshuaspence added a project: Maniphest.
joshuaspence added a subscriber: joshuaspence.

There's no easy way to do this. You can do something like this -- roughly, iterate through every transaction and discard the ones that look bad.

You could also possibly just delete every transaction of this type. This will destroy legitimate data too, but it might be that no one cares too much if you declare edge bankruptcy once.

<?php

require_once 'scripts/__init_script__.php';

$viewer = PhabricatorUser::getOmnipotentUser();

foreach (new LiskMigrationIterator(new ManiphestTransaction()) as $xaction) {
  $keep = should_keep_transaction($xaction, $viewer);
  
  // if (!$keep) {
  //   Use a destruction engine on the transaction.
  // }
}

function should_keep_transaction($xaction, $viewer) {
  $phid = $xaction->getPHID();

  $task_phid = $xaction->getObjectPHID();
  $task = id(new ManiphestTaskQuery())
    ->setViewer($viewer)
    ->withPHIDs(array($task_phid))
    ->executeOne();

  if (!$task) {
    echo "Examining transaction {$phid}, but the associated task ".
         "({$task_phid}) does not exist.\n";
    return false;
  }

  $task_id = $task->getID();
  echo "Examining transaction {$phid}, on task T{$task_id}.\n";

  $new = $xaction->getNewValue();
  $old = $xaction->getOldValue();
  
  switch ($xaction->getType()) {
    case ManiphestTransaction::TYPE_EDGE:
      switch ($xaction->getMetadataValue('edge:type')) {
        case PhabricatorEdgeConfig::TYPE_TASK_HAS_COMMIT:
          $old_phids = array_keys($old);
          $new_phids = array_keys($new);

          $add = array_diff($new, $old);
          $rem = array_diff($old, $new);
          $all = array_merge($add, $rem);
          
          foreach ($all as $commit_phid) {
            if (/* This Commit Exists */) {
              // The transaction adds or removes at least one commit which
              // actually exists.
              return true;
            }
          }
          
          // The transaction did not add or remove any commits which actually
          // exist.
          return false;
      }
  }
  
  return true;
}

This seems to work in most cases, however I am getting the following error a bunch of times;

[2014-06-27 03:09:22] ERROR 2: Illegal offset type in isset or empty at [/usr/src/phabricator/src/applications/phid/query/PhabricatorObjectQuery.php:112]
  #0 PhabricatorObjectQuery::loadObjectsByPHID(array, array) called at [<phabricator>/src/applications/phid/query/PhabricatorObjectQuery.php:63]
  #1 PhabricatorObjectQuery::loadPage() called at [<phabricator>/src/infrastructure/query/policy/PhabricatorPolicyAwareQuery.php:219]
  #2 PhabricatorPolicyAwareQuery::execute() called at [<phabricator>/src/infrastructure/query/policy/PhabricatorPolicyAwareQuery.php:159]
  #3 PhabricatorPolicyAwareQuery::executeOne() called at [/home/josh/scripts/purge_commit_maniphest_transactions.php:71]
  #4 commit_exists(array) called at [/home/josh/scripts/purge_commit_maniphest_transactions.php:51]
  #5 should_keep_transaction(ManiphestTransaction, PhabricatorUser) called at [/home/josh/scripts/purge_commit_maniphest_transactions.php:11]

Hmm, what does commit_exists() look like?

function commit_exists($phid) {
  return id(new PhabricatorObjectQuery())
    ->setViewer(PhabricatorUser::getOmnipotentUser())
    ->withPHIDs(array($phid))
    ->executeOne();
}

That looks correct -- gimmie the whole script and I'll delete some stuff locally and see if I can repro?

<?php

require_once '/usr/src/phabricator/scripts/__init_script__.php';

$console = PhutilConsole::getConsole();
$viewer = PhabricatorUser::getOmnipotentUser();
$engine = id(new PhabricatorDestructionEngine());

foreach (new LiskMigrationIterator(new ManiphestTransaction()) as $xaction) {
  $keep = should_keep_transaction($xaction, $viewer);
  
  if (!$keep) {
    $console->writeOut("Deleting transaction %s\n", $xaction->getPHID());
    $engine->destroyObject($xaction);
  }
}

function should_keep_transaction($xaction, $viewer) {
  $phid = $xaction->getPHID();

  $task_phid = $xaction->getObjectPHID();
  $task = id(new ManiphestTaskQuery())
    ->setViewer($viewer)
    ->withPHIDs(array($task_phid))
    ->executeOne();

  if (!$task) {
    $console->writeErr(
      "Examining transaction {$phid}, but the associated task ".
      "({$task_phid}) does not exist.\n");
    return false;
  }

  $task_id = $task->getID();
  $new = $xaction->getNewValue();
  $old = $xaction->getOldValue();
  
  switch ($xaction->getTransactionType()) {
    case ManiphestTransaction::TYPE_EDGE:
      switch ($xaction->getMetadataValue('edge:type')) {
        case PhabricatorEdgeConfig::TYPE_TASK_HAS_COMMIT:
          $old_phids = array_keys($old);
          $new_phids = array_keys($new);

          $add = array_diff($new, $old);
          $rem = array_diff($old, $new);
          $all = array_merge($add, $rem);
          
          foreach ($all as $commit_phid) {
            if (commit_exists($commit_phid)) {
              // The transaction adds or removes at least one commit which
              // actually exists.
              return true;
            }
          }
          
          // The transaction did not add or remove any commits which actually
          // exist.
          return false;
      }
  }
  
  return true;
}

function commit_exists($phid) {
  return id(new PhabricatorObjectQuery())
    ->setViewer(PhabricatorUser::getOmnipotentUser())
    ->withPHIDs(array($phid))
    ->executeOne();
}

Oh, sorry, my fault:

$add = array_diff($new, $old);
$rem = array_diff($old, $new);

That should be $new_phids / $old_phids.

(The stuff being diffed, that is.)

This worked, thanks!