Allow Nuance commands to try to apply immediately
Summary: Ref T12738. By default, we process Nuance commands in the background. The intent is to let the user continue working at full speed if Twitter or GitHub (or whatever) is being a little slow. Some commands don't do anything heavy and can be processed in the foreground. Let commands choose to try foreground execution. Test Plan: Threw complaints in the trash, saw them immediately go into the trash. Reviewers: chad Reviewed By: chad Subscribers: avivey Maniphest Tasks: T12738 Differential Revision: https://secure.phabricator.com/D18015
This commit is contained in:
@@ -19,6 +19,12 @@ abstract class NuanceCommandImplementation
|
|||||||
abstract public function getCommandName();
|
abstract public function getCommandName();
|
||||||
abstract public function canApplyToItem(NuanceItem $item);
|
abstract public function canApplyToItem(NuanceItem $item);
|
||||||
|
|
||||||
|
public function canApplyImmediately(
|
||||||
|
NuanceItem $item,
|
||||||
|
NuanceItemCommand $command) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
abstract protected function executeCommand(
|
abstract protected function executeCommand(
|
||||||
NuanceItem $item,
|
NuanceItem $item,
|
||||||
NuanceItemCommand $command);
|
NuanceItemCommand $command);
|
||||||
|
|||||||
@@ -14,6 +14,12 @@ final class NuanceTrashCommand
|
|||||||
return ($type instanceof NuanceFormItemType);
|
return ($type instanceof NuanceFormItemType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function canApplyImmediately(
|
||||||
|
NuanceItem $item,
|
||||||
|
NuanceItemCommand $command) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
protected function executeCommand(
|
protected function executeCommand(
|
||||||
NuanceItem $item,
|
NuanceItem $item,
|
||||||
NuanceItemCommand $command) {
|
NuanceItemCommand $command) {
|
||||||
|
|||||||
@@ -53,6 +53,25 @@ final class NuanceItemActionController extends NuanceController {
|
|||||||
$impl->setViewer($viewer);
|
$impl->setViewer($viewer);
|
||||||
$impl->setController($this);
|
$impl->setController($this);
|
||||||
|
|
||||||
|
$executors = NuanceCommandImplementation::getAllCommands();
|
||||||
|
$executor = idx($executors, $action);
|
||||||
|
if (!$executor) {
|
||||||
|
return new Aphront404Response();
|
||||||
|
}
|
||||||
|
|
||||||
|
$executor = id(clone $executor)
|
||||||
|
->setActor($viewer);
|
||||||
|
|
||||||
|
if (!$executor->canApplyToItem($item)) {
|
||||||
|
return $this->newDialog()
|
||||||
|
->setTitle(pht('Command Not Supported'))
|
||||||
|
->appendParagraph(
|
||||||
|
pht(
|
||||||
|
'This item does not support the specified command ("%s").',
|
||||||
|
$action))
|
||||||
|
->addCancelButton($cancel_uri);
|
||||||
|
}
|
||||||
|
|
||||||
$command = NuanceItemCommand::initializeNewCommand()
|
$command = NuanceItemCommand::initializeNewCommand()
|
||||||
->setItemPHID($item->getPHID())
|
->setItemPHID($item->getPHID())
|
||||||
->setAuthorPHID($viewer->getPHID())
|
->setAuthorPHID($viewer->getPHID())
|
||||||
@@ -64,17 +83,29 @@ final class NuanceItemActionController extends NuanceController {
|
|||||||
|
|
||||||
$command->save();
|
$command->save();
|
||||||
|
|
||||||
// TODO: Here, we should check if the command should be tried immediately,
|
// If this command can be applied immediately, try to apply it now.
|
||||||
// and just defer it to the daemons if not. If we're going to try to apply
|
|
||||||
// the command directly, we should first acquire the worker lock. If we
|
|
||||||
// can not, we should defer the command even if it's an immediate command.
|
|
||||||
// For the moment, skip all this stuff by deferring unconditionally.
|
|
||||||
|
|
||||||
$should_defer = true;
|
// In most cases, local commands (like closing an item) can be applied
|
||||||
if ($should_defer) {
|
// immediately.
|
||||||
|
|
||||||
|
// Commands that require making a call to a remote system (for example,
|
||||||
|
// to reply to a tweet or close a remote object) are usually done in the
|
||||||
|
// background so the user doesn't have to wait for the operation to
|
||||||
|
// complete before they can continue work.
|
||||||
|
|
||||||
|
$did_apply = false;
|
||||||
|
$immediate = $executor->canApplyImmediately($item, $command);
|
||||||
|
if ($immediate) {
|
||||||
|
// TODO: Move this stuff to a new Engine, and have the controller and
|
||||||
|
// worker both call into the Engine.
|
||||||
|
$worker = new NuanceItemUpdateWorker(array());
|
||||||
|
$did_apply = $worker->executeCommands($item, array($command));
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this can't be applied immediately or we were unable to get a lock
|
||||||
|
// fast enough, do the update in the background instead.
|
||||||
|
if (!$did_apply) {
|
||||||
$item->scheduleUpdate();
|
$item->scheduleUpdate();
|
||||||
} else {
|
|
||||||
// ...
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($queue) {
|
if ($queue) {
|
||||||
|
|||||||
@@ -6,9 +6,7 @@ final class NuanceItemUpdateWorker
|
|||||||
protected function doWork() {
|
protected function doWork() {
|
||||||
$item_phid = $this->getTaskDataValue('itemPHID');
|
$item_phid = $this->getTaskDataValue('itemPHID');
|
||||||
|
|
||||||
$hash = PhabricatorHash::digestForIndex($item_phid);
|
$lock = $this->newLock($item_phid);
|
||||||
$lock_key = "nuance.item.{$hash}";
|
|
||||||
$lock = PhabricatorGlobalLock::newLock($lock_key);
|
|
||||||
|
|
||||||
$lock->lock(1);
|
$lock->lock(1);
|
||||||
try {
|
try {
|
||||||
@@ -55,9 +53,6 @@ final class NuanceItemUpdateWorker
|
|||||||
private function applyCommands(NuanceItem $item) {
|
private function applyCommands(NuanceItem $item) {
|
||||||
$viewer = $this->getViewer();
|
$viewer = $this->getViewer();
|
||||||
|
|
||||||
$impl = $item->getImplementation();
|
|
||||||
$impl->setViewer($viewer);
|
|
||||||
|
|
||||||
$commands = id(new NuanceItemCommandQuery())
|
$commands = id(new NuanceItemCommandQuery())
|
||||||
->setViewer($viewer)
|
->setViewer($viewer)
|
||||||
->withItemPHIDs(array($item->getPHID()))
|
->withItemPHIDs(array($item->getPHID()))
|
||||||
@@ -68,8 +63,60 @@ final class NuanceItemUpdateWorker
|
|||||||
->execute();
|
->execute();
|
||||||
$commands = msort($commands, 'getID');
|
$commands = msort($commands, 'getID');
|
||||||
|
|
||||||
|
$this->executeCommandList($item, $commands);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function executeCommands(NuanceItem $item, array $commands) {
|
||||||
|
if (!$commands) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$item_phid = $item->getPHID();
|
||||||
|
$viewer = $this->getViewer();
|
||||||
|
|
||||||
|
$lock = $this->newLock($item_phid);
|
||||||
|
try {
|
||||||
|
$lock->lock(1);
|
||||||
|
} catch (PhutilLockException $ex) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$item = $this->loadItem($item_phid);
|
||||||
|
|
||||||
|
// Reload commands now that we have a lock, to make sure we don't
|
||||||
|
// execute any commands twice by mistake.
|
||||||
|
$commands = id(new NuanceItemCommandQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->withIDs(mpull($commands, 'getID'))
|
||||||
|
->execute();
|
||||||
|
|
||||||
|
$this->executeCommandList($item, $commands);
|
||||||
|
} catch (Exception $ex) {
|
||||||
|
$lock->unlock();
|
||||||
|
throw $ex;
|
||||||
|
}
|
||||||
|
|
||||||
|
$lock->unlock();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function executeCommandList(NuanceItem $item, array $commands) {
|
||||||
|
$viewer = $this->getViewer();
|
||||||
|
|
||||||
$executors = NuanceCommandImplementation::getAllCommands();
|
$executors = NuanceCommandImplementation::getAllCommands();
|
||||||
foreach ($commands as $command) {
|
foreach ($commands as $command) {
|
||||||
|
if ($command->getItemPHID() !== $item->getPHID()) {
|
||||||
|
throw new Exception(
|
||||||
|
pht('Trying to apply a command to the wrong item!'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($command->getStatus() !== NuanceItemCommand::STATUS_ISSUED) {
|
||||||
|
// Never execute commands which have already been issued.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
$command
|
$command
|
||||||
->setStatus(NuanceItemCommand::STATUS_EXECUTING)
|
->setStatus(NuanceItemCommand::STATUS_EXECUTING)
|
||||||
->save();
|
->save();
|
||||||
@@ -105,4 +152,10 @@ final class NuanceItemUpdateWorker
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function newLock($item_phid) {
|
||||||
|
$hash = PhabricatorHash::digestForIndex($item_phid);
|
||||||
|
$lock_key = "nuance.item.{$hash}";
|
||||||
|
return PhabricatorGlobalLock::newLock($lock_key);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user