<?php

/**
 * Used by unit tests to build storage fixtures.
 */
final class PhabricatorStorageFixtureScopeGuard extends Phobject {

  private $name;

  public function __construct($name) {
    $this->name = $name;

    execx(
      'php %s upgrade --force --no-adjust --namespace %s',
      $this->getStorageBinPath(),
      $this->name);

    PhabricatorLiskDAO::pushStorageNamespace($name);

    // Destructor is not called with fatal error.
    register_shutdown_function(array($this, 'destroy'));
  }

  public function destroy() {
    PhabricatorLiskDAO::popStorageNamespace();

    execx(
      'php %s destroy --force --namespace %s',
      $this->getStorageBinPath(),
      $this->name);
  }

  private function getStorageBinPath() {
    $root = dirname(phutil_get_library_root('phabricator'));
    return $root.'/scripts/sql/manage_storage.php';
  }

}

<?php

/**
 * Used by unit tests to build storage fixtures.
 */
final class PhabricatorStorageFixtureScopeGuard extends Phobject {

  private $name;

  public function __construct($name) {
    $this->name = $name;

    execx(
      'php %s upgrade --force --no-adjust --namespace %s',
      $this->getStorageBinPath(),
      $this->name);

    PhabricatorLiskDAO::pushStorageNamespace($name);

    // Destructor is not called with fatal error.
    register_shutdown_function(array($this, 'destroy'));
  }

  public function destroy() {
    PhabricatorLiskDAO::popStorageNamespace();

    // NOTE: We need to close all connections before destroying the databases.
    // If we do not, the "DROP DATABASE ..." statements may hang, waiting for
    // our connections to close.
    PhabricatorLiskDAO::closeAllConnections();

    execx(
      'php %s destroy --force --namespace %s',
      $this->getStorageBinPath(),
      $this->name);
  }

  private function getStorageBinPath() {
    $root = dirname(phutil_get_library_root('phabricator'));
    return $root.'/scripts/sql/manage_storage.php';
  }

}

Fix a possible deadlock in unit tests after an error
Fix a possible deadlock in unit tests after an error

Summary:
After certain types of errors, we may deadlock when trying to destroy test databases.

Specifically, we still have connections open to, say, `phabricator_unittest_abasonaknlbaklnasb_herald` (or whatever) and MySQL sometimes (not sure exactly when?) waits for them before destorying the database.

Test Plan:
  - Added `$m = null; $m->method()` to a fixture test to force a fatal.
  - Saw consistent deadlock, with `storage destroy` never exiting.
  - Added `--trace` to the `storage destroy` command and made it use `phutil_passthru()` so I could see what was happening.
  - Saw it hang on some arbitrary database.
  - Conneced to MySQL, used `show full processlist;` to see what was wrong.
  - Saw the `DROP DATABASE ...` command waiting for locks to release on the database, and other connections still open.
  - Applied patch.
  - Saw consistent success.
  - Used `storage destroy --unittest-fixtures` to clean up extra databases.

Reviewers: chad

Reviewed By: chad

Differential Revision: https://secure.phabricator.com/D14875
