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

Try to route cluster writes to nodes which won't need to synchronize first

Summary:
Ref T13109. Ref T13202. See PHI905. See PHI889. When we receive a write to a repository cluster, we currently send it to a random writable node.

Instead, we can prefer:

- the node currently holding the write lock; or
- any node which is already up to date.

These should simply be better nodes to take writes in all cases. The write lock is global for the repository, so there's no scaling benefit to spreading writes across different nodes, and these particular nodes will be able to accept the write more quickly.

Test Plan:
- This is observable by using `fprintf(STDERR, "%s\n", ...)` in the logic, then running `git push`. I'd like to pull this routing logic out of `PhabricatorRepository` at some point, probably into a dedicated `ClusterURIQuery` sort of class, but that is a larger change.
- Added some `fprintf(...)` stuff around which nodes were being selected.
- Added a `sleep(10)` after grabbing the write lock.
- In one window, pushed. Then pushed in a second window.
- Saw the second window select the lock holder as the write target based on it currently holding the lock.
- Without a concurrent push, saw pushes select up-to-date nodes based on their up-to-date-ness.

Reviewers: amckinley

Reviewed By: amckinley

Subscribers: joshuaspence, timhirsh

Maniphest Tasks: T13202, T13109

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

+64 -2
+64 -2
src/applications/repository/storage/PhabricatorRepository.php
··· 2010 2010 } 2011 2011 } 2012 2012 2013 - shuffle($results); 2013 + if ($writable) { 2014 + $results = $this->sortWritableAlmanacServiceURIs($results); 2015 + } else { 2016 + shuffle($results); 2017 + } 2014 2018 2015 2019 $result = head($results); 2016 2020 return $result['uri']; 2017 2021 } 2018 2022 2023 + private function sortWritableAlmanacServiceURIs(array $results) { 2024 + // See T13109 for discussion of how this method routes requests. 2025 + 2026 + // In the absence of other rules, we'll send traffic to devices randomly. 2027 + // We also want to select randomly among nodes which are equally good 2028 + // candidates to receive the write, and accomplish that by shuffling the 2029 + // list up front. 2030 + shuffle($results); 2031 + 2032 + $order = array(); 2033 + 2034 + // If some device is currently holding the write lock, send all requests 2035 + // to that device. We're trying to queue writes on a single device so they 2036 + // do not need to wait for read synchronization after earlier writes 2037 + // complete. 2038 + $writer = PhabricatorRepositoryWorkingCopyVersion::loadWriter( 2039 + $this->getPHID()); 2040 + if ($writer) { 2041 + $device_phid = $writer->getWriteProperty('devicePHID'); 2042 + foreach ($results as $key => $result) { 2043 + if ($result['devicePHID'] === $device_phid) { 2044 + $order[] = $key; 2045 + } 2046 + } 2047 + } 2048 + 2049 + // If no device is currently holding the write lock, try to send requests 2050 + // to a device which is already up to date and will not need to synchronize 2051 + // before it can accept the write. 2052 + $versions = PhabricatorRepositoryWorkingCopyVersion::loadVersions( 2053 + $this->getPHID()); 2054 + if ($versions) { 2055 + $max_version = (int)max(mpull($versions, 'getRepositoryVersion')); 2056 + 2057 + $max_devices = array(); 2058 + foreach ($versions as $version) { 2059 + if ($version->getRepositoryVersion() == $max_version) { 2060 + $max_devices[] = $version->getDevicePHID(); 2061 + } 2062 + } 2063 + $max_devices = array_fuse($max_devices); 2064 + 2065 + foreach ($results as $key => $result) { 2066 + if (isset($max_devices[$result['devicePHID']])) { 2067 + $order[] = $key; 2068 + } 2069 + } 2070 + } 2071 + 2072 + // Reorder the results, putting any we've selected as preferred targets for 2073 + // the write at the head of the list. 2074 + $results = array_select_keys($results, $order) + $results; 2075 + 2076 + return $results; 2077 + } 2078 + 2019 2079 public function supportsSynchronization() { 2020 2080 // TODO: For now, this is only supported for Git. 2021 2081 if (!$this->isGit()) { ··· 2036 2096 $parts = array( 2037 2097 "repo({$repository_phid})", 2038 2098 "serv({$service_phid})", 2039 - 'v2', 2099 + 'v3', 2040 2100 ); 2041 2101 2042 2102 return implode('.', $parts); ··· 2063 2123 $uri = $this->getClusterRepositoryURIFromBinding($binding); 2064 2124 $protocol = $uri->getProtocol(); 2065 2125 $device_name = $iface->getDevice()->getName(); 2126 + $device_phid = $iface->getDevice()->getPHID(); 2066 2127 2067 2128 $uris[] = array( 2068 2129 'protocol' => $protocol, 2069 2130 'uri' => (string)$uri, 2070 2131 'device' => $device_name, 2071 2132 'writable' => (bool)$binding->getAlmanacPropertyValue('writable'), 2133 + 'devicePHID' => $device_phid, 2072 2134 ); 2073 2135 } 2074 2136