Changeset View
Changeset View
Standalone View
Standalone View
src/applications/config/check/PhabricatorMySQLSetupCheck.php
| <?php | <?php | ||||
| final class PhabricatorMySQLSetupCheck extends PhabricatorSetupCheck { | final class PhabricatorMySQLSetupCheck extends PhabricatorSetupCheck { | ||||
| public function getDefaultGroup() { | public function getDefaultGroup() { | ||||
| return self::GROUP_MYSQL; | return self::GROUP_MYSQL; | ||||
| } | } | ||||
| public static function loadRawConfigValue($key) { | protected function executeChecks() { | ||||
| $conn_raw = id(new PhabricatorUser())->establishConnection('w'); | $refs = PhabricatorDatabaseRef::getActiveDatabaseRefs(); | ||||
| foreach ($refs as $ref) { | |||||
| try { | $this->executeRefChecks($ref); | ||||
| $value = queryfx_one($conn_raw, 'SELECT @@%Q', $key); | |||||
| $value = $value['@@'.$key]; | |||||
| } catch (AphrontQueryException $ex) { | |||||
| $value = null; | |||||
| } | } | ||||
| return $value; | |||||
| } | } | ||||
| protected function executeChecks() { | private function executeRefChecks(PhabricatorDatabaseRef $ref) { | ||||
| // TODO: These checks should be executed against every reachable replica? | $max_allowed_packet = $ref->loadRawMySQLConfigValue('max_allowed_packet'); | ||||
| // See T10759. | |||||
| if (PhabricatorEnv::isReadOnly()) { | |||||
| return; | |||||
| } | |||||
| $max_allowed_packet = self::loadRawConfigValue('max_allowed_packet'); | $host_name = $ref->getRefKey(); | ||||
| // This primarily supports setting the filesize limit for MySQL to 8MB, | // This primarily supports setting the filesize limit for MySQL to 8MB, | ||||
| // which may produce a >16MB packet after escaping. | // which may produce a >16MB packet after escaping. | ||||
| $recommended_minimum = (32 * 1024 * 1024); | $recommended_minimum = (32 * 1024 * 1024); | ||||
| if ($max_allowed_packet < $recommended_minimum) { | if ($max_allowed_packet < $recommended_minimum) { | ||||
| $message = pht( | $message = pht( | ||||
| "MySQL is configured with a small '%s' (%d), ". | 'On host "%s", MySQL is configured with a small "%s" (%d), which '. | ||||
| "which may cause some large writes to fail.", | 'may cause some large writes to fail. The recommended minimum value '. | ||||
| 'for this setting is "%d".', | |||||
| $host_name, | |||||
| 'max_allowed_packet', | 'max_allowed_packet', | ||||
| $max_allowed_packet); | $max_allowed_packet, | ||||
| $recommended_minimum); | |||||
| $this->newIssue('mysql.max_allowed_packet') | $this->newIssue('mysql.max_allowed_packet') | ||||
| ->setName(pht('Small MySQL "%s"', 'max_allowed_packet')) | ->setName(pht('Small MySQL "%s"', 'max_allowed_packet')) | ||||
| ->setMessage($message) | ->setMessage($message) | ||||
| ->setDatabaseRef($ref) | |||||
| ->addMySQLConfig('max_allowed_packet'); | ->addMySQLConfig('max_allowed_packet'); | ||||
| } | } | ||||
| $modes = self::loadRawConfigValue('sql_mode'); | $modes = $ref->loadRawMySQLConfigValue('sql_mode'); | ||||
| $modes = explode(',', $modes); | $modes = explode(',', $modes); | ||||
| if (!in_array('STRICT_ALL_TABLES', $modes)) { | if (!in_array('STRICT_ALL_TABLES', $modes)) { | ||||
| $summary = pht( | $summary = pht( | ||||
| 'MySQL is not in strict mode, but using strict mode is strongly '. | 'MySQL is not in strict mode (on host "%s"), but using strict mode '. | ||||
| 'encouraged.'); | 'is strongly encouraged.', | ||||
| $host_name); | |||||
| $message = pht( | $message = pht( | ||||
| "On your MySQL instance, the global %s is not set to %s. ". | "On database host \"%s\", the global %s is not set to %s. ". | ||||
| "It is strongly encouraged that you enable this mode when running ". | "It is strongly encouraged that you enable this mode when running ". | ||||
| "Phabricator.\n\n". | "Phabricator.\n\n". | ||||
| "By default MySQL will silently ignore some types of errors, which ". | "By default MySQL will silently ignore some types of errors, which ". | ||||
| "can cause data loss and raise security concerns. Enabling strict ". | "can cause data loss and raise security concerns. Enabling strict ". | ||||
| "mode makes MySQL raise an explicit error instead, and prevents this ". | "mode makes MySQL raise an explicit error instead, and prevents this ". | ||||
| "entire class of problems from doing any damage.\n\n". | "entire class of problems from doing any damage.\n\n". | ||||
| "You can find more information about this mode (and how to configure ". | "You can find more information about this mode (and how to configure ". | ||||
| "it) in the MySQL manual. Usually, it is sufficient to add this to ". | "it) in the MySQL manual. Usually, it is sufficient to add this to ". | ||||
| "your %s file (in the %s section) and then restart %s:\n\n". | "your %s file (in the %s section) and then restart %s:\n\n". | ||||
| "%s\n". | "%s\n". | ||||
| "(Note that if you run other applications against the same database, ". | "(Note that if you run other applications against the same database, ". | ||||
| "they may not work in strict mode. Be careful about enabling it in ". | "they may not work in strict mode. Be careful about enabling it in ". | ||||
| "these cases.)", | "these cases.)", | ||||
| $host_name, | |||||
| phutil_tag('tt', array(), 'sql_mode'), | phutil_tag('tt', array(), 'sql_mode'), | ||||
| phutil_tag('tt', array(), 'STRICT_ALL_TABLES'), | phutil_tag('tt', array(), 'STRICT_ALL_TABLES'), | ||||
| phutil_tag('tt', array(), 'my.cnf'), | phutil_tag('tt', array(), 'my.cnf'), | ||||
| phutil_tag('tt', array(), '[mysqld]'), | phutil_tag('tt', array(), '[mysqld]'), | ||||
| phutil_tag('tt', array(), 'mysqld'), | phutil_tag('tt', array(), 'mysqld'), | ||||
| phutil_tag('pre', array(), 'sql_mode=STRICT_ALL_TABLES')); | phutil_tag('pre', array(), 'sql_mode=STRICT_ALL_TABLES')); | ||||
| $this->newIssue('mysql.mode') | $this->newIssue('mysql.mode') | ||||
| ->setName(pht('MySQL %s Mode Not Set', 'STRICT_ALL_TABLES')) | ->setName(pht('MySQL %s Mode Not Set', 'STRICT_ALL_TABLES')) | ||||
| ->setSummary($summary) | ->setSummary($summary) | ||||
| ->setMessage($message) | ->setMessage($message) | ||||
| ->setDatabaseRef($ref) | |||||
| ->addMySQLConfig('sql_mode'); | ->addMySQLConfig('sql_mode'); | ||||
| } | } | ||||
| if (in_array('ONLY_FULL_GROUP_BY', $modes)) { | if (in_array('ONLY_FULL_GROUP_BY', $modes)) { | ||||
| $summary = pht( | $summary = pht( | ||||
| 'MySQL is in ONLY_FULL_GROUP_BY mode, but using this mode is strongly '. | 'MySQL is in ONLY_FULL_GROUP_BY mode (on host "%s"), but using this '. | ||||
| 'discouraged.'); | 'mode is strongly discouraged.', | ||||
| $host_name); | |||||
| $message = pht( | $message = pht( | ||||
| "On your MySQL instance, the global %s is set to %s. ". | "On database host \"%s\", the global %s is set to %s. ". | ||||
| "It is strongly encouraged that you disable this mode when running ". | "It is strongly encouraged that you disable this mode when running ". | ||||
| "Phabricator.\n\n". | "Phabricator.\n\n". | ||||
| "With %s enabled, MySQL rejects queries for which the select list ". | "With %s enabled, MySQL rejects queries for which the select list ". | ||||
| "or (as of MySQL 5.0.23) %s list refer to nonaggregated columns ". | "or (as of MySQL 5.0.23) %s list refer to nonaggregated columns ". | ||||
| "that are not named in the %s clause. More importantly, Phabricator ". | "that are not named in the %s clause. More importantly, Phabricator ". | ||||
| "does not work properly with this mode enabled.\n\n". | "does not work properly with this mode enabled.\n\n". | ||||
| "You can find more information about this mode (and how to configure ". | "You can find more information about this mode (and how to configure ". | ||||
| "it) in the MySQL manual. Usually, it is sufficient to change the %s ". | "it) in the MySQL manual. Usually, it is sufficient to change the %s ". | ||||
| "in your %s file (in the %s section) and then restart %s:\n\n". | "in your %s file (in the %s section) and then restart %s:\n\n". | ||||
| "%s\n". | "%s\n". | ||||
| "(Note that if you run other applications against the same database, ". | "(Note that if you run other applications against the same database, ". | ||||
| "they may not work with %s. Be careful about enabling ". | "they may not work with %s. Be careful about enabling ". | ||||
| "it in these cases and consider migrating Phabricator to a different ". | "it in these cases and consider migrating Phabricator to a different ". | ||||
| "database.)", | "database.)", | ||||
| $host_name, | |||||
| phutil_tag('tt', array(), 'sql_mode'), | phutil_tag('tt', array(), 'sql_mode'), | ||||
| phutil_tag('tt', array(), 'ONLY_FULL_GROUP_BY'), | phutil_tag('tt', array(), 'ONLY_FULL_GROUP_BY'), | ||||
| phutil_tag('tt', array(), 'ONLY_FULL_GROUP_BY'), | phutil_tag('tt', array(), 'ONLY_FULL_GROUP_BY'), | ||||
| phutil_tag('tt', array(), 'HAVING'), | phutil_tag('tt', array(), 'HAVING'), | ||||
| phutil_tag('tt', array(), 'GROUP BY'), | phutil_tag('tt', array(), 'GROUP BY'), | ||||
| phutil_tag('tt', array(), 'sql_mode'), | phutil_tag('tt', array(), 'sql_mode'), | ||||
| phutil_tag('tt', array(), 'my.cnf'), | phutil_tag('tt', array(), 'my.cnf'), | ||||
| phutil_tag('tt', array(), '[mysqld]'), | phutil_tag('tt', array(), '[mysqld]'), | ||||
| phutil_tag('tt', array(), 'mysqld'), | phutil_tag('tt', array(), 'mysqld'), | ||||
| phutil_tag('pre', array(), 'sql_mode=STRICT_ALL_TABLES'), | phutil_tag('pre', array(), 'sql_mode=STRICT_ALL_TABLES'), | ||||
| phutil_tag('tt', array(), 'ONLY_FULL_GROUP_BY')); | phutil_tag('tt', array(), 'ONLY_FULL_GROUP_BY')); | ||||
| $this->newIssue('mysql.mode') | $this->newIssue('mysql.mode') | ||||
| ->setName(pht('MySQL %s Mode Set', 'ONLY_FULL_GROUP_BY')) | ->setName(pht('MySQL %s Mode Set', 'ONLY_FULL_GROUP_BY')) | ||||
| ->setSummary($summary) | ->setSummary($summary) | ||||
| ->setMessage($message) | ->setMessage($message) | ||||
| ->setDatabaseRef($ref) | |||||
| ->addMySQLConfig('sql_mode'); | ->addMySQLConfig('sql_mode'); | ||||
| } | } | ||||
| $stopword_file = self::loadRawConfigValue('ft_stopword_file'); | $stopword_file = $ref->loadRawMySQLConfigValue('ft_stopword_file'); | ||||
| if ($this->shouldUseMySQLSearchEngine()) { | if ($this->shouldUseMySQLSearchEngine()) { | ||||
| if ($stopword_file === null) { | if ($stopword_file === null) { | ||||
| $summary = pht( | $summary = pht( | ||||
| 'Your version of MySQL does not support configuration of a '. | 'Your version of MySQL (on database host "%s") does not support '. | ||||
| 'stopword file. You will not be able to find search results for '. | 'configuration of a stopword file. You will not be able to find '. | ||||
| 'common words.'); | 'search results for common words.', | ||||
| $host_name); | |||||
| $message = pht( | $message = pht( | ||||
| "Your MySQL instance does not support the %s option. You will not ". | "Database host \"%s\" does not support the %s option. You will not ". | ||||
| "be able to find search results for common words. You can gain ". | "be able to find search results for common words. You can gain ". | ||||
| "access to this option by upgrading MySQL to a more recent ". | "access to this option by upgrading MySQL to a more recent ". | ||||
| "version.\n\n". | "version.\n\n". | ||||
| "You can ignore this warning if you plan to configure ElasticSearch ". | "You can ignore this warning if you plan to configure ElasticSearch ". | ||||
| "later, or aren't concerned about searching for common words.", | "later, or aren't concerned about searching for common words.", | ||||
| $host_name, | |||||
| phutil_tag('tt', array(), 'ft_stopword_file')); | phutil_tag('tt', array(), 'ft_stopword_file')); | ||||
| $this->newIssue('mysql.ft_stopword_file') | $this->newIssue('mysql.ft_stopword_file') | ||||
| ->setName(pht('MySQL %s Not Supported', 'ft_stopword_file')) | ->setName(pht('MySQL %s Not Supported', 'ft_stopword_file')) | ||||
| ->setSummary($summary) | ->setSummary($summary) | ||||
| ->setMessage($message) | ->setMessage($message) | ||||
| ->setDatabaseRef($ref) | |||||
| ->addMySQLConfig('ft_stopword_file'); | ->addMySQLConfig('ft_stopword_file'); | ||||
| } else if ($stopword_file == '(built-in)') { | } else if ($stopword_file == '(built-in)') { | ||||
| $root = dirname(phutil_get_library_root('phabricator')); | $root = dirname(phutil_get_library_root('phabricator')); | ||||
| $stopword_path = $root.'/resources/sql/stopwords.txt'; | $stopword_path = $root.'/resources/sql/stopwords.txt'; | ||||
| $stopword_path = Filesystem::resolvePath($stopword_path); | $stopword_path = Filesystem::resolvePath($stopword_path); | ||||
| $namespace = PhabricatorEnv::getEnvConfig('storage.default-namespace'); | $namespace = PhabricatorEnv::getEnvConfig('storage.default-namespace'); | ||||
| $summary = pht( | $summary = pht( | ||||
| 'MySQL is using a default stopword file, which will prevent '. | 'MySQL (on host "%s") is using a default stopword file, which '. | ||||
| 'searching for many common words.'); | 'will prevent searching for many common words.', | ||||
| $host_name); | |||||
| $message = pht( | $message = pht( | ||||
| "Your MySQL instance is using the builtin stopword file for ". | "Database host \"%s\" is using the builtin stopword file for ". | ||||
| "building search indexes. This can make Phabricator's search ". | "building search indexes. This can make Phabricator's search ". | ||||
| "feature less useful.\n\n". | "feature less useful.\n\n". | ||||
| "Stopwords are common words which are not indexed and thus can not ". | "Stopwords are common words which are not indexed and thus can not ". | ||||
| "be searched for. The default stopword file has about 500 words, ". | "be searched for. The default stopword file has about 500 words, ". | ||||
| "including various words which you are likely to wish to search ". | "including various words which you are likely to wish to search ". | ||||
| "for, such as 'various', 'likely', 'wish', and 'zero'.\n\n". | "for, such as 'various', 'likely', 'wish', and 'zero'.\n\n". | ||||
| "To make search more useful, you can use an alternate stopword ". | "To make search more useful, you can use an alternate stopword ". | ||||
| "file with fewer words. Alternatively, if you aren't concerned ". | "file with fewer words. Alternatively, if you aren't concerned ". | ||||
| "about searching for common words, you can ignore this warning. ". | "about searching for common words, you can ignore this warning. ". | ||||
| "If you later plan to configure ElasticSearch, you can also ignore ". | "If you later plan to configure ElasticSearch, you can also ignore ". | ||||
| "this warning: this stopword file only affects MySQL fulltext ". | "this warning: this stopword file only affects MySQL fulltext ". | ||||
| "indexes.\n\n". | "indexes.\n\n". | ||||
| "To choose a different stopword file, add this to your %s file ". | "To choose a different stopword file, add this to your %s file ". | ||||
| "(in the %s section) and then restart %s:\n\n". | "(in the %s section) and then restart %s:\n\n". | ||||
| "%s\n". | "%s\n". | ||||
| "(You can also use a different file if you prefer. The file ". | "(You can also use a different file if you prefer. The file ". | ||||
| "suggested above has about 50 of the most common English words.)\n\n". | "suggested above has about 50 of the most common English words.)\n\n". | ||||
| "Finally, run this command to rebuild indexes using the new ". | "Finally, run this command to rebuild indexes using the new ". | ||||
| "rules:\n\n". | "rules:\n\n". | ||||
| "%s", | "%s", | ||||
| $host_name, | |||||
| phutil_tag('tt', array(), 'my.cnf'), | phutil_tag('tt', array(), 'my.cnf'), | ||||
| phutil_tag('tt', array(), '[mysqld]'), | phutil_tag('tt', array(), '[mysqld]'), | ||||
| phutil_tag('tt', array(), 'mysqld'), | phutil_tag('tt', array(), 'mysqld'), | ||||
| phutil_tag('pre', array(), 'ft_stopword_file='.$stopword_path), | phutil_tag('pre', array(), 'ft_stopword_file='.$stopword_path), | ||||
| phutil_tag( | phutil_tag( | ||||
| 'pre', | 'pre', | ||||
| array(), | array(), | ||||
| "mysql> REPAIR TABLE {$namespace}_search.search_documentfield;")); | "mysql> REPAIR TABLE {$namespace}_search.search_documentfield;")); | ||||
| $this->newIssue('mysql.ft_stopword_file') | $this->newIssue('mysql.ft_stopword_file') | ||||
| ->setName(pht('MySQL is Using Default Stopword File')) | ->setName(pht('MySQL is Using Default Stopword File')) | ||||
| ->setSummary($summary) | ->setSummary($summary) | ||||
| ->setMessage($message) | ->setMessage($message) | ||||
| ->setDatabaseRef($ref) | |||||
| ->addMySQLConfig('ft_stopword_file'); | ->addMySQLConfig('ft_stopword_file'); | ||||
| } | } | ||||
| } | } | ||||
| $min_len = self::loadRawConfigValue('ft_min_word_len'); | $min_len = $ref->loadRawMySQLConfigValue('ft_min_word_len'); | ||||
| if ($min_len >= 4) { | if ($min_len >= 4) { | ||||
| if ($this->shouldUseMySQLSearchEngine()) { | if ($this->shouldUseMySQLSearchEngine()) { | ||||
| $namespace = PhabricatorEnv::getEnvConfig('storage.default-namespace'); | $namespace = PhabricatorEnv::getEnvConfig('storage.default-namespace'); | ||||
| $summary = pht( | $summary = pht( | ||||
| 'MySQL is configured to only index words with at least %d '. | 'MySQL is configured (on host "%s") to only index words with at '. | ||||
| 'characters.', | 'least %d characters.', | ||||
| $host_name, | |||||
| $min_len); | $min_len); | ||||
| $message = pht( | $message = pht( | ||||
| "Your MySQL instance is configured to use the default minimum word ". | "Database host \"%s\" is configured to use the default minimum word ". | ||||
| "length when building search indexes, which is 4. This means words ". | "length when building search indexes, which is 4. This means words ". | ||||
| "which are only 3 characters long will not be indexed and can not ". | "which are only 3 characters long will not be indexed and can not ". | ||||
| "be searched for.\n\n". | "be searched for.\n\n". | ||||
| "For example, you will not be able to find search results for words ". | "For example, you will not be able to find search results for words ". | ||||
| "like 'SMS', 'web', or 'DOS'.\n\n". | "like 'SMS', 'web', or 'DOS'.\n\n". | ||||
| "You can change this setting to 3 to allow these words to be ". | "You can change this setting to 3 to allow these words to be ". | ||||
| "indexed. Alternatively, you can ignore this warning if you are ". | "indexed. Alternatively, you can ignore this warning if you are ". | ||||
| "not concerned about searching for 3-letter words. If you later ". | "not concerned about searching for 3-letter words. If you later ". | ||||
| "plan to configure ElasticSearch, you can also ignore this warning: ". | "plan to configure ElasticSearch, you can also ignore this warning: ". | ||||
| "only MySQL fulltext search is affected.\n\n". | "only MySQL fulltext search is affected.\n\n". | ||||
| "To reduce the minimum word length to 3, add this to your %s file ". | "To reduce the minimum word length to 3, add this to your %s file ". | ||||
| "(in the %s section) and then restart %s:\n\n". | "(in the %s section) and then restart %s:\n\n". | ||||
| "%s\n". | "%s\n". | ||||
| "Finally, run this command to rebuild indexes using the new ". | "Finally, run this command to rebuild indexes using the new ". | ||||
| "rules:\n\n". | "rules:\n\n". | ||||
| "%s", | "%s", | ||||
| $host_name, | |||||
| phutil_tag('tt', array(), 'my.cnf'), | phutil_tag('tt', array(), 'my.cnf'), | ||||
| phutil_tag('tt', array(), '[mysqld]'), | phutil_tag('tt', array(), '[mysqld]'), | ||||
| phutil_tag('tt', array(), 'mysqld'), | phutil_tag('tt', array(), 'mysqld'), | ||||
| phutil_tag('pre', array(), 'ft_min_word_len=3'), | phutil_tag('pre', array(), 'ft_min_word_len=3'), | ||||
| phutil_tag( | phutil_tag( | ||||
| 'pre', | 'pre', | ||||
| array(), | array(), | ||||
| "mysql> REPAIR TABLE {$namespace}_search.search_documentfield;")); | "mysql> REPAIR TABLE {$namespace}_search.search_documentfield;")); | ||||
| $this->newIssue('mysql.ft_min_word_len') | $this->newIssue('mysql.ft_min_word_len') | ||||
| ->setName(pht('MySQL is Using Default Minimum Word Length')) | ->setName(pht('MySQL is Using Default Minimum Word Length')) | ||||
| ->setSummary($summary) | ->setSummary($summary) | ||||
| ->setMessage($message) | ->setMessage($message) | ||||
| ->setDatabaseRef($ref) | |||||
| ->addMySQLConfig('ft_min_word_len'); | ->addMySQLConfig('ft_min_word_len'); | ||||
| } | } | ||||
| } | } | ||||
| $bool_syntax = self::loadRawConfigValue('ft_boolean_syntax'); | $bool_syntax = $ref->loadRawMySQLConfigValue('ft_boolean_syntax'); | ||||
| if ($bool_syntax != ' |-><()~*:""&^') { | if ($bool_syntax != ' |-><()~*:""&^') { | ||||
| if ($this->shouldUseMySQLSearchEngine()) { | if ($this->shouldUseMySQLSearchEngine()) { | ||||
| $summary = pht( | $summary = pht( | ||||
| 'MySQL is configured to search on fulltext indexes using "OR" by '. | 'MySQL (on host "%s") is configured to search on fulltext indexes '. | ||||
| 'default. Using "AND" is usually the desired behaviour.'); | 'using "OR" by default. Using "AND" is usually the desired '. | ||||
| 'behaviour.', | |||||
| $host_name); | |||||
| $message = pht( | $message = pht( | ||||
| "Your MySQL instance is configured to use the default Boolean ". | "Database host \"%s\" is configured to use the default Boolean ". | ||||
| "search syntax when using fulltext indexes. This means searching ". | "search syntax when using fulltext indexes. This means searching ". | ||||
| "for 'search words' will yield the query 'search OR words' ". | "for 'search words' will yield the query 'search OR words' ". | ||||
| "instead of the desired 'search AND words'.\n\n". | "instead of the desired 'search AND words'.\n\n". | ||||
| "This might produce unexpected search results. \n\n". | "This might produce unexpected search results. \n\n". | ||||
| "You can change this setting to a more sensible default. ". | "You can change this setting to a more sensible default. ". | ||||
| "Alternatively, you can ignore this warning if ". | "Alternatively, you can ignore this warning if ". | ||||
| "using 'OR' is the desired behaviour. If you later plan ". | "using 'OR' is the desired behaviour. If you later plan ". | ||||
| "to configure ElasticSearch, you can also ignore this warning: ". | "to configure ElasticSearch, you can also ignore this warning: ". | ||||
| "only MySQL fulltext search is affected.\n\n". | "only MySQL fulltext search is affected.\n\n". | ||||
| "To change this setting, add this to your %s file ". | "To change this setting, add this to your %s file ". | ||||
| "(in the %s section) and then restart %s:\n\n". | "(in the %s section) and then restart %s:\n\n". | ||||
| "%s\n", | "%s\n", | ||||
| $host_name, | |||||
| phutil_tag('tt', array(), 'my.cnf'), | phutil_tag('tt', array(), 'my.cnf'), | ||||
| phutil_tag('tt', array(), '[mysqld]'), | phutil_tag('tt', array(), '[mysqld]'), | ||||
| phutil_tag('tt', array(), 'mysqld'), | phutil_tag('tt', array(), 'mysqld'), | ||||
| phutil_tag('pre', array(), 'ft_boolean_syntax=\' |-><()~*:""&^\'')); | phutil_tag('pre', array(), 'ft_boolean_syntax=\' |-><()~*:""&^\'')); | ||||
| $this->newIssue('mysql.ft_boolean_syntax') | $this->newIssue('mysql.ft_boolean_syntax') | ||||
| ->setName(pht('MySQL is Using the Default Boolean Syntax')) | ->setName(pht('MySQL is Using the Default Boolean Syntax')) | ||||
| ->setSummary($summary) | ->setSummary($summary) | ||||
| ->setMessage($message) | ->setMessage($message) | ||||
| ->setDatabaseRef($ref) | |||||
| ->addMySQLConfig('ft_boolean_syntax'); | ->addMySQLConfig('ft_boolean_syntax'); | ||||
| } | } | ||||
| } | } | ||||
| $innodb_pool = self::loadRawConfigValue('innodb_buffer_pool_size'); | $innodb_pool = $ref->loadRawMySQLConfigValue('innodb_buffer_pool_size'); | ||||
| $innodb_bytes = phutil_parse_bytes($innodb_pool); | $innodb_bytes = phutil_parse_bytes($innodb_pool); | ||||
| $innodb_readable = phutil_format_bytes($innodb_bytes); | $innodb_readable = phutil_format_bytes($innodb_bytes); | ||||
| // This is arbitrary and just trying to detect values that the user | // This is arbitrary and just trying to detect values that the user | ||||
| // probably didn't set themselves. The Mac OS X default is 128MB and | // probably didn't set themselves. The Mac OS X default is 128MB and | ||||
| // 40% of an AWS EC2 Micro instance is 245MB, so keeping it somewhere | // 40% of an AWS EC2 Micro instance is 245MB, so keeping it somewhere | ||||
| // between those two values seems like a reasonable approximation. | // between those two values seems like a reasonable approximation. | ||||
| $minimum_readable = '225MB'; | $minimum_readable = '225MB'; | ||||
| $minimum_bytes = phutil_parse_bytes($minimum_readable); | $minimum_bytes = phutil_parse_bytes($minimum_readable); | ||||
| if ($innodb_bytes < $minimum_bytes) { | if ($innodb_bytes < $minimum_bytes) { | ||||
| $summary = pht( | $summary = pht( | ||||
| 'MySQL is configured with a very small innodb_buffer_pool_size, '. | 'MySQL (on host "%s") is configured with a very small '. | ||||
| 'which may impact performance.'); | 'innodb_buffer_pool_size, which may impact performance.', | ||||
| $host_name); | |||||
| $message = pht( | $message = pht( | ||||
| "Your MySQL instance is configured with a very small %s (%s). ". | "Database host \"%s\" is configured with a very small %s (%s). ". | ||||
| "This may cause poor database performance and lock exhaustion.\n\n". | "This may cause poor database performance and lock exhaustion.\n\n". | ||||
| "There are no hard-and-fast rules to setting an appropriate value, ". | "There are no hard-and-fast rules to setting an appropriate value, ". | ||||
| "but a reasonable starting point for a standard install is something ". | "but a reasonable starting point for a standard install is something ". | ||||
| "like 40%% of the total memory on the machine. For example, if you ". | "like 40%% of the total memory on the machine. For example, if you ". | ||||
| "have 4GB of RAM on the machine you have installed Phabricator on, ". | "have 4GB of RAM on the machine you have installed Phabricator on, ". | ||||
| "you might set this value to %s.\n\n". | "you might set this value to %s.\n\n". | ||||
| "You can read more about this option in the MySQL documentation to ". | "You can read more about this option in the MySQL documentation to ". | ||||
| "help you make a decision about how to configure it for your use ". | "help you make a decision about how to configure it for your use ". | ||||
| "case. There are no concerns specific to Phabricator which make it ". | "case. There are no concerns specific to Phabricator which make it ". | ||||
| "different from normal workloads with respect to this setting.\n\n". | "different from normal workloads with respect to this setting.\n\n". | ||||
| "To adjust the setting, add something like this to your %s file (in ". | "To adjust the setting, add something like this to your %s file (in ". | ||||
| "the %s section), replacing %s with an appropriate value for your ". | "the %s section), replacing %s with an appropriate value for your ". | ||||
| "host and use case. Then restart %s:\n\n". | "host and use case. Then restart %s:\n\n". | ||||
| "%s\n". | "%s\n". | ||||
| "If you're satisfied with the current setting, you can safely ". | "If you're satisfied with the current setting, you can safely ". | ||||
| "ignore this setup warning.", | "ignore this setup warning.", | ||||
| $host_name, | |||||
| phutil_tag('tt', array(), 'innodb_buffer_pool_size'), | phutil_tag('tt', array(), 'innodb_buffer_pool_size'), | ||||
| phutil_tag('tt', array(), $innodb_readable), | phutil_tag('tt', array(), $innodb_readable), | ||||
| phutil_tag('tt', array(), '1600M'), | phutil_tag('tt', array(), '1600M'), | ||||
| phutil_tag('tt', array(), 'my.cnf'), | phutil_tag('tt', array(), 'my.cnf'), | ||||
| phutil_tag('tt', array(), '[mysqld]'), | phutil_tag('tt', array(), '[mysqld]'), | ||||
| phutil_tag('tt', array(), '1600M'), | phutil_tag('tt', array(), '1600M'), | ||||
| phutil_tag('tt', array(), 'mysqld'), | phutil_tag('tt', array(), 'mysqld'), | ||||
| phutil_tag('pre', array(), 'innodb_buffer_pool_size=1600M')); | phutil_tag('pre', array(), 'innodb_buffer_pool_size=1600M')); | ||||
| $this->newIssue('mysql.innodb_buffer_pool_size') | $this->newIssue('mysql.innodb_buffer_pool_size') | ||||
| ->setName(pht('MySQL May Run Slowly')) | ->setName(pht('MySQL May Run Slowly')) | ||||
| ->setSummary($summary) | ->setSummary($summary) | ||||
| ->setMessage($message) | ->setMessage($message) | ||||
| ->setDatabaseRef($ref) | |||||
| ->addMySQLConfig('innodb_buffer_pool_size'); | ->addMySQLConfig('innodb_buffer_pool_size'); | ||||
| } | } | ||||
| $conn_w = id(new PhabricatorUser())->establishConnection('w'); | $conn = $ref->newManagementConnection(); | ||||
| $ok = PhabricatorStorageManagementAPI::isCharacterSetAvailableOnConnection( | $ok = PhabricatorStorageManagementAPI::isCharacterSetAvailableOnConnection( | ||||
| 'utf8mb4', | 'utf8mb4', | ||||
| $conn_w); | $conn); | ||||
| if (!$ok) { | if (!$ok) { | ||||
| $summary = pht( | $summary = pht( | ||||
| 'You are using an old version of MySQL, and should upgrade.'); | 'You are using an old version of MySQL (on host "%s"), and should '. | ||||
| 'upgrade.', | |||||
| $host_name); | |||||
| $message = pht( | $message = pht( | ||||
| 'You are using an old version of MySQL which has poor unicode '. | 'You are using an old version of MySQL (on host "%s") which has poor '. | ||||
| 'support (it does not support the "utf8mb4" collation set). You will '. | 'unicode support (it does not support the "utf8mb4" collation set). '. | ||||
| 'encounter limitations when working with some unicode data.'. | 'You will encounter limitations when working with some unicode data.'. | ||||
| "\n\n". | "\n\n". | ||||
| 'We strongly recommend you upgrade to MySQL 5.5 or newer.'); | 'We strongly recommend you upgrade to MySQL 5.5 or newer.', | ||||
| $host_name); | |||||
| $this->newIssue('mysql.utf8mb4') | $this->newIssue('mysql.utf8mb4') | ||||
| ->setName(pht('Old MySQL Version')) | ->setName(pht('Old MySQL Version')) | ||||
| ->setSummary($summary) | ->setSummary($summary) | ||||
| ->setDatabaseRef($ref) | |||||
| ->setMessage($message); | ->setMessage($message); | ||||
| } | } | ||||
| $info = queryfx_one( | $info = queryfx_one( | ||||
| $conn_w, | $conn, | ||||
| 'SELECT UNIX_TIMESTAMP() epoch'); | 'SELECT UNIX_TIMESTAMP() epoch'); | ||||
| $epoch = (int)$info['epoch']; | $epoch = (int)$info['epoch']; | ||||
| $local = PhabricatorTime::getNow(); | $local = PhabricatorTime::getNow(); | ||||
| $delta = (int)abs($local - $epoch); | $delta = (int)abs($local - $epoch); | ||||
| if ($delta > 60) { | if ($delta > 60) { | ||||
| $this->newIssue('mysql.clock') | $this->newIssue('mysql.clock') | ||||
| ->setName(pht('Major Web/Database Clock Skew')) | ->setName(pht('Major Web/Database Clock Skew')) | ||||
| ->setSummary( | ->setSummary( | ||||
| pht( | pht( | ||||
| 'This host is set to a very different time than the database.')) | 'This web host ("%s") is set to a very different time than a '. | ||||
| 'database host "%s".', | |||||
| php_uname('n'), | |||||
| $host_name)) | |||||
| ->setMessage( | ->setMessage( | ||||
| pht( | pht( | ||||
| 'The database host and this host ("%s") disagree on the current '. | 'A database host ("%s") and this web host ("%s") disagree on the '. | ||||
| 'time by more than 60 seconds (absolute skew is %s seconds). '. | 'current time by more than 60 seconds (absolute skew is %s '. | ||||
| 'Check that the current time is set correctly everywhere.', | 'seconds). Check that the current time is set correctly '. | ||||
| 'everywhere.', | |||||
| $host_name, | |||||
| php_uname('n'), | php_uname('n'), | ||||
| new PhutilNumber($delta))); | new PhutilNumber($delta))); | ||||
| } | } | ||||
| } | } | ||||
| protected function shouldUseMySQLSearchEngine() { | protected function shouldUseMySQLSearchEngine() { | ||||
| $search_engine = PhabricatorFulltextStorageEngine::loadEngine(); | $search_engine = PhabricatorFulltextStorageEngine::loadEngine(); | ||||
| return ($search_engine instanceof PhabricatorMySQLFulltextStorageEngine); | return ($search_engine instanceof PhabricatorMySQLFulltextStorageEngine); | ||||
| } | } | ||||
| } | } | ||||