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

Update SchemaQuery and the web UI to accommodate multiple master databases

Summary:
Depends on D16115. Ref T11044. In the brave new world of multiple masters, we need to check the schemata on each master when looking for missing storage patches, keys, schema changes, etc.

This realigns all the "check out what's up with that schema" calls to work for multiple hosts, and updates the web UI to include a "Server" column and allow you to browse per-server.

This doesn't update `bin/storage`, so it breaks things on its own (and unit tests probably won't pass). I'll update that in the next change.

Test Plan: Configured local environment in cluster mode with multiple masters, saw both hosts' status reported in web UI.

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T11044

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

+337 -185
+2 -1
src/applications/config/application/PhabricatorConfigApplication.php
··· 45 45 'group/(?P<key>[^/]+)/' => 'PhabricatorConfigGroupController', 46 46 'version/' => 'PhabricatorConfigVersionController', 47 47 'database/'. 48 + '(?:(?P<ref>[^/]+)/'. 48 49 '(?:(?P<database>[^/]+)/'. 49 50 '(?:(?P<table>[^/]+)/'. 50 - '(?:(?:col/(?P<column>[^/]+)|key/(?P<key>[^/]+))/)?)?)?' 51 + '(?:(?:col/(?P<column>[^/]+)|key/(?P<key>[^/]+))/)?)?)?)?' 51 52 => 'PhabricatorConfigDatabaseStatusController', 52 53 'dbissue/' => 'PhabricatorConfigDatabaseIssueController', 53 54 '(?P<verb>ignore|unignore)/(?P<key>[^/]+)/'
-16
src/applications/config/controller/PhabricatorConfigDatabaseController.php
··· 3 3 abstract class PhabricatorConfigDatabaseController 4 4 extends PhabricatorConfigController { 5 5 6 - protected function buildSchemaQuery() { 7 - $ref = PhabricatorDatabaseRef::getMasterDatabaseRef(); 8 - 9 - $api = id(new PhabricatorStorageManagementAPI()) 10 - ->setUser($ref->getUser()) 11 - ->setHost($ref->getHost()) 12 - ->setPort($ref->getPort()) 13 - ->setNamespace(PhabricatorLiskDAO::getDefaultStorageNamespace()) 14 - ->setPassword($ref->getPass()); 15 - 16 - $query = id(new PhabricatorConfigSchemaQuery()) 17 - ->setAPI($api); 18 - 19 - return $query; 20 - } 21 - 22 6 protected function renderIcon($status) { 23 7 switch ($status) { 24 8 case PhabricatorConfigStorageSchema::STATUS_OKAY:
+49 -39
src/applications/config/controller/PhabricatorConfigDatabaseIssueController.php
··· 6 6 public function handleRequest(AphrontRequest $request) { 7 7 $viewer = $request->getViewer(); 8 8 9 - $query = $this->buildSchemaQuery(); 9 + $query = new PhabricatorConfigSchemaQuery(); 10 10 11 - $actual = $query->loadActualSchema(); 12 - $expect = $query->loadExpectedSchema(); 13 - $comp = $query->buildComparisonSchema($expect, $actual); 11 + $actual = $query->loadActualSchemata(); 12 + $expect = $query->loadExpectedSchemata(); 13 + $comp_servers = $query->buildComparisonSchemata($expect, $actual); 14 14 15 15 $crumbs = $this->buildApplicationCrumbs(); 16 16 $crumbs->addTextCrumb(pht('Database Issues')); ··· 18 18 19 19 // Collect all open issues. 20 20 $issues = array(); 21 - foreach ($comp->getDatabases() as $database_name => $database) { 22 - foreach ($database->getLocalIssues() as $issue) { 23 - $issues[] = array( 24 - $database_name, 25 - null, 26 - null, 27 - null, 28 - $issue, 29 - ); 30 - } 31 - foreach ($database->getTables() as $table_name => $table) { 32 - foreach ($table->getLocalIssues() as $issue) { 21 + foreach ($comp_servers as $ref_name => $comp) { 22 + foreach ($comp->getDatabases() as $database_name => $database) { 23 + foreach ($database->getLocalIssues() as $issue) { 33 24 $issues[] = array( 25 + $ref_name, 34 26 $database_name, 35 - $table_name, 36 27 null, 37 28 null, 29 + null, 38 30 $issue, 39 31 ); 40 32 } 41 - foreach ($table->getColumns() as $column_name => $column) { 42 - foreach ($column->getLocalIssues() as $issue) { 33 + foreach ($database->getTables() as $table_name => $table) { 34 + foreach ($table->getLocalIssues() as $issue) { 43 35 $issues[] = array( 36 + $ref_name, 44 37 $database_name, 45 38 $table_name, 46 - 'column', 47 - $column_name, 39 + null, 40 + null, 48 41 $issue, 49 42 ); 50 43 } 51 - } 52 - foreach ($table->getKeys() as $key_name => $key) { 53 - foreach ($key->getLocalIssues() as $issue) { 54 - $issues[] = array( 55 - $database_name, 56 - $table_name, 57 - 'key', 58 - $key_name, 59 - $issue, 60 - ); 44 + foreach ($table->getColumns() as $column_name => $column) { 45 + foreach ($column->getLocalIssues() as $issue) { 46 + $issues[] = array( 47 + $ref_name, 48 + $database_name, 49 + $table_name, 50 + 'column', 51 + $column_name, 52 + $issue, 53 + ); 54 + } 55 + } 56 + foreach ($table->getKeys() as $key_name => $key) { 57 + foreach ($key->getLocalIssues() as $issue) { 58 + $issues[] = array( 59 + $ref_name, 60 + $database_name, 61 + $table_name, 62 + 'key', 63 + $key_name, 64 + $issue, 65 + ); 66 + } 61 67 } 62 68 } 63 69 } 64 70 } 65 - 66 71 67 72 // Sort all open issues so that the most severe issues appear first. 68 73 $order = array(); 69 74 $counts = array(); 70 75 foreach ($issues as $key => $issue) { 71 - $const = $issue[4]; 76 + $const = $issue[5]; 72 77 $status = PhabricatorConfigStorageSchema::getIssueStatus($const); 73 78 $severity = PhabricatorConfigStorageSchema::getStatusSeverity($status); 74 79 $order[$key] = sprintf( 75 80 '~%d~%s%s%s', 76 81 9 - $severity, 77 - $issue[0], 78 82 $issue[1], 79 - $issue[3]); 83 + $issue[2], 84 + $issue[4]); 80 85 81 86 if (empty($counts[$status])) { 82 87 $counts[$status] = 0; ··· 91 96 // Render the issues. 92 97 $rows = array(); 93 98 foreach ($issues as $issue) { 94 - $const = $issue[4]; 99 + $const = $issue[5]; 100 + 101 + $uri = $this->getApplicationURI('/database/'.$issue[0].'/'.$issue[1].'/'); 95 102 96 103 $database_link = phutil_tag( 97 104 'a', 98 105 array( 99 - 'href' => $this->getApplicationURI('/database/'.$issue[0].'/'), 106 + 'href' => $uri, 100 107 ), 101 - $issue[0]); 108 + $issue[1]); 102 109 103 110 $rows[] = array( 104 111 $this->renderIcon( 105 112 PhabricatorConfigStorageSchema::getIssueStatus($const)), 113 + $issue[0], 106 114 $database_link, 107 - $issue[1], 108 115 $issue[2], 109 116 $issue[3], 117 + $issue[4], 110 118 PhabricatorConfigStorageSchema::getIssueDescription($const), 111 119 ); 112 120 } ··· 117 125 ->setHeaders( 118 126 array( 119 127 null, 128 + pht('Server'), 120 129 pht('Database'), 121 130 pht('Table'), 122 131 pht('Type'), ··· 125 134 )) 126 135 ->setColumnClasses( 127 136 array( 137 + null, 128 138 null, 129 139 null, 130 140 null,
+205 -102
src/applications/config/controller/PhabricatorConfigDatabaseStatusController.php
··· 7 7 private $table; 8 8 private $column; 9 9 private $key; 10 + private $ref; 10 11 11 12 public function handleRequest(AphrontRequest $request) { 12 13 $viewer = $request->getViewer(); ··· 14 15 $this->table = $request->getURIData('table'); 15 16 $this->column = $request->getURIData('column'); 16 17 $this->key = $request->getURIData('key'); 18 + $this->ref = $request->getURIData('ref'); 17 19 18 - $query = $this->buildSchemaQuery(); 20 + $query = new PhabricatorConfigSchemaQuery(); 19 21 20 - $actual = $query->loadActualSchema(); 21 - $expect = $query->loadExpectedSchema(); 22 - $comp = $query->buildComparisonSchema($expect, $actual); 22 + $actual = $query->loadActualSchemata(); 23 + $expect = $query->loadExpectedSchemata(); 24 + $comp = $query->buildComparisonSchemata($expect, $actual); 23 25 24 - if ($this->column) { 25 - return $this->renderColumn( 26 - $comp, 27 - $expect, 28 - $actual, 29 - $this->database, 30 - $this->table, 31 - $this->column); 32 - } else if ($this->key) { 33 - return $this->renderKey( 34 - $comp, 35 - $expect, 36 - $actual, 37 - $this->database, 38 - $this->table, 39 - $this->key); 40 - } else if ($this->table) { 41 - return $this->renderTable( 42 - $comp, 43 - $expect, 44 - $actual, 45 - $this->database, 46 - $this->table); 47 - } else if ($this->database) { 48 - return $this->renderDatabase( 49 - $comp, 50 - $expect, 51 - $actual, 52 - $this->database); 53 - } else { 54 - return $this->renderServer( 55 - $comp, 56 - $expect, 57 - $actual); 26 + if ($this->ref !== null) { 27 + $server_actual = idx($actual, $this->ref); 28 + if (!$server_actual) { 29 + return new Aphront404Response(); 30 + } 31 + 32 + $server_comparison = $comp[$this->ref]; 33 + $server_expect = $expect[$this->ref]; 34 + 35 + if ($this->column) { 36 + return $this->renderColumn( 37 + $server_comparison, 38 + $server_expect, 39 + $server_actual, 40 + $this->database, 41 + $this->table, 42 + $this->column); 43 + } else if ($this->key) { 44 + return $this->renderKey( 45 + $server_comparison, 46 + $server_expect, 47 + $server_actual, 48 + $this->database, 49 + $this->table, 50 + $this->key); 51 + } else if ($this->table) { 52 + return $this->renderTable( 53 + $server_comparison, 54 + $server_expect, 55 + $server_actual, 56 + $this->database, 57 + $this->table); 58 + } else if ($this->database) { 59 + return $this->renderDatabase( 60 + $server_comparison, 61 + $server_expect, 62 + $server_actual, 63 + $this->database); 64 + } 58 65 } 66 + 67 + return $this->renderServers( 68 + $comp, 69 + $expect, 70 + $actual); 59 71 } 60 72 61 73 private function buildResponse($title, $body) { ··· 66 78 $title = pht('Database Status'); 67 79 } 68 80 81 + $ref = $this->ref; 82 + $database = $this->database; 83 + $table = $this->table; 84 + $column = $this->column; 85 + $key = $this->key; 86 + 87 + $links = array(); 88 + $links[] = array( 89 + pht('Database Status'), 90 + 'database/', 91 + ); 92 + 93 + if ($database) { 94 + $links[] = array( 95 + $database, 96 + "database/{$ref}/{$database}/", 97 + ); 98 + } 99 + 100 + if ($table) { 101 + $links[] = array( 102 + $table, 103 + "database/{$ref}/{$database}/{$table}/", 104 + ); 105 + } 106 + 107 + if ($column) { 108 + $links[] = array( 109 + $column, 110 + "database/{$ref}/{$database}/{$table}/col/{$column}/", 111 + ); 112 + } 113 + 114 + if ($key) { 115 + $links[] = array( 116 + $key, 117 + "database/{$ref}/{$database}/{$table}/key/{$key}/", 118 + ); 119 + } 120 + 69 121 $crumbs = $this->buildApplicationCrumbs(); 70 122 $crumbs->setBorder(true); 71 - if ($this->database) { 72 - $crumbs->addTextCrumb( 73 - pht('Database Status'), 74 - $this->getApplicationURI('database/')); 75 - if ($this->table) { 76 - $crumbs->addTextCrumb( 77 - $this->database, 78 - $this->getApplicationURI('database/'.$this->database.'/')); 79 - if ($this->column || $this->key) { 80 - $crumbs->addTextCrumb( 81 - $this->table, 82 - $this->getApplicationURI( 83 - 'database/'.$this->database.'/'.$this->table.'/')); 84 - if ($this->column) { 85 - $crumbs->addTextCrumb($this->column); 86 - } else { 87 - $crumbs->addTextCrumb($this->key); 88 - } 89 - } else { 90 - $crumbs->addTextCrumb($this->table); 91 - } 123 + 124 + $last_key = last_key($links); 125 + foreach ($links as $link_key => $link) { 126 + list($name, $href) = $link; 127 + if ($link_key == $last_key) { 128 + $crumbs->addTextCrumb($name); 92 129 } else { 93 - $crumbs->addTextCrumb($this->database); 130 + $crumbs->addTextCrumb($name, $this->getApplicationURI($href)); 94 131 } 95 - } else { 96 - $crumbs->addTextCrumb(pht('Database Status')); 97 132 } 98 133 99 134 $doc_link = PhabricatorEnv::getDoclink('Managing Storage Adjustments'); ··· 121 156 } 122 157 123 158 124 - private function renderServer( 125 - PhabricatorConfigServerSchema $comp, 126 - PhabricatorConfigServerSchema $expect, 127 - PhabricatorConfigServerSchema $actual) { 159 + private function renderServers( 160 + array $comp_servers, 161 + array $expect_servers, 162 + array $actual_servers) { 128 163 129 164 $charset_issue = PhabricatorConfigStorageSchema::ISSUE_CHARSET; 130 165 $collation_issue = PhabricatorConfigStorageSchema::ISSUE_COLLATION; 131 166 132 167 $rows = array(); 133 - foreach ($comp->getDatabases() as $database_name => $database) { 134 - $actual_database = $actual->getDatabase($database_name); 135 - if ($actual_database) { 136 - $charset = $actual_database->getCharacterSet(); 137 - $collation = $actual_database->getCollation(); 138 - } else { 139 - $charset = null; 140 - $collation = null; 141 - } 168 + foreach ($comp_servers as $ref_key => $comp) { 169 + $actual = $actual_servers[$ref_key]; 170 + $expect = $expect_servers[$ref_key]; 171 + foreach ($comp->getDatabases() as $database_name => $database) { 172 + $actual_database = $actual->getDatabase($database_name); 173 + if ($actual_database) { 174 + $charset = $actual_database->getCharacterSet(); 175 + $collation = $actual_database->getCollation(); 176 + } else { 177 + $charset = null; 178 + $collation = null; 179 + } 142 180 143 - $status = $database->getStatus(); 144 - $issues = $database->getIssues(); 181 + $status = $database->getStatus(); 182 + $issues = $database->getIssues(); 145 183 146 - $rows[] = array( 147 - $this->renderIcon($status), 148 - phutil_tag( 149 - 'a', 184 + $uri = $this->getURI( 150 185 array( 151 - 'href' => $this->getApplicationURI( 152 - '/database/'.$database_name.'/'), 153 - ), 154 - $database_name), 155 - $this->renderAttr($charset, $database->hasIssue($charset_issue)), 156 - $this->renderAttr($collation, $database->hasIssue($collation_issue)), 157 - ); 186 + 'ref' => $ref_key, 187 + 'database' => $database_name, 188 + )); 189 + 190 + $rows[] = array( 191 + $this->renderIcon($status), 192 + $ref_key, 193 + phutil_tag( 194 + 'a', 195 + array( 196 + 'href' => $uri, 197 + ), 198 + $database_name), 199 + $this->renderAttr($charset, $database->hasIssue($charset_issue)), 200 + $this->renderAttr($collation, $database->hasIssue($collation_issue)), 201 + ); 202 + } 158 203 } 159 204 160 205 $table = id(new AphrontTableView($rows)) 161 206 ->setHeaders( 162 207 array( 163 208 null, 209 + pht('Server'), 164 210 pht('Database'), 165 211 pht('Charset'), 166 212 pht('Collation'), ··· 168 214 ->setColumnClasses( 169 215 array( 170 216 null, 217 + null, 171 218 'wide pri', 172 219 null, 173 220 null, ··· 200 247 foreach ($database->getTables() as $table_name => $table) { 201 248 $status = $table->getStatus(); 202 249 250 + $uri = $this->getURI( 251 + array( 252 + 'table' => $table_name, 253 + )); 254 + 203 255 $rows[] = array( 204 256 $this->renderIcon($status), 205 257 phutil_tag( 206 258 'a', 207 259 array( 208 - 'href' => $this->getApplicationURI( 209 - '/database/'.$database_name.'/'.$table_name.'/'), 260 + 'href' => $uri, 210 261 ), 211 262 $table_name), 212 263 $this->renderAttr( ··· 252 303 $properties = $this->buildProperties( 253 304 array( 254 305 array( 306 + pht('Server'), 307 + $this->ref, 308 + ), 309 + array( 255 310 pht('Character Set'), 256 311 $actual_charset, 257 312 ), ··· 325 380 $data_type = $expect_column->getDataType(); 326 381 } 327 382 383 + $uri = $this->getURI( 384 + array( 385 + 'column' => $column_name, 386 + )); 387 + 328 388 $rows[] = array( 329 389 $this->renderIcon($status), 330 390 phutil_tag( 331 391 'a', 332 392 array( 333 - 'href' => $this->getApplicationURI( 334 - 'database/'. 335 - $database_name.'/'. 336 - $table_name.'/'. 337 - 'col/'. 338 - $column_name.'/'), 393 + 'href' => $uri, 339 394 ), 340 395 $column_name), 341 396 $data_type, ··· 407 462 $key->hasIssue($longkey_issue)); 408 463 } 409 464 465 + $uri = $this->getURI( 466 + array( 467 + 'key' => $key_name, 468 + )); 469 + 410 470 $key_rows[] = array( 411 471 $this->renderIcon($status), 412 472 phutil_tag( 413 473 'a', 414 474 array( 415 - 'href' => $this->getApplicationURI( 416 - 'database/'. 417 - $database_name.'/'. 418 - $table_name.'/'. 419 - 'key/'. 420 - $key_name.'/'), 475 + 'href' => $uri, 421 476 ), 422 477 $key_name), 423 478 $this->renderAttr( ··· 465 520 $properties = $this->buildProperties( 466 521 array( 467 522 array( 523 + pht('Server'), 524 + $this->ref, 525 + ), 526 + array( 468 527 pht('Collation'), 469 528 $actual_collation, 470 529 ), ··· 561 620 562 621 $properties = $this->buildProperties( 563 622 array( 623 + array( 624 + pht('Server'), 625 + $this->ref, 626 + ), 564 627 array( 565 628 pht('Data Type'), 566 629 $data_type, ··· 679 742 $properties = $this->buildProperties( 680 743 array( 681 744 array( 745 + pht('Server'), 746 + $this->ref, 747 + ), 748 + array( 682 749 pht('Unique'), 683 750 $this->renderBoolean($actual_unique), 684 751 ), ··· 743 810 $view->addProperty(pht('Schema Status'), $status_view); 744 811 745 812 return phutil_tag_div('config-page-property', $view); 813 + } 814 + 815 + private function getURI(array $properties) { 816 + $defaults = array( 817 + 'ref' => $this->ref, 818 + 'database' => $this->database, 819 + 'table' => $this->table, 820 + 'column' => $this->column, 821 + 'key' => $this->key, 822 + ); 823 + 824 + $properties = $properties + $defaults; 825 + $properties = array_select_keys($properties, array_keys($defaults)); 826 + 827 + $parts = array(); 828 + foreach ($properties as $key => $property) { 829 + if (!strlen($property)) { 830 + continue; 831 + } 832 + 833 + if ($key == 'column') { 834 + $parts[] = 'col'; 835 + } else if ($key == 'key') { 836 + $parts[] = 'key'; 837 + } 838 + 839 + $parts[] = $property; 840 + } 841 + 842 + if ($parts) { 843 + $parts = implode('/', $parts).'/'; 844 + } else { 845 + $parts = null; 846 + } 847 + 848 + return $this->getApplicationURI('/database/'.$parts); 746 849 } 747 850 748 851 }
+60 -27
src/applications/config/schema/PhabricatorConfigSchemaQuery.php
··· 2 2 3 3 final class PhabricatorConfigSchemaQuery extends Phobject { 4 4 5 - private $api; 5 + private function getDatabaseNames(PhabricatorDatabaseRef $ref) { 6 + $api = $this->getAPI($ref); 7 + $patches = PhabricatorSQLPatchList::buildAllPatches(); 8 + return $api->getDatabaseList( 9 + $patches, 10 + $only_living = true); 11 + } 6 12 7 - public function setAPI(PhabricatorStorageManagementAPI $api) { 8 - $this->api = $api; 9 - return $this; 13 + private function getAPI(PhabricatorDatabaseRef $ref) { 14 + return id(new PhabricatorStorageManagementAPI()) 15 + ->setUser($ref->getUser()) 16 + ->setHost($ref->getHost()) 17 + ->setPort($ref->getPort()) 18 + ->setNamespace(PhabricatorLiskDAO::getDefaultStorageNamespace()) 19 + ->setPassword($ref->getPass()); 10 20 } 11 21 12 - protected function getAPI() { 13 - if (!$this->api) { 14 - throw new PhutilInvalidStateException('setAPI'); 22 + public function loadActualSchemata() { 23 + $refs = PhabricatorDatabaseRef::getMasterDatabaseRefs(); 24 + 25 + $schemata = array(); 26 + foreach ($refs as $ref) { 27 + $schema = $this->loadActualSchemaForServer($ref); 28 + $schemata[$schema->getRef()->getRefKey()] = $schema; 15 29 } 16 - return $this->api; 17 - } 18 30 19 - protected function getConn() { 20 - return $this->getAPI()->getConn(null); 31 + return $schemata; 21 32 } 22 33 23 - private function getDatabaseNames() { 24 - $api = $this->getAPI(); 25 - $patches = PhabricatorSQLPatchList::buildAllPatches(); 26 - return $api->getDatabaseList( 27 - $patches, 28 - $only_living = true); 29 - } 34 + private function loadActualSchemaForServer(PhabricatorDatabaseRef $ref) { 35 + $databases = $this->getDatabaseNames($ref); 30 36 31 - public function loadActualSchema() { 32 - $databases = $this->getDatabaseNames(); 37 + $conn = $ref->newManagementConnection(); 33 38 34 - $conn = $this->getConn(); 35 39 $tables = queryfx_all( 36 40 $conn, 37 41 'SELECT TABLE_SCHEMA, TABLE_NAME, TABLE_COLLATION ··· 92 96 // primary, unique, and foreign keys, so we can't use them here. We pull 93 97 // indexes later on using SHOW INDEXES. 94 98 95 - $server_schema = new PhabricatorConfigServerSchema(); 99 + $server_schema = id(new PhabricatorConfigServerSchema()) 100 + ->setRef($ref); 96 101 97 102 $tables = igroup($tables, 'TABLE_SCHEMA'); 98 103 foreach ($tables as $database_name => $database_tables) { ··· 177 182 return $server_schema; 178 183 } 179 184 180 - public function loadExpectedSchema() { 181 - $databases = $this->getDatabaseNames(); 182 - $info = $this->getAPI()->getCharsetInfo(); 185 + public function loadExpectedSchemata() { 186 + $refs = PhabricatorDatabaseRef::getMasterDatabaseRefs(); 187 + 188 + $schemata = array(); 189 + foreach ($refs as $ref) { 190 + $schema = $this->loadExpectedSchemaForServer($ref); 191 + $schemata[$schema->getRef()->getRefKey()] = $schema; 192 + } 193 + 194 + return $schemata; 195 + } 196 + 197 + public function loadExpectedSchemaForServer(PhabricatorDatabaseRef $ref) { 198 + $databases = $this->getDatabaseNames($ref); 199 + $info = $this->getAPI($ref)->getCharsetInfo(); 183 200 184 201 $specs = id(new PhutilClassMapQuery()) 185 202 ->setAncestorClass('PhabricatorConfigSchemaSpec') 186 203 ->execute(); 187 204 188 - $server_schema = new PhabricatorConfigServerSchema(); 205 + $server_schema = id(new PhabricatorConfigServerSchema()) 206 + ->setRef($ref); 207 + 189 208 foreach ($specs as $spec) { 190 209 $spec 191 210 ->setUTF8Charset( ··· 201 220 return $server_schema; 202 221 } 203 222 204 - public function buildComparisonSchema( 223 + public function buildComparisonSchemata( 224 + array $expect_servers, 225 + array $actual_servers) { 226 + 227 + $schemata = array(); 228 + foreach ($actual_servers as $key => $actual_server) { 229 + $schemata[$key] = $this->buildComparisonSchemaForServer( 230 + $expect_servers[$key], 231 + $actual_server); 232 + } 233 + 234 + return $schemata; 235 + } 236 + 237 + private function buildComparisonSchemaForServer( 205 238 PhabricatorConfigServerSchema $expect, 206 239 PhabricatorConfigServerSchema $actual) { 207 240
+10
src/applications/config/schema/PhabricatorConfigServerSchema.php
··· 3 3 final class PhabricatorConfigServerSchema 4 4 extends PhabricatorConfigStorageSchema { 5 5 6 + private $ref; 6 7 private $databases = array(); 8 + 9 + public function setRef(PhabricatorDatabaseRef $ref) { 10 + $this->ref = $ref; 11 + return $this; 12 + } 13 + 14 + public function getRef() { 15 + return $this->ref; 16 + } 7 17 8 18 public function addDatabase(PhabricatorConfigDatabaseSchema $database) { 9 19 $key = $database->getName();
+11
src/infrastructure/cluster/PhabricatorDatabaseRef.php
··· 157 157 return $this->isIndividual; 158 158 } 159 159 160 + public function getRefKey() { 161 + $host = $this->getHost(); 162 + 163 + $port = $this->getPort(); 164 + if (strlen($port)) { 165 + return "{$host}:{$port}"; 166 + } 167 + 168 + return $host; 169 + } 170 + 160 171 public static function getConnectionStatusMap() { 161 172 return array( 162 173 self::STATUS_OKAY => array(