Я уже писал о том, как работает автообновление в e2. Однако там я говорил о пользовательском experience, а не о технической реализации. Сегодня расскажу о том, как это всё работает изнутри. (Сразу скажу, что работает это весьма успешно, несмотря на то, что реализовано очень коряво.)
Стержнем всей системы является файл verlog.txt, который я тут усердно веду, вписывая в него каждое изменение. Эта страница на сайте e2 генерируется именно на базе файла verlog.txt. Помимо текстового описания обновлений в нём содержится информация о том, какие файлы изменились в данной версии (если этого не указано, значит изменился только core.php). Когда e2 обращается к серверу за информацией о доступных обновлениях, он передаёт ему свою текущую версию, а сервер отвечает куском верлога от его версии до последней доступной.
Например, вы пытаетесь обновиться с v1099; последняя доступная версия сейчас — v1109. Вот, посмотрите, что выдаёт сервер. Если хотите, можете это unserialize’нуть и print_r’нуть. Дак вот, теперь e2 знает, что качать. Сами файлы последнего дистрибутива всегда лежат по адресу e2.ilyabirman.ru/download/updates/путь-к-файлу. Если вы вдруг хотите посмотреть, как в последнем дистрибутиве выглядит design_single_note.php, то возьмите и посмотрите. Теперь e2 попытается скачать все файлы, которые изменились между вашей версией и последней доступной. Если хотя бы один файл скачать не удалось, либо скрипту осталось выполняться меньше 5 секунд, то обновление отменяется. Если же всё хорошо, то старые файлы заменяются новыми.
После этого начинается самое интересное. При генерации следующей страницы e2 видит, что его версия больше той, которая прописана в реестре (при установке и после окончания обновления движок прописывает в реестр собственную версию). Это значит, что новое ядро выполняется в старой среде. Тогда запускается служба perform_update, задача которой состоит в том, чтобы выполнить все остальные действия по обновлению e2: изменить структуру БД и реестра или, например, удалить файлы, которые больше не нужны. Но откуда она знает, что именно нужно делать?
Дело в том, что эта служба сама уже выполняется из нового core.php. А каждый core.php, так уж получилось, знает, чем он отличается от всех предыдущих. Я знаю, что это коряво, но работает — и это главное. То есть, ещё раз, в самой службе perform_update просто-напросто написан примерно вот такой код:
...
if ($from < 894) { /* Делаем одно /* }
if ($from < 932) { /* Делаем другое */ }
if ($from < 1026) { /* Делаем третье */ }
...
Поскольку такого рода изменения нужно вносить не очень часто, то эти строки не так уж сильно увеличивают размер core.php, но всё-таки увеличивают. Начиная с версии v1027 e2 больше не умеет автообновляться с версий, выпущенных до Release 1:
v1027. e2 больше не будет автообновляться с pre-release-версий (то есть, если у вас стоит e2 v850 и более ранних, то обновляться теперь придётся вручную). Это позволило выкинуть из кода около 9 килобайт мусора, который оставался только для возможности такого обновления. Просто предполагается, что это никому давно не надо.
Напомню, что система обновления в e2 работает начиная с v294. Для диапазона v294...v850 9 килобайт — это не так страшно.
После того, как все эти if’ы пройдены и все изменения сделаны, в реестр прописывается новая версия e2, поэтому следующая генерация страницы уже не приведёт к вызову службы perform_update.
Всё это очень хорошо до тех пор, пока не возникнут какие-нибудь проблемы.
Во-первых, проблемы могут возникнуть во время замены старых файлов новыми. Пользователь мог не прочитать инструкцию и забыть прописать права 777 ко всем файлам и папкам e2. Если не удалось заменить ни одного файла, то это ещё хорошо — тогда просто не получится обновиться. А что произойдёт, если удалось обновить только часть файлов? Этого никто не знает. Конечно, в этом случае всегда можно сказать: «надо было читать документацию». Это явно не самое лучшее решение. Да и вообще, это могла быть не вина пользователя, например, во время обновления отключили электричество.
Во-вторых, проблемы могут возникнуть во время работы perform_update. Например, несколько if’ов были успешно пройдены, а потом в каком-нибудь произошла ошибка. В этом случае в реестре останется прописана старая версия e2, поэтому при следующем вызове все if’ы, включая успешно пройденные, будут проходиться заново. Это почти наверняка не страшно, так как там не делается никаких действий, которые нельзя было бы сделать 2 раза подряд (например, если дважды записать в реестр одно и то же значение, хуже никому не будет, а дважды добавить в таблицу одну и ту же колонку просто не даст сама СУБД). Но это очень коряво.
В будущих версиях я думаю немного улучишить всю эту схему. Во-первых, я хочу, чтобы код для perform_update не входил в core.php, а скачивался с сервера обновлений вместе с самими новыми файлами. Потому, что в ядре ему делать нечего. Во-вторых, я хочу, чтобы e2 вёл журнал процесса обновления и, в случае, если он был прерван, мог продолжить его с того же места. В-третьх, я хочу, чтобы в ситуации, когда движок находится где-то в середине процесса обновления, все режимы кроме login и все службы кроме login и perform_update падали в STOP, а perform_update выполнялась только если пользователь залогинен.
К сожалению, когда я придумывал схему автообновления для e2, я не предусмотрел возможность обновления самого кода системы автообновления без обновления всего остального. В Windows, например, такая штука есть. Помните, когда вы заходите на Windows Update, вам сначала предлагают скачать последнюю версию самой обновлялки? e2 так не умеет, поэтому обновляться до каждой следующей версии приходится средствами предыдущей, что иногда сильно осложняет задачу.
Так что вот, учитесь на чужих ошибках, пока дают!