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

Read lock all transaction edits

Summary: Ref T13054. Fixes T12714. Applies read locks to all transactions instead of only a very select subset (chat messages in Conpherence).

Test Plan: See <T13054#235650> for discussion and testing.

Maniphest Tasks: T13054, T12714

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

+61 -121
+4
src/applications/auth/engine/PhabricatorAuthPasswordEngine.php
··· 300 300 $password->upgradePasswordHasher($envelope, $this->getObject()); 301 301 $new_hasher = $password->getHasher(); 302 302 303 + // NOTE: We must save the change before applying transactions because 304 + // the editor will reload the object to obtain a read lock. 305 + $password->save(); 306 + 303 307 $xactions = array(); 304 308 305 309 $xactions[] = $password->getApplicationTransactionTemplate()
-18
src/applications/conpherence/editor/ConpherenceEditor.php
··· 99 99 return pht('%s created this room.', $author); 100 100 } 101 101 102 - /** 103 - * We really only need a read lock if we have a comment. In that case, we 104 - * must update the messagesCount field on the conpherence and 105 - * seenMessagesCount(s) for the participant(s). 106 - */ 107 - protected function shouldReadLock( 108 - PhabricatorLiskDAO $object, 109 - PhabricatorApplicationTransaction $xaction) { 110 - 111 - $lock = false; 112 - switch ($xaction->getTransactionType()) { 113 - case PhabricatorTransactions::TYPE_COMMENT: 114 - $lock = true; 115 - break; 116 - } 117 - 118 - return $lock; 119 - } 120 102 121 103 protected function applyBuiltinInternalTransaction( 122 104 PhabricatorLiskDAO $object,
+57 -103
src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php
··· 924 924 if ($object->getID()) { 925 925 $this->buildOldRecipientLists($object, $xactions); 926 926 927 - foreach ($xactions as $xaction) { 927 + $object->openTransaction(); 928 + $transaction_open = true; 928 929 929 - // If any of the transactions require a read lock, hold one and 930 - // reload the object. We need to do this fairly early so that the 931 - // call to `adjustTransactionValues()` (which populates old values) 932 - // is based on the synchronized state of the object, which may differ 933 - // from the state when it was originally loaded. 930 + $object->beginReadLocking(); 931 + $read_locking = true; 934 932 935 - if ($this->shouldReadLock($object, $xaction)) { 936 - $object->openTransaction(); 937 - $object->beginReadLocking(); 938 - $transaction_open = true; 939 - $read_locking = true; 940 - $object->reload(); 941 - break; 942 - } 943 - } 933 + $object->reload(); 944 934 } 945 935 946 936 if ($this->shouldApplyInitialEffects($object, $xactions)) { ··· 951 941 } 952 942 } 953 943 954 - if ($this->shouldApplyInitialEffects($object, $xactions)) { 955 - $this->applyInitialEffects($object, $xactions); 956 - } 944 + try { 945 + if ($this->shouldApplyInitialEffects($object, $xactions)) { 946 + $this->applyInitialEffects($object, $xactions); 947 + } 957 948 958 - foreach ($xactions as $xaction) { 959 - $this->adjustTransactionValues($object, $xaction); 960 - } 949 + foreach ($xactions as $xaction) { 950 + $this->adjustTransactionValues($object, $xaction); 951 + } 961 952 962 - try { 963 953 $xactions = $this->filterTransactions($object, $xactions); 964 - } catch (Exception $ex) { 965 - if ($read_locking) { 966 - $object->endReadLocking(); 967 - } 968 - if ($transaction_open) { 969 - $object->killTransaction(); 954 + 955 + // TODO: Once everything is on EditEngine, just use getIsNewObject() to 956 + // figure this out instead. 957 + $mark_as_create = false; 958 + $create_type = PhabricatorTransactions::TYPE_CREATE; 959 + foreach ($xactions as $xaction) { 960 + if ($xaction->getTransactionType() == $create_type) { 961 + $mark_as_create = true; 962 + } 970 963 } 971 - throw $ex; 972 - } 973 964 974 - // TODO: Once everything is on EditEngine, just use getIsNewObject() to 975 - // figure this out instead. 976 - $mark_as_create = false; 977 - $create_type = PhabricatorTransactions::TYPE_CREATE; 978 - foreach ($xactions as $xaction) { 979 - if ($xaction->getTransactionType() == $create_type) { 980 - $mark_as_create = true; 965 + if ($mark_as_create) { 966 + foreach ($xactions as $xaction) { 967 + $xaction->setIsCreateTransaction(true); 968 + } 981 969 } 982 - } 983 970 984 - if ($mark_as_create) { 971 + // Now that we've merged, filtered, and combined transactions, check for 972 + // required capabilities. 985 973 foreach ($xactions as $xaction) { 986 - $xaction->setIsCreateTransaction(true); 974 + $this->requireCapabilities($object, $xaction); 987 975 } 988 - } 989 976 990 - // Now that we've merged, filtered, and combined transactions, check for 991 - // required capabilities. 992 - foreach ($xactions as $xaction) { 993 - $this->requireCapabilities($object, $xaction); 994 - } 977 + $xactions = $this->sortTransactions($xactions); 978 + $file_phids = $this->extractFilePHIDs($object, $xactions); 995 979 996 - $xactions = $this->sortTransactions($xactions); 997 - $file_phids = $this->extractFilePHIDs($object, $xactions); 980 + if ($is_preview) { 981 + $this->loadHandles($xactions); 982 + return $xactions; 983 + } 998 984 999 - if ($is_preview) { 1000 - $this->loadHandles($xactions); 1001 - return $xactions; 1002 - } 985 + $comment_editor = id(new PhabricatorApplicationTransactionCommentEditor()) 986 + ->setActor($actor) 987 + ->setActingAsPHID($this->getActingAsPHID()) 988 + ->setContentSource($this->getContentSource()); 1003 989 1004 - $comment_editor = id(new PhabricatorApplicationTransactionCommentEditor()) 1005 - ->setActor($actor) 1006 - ->setActingAsPHID($this->getActingAsPHID()) 1007 - ->setContentSource($this->getContentSource()); 1008 - 1009 - if (!$transaction_open) { 1010 - $object->openTransaction(); 1011 - } 990 + if (!$transaction_open) { 991 + $object->openTransaction(); 992 + $transaction_open = true; 993 + } 1012 994 1013 - try { 1014 995 foreach ($xactions as $xaction) { 1015 996 $this->applyInternalEffects($object, $xaction); 1016 997 } ··· 1070 1051 } 1071 1052 1072 1053 $xactions = $this->applyFinalEffects($object, $xactions); 1054 + 1073 1055 if ($read_locking) { 1074 1056 $object->endReadLocking(); 1075 1057 $read_locking = false; 1076 1058 } 1077 1059 1078 - $object->saveTransaction(); 1060 + if ($transaction_open) { 1061 + $object->saveTransaction(); 1062 + $transaction_open = false; 1063 + } 1079 1064 } catch (Exception $ex) { 1080 - $object->killTransaction(); 1065 + if ($read_locking) { 1066 + $object->endReadLocking(); 1067 + $read_locking = false; 1068 + } 1069 + 1070 + if ($transaction_open) { 1071 + $object->killTransaction(); 1072 + $transaction_open = false; 1073 + } 1074 + 1081 1075 throw $ex; 1082 1076 } 1083 1077 ··· 1317 1311 protected function didApplyTransactions($object, array $xactions) { 1318 1312 // Hook for subclasses. 1319 1313 return $xactions; 1320 - } 1321 - 1322 - 1323 - /** 1324 - * Determine if the editor should hold a read lock on the object while 1325 - * applying a transaction. 1326 - * 1327 - * If the editor does not hold a lock, two editors may read an object at the 1328 - * same time, then apply their changes without any synchronization. For most 1329 - * transactions, this does not matter much. However, it is important for some 1330 - * transactions. For example, if an object has a transaction count on it, both 1331 - * editors may read the object with `count = 23`, then independently update it 1332 - * and save the object with `count = 24` twice. This will produce the wrong 1333 - * state: the object really has 25 transactions, but the count is only 24. 1334 - * 1335 - * Generally, transactions fall into one of four buckets: 1336 - * 1337 - * - Append operations: Actions like adding a comment to an object purely 1338 - * add information to its state, and do not depend on the current object 1339 - * state in any way. These transactions never need to hold locks. 1340 - * - Overwrite operations: Actions like changing the title or description 1341 - * of an object replace the current value with a new value, so the end 1342 - * state is consistent without a lock. We currently do not lock these 1343 - * transactions, although we may in the future. 1344 - * - Edge operations: Edge and subscription operations have internal 1345 - * synchronization which limits the damage race conditions can cause. 1346 - * We do not currently lock these transactions, although we may in the 1347 - * future. 1348 - * - Update operations: Actions like incrementing a count on an object. 1349 - * These operations generally should use locks, unless it is not 1350 - * important that the state remain consistent in the presence of races. 1351 - * 1352 - * @param PhabricatorLiskDAO Object being updated. 1353 - * @param PhabricatorApplicationTransaction Transaction being applied. 1354 - * @return bool True to synchronize the edit with a lock. 1355 - */ 1356 - protected function shouldReadLock( 1357 - PhabricatorLiskDAO $object, 1358 - PhabricatorApplicationTransaction $xaction) { 1359 - return false; 1360 1314 } 1361 1315 1362 1316 private function loadHandles(array $xactions) {