@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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

+111 -15
+6
src/applications/nuance/command/NuanceCommandImplementation.php
··· 19 19 abstract public function getCommandName(); 20 20 abstract public function canApplyToItem(NuanceItem $item); 21 21 22 + public function canApplyImmediately( 23 + NuanceItem $item, 24 + NuanceItemCommand $command) { 25 + return false; 26 + } 27 + 22 28 abstract protected function executeCommand( 23 29 NuanceItem $item, 24 30 NuanceItemCommand $command);
+6
src/applications/nuance/command/NuanceTrashCommand.php
··· 14 14 return ($type instanceof NuanceFormItemType); 15 15 } 16 16 17 + public function canApplyImmediately( 18 + NuanceItem $item, 19 + NuanceItemCommand $command) { 20 + return true; 21 + } 22 + 17 23 protected function executeCommand( 18 24 NuanceItem $item, 19 25 NuanceItemCommand $command) {
+40 -9
src/applications/nuance/controller/NuanceItemActionController.php
··· 53 53 $impl->setViewer($viewer); 54 54 $impl->setController($this); 55 55 56 + $executors = NuanceCommandImplementation::getAllCommands(); 57 + $executor = idx($executors, $action); 58 + if (!$executor) { 59 + return new Aphront404Response(); 60 + } 61 + 62 + $executor = id(clone $executor) 63 + ->setActor($viewer); 64 + 65 + if (!$executor->canApplyToItem($item)) { 66 + return $this->newDialog() 67 + ->setTitle(pht('Command Not Supported')) 68 + ->appendParagraph( 69 + pht( 70 + 'This item does not support the specified command ("%s").', 71 + $action)) 72 + ->addCancelButton($cancel_uri); 73 + } 74 + 56 75 $command = NuanceItemCommand::initializeNewCommand() 57 76 ->setItemPHID($item->getPHID()) 58 77 ->setAuthorPHID($viewer->getPHID()) ··· 64 83 65 84 $command->save(); 66 85 67 - // TODO: Here, we should check if the command should be tried immediately, 68 - // and just defer it to the daemons if not. If we're going to try to apply 69 - // the command directly, we should first acquire the worker lock. If we 70 - // can not, we should defer the command even if it's an immediate command. 71 - // For the moment, skip all this stuff by deferring unconditionally. 86 + // If this command can be applied immediately, try to apply it now. 87 + 88 + // In most cases, local commands (like closing an item) can be applied 89 + // immediately. 90 + 91 + // Commands that require making a call to a remote system (for example, 92 + // to reply to a tweet or close a remote object) are usually done in the 93 + // background so the user doesn't have to wait for the operation to 94 + // complete before they can continue work. 72 95 73 - $should_defer = true; 74 - if ($should_defer) { 96 + $did_apply = false; 97 + $immediate = $executor->canApplyImmediately($item, $command); 98 + if ($immediate) { 99 + // TODO: Move this stuff to a new Engine, and have the controller and 100 + // worker both call into the Engine. 101 + $worker = new NuanceItemUpdateWorker(array()); 102 + $did_apply = $worker->executeCommands($item, array($command)); 103 + } 104 + 105 + // If this can't be applied immediately or we were unable to get a lock 106 + // fast enough, do the update in the background instead. 107 + if (!$did_apply) { 75 108 $item->scheduleUpdate(); 76 - } else { 77 - // ... 78 109 } 79 110 80 111 if ($queue) {
+59 -6
src/applications/nuance/worker/NuanceItemUpdateWorker.php
··· 6 6 protected function doWork() { 7 7 $item_phid = $this->getTaskDataValue('itemPHID'); 8 8 9 - $hash = PhabricatorHash::digestForIndex($item_phid); 10 - $lock_key = "nuance.item.{$hash}"; 11 - $lock = PhabricatorGlobalLock::newLock($lock_key); 9 + $lock = $this->newLock($item_phid); 12 10 13 11 $lock->lock(1); 14 12 try { ··· 55 53 private function applyCommands(NuanceItem $item) { 56 54 $viewer = $this->getViewer(); 57 55 58 - $impl = $item->getImplementation(); 59 - $impl->setViewer($viewer); 60 - 61 56 $commands = id(new NuanceItemCommandQuery()) 62 57 ->setViewer($viewer) 63 58 ->withItemPHIDs(array($item->getPHID())) ··· 68 63 ->execute(); 69 64 $commands = msort($commands, 'getID'); 70 65 66 + $this->executeCommandList($item, $commands); 67 + } 68 + 69 + public function executeCommands(NuanceItem $item, array $commands) { 70 + if (!$commands) { 71 + return true; 72 + } 73 + 74 + $item_phid = $item->getPHID(); 75 + $viewer = $this->getViewer(); 76 + 77 + $lock = $this->newLock($item_phid); 78 + try { 79 + $lock->lock(1); 80 + } catch (PhutilLockException $ex) { 81 + return false; 82 + } 83 + 84 + try { 85 + $item = $this->loadItem($item_phid); 86 + 87 + // Reload commands now that we have a lock, to make sure we don't 88 + // execute any commands twice by mistake. 89 + $commands = id(new NuanceItemCommandQuery()) 90 + ->setViewer($viewer) 91 + ->withIDs(mpull($commands, 'getID')) 92 + ->execute(); 93 + 94 + $this->executeCommandList($item, $commands); 95 + } catch (Exception $ex) { 96 + $lock->unlock(); 97 + throw $ex; 98 + } 99 + 100 + $lock->unlock(); 101 + 102 + return true; 103 + } 104 + 105 + private function executeCommandList(NuanceItem $item, array $commands) { 106 + $viewer = $this->getViewer(); 107 + 71 108 $executors = NuanceCommandImplementation::getAllCommands(); 72 109 foreach ($commands as $command) { 110 + if ($command->getItemPHID() !== $item->getPHID()) { 111 + throw new Exception( 112 + pht('Trying to apply a command to the wrong item!')); 113 + } 114 + 115 + if ($command->getStatus() !== NuanceItemCommand::STATUS_ISSUED) { 116 + // Never execute commands which have already been issued. 117 + continue; 118 + } 119 + 73 120 $command 74 121 ->setStatus(NuanceItemCommand::STATUS_EXECUTING) 75 122 ->save(); ··· 103 150 throw $ex; 104 151 } 105 152 } 153 + } 154 + 155 + private function newLock($item_phid) { 156 + $hash = PhabricatorHash::digestForIndex($item_phid); 157 + $lock_key = "nuance.item.{$hash}"; 158 + return PhabricatorGlobalLock::newLock($lock_key); 106 159 } 107 160 108 161 }