@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.

Dramatically limit the range of failures which can cause duplicate mail

Summary:
Ref T8574. Currently, failures during mail body construction, feed publishing, or search indexing could cause us to retry the publishing task and potentially send duplicate mail.

Instead, build (but do not send) the mail first, then send all the mail at the very end.

This isn't completley perfect, but should make it enormously harder for duplicate mail to be generated.

Test Plan: Sent some mail, ran the daemons, saw it show up normally in the outbound queue.

Reviewers: btrahan

Reviewed By: btrahan

Subscribers: epriestley

Maniphest Tasks: T8574

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

+29 -27
+2 -2
src/applications/metamta/replyhandler/PhabricatorMailTarget.php
··· 55 55 return $this->viewer; 56 56 } 57 57 58 - public function sendMail(PhabricatorMetaMTAMail $mail) { 58 + public function willSendMail(PhabricatorMetaMTAMail $mail) { 59 59 $viewer = $this->getViewer(); 60 60 61 61 $mail->addPHIDHeaders('X-Phabricator-To', $this->rawToPHIDs); ··· 92 92 $mail->addCCs($cc); 93 93 } 94 94 95 - return $mail->save(); 95 + return $mail; 96 96 } 97 97 98 98 private function getRecipientsSummary(
+3 -9
src/applications/releeph/editor/ReleephRequestTransactionalEditor.php
··· 166 166 protected function shouldSendMail( 167 167 PhabricatorLiskDAO $object, 168 168 array $xactions) { 169 - return true; 170 - } 171 - 172 - protected function sendMail( 173 - PhabricatorLiskDAO $object, 174 - array $xactions) { 175 169 176 170 // Avoid sending emails that only talk about commit discovery. 177 171 $types = array_unique(mpull($xactions, 'getTransactionType')); 178 172 if ($types === array(ReleephRequestTransaction::TYPE_DISCOVERY)) { 179 - return null; 173 + return false; 180 174 } 181 175 182 176 // Don't email people when we discover that something picks or reverts OK. 183 177 if ($types === array(ReleephRequestTransaction::TYPE_PICK_STATUS)) { 184 178 if (!mfilter($xactions, 'isBoringPickStatus', true /* negate */)) { 185 179 // If we effectively call "isInterestingPickStatus" and get nothing... 186 - return null; 180 + return false; 187 181 } 188 182 } 189 183 190 - return parent::sendMail($object, $xactions); 184 + return true; 191 185 } 192 186 193 187 protected function buildReplyHandler(PhabricatorLiskDAO $object) {
+24 -16
src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php
··· 1040 1040 // Hook for edges or other properties that may need (re-)loading 1041 1041 $object = $this->willPublish($object, $xactions); 1042 1042 1043 - $mailed = array(); 1043 + $messages = array(); 1044 1044 if (!$this->getDisableEmail()) { 1045 1045 if ($this->shouldSendMail($object, $xactions)) { 1046 - $mailed = $this->sendMail($object, $xactions); 1046 + $messages = $this->buildMail($object, $xactions); 1047 1047 } 1048 1048 } 1049 1049 ··· 1055 1055 } 1056 1056 1057 1057 if ($this->shouldPublishFeedStory($object, $xactions)) { 1058 - $this->publishFeedStory( 1059 - $object, 1060 - $xactions, 1061 - $mailed); 1058 + $mailed = array(); 1059 + foreach ($messages as $mail) { 1060 + foreach ($mail->buildRecipientList() as $phid) { 1061 + $mailed[$phid] = true; 1062 + } 1063 + } 1064 + 1065 + $this->publishFeedStory($object, $xactions, $mailed); 1066 + } 1067 + 1068 + // NOTE: This actually sends the mail. We do this last to reduce the chance 1069 + // that we send some mail, hit an exception, then send the mail again when 1070 + // retrying. 1071 + foreach ($messages as $mail) { 1072 + $mail->save(); 1062 1073 } 1063 1074 1064 1075 return $xactions; ··· 2241 2252 /** 2242 2253 * @task mail 2243 2254 */ 2244 - protected function sendMail( 2255 + private function buildMail( 2245 2256 PhabricatorLiskDAO $object, 2246 2257 array $xactions) { 2247 2258 ··· 2255 2266 // Set this explicitly before we start swapping out the effective actor. 2256 2267 $this->setActingAsPHID($this->getActingAsPHID()); 2257 2268 2258 - 2259 - $mailed = array(); 2269 + $messages = array(); 2260 2270 foreach ($targets as $target) { 2261 2271 $original_actor = $this->getActor(); 2262 2272 ··· 2270 2280 // Reload handles for the new viewer. 2271 2281 $this->loadHandles($xactions); 2272 2282 2273 - $mail = $this->sendMailToTarget($object, $xactions, $target); 2283 + $mail = $this->buildMailForTarget($object, $xactions, $target); 2274 2284 } catch (Exception $ex) { 2275 2285 $caught = $ex; 2276 2286 } ··· 2283 2293 } 2284 2294 2285 2295 if ($mail) { 2286 - foreach ($mail->buildRecipientList() as $phid) { 2287 - $mailed[$phid] = true; 2288 - } 2296 + $messages[] = $mail; 2289 2297 } 2290 2298 } 2291 2299 2292 - return array_keys($mailed); 2300 + return $messages; 2293 2301 } 2294 2302 2295 - private function sendMailToTarget( 2303 + private function buildMailForTarget( 2296 2304 PhabricatorLiskDAO $object, 2297 2305 array $xactions, 2298 2306 PhabricatorMailTarget $target) { ··· 2354 2362 $mail->setParentMessageID($this->getParentMessageID()); 2355 2363 } 2356 2364 2357 - return $target->sendMail($mail); 2365 + return $target->willSendMail($mail); 2358 2366 } 2359 2367 2360 2368 private function addMailProjectMetadata(