instantsoft / icms2

Self-hosted Site Management System
https://instantcms.ru
GNU General Public License v2.0
295 stars 120 forks source link

Сделать проверку правильности обновления. #1191

Closed Risgit closed 4 years ago

Risgit commented 4 years ago

Пострадавших от кривого обновления, судя по сообщениям на форуме, уже больше, чем от коронавируса. Может быть есть смысл сделать компонент (или добавить нужные функции в компонент update), который будет проверять обновились ли файлы и обновилась ли база данных? Простейший метод:

  1. При обновлении переписывается файл, в котором записывается время обновления и версия.
  2. Еще при обновлении добавляется строка в какую-нибудь таблицу. Файл должен копироваться на сервер самым последним. То есть должен иметь максимально последнее имя, максимально последнюю дату изменения и еще что там может быть, чтобы файл копировался на сервер самым последним. Запись тестовой строки в базу также должна происходить в самую последнюю очередь, чтобы была гарантия, что уж если эта запись в базе корректна, то остальные операции с базой тоже прошли корректно. Метод посложнее: Создается таблица, в которую при обновлении пишутся все изменения в файлах и базе. После обновления проверочный компонент проверяет время обновления нужных файлов (filemtime или еще как) и время обновления нужных таблиц (SHOW TABLE STATUS FROM или как-нибудь еще).

В случае одобрения разработчиками, готов попробовать сделать что-то этакое.

fuzegit commented 4 years ago

Да они просто файлы заменяют без установки в админке и наоборот. Копирование по ftp, если устанавливается через админку, проверяет каждую операцию, при сбое хотя бы одно файла, об этом сообщается. Так же, при выполнении инструкций самого пакета всё проверяется. Всё, что вы предлагаете лишь еще сильней усложнит.

Максимум, что можно сделать, так это сделать копирование файлов/директорий средствами php, если права доступа позволяют.

Risgit commented 4 years ago

То есть пишем в конец файла install.sql что-то такое:

DROP TABLE IF EXISTS `{#}version`;
CREATE TABLE `{#}version` (
  `version` varchar(11) DEFAULT NULL,
  `date_update` timestamp NULL DEFAULT current_timestamp()
) ;
INSERT INTO `cms_version` (`version`, `date_update`) VALUES
('2.13.2', '2020-04-09 19:31:37');

Уж если последняя команда из файла install.sql выполнена - значит и предыдущие выполнены. В пакет обновления (обязательно в папку, которая не 777, например в system, чтобы быть уверенным, что в самые строгие для записи папки все файлы скопировались) включаем файлик с записью версии и даты копирования. А потом в файле install.php (или что там исполняется в последнюю очередь при обновлении) запускаем функцию сравнения того, что в базе и того, что записано в файлике проверочном.

public function update_check(){
$version = $core->model->getRow('version')['version'];
$date_update = $core->model->getRow('version')['date_update'];
if ($version != $версия_из_файла || $date_update > filemtime($наш_файл_с_версией) + сутки){
cmsUser::addSessionMessage('Обновление прошло неудачно. Попробуйте повторить обновление в ручном режиме', 'error');
} else {
cmsUser::addSessionMessage('Обновление прошло удачно. Версия системы 2.13.2', 'success');
}

Ну и отразить в инструкции, что если человек не видел сообщения об успешности или получил сообщение об ошибке - пусть идет на переустановку обновления (или на пересадку рук).

fuzegit commented 4 years ago

Уж если последняя команда из файла install.sql выполнена - значит и предыдущие выполнены.

Так именно так и есть. Если в процессе обновления/установки возникли ошибки, то эта ошибка и показывается, а процесс прерывается. Все пакеты обновления сделаны таким образом, что если их запустить еще раз, то выполнится только то, что не выполнилось.

Ну и отразить в инструкции, что если человек не видел сообщения об успешности или получил сообщение об ошибке - пусть идет на переустановку обновления (или на пересадку рук).

Опять же, так и есть сейчас.

В пакет обновления (обязательно в папку, которая не 777, например в system, чтобы быть уверенным, что в самые строгие для записи папки все файлы скопировались) включаем файлик с записью версии и даты копирования.

При ручном обновлении это не имеет смысла. То, что вы предлагаете, проблему не решит. Более того, забывчивость устанавливать пакет или копировать файлы, вообще ничего не решит.

Я попробую снимать видео к каждому обновлению.

Risgit commented 4 years ago

Да, согласен, но есть нюанс. Реально несколько раз помогал людям обновлять движок и реально возникали проблемы. Весь процесс проходит "на ура", никаких ошибок в процессе нет. Но после оказываются необновленными файлы и необновленными таблицы. Я это видел сам! Как правило, проблема в том, что на разных диких шаред-хостингах все папки от разных юзеров, а база данных - от еще кого-то. Или дикие задержки при обновлении огромных таблиц на дохлых хостингах. Может при окончании обновления (можно даже задержку задать для гарантии) всё же производить проверку на количество файлов именно с нужной датой? Например так:

$objects = new RecursiveIteratorIterator(new RecursiveDirectoryIterator(__DIR__ ), RecursiveIteratorIterator::SELF_FIRST);
    $counter = 0;
    foreach($objects as $name => $object){

                    if (strstr($name, 'php') !== false && filemtime($name) == 1585371582){
                        $counter++;
                    }
            }

    if ($counter != 445) {cmsUser::addSessionMessage('Обновление прошло удачно. Версия системы 2.13.0', 'success');}

Ну и проверку наличия нужных полей в таблице (которые добавились при последнем обновлении).

evgip commented 4 years ago

Ну и проверку наличия нужных полей в таблице

А они не проверяются? Просто читал форум, и сложилось впечатление (со стороны), что основная проблема - поля. Как так получается, уж не знаю.

Risgit commented 4 years ago

А они не проверяются разве?

Судя по постоянным проблемам - плохо проверяются.

fuzegit commented 4 years ago

Весь процесс проходит "на ура", никаких ошибок в процессе нет. Но после оказываются необновленными файлы и необновленными таблицы.

Нет. Проблема значит была в чем-то другом.

Или дикие задержки при обновлении огромных таблиц на дохлых хостингах.

Так не бывает. Операция выполнилась - изменения доступны. Кроме разумеется случаев, когда суперсисадмины заоптимизировали кэш MySQL как-то, что в принципе маловероятно, поскольку MySQL сбрасывает кэш таблицы после изменения.

Может при окончании обновления (можно даже задержку задать для гарантии) всё же производить проверку на количество файлов именно с нужной датой?

Это лишнее. Если возникают такие проблемы, то значит включен какой-нибудь opcache. Опять же криво включен (там есть куча опций, включив которые можно заменить хоть все файлы, а работать будет условно на старых), бездумно, типа как на форуме у нас советовать любят. Вот сброс opcache надо предусмотреть при установке дополнений. Сброс кэша, если включен. Если рассматривать копирование по ftp, то опять же, если хоть один файл не скопировался https://github.com/instantsoft/icms2/blob/master/system/controllers/admin/actions/install_ftp.php#L92 всё прерывается, показывается ошибка.

Ну и проверку наличия нужных полей в таблице (которые добавились при последнем обновлении).

Т.е. вы предлагаете задублировать весь install.php пакета обновления? Вот есть файл, например, https://github.com/instantsoft/icms2/blob/master/update/previous/2.10.1-2.11.0/install.php#L18

В нём выполняются операции: если поля нет в таблице - добавляем. Всё. Окей, предположим произошла ошибка запроса по какой-то причине, идём в метод query https://github.com/instantsoft/icms2/blob/master/system/core/database.php#L312 и видим, что в случае ошибки запроса всё умрёт, а на экран выведется сама ошибка. Единственная возможность, что instal.php и install.sql не выполнится, это то, что эти файлы недоступны для чтения.

fuzegit commented 4 years ago

Судя по постоянным проблемам - плохо проверяются.

Не вопрос, давайте проверим работу метода isFieldExists https://github.com/instantsoft/icms2/blob/master/update/previous/2.10.1-2.11.0/install.php#L18 https://github.com/instantsoft/icms2/blob/master/system/core/database.php#L971

может строгая проверка in_array косячит? Так тогда бы возвращалась бы false и таблицы бы обновлялись при первом вызове.

Надо разобраться в причине на конкретном примере, а не бить напалмом. Я готов сам рассмотреть пример, где с первого раза ничего не обновилось.

Risgit commented 4 years ago

Надо разобраться в причине на конкретном примере, а не бить напалмом. Я готов сам рассмотреть пример, где с первого раза ничего не обновилось.

Посмотрел. Всё должно работать. Если честно, ума не приложу, как у людей получается неправильно обновиться. Я пытался как-то эмулировать глюки. Безуспешно. Предложение: отложить решение вопроса до следующего обновления. Там обязательно возникнут проблемы с обновлением у одной известной девушки. У неё проблемы постоянно возникают. Попрошу доступ к логам сервера, посмотрю права на папки, логи запросов mysql. Тогда можно будет проанализировать причину.

fuzegit commented 4 years ago

Там обязательно возникнут проблемы с обновлением у одной известной девушки.

отключите ей opcache если включен.