LINUX.ORG.RU

Есть мысли на счёт идеологии реализации версионности объектов для ORM?


0

0

Задача грубо: для ORM реализовать хранение истории версий некоторых объектов.

Задача в лоб: ввести в БД доп. поле version. Вместо поиска объекта по первичному ключу ID=<value> запрашивать WHERE ID=<value> ORDER BY version DESC LIMIT 1.

Плюс - простота реализации.

Минусы - архив малонужных версий лежит в общей куче, жрёт место и ресуры
- Тяжелеют запросы
- Теряется общая прозрачность системы.

Другой вариант: Заведение параллельной таблицы для версий. Всё то же самое, но version добавляется лишь к альтернативной таблице, куда и сваливается архив.

Плюс - нет потери производительности.

Минусы - придётся вводить новые сущности в виде архивных объектов.
- Снижается общая наглядность.
- Для каждого версионного объекта требуется лишняя таблица.

Третий вариант - одна таблица из полей типа `original_class_name`, `original_id`, `version`, `original_field`, `original_value`.

Т.е. каждое поле версионного объекта лежит в отдельной таблице общей записью.

Плюсы - можно реализовать единый интерфейс бэкапа объектов.
- Нужна только одна лишняя таблица.

Минусы - уродливая таблица
- Усложнение структуры запросов.

...

Есть ещё какие-то мысли?

Как такое во «взрослых системах» делают? :)

★★★★★

Ответ на: комментарий от Legioner

Будут проблемы с выборками массивов объектов по определённому критерию... Хотя можно ввести признак «это версия» и фильтровать по его неустановке. Не очень красиво.

Будут проблемы у объектов, которые ссылаются на данный по ID. Придётся у текущей записи держать ID постоянный, а с новым ID сохранять бэкап-версию...

...

Пока всё больше склоняюсь к варианту отдельной бэкап-таблицы и отдельного бэкап-класса для каждого версионного класса.

Т.е. что-нибудь типа поля version_engine, куда будет прописываться бэкап-класс при его наличии для данного... Тем более, что у бэкап-классов, по идее, не требуется никакого функционала кроме описания БД-мапинга. А методы извлечения/бэкапа версии можно занести в базовый класс для бэкапного.

...

Но пока ещё подожду, вдруг кто-то что-то придумает ещё :)

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

>почему тебя волнует "прозрачность системы"? ОРМ для того и нужен, чтобы скрыть базу.

Потому что система по сути микроядерная, ORM - такой же компонент системы, как и другие, может заменяться и т.п.

И в системе хочется, чтобы всё было прозрачно и красиво. Хватит итак пока некоторых древних пережитков, до которых руки не доходят :)

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

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

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

>Есть ещё идея хранить в кортеже не значения, а массивы значений, если СУБД позволяет.

Так проще воспользоваться первым вариантом :) Несколько записей, различающихся полем version делают то же самое.

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

>продолжая мысль про next_id можно хранить prev_id со всеми вытекающими ++

Остаётся некрасивость в виде дополнительного условия ко всем выборкам текущих версий (... AND prev_id = 0 ...).

Нет, похоже, всё ж, надо делать с отдельной таблицей и отдельным классом-бэкапом к текущему...

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

Доброго!

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

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

Почему так?

Потому что в СУБД есть много вещей: ссылочная целостность, транзакции, уникальные ключи.

Например, вариант с version не проходит, т.к. теряется уникальность первичного ключа. Плюс при обращении по ссылке нужно пройти по всей истории. Варианты с next_id и prev_id не годятся по той же причине - либо при обновлении нужно обновить все записи, которые ссылаются на данную, либо само разыменование сильно замедляется.

Также имеется операция удаления. Если мы храним удалённые версии в той же таблице, то нужно иметь флаг "запись удалена". Понятие ссылочной целостности, в общем-то, теряет смысл. Это можно, конечно, пытаться лечить, добавив флаг удаления в первичный ключ. Но не факт, что любая СУБД позволит менять первичный ключ, и к тому же, всё это - сильный удар по эффективности.

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

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

Ищу работу от 4$/час (особенно, на лиспе). Консультирую по применению лиспа

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

> Ищу работу от 4$/час (особенно, на лиспе). Консультирую по применению лиспа > den73 (*) (12.12.2008 12:49:48)

73 - год рождения? люди 73го года рождения знающие лисп ищут работу на лоре? похоже кризис глубже чем казался...

gods-little-toy ★★★
()
Ответ на: комментарий от den73

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

Третий вариант в перечисленных мною.

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

по теме: имхо если хоть чуть-чуть интересует производительность - параллельная таблица.

В крайнем случае - сделать table partitioning так чтоб были партишены, содержащие все данные последних версий и только их. Поверх него навернуть views с "where version=last", и тогда получим красоту:

* views дают работать с последней версией как будто никакой истории не существует

* исторические данные не перемешаны с текущими

* но их все-таки можно достать, и запросы к ним имеют похожую форму.

* единственный минус - надо либо снапшотить историю, либо update'ы получаются медленнее. но от этого и в других решениях не уйдешь.

я это, правда, теоретически излагаю. поднять такое решение еще не доводилось.

gods-little-toy ★★★
()

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

подозреваю, что взору откроются две крайности

1) историю вести легко, но обращения к предыдущим версиям нетривиальны и медленны

2) историю вести сложно и куча оверхеда, зато можно вписать во where "... and date= <sometime-in-the-past>" и узнать что этот запрос возвращал год назад

и континуум между ними...

gods-little-toy ★★★
()

1. А не проще ли взять вместо RDBMS+ORM какую-то OODB с "живыми объектами", и не городить велосипед?

2. Или подумать на тему юзанья версий в версионниках вроде PostgreSQL.

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

>А не проще ли взять вместо RDBMS+ORM какую-то OODB с "живыми объектами"

Планируется не решение одной задачи, а проработка универсального инструмента, которым можно решать задачи в жёстко заданных инструментальных рамках :)

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

> 2. Или подумать на тему юзанья версий в версионниках вроде PostgreSQL.

эмм, а как ты это конкретно себе представляешь?? Особенно когда версий накопится серьезное количество???

gods-little-toy ★★★
()
Ответ на: комментарий от KRoN73

Так не получится. Компромиссы... Везде компромиссы. Либо одно делается быстро, либо другое удобно.

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

Это я имел в виду "не получится универсального удовлетворительного решения"

den73 ★★★★★
()

> Есть ещё какие-то мысли?

Хеш-функциями воспользоваться. Например, у объекта в ООБД есть Object ID, OID. Первичный ключ. Он не обязан идти подряд как 1,2,3,.. , а может быть каким-то GUID. Который будет выдавать нужная хеш-функция. Значения OID следущих/предыдущих версий можно получать как хеш1(OID), хеш2(OID) (что мешает ввести 2 ключа OID? prev_oid,next_oid) . Где хранить сами значения -- это по вкусу. Нужно только управлять видимостью (доступностью по ссылкам) нужных OID.

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