metaclass: (Default)
[personal profile] metaclass
В связи с тем, что я таки частично реализовал свою давнишнюю идею генерить опердень из модели опердени, хранящейся в этой же опердени, очередной раз решил поразмыслить над вопросом "как же должна выглядеть миграция данных при изменении модели".

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

Но предположим, что мы сделали генератор опердени и дали его специально обученным настройщикам клепать предметную область. При этом они могут менять описания таблиц и полей (как сделано на данный момент) или структуры хранящихся объектов (как хотелось бы сделать будущем) и это, само собой, должно как-то в итоге отобразится на рабочей базе.
Первая мысль - что их изменения в рабочую базу попадают только в виде сгруппированного набора непротиворечивых действий, т.е. вперемежку изменений данных и метаданных таким образом чтобы база не оказалась в состоянии типа "в not null полях сидят null". Некоторые базы такое не дают делать, у некоторых при DDL операциях такое возможно, потом бэкап черезжопными методами восстанавливать придется.
Второе - что их работа над моделью должна вестись под системой контроля версий. Но работа ведется, во-первых, не над текстом модели, а в специализированном GUI (который может отражать состояние некоего текста, а может и не отражать - сейчас модель хранится в специально заточенной базе Firebird), а во вторых, _текстовый_ контроль версий не отражает происходящего с моделью в, условно говоря, строго типизированном виде.
Т.е. например "добавить поле в таблицу" с точки зрения системы типов выглядит как модификация описания этого типа методом "добавить в список полей еще один элемент", а с точки зрения какого-нибудь меркуриала - это просто появление новой строки где-то в середине файла модели и ему пофиг на смысл этой строки. Конечно, из hg diff можно было бы сделать описание операции в типизированном виде, но это, по моему убеждению, есть удаление гланд через задницу, т.к. текст должен генерироваться из строго типизированных данных, а не оные данные должны доставаться методом парсинга текста хз какого формата.

На данный момент у меня рабочая идея примерно такая: закрыть бухгалтерам доступ к базе опердени, написать по емылу им, что их софт больше не обслуживается, выключить телефон, завтра вместо работы пойти купить зеркалку и поехать фотографировать уток на речку к базе данных модели добавляется лог всех произведенных в ней изменений в отдельные таблицы со сквозной нумерацией. Этот номер в логе и будет текущей "ревизией" модели. При каждой генерации и деплойменте в результатирующий скрипт деплоймента вставляется номер ревизии. И в состав скрипта деплоймента будут входить все изменения от начала (ну или если мы точно знаем что база какой-то версии - от этой версии) до текущей ревизии.

Сейчас у меня кодогенерация происходит по текущей модели, т.е. условно говоря, это "создать базу с нуля до текущего состояния". А реально нужно другое, более сложное: нужно накатить на существующую базу изменения "от прошлой кодогенерации до текущей". Причем накатить непротиворечивым образом. И вместо кодогенератора который делает из модели просто скрипты создания всех нужных объектов базы данных и описание GUI к ним, нужно каким-то образом из списка изменений сделать скрипты изменения схемы базы данных и обновления данных в базе (я думаю, не всегда тривиальных).
И в таком случае вылазят всякие хитрые заморочки, в основном связанные с зависимостями объектов на уровне метаданных и зависимостями на уровне данных - например, в Firebird грохнуть объект, на который ссылается другой объект просто невозможно - нужно сначала грохнуть зависимый(а если от него еще что-нибудь зависит - то по цепочке, или же сделать объект с пустым телом). На уровне данных - это всякие внешние ключи, которые не дадут изменить данные если их создать слишком рано (поэтому всякие утилиты импорта данных сначала всасывают данные а потом создают внешние ключи).

С теоретической точки зрения это все выглядит как список функций "МодельN+1=F(N)(МодельN)", потом функция, которая из функций преобразования модели генерит скрипты преобразования базы данных, возможно, анализируя список изменений на предмет "сгрупировать изменения в один скрипт", и функций преобразования "старая версия объекта"->"новая версия объекта" и анализа "можно это сделать средствами SQL(alter table) или придется долбится курсорами по всей базе до посинения".

Кстати, я до сих пор не очень понимаю, что в таких случаях предлагаю всякие NoSQL базы. Я так понимаю, там же ложится/достается чисто объект по ключу в хранилище. А если мы поменяем описание объекта в коде программы, нужно ж будет описывать явно "что делаем при загрузке старой версии объекта". О ад-хок запросах к таким вещам даже и думать не хочется.

Date: 2010-04-07 12:40 am (UTC)
wizzard: (Default)
From: [personal profile] wizzard
ооо, какие интересные темы пошли.

интересно как штуки "выделить емейл из одной таблички и имя из другой в отдельную табличку "люди" и создать форейн кеи в ту табличку" изображаются.

Date: 2010-04-07 06:36 am (UTC)
From: [identity profile] metaclass.livejournal.com
Вот именно, меня эта тема тоже напрягает.
Никаких DSL изобретать для этой цели не хочется - проще всего написать простейшую функцию, которая пройдется по таблицам базы и вытащит нужные данные. Типа "создать таблицу c полями, затем сложить в нее всех людей, затем добавить внешние ключи, а старые поля грохнуть".
Ключевой аспект - что добавление внешнего ключа переводит модель в несамосогласованный вид и не дает применять изменение к базе до тех пор, пока не будет написана функция, которая вернет такую базу в согласованный вид.

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

Date: 2010-04-07 06:13 pm (UTC)
From: [identity profile] zamotivator.livejournal.com
Почему бы не расширить DDL - ввести туда типы данных email, телефонный номер?

Date: 2010-04-07 07:02 pm (UTC)
From: [identity profile] metaclass.livejournal.com
DDL расширять не нужно, нормальные СУБД позволяют собственные типы определять. А которые не позволяют - там можно обходными маневрами, т.е. в модели определены типы, а в базе поля создаются из базовых типов.

Date: 2010-04-07 01:36 am (UTC)
From: [identity profile] norguhtar.livejournal.com
Ну как бе есть такое решение когда у нас есть три связаных сущности:
1. тип объекта
2. его атрибуты
3. его значения
ну и связывающие таблицы. Но это довольно муторно и работает на больших объемах данных стремно.

Date: 2010-04-07 06:37 am (UTC)
From: [identity profile] metaclass.livejournal.com
Ага, посмотрев на работу такого, я в общем случае отказался от этого решения - только для мелких справочников или же объектов, у которых структуру меняет пользователь сам и часто, использую. И ссылочную целостность в таком варианте не сделаешь.

Date: 2010-04-07 04:24 am (UTC)
From: [identity profile] thedeemon.livejournal.com
>С теоретической точки зрения это все выглядит как список функций "МодельN+1=F(N)(МодельN)", потом функция, которая из функций преобразования модели генерит скрипты преобразования базы данных, возможно, анализируя список изменений на предмет "сгрупировать изменения в один скрипт", и функций преобразования "старая версия объекта"->"новая версия объекта" и анализа "можно это сделать средствами SQL(alter table) или придется долбится курсорами по всей базе до посинения".

Почитай как сделаны migrations в ruby on rails, там как раз оно самое, даже в две стороны, насколько я помню.

Date: 2010-04-07 06:17 am (UTC)
From: [identity profile] lionet.livejournal.com
Там, к сожалению, миграции прописываются практически руками. Речь не идёт о том, чтобы по двум моделям автоматом построить up/down migration path.

Date: 2010-04-07 06:59 am (UTC)
From: [identity profile] metaclass.livejournal.com
По двум моделям, кстати есть методы построения (во всяком случае есть diff для баз данных с накатыванием изменений). Но это какой-то пост-фактум метод, я бы предпочел, чтобы изменения накатывались по логу изменений в модели.
Кстати, еще ж нужно предусмотреть откаты ошибочных изменений в модели(полное удаление changeset, чтобы оно в базу не попало) и бранчи, на случай если над моделью работает несколько человек одновременно

Date: 2010-04-07 07:14 am (UTC)
From: [identity profile] thedeemon.livejournal.com
Ну, там именно функции "МодельN+1=F(N)(МодельN)", как ребе и хочет. Да, надо писать руками, зато полный контроль и гибкость - автоматический обновитель, понимающий все детали и тонкости, может быть построить сложнее, чем ИИ с копией мозга ребе metaclass'a.

Date: 2010-04-07 06:00 am (UTC)
From: [identity profile] denisioru.livejournal.com
А если мы поменяем описание объекта в коде программы, нужно ж будет описывать явно "что делаем при загрузке старой версии объекта"

^^^^^^^^^^^^
Некоторые делают проще - пишут конвертер БД с релиза 1.1 до 1.2. В результате видел набор примерно такой:
converter1_2.exe
converter2_3.exe
converter3_4.exe
converter4_5.exe
и на выходе теоретически должна была получиться версия 1.5

Правда сопряжено сие с выводом БД в оффлайн на определенное время.

Date: 2010-04-07 06:03 am (UTC)
From: [identity profile] lionet.livejournal.com
Иными словами, в базе хранится только лог изменений, а непротиворечивая модель, если она есть, появляется только в памяти машины, которая хочет проверить консистентность этой модели. То есть, мы снапшоты не храним (разве что, только в целях оптимизации), мы их вычисляем всегда при наличии необходимости.

И да, алгебра преобразований над моделью должна позволять безмозглое транслирование в SQL's ALTER TABLEs.

Date: 2010-04-07 06:56 am (UTC)
From: [identity profile] metaclass.livejournal.com
Для варианта "только лог изменений" нужно немного другая база данных, нежели сейчас - сбивать снапшот каждый раз для показа в gui в sql не очень удобно. Хотя с учетом того, что модель генерируется из описания в самой себе - предположительно будет меньше проблем с переходом от "снапшоты и логи изменений" к "только лог изменений и расчет текущего состояния на основе его".

Date: 2010-04-07 07:32 am (UTC)
From: [identity profile] lionet.livejournal.com
Да, нужна другая база данных, гораздо более простая. Просто файл последовательного доступа, с операцией append ;)

И реально должно упростить всё. Сколько раз схема изменяется в год? Пусть сотню тысяч раз. Сто тысяч строк прочитать, собрать в непротиворечивый граф в голове, и провалидировать будет стоить меньше секунды.

Date: 2010-04-07 07:37 am (UTC)
From: [identity profile] metaclass.livejournal.com
Эта база данных и ее формат (текстовый, кстати) это отдельная мрачная тема, я пока с ней связываться не хочу. На Firebird я хотя бы частично сделал, а с такой штукой поначалу забрел в страшные дебри реализации - потом доделаю.

Date: 2010-04-07 06:11 am (UTC)
From: [identity profile] max-posedon.livejournal.com
Существует же 100500 DSL-ей по миграции данных и схем баз данных, может стоит их посмотреть?

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

P.S. пример из старого рабочего

class GeodatasUniqueAltAndLen < ActiveRecord::Migration
def self.up
create_table "geodatas_unique_alt_and_lens", :force => false, :row_version => false do |t|
t.column "Breite_Abs", :decimal, :precision => 10, :scale => 8
t.column "Laenge_Abs", :decimal, :precision => 10, :scale => 8
end

load_from_sqlscript("geodatas_unique_alt_and_lens", geodatas_unique_script)
end

def self.load_from_sqlscript(table_name, script)
cnx = ActiveRecord::Base.connection
cnx.insert(script)
end

def self.down
drop_table :geodatas_unique_alt_and_len
end

def self.geodatas_unique_script
<<'END_OF_DATA'
INSERT INTO geodatas_unique_alt_and_lens
("Breite_Abs", "Laenge_Abs")
select distinct "Breite_Abs", "Laenge_Abs"
from geodatas
group by "Breite_Abs","Laenge_Abs"
END_OF_DATA
end
end

Date: 2010-04-07 06:19 am (UTC)
From: [identity profile] lionet.livejournal.com
Это DSL для миграции данных, а нужно на основании двух наборов данных построить DSL для миграции между ними. Выше уровень абстракции.

Date: 2010-04-07 07:30 am (UTC)
From: [identity profile] max-posedon.livejournal.com
Э не, это у вас не тот уровень абстракции.

В Rails поля модели выводяться из миграций(!), и это правильный имхо подход. Более высокоуровневый.
А попытка вывести миграцию из разницы моделей, это уже попахивает странным.

Впрочем то, что хочет metaclass в чистом виде реализовано например у http://south.aeracode.org/docs/about.html (вывод миграции из diff-а модели и базы данных)

Date: 2010-04-07 06:19 pm (UTC)
From: [identity profile] kurilka.livejournal.com
да south вполне себе решение для небольших изменений (когда получается непротиворечивый diff), хорошее

Date: 2010-04-07 06:53 am (UTC)
From: [identity profile] metaclass.livejournal.com
Ну, такой скрипт я и на SQL напишу, еще и проще - никаких рубей и прочей постороннести не нужно.

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

Date: 2010-04-07 07:30 am (UTC)
From: [identity profile] max-posedon.livejournal.com
ответил тебе и lionet чуть выше.

Date: 2010-04-07 08:05 am (UTC)
From: [identity profile] norian.livejournal.com
а ежели генерить новую избу в сторонке и переселять тараканов по принципу :

то, что не изменилось, переселяецца автоматически
что изменилось, затачиваецца напильнегом

Date: 2010-04-07 09:01 am (UTC)
From: [identity profile] metaclass.livejournal.com
Когда больше десятка гигабайт это все становится сильно печально. Особенно в плане всяких там индексов, которые в новой базе для ускорения желательно отключать и потом пересоздавать, итд.

А еще бывает уныние когда места для второй базы такого же размера тупо нет :)

Date: 2010-04-07 10:07 am (UTC)
From: [identity profile] norian.livejournal.com
а терабайтник вместо зеркалки ? ;о))

индексы для покорёженных таблиц в любом случае придёцца пересоздать ..

Date: 2010-04-07 10:24 am (UTC)
From: [identity profile] metaclass.livejournal.com
У меня место как раз есть.
А вот у клиентов - нету. И винты покупать им запрещено :)
Как-то базу у них обслуживали, так админы начали долбится на телефон с воплям "а чего это вы так резко дисковое пространство используете"

Date: 2010-04-07 12:01 pm (UTC)
From: [identity profile] metaclass.livejournal.com
У меня места и производительности хватает, а вот у клиентов то места нет, то комп медленный, то времени мало - нужно базу в онлайн возвращать.

Date: 2010-04-07 09:56 am (UTC)
From: [identity profile] permea-kra.livejournal.com
эээ. Ребе, есть мнение, что студентам чем писать функцию конверсии, лучше взаимоотношения между узлами прописать между и внутри моделей.

Date: 2010-04-07 12:00 pm (UTC)
From: [identity profile] metaclass.livejournal.com
Вот мне так тоже кажется, но не все трансформации можно описать такими взаимоотношениями, и самое главное - функция конверсии в части случаев будет выглядеть проще, чем описание этих отношений.

Date: 2010-04-07 02:03 pm (UTC)
From: [identity profile] permea-kra.livejournal.com
Те, которые нельзя, либо описываются несколькими шагами (трансформировать в, вычислить нехватающее по констрейтам), либо не позволяют мигрировать без добивания дополнительной инфы..

Date: 2010-04-07 10:04 am (UTC)
From: [identity profile] b00ter.livejournal.com
О, я тоже в это наступил. Как разрешить - так и не разобрался, потому забил. Но вообще нужно, да.
Как вариант - отказаться от удаления (вообще любого), ввести версионность объектов и метаданных, отказаться от мержинга и соблюдать строгую очередность. Вот бы еще БД, которая метаданные с таблицами таскала и голову не морочила... Cache чтоль вспомнить?

Date: 2010-04-07 12:01 pm (UTC)
From: [identity profile] metaclass.livejournal.com
Удаление можно, но только логическое. В том смысле, как файлы в системах контроля версий удаляются - история остается.

Date: 2010-04-07 12:06 pm (UTC)
From: [identity profile] b00ter.livejournal.com
Ага, его и имел ввиду.

Date: 2010-04-07 01:53 pm (UTC)
From: [identity profile] clayrat.livejournal.com
а вот такую штуку видели: http://en.wikipedia.org/wiki/Datalog ?

Profile

metaclass: (Default)
metaclass

April 2017

S M T W T F S
      1
2345678
9101112 131415
16171819202122
23242526272829
30      

Most Popular Tags

Style Credit

Expand Cut Tags

No cut tags
Page generated Aug. 27th, 2025 10:34 am
Powered by Dreamwidth Studios