LINUX.ORG.RU

Как учитывать изменения в структуре БД в VCS?

 , ,


1

3

Подробнее о проблеме: вот я пишу какое-либо веб-приложение. У меня есть ряд текстовых файлов (.php, .js, - не важно) в каталоге, все изменения в котором отслеживаются каким-нибудь git'ом. НО процесс развития веб-приложения - это не только развитие кода, но и развитие БД.

Внимание вопрос, как отслеживать такие изменения в БД, через VCS, так, чтоб я мог откатить, клонировать и тому подобные пряники.

Предполагаемые решения: сразу спроектировать структуру БД так, чтоб потом не потребовалось учитывать это в системе контроля версий - К. О. мне уже это говорил, мне хочется большего.

Написать скрипт, который будет сохранять перед коммитом структуру БД в такой же текстовый файлик, с тем чтоб изменения в нем так же отслеживались VCS'ом. Так и планирую сделать, но сперва решил воззвать к коллективной мудрости, а ну как уже реализовано или есть более мудрое решение проблемы, о котором я не подумал.

★★★★★

ну если ты просто про структуру, то ИМХО просто можно сохранить выхлоп SHOW CREATE TABLE в текстовый файлик, и сунуть его DVCS.

drBatty ★★
()
Ответ на: комментарий от drBatty

И какой-же смысл в хранении сырого DDL в VCS? Ручками откатывать, если например фича кривая задеплоена была на application server'е? Я всё же предпочитаю автоматизировать свой труд.

resurtm ★★★
()
Ответ на: комментарий от resurtm

Phinx

Как работает, в двух словах? Я ей даю доступ к нужной БД, а оно интегрируется в гит и я забываю вообще про проблему?

Оно только за структуру или содержание БД тоже мониторит?

pihter ★★★★★
() автор топика
Ответ на: комментарий от pihter

Как работает, в двух словах?

1. Генерируешь класс миграции.
2. В методе UP описываешь всё то, что нужно добавить в базу.
3. В методе DOWN описываешь всё то, что нужно убрать, чтобы база приняла такое же состояние, что и до изменений миграцией этой.
4. Можешь довольно прозрачно откатывать/накатывать миграции.
5. Трекинг последней/активной миграции производится автоматически за тебя.

<?php

use Phinx\Migration\AbstractMigration;

class MyNewMigration extends AbstractMigration
{
    /**
     * Migrate Up.
     */
    public function up()
    {
        $table = $this->table('users');
        $table->renameColumn('bio', 'biography');
    }

    /**
     * Migrate Down.
     */
    public function down()
    {
        $table = $this->table('users');
        $table->renameColumn('biography', 'bio');
    }
}

Почитай в общем документацию. Либа ведь простейшая и маленькая.

Если тебе нужна генерация автоматических миграций, то могу сказать, что эта задача очень нетривиальная. Если такие библиотеки и есть, то они однозначно переусложнены, bloatware и в целом забиты магией.

resurtm ★★★
()
Последнее исправление: resurtm (всего исправлений: 2)
Ответ на: комментарий от Galaxy

Использовать механизм миграций Вашего фреймворка.

Судя по тому, как ТС описывает проблему, фреймворка у него нет. У него pure PHP. :-)

resurtm ★★★
()
Ответ на: комментарий от resurtm

А, и да, забыл сказать. В VCS храним только классы миграций. Никаких сырых SQL и прочего хранить не нужно.

Старые миграции менять запрещено, чтобы у коллег в другой части планеты не возникли внезапные чудесные проблемы. :-)

Удалять столбцы/таблицы/constraint'ы нужно очень аккуратно. В некоторых проектах удалять что-то вообще запрещено.

resurtm ★★★
()
Последнее исправление: resurtm (всего исправлений: 1)
Ответ на: комментарий от resurtm

Ещё: юзать модели (M в MVC) и прочие сервисные слои приложения тоже запрещается. Потому как может быть рассинхронизация между логикой приложения и базой, а при накатывании миграции в такой ситуации можно нехилого гемора на полдня огрести.

Вообще, миграции должны быть предельно простыми и по возможности выглядеть как аццкая декларативщина.

resurtm ★★★
()
Последнее исправление: resurtm (всего исправлений: 1)
Ответ на: комментарий от resurtm

И какой-же смысл в хранении сырого DDL в VCS? Ручками откатывать, если например фича кривая задеплоена была на application server'е? Я всё же предпочитаю автоматизировать свой труд.

ну если деплоят кривые изменения структуры постоянно, то да, костыль нужен. Но проще, если данный костыль использует готовую болванку в виде DDL кода. Типа

CREATE TABLE t1 (
...
);

CREATE TABLE t2(
...
);
Такую болванку нетрудно и в пустую базу засунуть без лишних телодвижений, и изменения в ней видны, да и вообще KISS (если проект простой, то можно вообще не делать миграционных костылей).

В методе UP описываешь всё то, что нужно добавить в базу.

Ага, новый класс, в нём новый метод, в нём новая функция, а в ней CREATE TABLE. Зачем такие сложности?

drBatty ★★
()
Ответ на: комментарий от resurtm

$table = $this->table('users');

$table->renameColumn('bio', 'biography');

ну и зачем придумали VCS?

Я думал для того, что-бы было так

< `bio` VARCHAR(17),
---
> `biography` VARCHAR(32),
сразу понятно, что и зачем поменялось. У вас ещё надо посмотреть renameColumn().

drBatty ★★
()
Ответ на: комментарий от resurtm

Никаких сырых SQL и прочего хранить не нужно.

предлагаете создавать таблицу с нуля, специальными методами, отдельно для каждого столбца? А зачем, если есть DDL?

drBatty ★★
()
Ответ на: комментарий от drBatty

Огромный минус работы с диффами DDL — отсутствие поддержки cross RDBMS. Для опенсорсного/коробочного софта это очень критично. Этот минус перекрывает собой остальные минусы твоего подхода.

resurtm ★★★
()
Ответ на: комментарий от drBatty

создавать таблицу с нуля, специальными методами, отдельно для каждого столбца?

Почитали бы документацию phinx. Создавать можно целые таблицы. Приведу оттуда пример (раз уж вам сложно глянуть):

<?php

use Phinx\Migration\AbstractMigration;

class MyNewMigration extends AbstractMigration
{
    /**
     * Migrate Up.
     */
    public function up()
    {
        $users = $this->table('users');
        $users->addColumn('username', 'string', array('limit' => 20))
              ->addColumn('password', 'string', array('limit' => 40))
              ->addColumn('password_salt', 'string', array('limit' => 40))
              ->addColumn('email', 'string', array('limit' => 100))
              ->addColumn('first_name', 'string', array('limit' => 30))
              ->addColumn('last_name', 'string', array('limit' => 30))
              ->addColumn('created', 'datetime')
              ->addColumn('updated', 'datetime', array('default' => null))
              ->addIndex(array('username', 'email'), array('unique' => true))
              ->save();
    }

    /**
     * Migrate Down.
     */
    public function down()
    {

    }
}
resurtm ★★★
()
Последнее исправление: resurtm (всего исправлений: 1)
Ответ на: комментарий от drBatty

Ага, новый класс, в нём новый метод, в нём новая функция, а в ней CREATE TABLE. Зачем такие сложности?

Мне кажется (повторюсь, может просто кажется), что у вас слабо развито абстрактное мышление. Классы и интерфейсы — это в 100 500 раз намного более расширяемое и надёжное решение, чем диффы DDL.

resurtm ★★★
()
Ответ на: комментарий от resurtm

1. Генерируешь класс миграции...

Нифига тут сколько телодвижений, коль так, дак действительно проще .sql-файлик хранить в каталоге, подконтрольном VCS

Если эту логику применить к обычным текстовым исходникам, то на каждый коммит нужно было бы писать кусок кода с классом и двумя функциями замены А на Б и Б на А при откате. Зачем, когда есть VCS и она сама отследит?

Я думал мне посоветуют своего рода VCS для _структуры_ БД, а так, действительно проще отзеркалить БД в текстовые файлы и управлять средствами VCS.

Это костыльно немного, но в разы менее костыльно, чем то, что ты предлагаешь.

pihter ★★★★★
() автор топика
Ответ на: комментарий от resurtm

Огромный минус работы с диффами DDL — отсутствие поддержки cross RDBMS.

есть такое.

Для опенсорсного/коробочного софта это очень критично.

Почему-же?

Почитали бы документацию phinx. Создавать можно целые таблицы. Приведу оттуда пример (раз уж вам сложно глянуть)

мне не сложно, я такое видел.

Хорошо, я пишу что-то с phinx, и мне понадобилась структура БД. Вы предлагаете парсить исходники function up()? Или я чего-то не понимаю? Как мне написать метод M(), который работает с полем C из таблицы T, и не просто так, а с учётом того, что миграция потом переименует T->T1 и C->C1?

drBatty ★★
()
Ответ на: комментарий от resurtm

Судя по тому, как ТС описывает проблему, фреймворка у него нет. У него pure PHP. :-)

Угу, развлекаюсь :)

pihter ★★★★★
() автор топика
Ответ на: комментарий от resurtm

Удалять столбцы/таблицы/constraint'ы нужно очень аккуратно. В некоторых проектах удалять что-то вообще запрещено.

Нафиг оно тогда вообще нужно? А то что таким макаром структура базы разрастется и будет изобиловать атавизмами?

pihter ★★★★★
() автор топика
Ответ на: комментарий от resurtm

Ещё: юзать модели (M в MVC) и прочие сервисные слои приложения тоже запрещается. Потому как может быть рассинхронизация между логикой приложения и базой

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

pihter ★★★★★
() автор топика
Ответ на: комментарий от pihter

Это костыльно немного

что тут костыльного? VCS она для хранения ИСХОДНИКОВ. DDL это вообще-то тоже Language, т.е. исходник.

drBatty ★★
()
Ответ на: комментарий от drBatty

Такую болванку нетрудно и в пустую базу засунуть без лишних телодвижений, и изменения в ней видны, да и вообще KISS (если проект простой, то можно вообще не делать миграционных костылей).

Согласен. Я реквестирую автоматический костыль по описанному тобой принципу :)

pihter ★★★★★
() автор топика
Ответ на: комментарий от pihter

Нафиг оно тогда вообще нужно? А то что таким макаром структура базы разрастется и будет изобиловать атавизмами?

не только она, ещё и исходники будут содержать ВСЕ версии БД. Вспомни:

$table->renameColumn('bio', 'biography');

Фактически resurtm предлагает нам засунуть в каждый проект свою велосипедную VCS, причём такую, которая поэтапно создаёт всю БД за ВСЕ моменты её существования.

ИМХО бред какой-то.

drBatty ★★
()
Ответ на: комментарий от pihter

реквестирую автоматический костыль

ну типа

echo "SHOW TABLES" |\
	mysql -u"$DB_USER" -p"$DB_PASSWORD" -h"$DB_HOST" "$DB_BASE" |\
		sed '1d' |\
		while read TABLE; do
			echo "SHOW CREATE TABLE $TABLE" |\
				mysql -u"$DB_USER" -p"$DB_PASSWORD" -h"$DB_HOST" "$DB_BASE" |\
				sed 's/\\n/\n/g'
			echo
		done
Конечно проект не должен сам создавать/менять таблицы в этой базе. ИМХО к «своим» таблицам можно добавлять префикс(AFAIK оно часто и так добавляется IRL), или даже сделать для них отдельную СУБД. ИМХО это оправдано для слишком уж сложных проектов.

drBatty ★★
()
Ответ на: комментарий от pihter

Это костыльно немного, но в разы менее костыльно, чем то, что ты предлагаешь.

Тысячи фреймворков на Perl, PHP, Java, Python, Ruby ошибаются и предлагают кривые методы решения задачи. Да, конечно и несомненно! :-)

Собственно, я не поленюсь и выложу вот это, чтобы ты не сомневался в том, что ты заблуждаешься:

1. Ruby on Rails: http://guides.rubyonrails.org/migrations.html#using-the-up-down-methods
2. Django South, Python: http://south.readthedocs.org/en/latest/migrationstructure.html#migration-stru...
3. PHP, Symfony2: http://symfony.com/doc/current/bundles/DoctrineMigrationsBundle/index.html
4. PHP, Yii: http://www.yiiframework.com/doc/guide/1.1/en/database.migration#creating-migr...
5. Java, Flyway: http://flywaydb.org/documentation/migration/java.html

resurtm ★★★
()

Про миграции уже сказали.
Если ты (ну, вдруг) пишешь строго для MySQL, то можешь заюзать MySQL Workbench - наколбась в нем структуру, можно даже задать начальные данные, если у тебя проект еще не в эксплуатации, и коммить в отдельный каталог
1) сам проект воркбенча;
2) полный скрипт создания базы;
3) скрипт с ALTERами относительно существующей структуры;
4) - опционально - исходное наполнение базы
П. 1 понятно для чего, п.2 дает тебе возможность повесить на хук пересоздание базы при откате, п.3 дает тебе файлик, который можно читать в дополнение к diff'ам «полных» sql'ных скриптов.
Но в продакшене, понятное дело, так не развернешься.

thesis ★★★★★
()
Ответ на: комментарий от drBatty

Для опенсорсного/коробочного софта это очень критично.

Почему-же?

Ты писал когда-нибудь middleware, который нужно поддерживать годами? Нельзя ломать обратную совместимость, нельзя вообще ничего ломать.

Предположим мы пишем такой крупный проект, который поддерживает Oracle, PgSQL, MySQL и SQLite3. У нас есть 1000 клиентов по всему миру: 250 юзает наш продукт с Oracle, 250 с PgSQL, 250 с MySQL, 250 с SQLite3.

Я понимаю, что ты сейчас предложишь писать миграционные SQL для каждого типа СУБД (т.е. на каждое изменение в БД у нас будет 4 файла), но ты не понимаешь, что эту задачу можно намного упростить: можно писать только один класс миграции и сразу под все типы БД.

Хинт в том, что этот класс будет использовать методы абстрактного интерфейса для доступа к БД из нашего фреймворка/проекта/платформы. Дальше пояснять? :-)

resurtm ★★★
()
Последнее исправление: resurtm (всего исправлений: 1)
Ответ на: комментарий от resurtm

Тысячи фреймворков на Perl, PHP, Java, Python, Ruby ошибаются

миллионы мух тоже ошибаются.

Собственно, я не поленюсь и выложу вот это

а ты можешь бесконечно продолжать список велосипедов. Я в курсе, что этот велосипед в каждом фреймворке свой лучший. Вот только, почему нельзя задействовать имеющиеся средства? У тебя же УЖЕ есть VCS, зачем свою делать?

drBatty ★★
()
Ответ на: комментарий от resurtm

А вот такой вопрос.
Если есть VCS, наполненная файлами миграций. Как известно,

5. Трекинг последней/активной миграции производится автоматически за тебя.

А в идеале хочется некий pre-rollback скрипт, позволяющий откатить из СУБД все, напиханное туда после некоторой ревизии, а не смешанный контроль версий (сырцы отдельно, бд - отдельно).
Вот как бы такого достичь?

thesis ★★★★★
()
Ответ на: комментарий от resurtm

Хинт в том, что этот класс будет использовать методы абстрактного интерфейса для доступа к БД из нашего фреймворка/проекта/платформы. Дальше пояснять?

Мне непонятно, зачем такие сложности, когда СУБД всего ЧЕТЫРЕ? А вот ревизий кода DDL Over9000(и каждая ревизия DDL ведёт добавление хотя-бы одного лишнего метода).

Нельзя ломать обратную совместимость, нельзя вообще ничего ломать.

вот это и есть основной минус. Ничего в DDL ломать нельзя, за то несложно добавить поддержку пятой СУБД. А вот на практике надо как раз наоборот — добавить какое-нить лишнее поле в БД.

ИМХО — типичная ошибка _проектирования_.

drBatty ★★
()
Ответ на: комментарий от pihter

cross RDBMS

кто все эти буквы?

ну я это так понял, что два приложения сразу юзают одну СУБД. Например сайтег с форумом и с галереей фоток. Причём и то и то лежит в одной БД. Причём и то и то можно в любой момент выкинуть. Но форум всё равно не может показывать фотки из галереи, ибо и то и другое постоянно само по себе меняется. (и да, галерею и форум делают разные люди и в разное время).

drBatty ★★
()
Ответ на: комментарий от vertexua

flyway или аналоги

Flyway, в отличие, например, от системы миграции в Rails, не поддерживает откат изменений. Авторы библиотеки мотивируют это тем, что после внесения деструктивных и необратимых изменений выполнить откат состояния базы так, чтобы все пропавшие или изменившиеся данные восстановились к прежнему состоянию, в общем случае невозможно. Вместо этого предлагается вполне разумный подход использования механизмов резервирования. Например перед применением очередной миграции можно делать выгрузку дампа или снимок базы (в зависимости от имеющегося в конкретной СУБД функционала резервирования).

Ерунда какая-то... если я буду делать дамп, нафиг мне это далось все?

pihter ★★★★★
() автор топика
Ответ на: комментарий от drBatty

что тут костыльного?

А то, что БД сама собой .sql исходник не кладет в мой каталог с другими исходниками. Т. е. мне нужно либо ручками дампить данные/структуру в этот каталог, либо накатать скриптик и вызывать сие действие перед коммитом. и восстанавливать данные/структуру из дампа в БД после отката, нужно тоже ручками/скриптиком, но само-собой, средствами VCS это не делается (хотя, имхо, нужно бы), потому и костыльно немного, хотя идеологически, ящитаю, правильно

pihter ★★★★★
() автор топика
Ответ на: комментарий от maxcom

Liquibase

Во, это, похоже, то, что нужно. Оно автоматом умеет или нужно ручками чейндсеты писать?

pihter ★★★★★
() автор топика
Ответ на: комментарий от pihter

А то, что БД сама собой .sql исходник не кладет в мой каталог с другими исходниками.

а это уже проблема БД. Они by design не умеют отделять код от данных. Это как с HTML, в котором нужно применять костыли-шаблоны. Сам по себе HTML представляет из себя данные и код вперемешку (всякие css и даже <table> тоже код). Вот если-бы можно было-бы сделать отдельно дамп структуры, а отдельно — данных, тогда-бы другое дело. А у нас — «гибкость».

ручками/скриптиком, но само-собой, средствами VCS это не делается (хотя, имхо, нужно бы)

неа. VCS и не должна предоставлять такой функционал. И не представляет. У тебя так бескостыльно по той простой причине, что ты исходники используешь AS IS. А вообще-то их надо сначала собрать, а потом и опубликовать. Этого VCS не делает и не должна. Откат опубликованного так вообще в принципе невозможен. В общем-то, как и сборка (если бинарники в VCS не пихать, как делают многие ынтерпрайзные разрабы).

drBatty ★★
()
Ответ на: комментарий от drBatty

ну типа

то есть скриптом выгружать структуру таблицы в каталог, подконтрольный VCS, перед каждым коммитом? и, соответственно, отправлять сей код на исполнение после отката, предварительно удалив таблицу? То, о чем я и говорил.

но _внишним скриптом_, сами по себе VCS такого не умеют? И готовых скриптов нет? самому сочинять? не так уж сложно, понимаю, просто уверен что до меня такое уже ихобрели

pihter ★★★★★
() автор топика
Ответ на: комментарий от resurtm

Тысячи фреймворков на Perl, PHP, Java, Python, Ruby ошибаются и предлагают кривые методы решения задачи. Да, конечно и несомненно! :-)

Ну тысячи - это ты загнул :) а вообще, впечатляет, конечно, но я не понимаю... то есть, по-твоему, взять SQL-код, и запихнуть его в класс с методами (взад, вперед) и засунуть все это в код получается проще, монятнее и логичнее, чем с каждой версией скрипта some_script_v21.php рядом хранить файлик table_for_some_script_v21.sql с sql-кодом создания таблицы, которая точно для этой версии скрипта, и их синхронность будет гарантироваться тем, что они внутри _одного_ коммита в VCS?

То есть, исповедуя простой подход, я не загрязняю код, я гарантированно и легко могу откатиться, я гарантированно не получу рассинхронизации версии БД и скрипта.

Тот факт, что по твоим пруфам, все делают по другому, наводит на мысли, что я чего-то не понимаю...

pihter ★★★★★
() автор топика
Ответ на: комментарий от thesis

Но в продакшене, понятное дело, так не развернешься.

Ой, у меня все страшно: проект развивается, но уже крутится в продакшене. Причем развивается серьезно и изменения не только в текстах программ, но и в структуре каталогов(ужос!) и в структуре БД. Я знаю, что я - наркоман и так делать нельзя, но, блджад, они мне дают деньги, и это заставляет меня развивать проект вот таким вот методом.

// достал щит, закрываться от летящих помидор...

pihter ★★★★★
() автор топика
Ответ на: комментарий от resurtm

Предположим мы пишем такой крупный проект, который поддерживает Oracle, PgSQL, MySQL и SQLite3. У нас есть 1000 клиентов по всему миру: 250 юзает наш продукт с Oracle, 250 с PgSQL, 250 с MySQL, 250 с SQLite3.

Если такая ботва, то конечно проще написать один класс, который будет менять БД неявно, для каждой БД нативными методами.

Но согласись, если проект на одной СУБД - то получается лишний огород?

pihter ★★★★★
() автор топика
Ответ на: комментарий от pihter

Да какие помидоры. Ты не наркоман и делать так можно, но мой простой частный вариант тебе не подходит.

thesis ★★★★★
()
Ответ на: комментарий от pihter

Действительно, если ты удалил столбик или слил два числовых столбика в их сумму, то вернуть назад нельзя, только бекапами и это не тривиально. Flyway позволяет следить за историей версий, он накатывает дельты по очереди, пропуская уже примененные ранее версии. То-есть вообщем можешь его запустить три раза и все будет нормально.

Еще он поддерживает много форматов дельт, например часть на sql, потом парочка на Java с более сложным кодом. И т.д.

Короче полностью автоматический менеджмент базы не выгорел, не тривиальная это задача. Как и автоматическая генерация DTO и подобные

vertexua ★★★★★
()
Последнее исправление: vertexua (всего исправлений: 1)
Ответ на: комментарий от drBatty

ну я это так понял, что два приложения сразу юзают одну СУБД

А... ясно. Но это лечится проектированием БД. Так чтоб они могли оба ее юзать, но чтоб изменения в одной части БД были некритичны для другой. Либо запихивать и форум и галерею в один коммит с состоянием БД. Короче, не страшно.

А подход, который предлагает resurtm как эту проблему решает? не соображу...

pihter ★★★★★
() автор топика

можно вести историю изменений в самой БД: заводишь табличку с полями текущая версия, дата изменения, команда(ы) для апдейта с предыдущей схемы базы до текущей

Harald ★★★★★
()
Ответ на: комментарий от drBatty

а это уже проблема БД

Соглашусь, тут СУБД неправа, а не VCS.

Ну пришли к тому, что костыль нужно делать для СУБД :)

pihter ★★★★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.