@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
at upstream/main 221 lines 6.6 kB view raw
1<?php 2 3final class PhabricatorDatabaseRefParser 4 extends Phobject { 5 6 private $defaultPort = 3306; 7 private $defaultUser; 8 private $defaultPass; 9 10 public function setDefaultPort($default_port) { 11 $this->defaultPort = $default_port; 12 return $this; 13 } 14 15 public function getDefaultPort() { 16 return $this->defaultPort; 17 } 18 19 public function setDefaultUser($default_user) { 20 $this->defaultUser = $default_user; 21 return $this; 22 } 23 24 public function getDefaultUser() { 25 return $this->defaultUser; 26 } 27 28 public function setDefaultPass($default_pass) { 29 $this->defaultPass = $default_pass; 30 return $this; 31 } 32 33 public function getDefaultPass() { 34 return $this->defaultPass; 35 } 36 37 public function newRefs(array $config) { 38 $default_port = $this->getDefaultPort(); 39 $default_user = $this->getDefaultUser(); 40 $default_pass = $this->getDefaultPass(); 41 42 $refs = array(); 43 44 $master_count = 0; 45 foreach ($config as $key => $server) { 46 $host = $server['host']; 47 $port = idx($server, 'port', $default_port); 48 $user = idx($server, 'user', $default_user); 49 $disabled = idx($server, 'disabled', false); 50 51 $pass = idx($server, 'pass'); 52 if ($pass) { 53 $pass = new PhutilOpaqueEnvelope($pass); 54 } else { 55 $pass = clone $default_pass; 56 } 57 58 $role = $server['role']; 59 $is_master = ($role == 'master'); 60 61 $use_persistent = (bool)idx($server, 'persistent', false); 62 63 $ref = id(new PhabricatorDatabaseRef()) 64 ->setHost($host) 65 ->setPort($port) 66 ->setUser($user) 67 ->setPass($pass) 68 ->setDisabled($disabled) 69 ->setIsMaster($is_master) 70 ->setUsePersistentConnections($use_persistent); 71 72 if ($is_master) { 73 $master_count++; 74 } 75 76 $refs[$key] = $ref; 77 } 78 79 $is_partitioned = ($master_count > 1); 80 if ($is_partitioned) { 81 $default_ref = null; 82 $partition_map = array(); 83 foreach ($refs as $key => $ref) { 84 if (!$ref->getIsMaster()) { 85 continue; 86 } 87 88 $server = $config[$key]; 89 $partition = idx($server, 'partition'); 90 if (!is_array($partition)) { 91 throw new Exception( 92 pht( 93 'This server is configured with multiple master databases, '. 94 'but master "%s" is missing a "partition" configuration key to '. 95 'define application partitioning.', 96 $ref->getRefKey())); 97 } 98 99 $application_map = array(); 100 foreach ($partition as $application) { 101 if ($application === 'default') { 102 if ($default_ref) { 103 throw new Exception( 104 pht( 105 'Multiple masters (databases "%s" and "%s") specify that '. 106 'they are the "default" partition. Only one master may be '. 107 'the default.', 108 $ref->getRefKey(), 109 $default_ref->getRefKey())); 110 } else { 111 $default_ref = $ref; 112 $ref->setIsDefaultPartition(true); 113 } 114 } else if (isset($partition_map[$application])) { 115 throw new Exception( 116 pht( 117 'Multiple masters (databases "%s" and "%s") specify that '. 118 'they are the partition for application "%s". Each '. 119 'application may be allocated to only one partition.', 120 $partition_map[$application]->getRefKey(), 121 $ref->getRefKey(), 122 $application)); 123 } else { 124 // TODO: We should check that the application is valid, to 125 // prevent typos in application names. However, we do not 126 // currently have an efficient way to enumerate all of the valid 127 // application database names. 128 129 $partition_map[$application] = $ref; 130 $application_map[$application] = $application; 131 } 132 } 133 134 $ref->setApplicationMap($application_map); 135 } 136 } else { 137 // If we only have one master, make it the default. 138 foreach ($refs as $ref) { 139 if ($ref->getIsMaster()) { 140 $ref->setIsDefaultPartition(true); 141 } 142 } 143 } 144 145 $ref_map = array(); 146 $master_keys = array(); 147 foreach ($refs as $ref) { 148 $ref_key = $ref->getRefKey(); 149 if (isset($ref_map[$ref_key])) { 150 throw new Exception( 151 pht( 152 'Multiple configured databases have the same internal '. 153 'key, "%s". You may have listed a database multiple times.', 154 $ref_key)); 155 } else { 156 $ref_map[$ref_key] = $ref; 157 if ($ref->getIsMaster()) { 158 $master_keys[] = $ref_key; 159 } 160 } 161 } 162 163 foreach ($refs as $key => $ref) { 164 if ($ref->getIsMaster()) { 165 continue; 166 } 167 168 $server = $config[$key]; 169 170 $partition = idx($server, 'partition'); 171 if ($partition !== null) { 172 throw new Exception( 173 pht( 174 'Database "%s" is configured as a replica, but specifies a '. 175 '"partition". Only master databases may have a partition '. 176 'configuration. Replicas use the same configuration as the '. 177 'master they follow.', 178 $ref->getRefKey())); 179 } 180 181 $master_key = idx($server, 'master'); 182 if ($master_key === null) { 183 if ($is_partitioned) { 184 throw new Exception( 185 pht( 186 'Database "%s" is configured as a replica, but does not '. 187 'specify which "master" it follows in configuration. Valid '. 188 'masters are: %s.', 189 $ref->getRefKey(), 190 implode(', ', $master_keys))); 191 } else if ($master_keys) { 192 $master_key = head($master_keys); 193 } else { 194 throw new Exception( 195 pht( 196 'Database "%s" is configured as a replica, but there is no '. 197 'master configured.', 198 $ref->getRefKey())); 199 } 200 } 201 202 if (!isset($ref_map[$master_key])) { 203 throw new Exception( 204 pht( 205 'Database "%s" is configured as a replica and specifies a '. 206 'master ("%s"), but that master is not a valid master. Valid '. 207 'masters are: %s.', 208 $ref->getRefKey(), 209 $master_key, 210 implode(', ', $master_keys))); 211 } 212 213 $master_ref = $ref_map[$master_key]; 214 $ref->setMasterRef($ref_map[$master_key]); 215 $master_ref->addReplicaRef($ref); 216 } 217 218 return array_values($refs); 219 } 220 221}