Page MenuHomePhabricator

D15761.diff
No OneTemporary

D15761.diff

diff --git a/src/applications/repository/editor/PhabricatorRepositoryEditor.php b/src/applications/repository/editor/PhabricatorRepositoryEditor.php
--- a/src/applications/repository/editor/PhabricatorRepositoryEditor.php
+++ b/src/applications/repository/editor/PhabricatorRepositoryEditor.php
@@ -683,6 +683,10 @@
$object->save();
}
+ if ($this->getIsNewObject()) {
+ $object->synchronizeWorkingCopyAfterCreation();
+ }
+
return $xactions;
}
diff --git a/src/applications/repository/storage/PhabricatorRepository.php b/src/applications/repository/storage/PhabricatorRepository.php
--- a/src/applications/repository/storage/PhabricatorRepository.php
+++ b/src/applications/repository/storage/PhabricatorRepository.php
@@ -1932,7 +1932,7 @@
return null;
}
- $bindings = $service->getBindings();
+ $bindings = $service->getActiveBindings();
if (!$bindings) {
throw new Exception(
pht(
@@ -1954,10 +1954,6 @@
$uris = array();
foreach ($bindings as $binding) {
- if ($binding->getIsDisabled()) {
- continue;
- }
-
$iface = $binding->getInterface();
// If we're never proxying this and it's locally satisfiable, return
@@ -2228,6 +2224,37 @@
/**
+ * Synchronize repository version information after creating a repository.
+ *
+ * This initializes working copy versions for all currently bound devices to
+ * 0, so that we don't get stuck making an ambiguous choice about which
+ * devices are leaders when we later synchronize before a read.
+ *
+ * @task sync
+ */
+ public function synchronizeWorkingCopyAfterCreation() {
+ if (!$this->shouldEnableSynchronization()) {
+ return;
+ }
+
+ $repository_phid = $this->getPHID();
+
+ $service = $this->loadAlmanacService();
+ if (!$service) {
+ throw new Exception(pht('Failed to load repository cluster service.'));
+ }
+
+ $bindings = $service->getActiveBindings();
+ foreach ($bindings as $binding) {
+ PhabricatorRepositoryWorkingCopyVersion::updateVersion(
+ $repository_phid,
+ $binding->getDevicePHID(),
+ 0);
+ }
+ }
+
+
+ /**
* @task sync
*/
public function synchronizeWorkingCopyBeforeRead() {
@@ -2255,34 +2282,91 @@
if ($this_version) {
$this_version = (int)$this_version->getRepositoryVersion();
} else {
- $this_version = 0;
+ $this_version = -1;
}
if ($versions) {
+ // This is the normal case, where we have some version information and
+ // can identify which nodes are leaders. If the current node is not a
+ // leader, we want to fetch from a leader and then update our version.
+
$max_version = (int)max(mpull($versions, 'getRepositoryVersion'));
+ if ($max_version > $this_version) {
+ $fetchable = array();
+ foreach ($versions as $version) {
+ if ($version->getRepositoryVersion() == $max_version) {
+ $fetchable[] = $version->getDevicePHID();
+ }
+ }
+
+ $this->synchronizeWorkingCopyFromDevices($fetchable);
+
+ PhabricatorRepositoryWorkingCopyVersion::updateVersion(
+ $repository_phid,
+ $device_phid,
+ $max_version);
+ }
+
+ $result_version = $max_version;
} else {
- $max_version = 0;
- }
+ // If no version records exist yet, we need to be careful, because we
+ // can not tell which nodes are leaders.
- if ($max_version > $this_version) {
- $fetchable = array();
- foreach ($versions as $version) {
- if ($version->getRepositoryVersion() == $max_version) {
- $fetchable[] = $version->getDevicePHID();
- }
+ // There might be several nodes with arbitrary existing data, and we have
+ // no way to tell which one has the "right" data. If we pick wrong, we
+ // might erase some or all of the data in the repository.
+
+ // Since this is dangeorus, we refuse to guess unless there is only one
+ // device. If we're the only device in the group, we obviously must be
+ // a leader.
+
+ $service = $this->loadAlmanacService();
+ if (!$service) {
+ throw new Exception(pht('Failed to load repository cluster service.'));
+ }
+
+ $bindings = $service->getActiveBindings();
+ $device_map = array();
+ foreach ($bindings as $binding) {
+ $device_map[$binding->getDevicePHID()] = true;
+ }
+
+ if (count($device_map) > 1) {
+ throw new Exception(
+ pht(
+ 'Repository "%s" exists on more than one device, but no device '.
+ 'has any repository version information. Phabricator can not '.
+ 'guess which copy of the existing data is authoritative. Remove '.
+ 'all but one device from service to mark the remaining device '.
+ 'as the authority.',
+ $this->getDisplayName()));
}
- $this->synchronizeWorkingCopyFromDevices($fetchable);
+ if (empty($device_map[$device->getPHID()])) {
+ throw new Exception(
+ pht(
+ 'Repository "%s" is being synchronized on device "%s", but '.
+ 'this device is not bound to the corresponding cluster '.
+ 'service ("%s").',
+ $this->getDisplayName(),
+ $device->getName(),
+ $service->getName()));
+ }
+ // The current device is the only device in service, so it must be a
+ // leader. We can safely have any future nodes which come online read
+ // from it.
PhabricatorRepositoryWorkingCopyVersion::updateVersion(
$repository_phid,
$device_phid,
- $max_version);
+ 0);
+
+ $result_version = 0;
}
$read_lock->unlock();
- return $max_version;
+ return $result_version;
}
@@ -2399,15 +2483,10 @@
}
$device_map = array_fuse($device_phids);
- $bindings = $service->getBindings();
+ $bindings = $service->getActiveBindings();
$fetchable = array();
foreach ($bindings as $binding) {
- // We can't fetch from disabled nodes.
- if ($binding->getIsDisabled()) {
- continue;
- }
-
// We can't fetch from nodes which don't have the newest version.
$device_phid = $binding->getDevicePHID();
if (empty($device_map[$device_phid])) {

File Metadata

Mime Type
text/plain
Expires
Mon, Mar 10, 5:38 PM (2 w, 6 d ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
7317132
Default Alt Text
D15761.diff (6 KB)

Event Timeline