Уведомления

Группа в Telegram: @pythonsu

#1 Май 1, 2008 07:30:53

Андрей Светлов
От:
Зарегистрирован: 2007-05-15
Сообщения: 3137
Репутация: +  14  -
Профиль   Адрес электронной почты  

ORM

На новом проекте пришлось изобретать велосипед.

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

sqlaclhemy напрямую использовать нельзя.

За две недели написал свою ORM. Из алхимии безбожно драл идею (session, query, mapper&table definition, query language from sql package). Unit of work взят с минимальными изменениями. topologic sort - без изменений.
В результате - есть sessions, уверенные тразакции, простой в описании mapping объектов на внутренне предсталение, ленивая загрузка, foreingn relations, one-to-many relations with back references, язык построения запросов для query.filter, query.order_by и прочего. (Я забыл упомянуть что-то важное???)
Наследование доменных объектов (как в алхимии) - очень важное требование. Поддерживается аналог polimorphic_on для описаня mapper.

Результат - таки работает.

Выводы:
не очень-то сложно написать “с нуля” ORM. Если знаешь, что нужно сделать.
sqlalchemy - отличная штука. Даже простое заимствование архитектуры мне сильно облегчило жизнь.

Почему пишу:
не бойтесь “изобретать велосипед”, если это действительно необходимо (изучите все аналоги перед таким серьезным шагом).
написание такой непростой вещи, как ORM, на деле заняло не очень много времени (конечно же, я имел превосходный пример для подражания)

Может кому-нибудь будет интересно….



Офлайн

#2 Май 1, 2008 08:34:49

j2a
От:
Зарегистрирован: 2006-06-29
Сообщения: 869
Репутация: +  1  -
Профиль   Отправить e-mail  

ORM

Интересно.

У тебя блог есть?



Офлайн

#3 Май 1, 2008 13:10:22

Ferroman
От:
Зарегистрирован: 2006-11-16
Сообщения: 2759
Репутация: +  1  -
Профиль   Отправить e-mail  

ORM

Да, дествительно интересно. Ещё бы подробностей ну хоть чуть-чуть :)
Построение архитектуры, и некоторые “особенности” при решении такой задачи - очень интересный опыт…
Дьявол то он - в деталях :)

Офлайн

#4 Май 2, 2008 01:17:49

ZZZ
От: Москва
Зарегистрирован: 2008-04-03
Сообщения: 2161
Репутация: +  26  -
Профиль   Адрес электронной почты  

ORM

Андрей Светлов
На новом проекте пришлось изобретать велосипед.
Мне пришлось делать практически тоже самое. Я создал расширяемый набор классов для разных частей СУБД. При этом обысная схема работы такова:

(PostgreSQL – psycopg2 –
– Обработчик курсоров (на выходе словарь или словарь итераторов) –
– Proxy – Socket)
– TCP/IP –
– (Socket – Proxy –
– Удобная для этого случая обёртка – Целевой модуль)

С виду громоздко, но вреали получилось довольно удобно.

Небольшое описание работы объекта Proxy есть здесь: pydev.ru. Это я к вопросу о велосипедах и twisted… Одинаковый и у сервера, и у клиента. Разница лишь в одном файле, обеспечивающий приведение типов – на клиенте я привожу unicode к QString, время к QTime… и т.д., и т.п…
Кстати итераторы у меня обрабатываются именно как итераторы, но при этом не нужно для получения каждого обекта из СУБД отправлять на сервер запрос next, хотя и можно, если нужно. Красиво вышло. А как с этим в twisted?



Отредактировано (Май 2, 2008 01:26:05)

Офлайн

#5 Май 2, 2008 06:37:25

Андрей Светлов
От:
Зарегистрирован: 2007-05-15
Сообщения: 3137
Репутация: +  14  -
Профиль   Адрес электронной почты  

ORM

ZZZ
Увидел, что у тебя есть. Не совсем понял, зачем используется proxy… Только для сокрытия факта обращения к БД?
Маловато будет… Если еще на прокси осуществляется кеширование данных - самое оно. Позволит сильно снизить нагрузку на базу данных.
Что не понравилось: не увидел, где именно место для доменного объекта (представляющего собой строку из базы данных).
Если это прокси - ты вынужден делать запрос по сети каждый раз. Если клиент - все еще сложнее. Ты должен создавать еще одну тяжелую прослойку.

У меня получилось: есть доменные объекты. Каждый имеет описание (схему, mapper+Table в терминах алхимии). Схемы, в отличие от алхимии, полиморфны (т.е. имеют базовый класс и перегруженные методы. Еще полиморфны колонки (сейчас каждая схема работает со своим типом).

Перед чтением дальнейшей части настоятельно рекомендую ознакомиться с основами sqlalchemy.

Есть абстрактный язык запросов ql (сейчас это самописное подмножество sqlalchemy.sql)

Что потребовалось перегружать в схеме:
- выполнять запрос, возвращая внутренние представления объектов. (rows для db, ael or acm objects for FrontArena)
- создавать объект по внутреннему представлению
- помогать в построении запроса, позвращая то, что может быть выполнено в первом пункте. В отличии от алхимии, формат запроса зависит от явно указанной схемы. А не от диалекта базы данных (MySQL, PostreSQL, Oracle…), указанного в metadata. Почему у себя не использую диалекты? Да потому, что описания колонок для разных схем кардинально отличаются по списку требуемых для описания параметров.
следующие требования появились для оптимизации скорости…
- получать объект по первичному ключу (primary key)
- получать объект по внешнему ключу (foreign key)
- получать коллекцию объектов в случае отнощения один-ко-многим или один-к-одному со стороны “одного”, не имеющего foreign key.
FrontArena позволяет сильно оптимизировать эти запросы.

Для чего нужны перегруженные колонки:
- чтобы правильно представить себя для ql
- чтобы позволить схеме правильно взять значение из внутреннего представления.

FrontArena - мощная система для финансовых операций. Просто помните, что это - не sql, но нечто по своей архитектуре выглядящее как объектно-ориентированная база данных. Мне нужен фасад и для нее тоже.

PS. Понимаю, что без примеров мои слова мало кому понятны. Попытаюсь написать более развернуто (и даже заведу для этого блог) после того, как вернусь из Нью-Йорка в Киев. Возвращаюсь в выходные.
Заказчик дал добро на использование кода библиотеки для иллюстраций.
Более того, высказал заинтересованность в том, чтобы перенести библиотеку в open source. Звучит неплохо, и довольно легко выделить общую часть, не зависящую от нижнего уровня (plain database, sqlalchemy, FrontArena objects). Но для этого мне нужен по меньшей мере еще один участник, использующий данную штуку для своих целей. Интерполяция по одной точке обречена на неудачу, а фреймворк для одного-единственного проекта неизбежно будет содержать слишком много специфического для этого проекта кода.

PPS. Спасибо за проявленный интерес. Этот пост заставил заново взглянуть на получившуюся архитектуру и переписать часть кода. Переместив ответственность выполнения отдельных операций на те классы, которые действительно владеют всей необходимой информацией. Лучшая инкаплуляция. Внешне ничего не изменилось.



Отредактировано (Май 2, 2008 06:39:01)

Офлайн

#6 Май 3, 2008 00:04:50

ZZZ
От: Москва
Зарегистрирован: 2008-04-03
Сообщения: 2161
Репутация: +  26  -
Профиль   Адрес электронной почты  

ORM

Андрей Светлов
Увидел, что у тебя есть. Не совсем понял, зачем используется proxy… Только для сокрытия факта обращения к БД?
Не совсем. Да и не всё это.
Это, фактически, RPC, с возможностью фрагментации больших данных, для лучшей асинхронности. При этом, вся работа по разбиению/сборки данных делается без участия пользователя этого прокси. И, что самое главное, удобный интерфейс. Когда я писал тот пост, я ещё не знал, удобно ли будет с ним работать, а сейчас… Он идеально вписался в остальную часть архитектуры и удобно работает.

Андрей Светлов
Маловато будет… Если еще на прокси осуществляется кеширование данных - самое оно. Позволит сильно снизить нагрузку на базу данных.
Кэширование у меня организуется отдельно. Правда оно пока в зачаточном состоянии, но способ работы уже определён и для некоторых вещей уже работает.

Андрей Светлов
Что не понравилось: не увидел, где именно место для доменного объекта (представляющего собой строку из базы данных).
Если это прокси - ты вынужден делать запрос по сети каждый раз. Если клиент - все еще сложнее. Ты должен создавать еще одну тяжелую прослойку.
Извини, я не понял сути вопроса…
Строка из базы данных сразу преобразуется в словарь и только после используется/передаётся.

Андрей Светлов
Перед чтением дальнейшей части настоятельно рекомендую ознакомиться с основами sqlalchemy.
Во! Это интересно…
(я вчера ночью часа два искал, что же за зверь эта “алхимия”)

Андрей Светлов
Есть абстрактный язык запросов ql (сейчас это самописное подмножество sqlalchemy.sql)
Хм… +1 за умные мысли и наставление на путь истиный…

Андрей Светлов
PS. Понимаю, что без примеров мои слова мало кому понятны. Попытаюсь написать более развернуто (и даже заведу для этого блог) после того, как вернусь из Нью-Йорка в Киев. Возвращаюсь в выходные.
Удачно долететь! Будем ждать новых мыслей…

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



Офлайн

#7 Май 3, 2008 05:24:05

Андрей Светлов
От:
Зарегистрирован: 2007-05-15
Сообщения: 3137
Репутация: +  14  -
Профиль   Адрес электронной почты  

ORM

ZZZ
Это, фактически, RPC, с возможностью фрагментации больших данных, для лучшей асинхронности. При этом, вся работа по разбиению/сборки данных делается без участия пользователя этого прокси. И, что самое главное, удобный интерфейс. Когда я писал тот пост, я ещё не знал, удобно ли будет с ним работать, а сейчас… Он идеально вписался в остальную часть архитектуры и удобно работает.
Эта формулировка значительно лучше. По поводу удобства интерфейса: быть может, а может быть можно и лучше… Сложно говорить, не сравнив твой и мой код. Свои примеры надеюсь выложить на следующей неделе.

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

ZZZ
Андрей Светлов
Что не понравилось: не увидел, где именно место для доменного объекта (представляющего собой строку из базы данных).
Если это прокси - ты вынужден делать запрос по сети каждый раз. Если клиент - все еще сложнее. Ты должен создавать еще одну тяжелую прослойку.
Извини, я не понял сути вопроса…
Строка из базы данных сразу преобразуется в словарь и только после используется/передаётся.
В этом то и проблема. Словарь (python dict, как я понимаю) - слишком низкий уровень для полноценного общения. Нужна еще информация о foreign key и о коллекциях.
А еще - о наследовании. У меня часто объект из базы данных на самом деле может относится к унаследованному типу. Для лучшего понимания данного тезиса стоит скачать sqlalchemy trunk и посмотреть в sqlalchemy\examples\polymorph\single.py. Пример хорошо иллюстрирует задачи наследования, актуальные для меня.

В моей интерпретации foreign key - “прозрачный” атрибут в доменном объекте, возвращающий объект, на который ссылается. Делая дополнительный запрос, если требуется. Из-за глобального кеширования всего в сессии, владеющей объектом, чаще всего реального запроса не происходит - все уже есть в кеше. Если foreign key является отображением связи “один-ко-многим” (“один-к-одному частный случай, не заслуживающий отдельного описания) со стороны ”многих", то коллекция осуществляет обратную связь. В sqlalchemy это relations, задаваемые в mapper (with backrefs if you need. BackRef is sqlalchemy class from sqlalchemy.orm).

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

ZZZ
Удачно долететь! Будем ждать новых мыслей…
Новые мысли появились уже сегодня.
При написании unittests для моих объектов.
setUp подменяет методы обращения схем к своим данным (а все они регистрируются в общем хранилище, таких методов сейчас пять) на свои.
tearDown очищает все изменения.
Да, я тоже ненавижу стандартый unittest. Люблю nose. Но вынужден unittest использовать. Сам запускаю его из nosetests надстройки.
- При выполнении юнит-тестов реально используется sqlite in memory database. Заполненная данными, требуемыми для теста.
- Позволяющая номально обрабатывать все запросы.
- гененрирующая таблицы для моих схем когда нужно (поскольку схемы используют общий интерфейс описания - это возможно. В моей модели всегда можно представить объект в реляционном виде)
- заполняющая эти таблицы моими данными для тестов.
- После всего этого я имею возможность запускать мои юнит-тесты. Они видят данные так же, как и enterprise машины. Могут их менять и проч.

В результате юнит-тесты на замечают разницы - запущены они на реальной конфигурации или на обманке.

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



Офлайн

#8 Май 4, 2008 00:48:37

ZZZ
От: Москва
Зарегистрирован: 2008-04-03
Сообщения: 2161
Репутация: +  26  -
Профиль   Адрес электронной почты  

ORM

Андрей Светлов
Эта формулировка значительно лучше.
Я думал, что в моём примере это-то понятно… Там же не слова про базы данных.
:-)

Андрей Светлов
По поводу удобства интерфейса: быть может, а может быть можно и лучше… Сложно говорить, не сравнив твой и мой код. Свои примеры надеюсь выложить на следующей неделе.
Давай. Интересно сравнить.

Андрей Светлов
По поводу кеширования: у меня это неотъемлимая часть фреймворка. Позволяющая использовать механизмы кеширования нижних уровней (FrontArena и наш самописный сервис для отслеживания изменений) и обеспечивающая более высокоуровневый кеш.
У меня так не получится. Слишком разнородные запросы. У меня клиент далеко не только к СУБД стучтся. Например “прямое” общение двух (и более!) клиентов. Оно всё-равно будет идти через сервер, но минуя СУБД.

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

Андрей Светлов
В этом то и проблема. Словарь (python dict, как я понимаю) - слишком низкий уровень для полноценного общения. Нужна еще информация о foreign key и о коллекциях.
Не вижу проблемы. Если, например, нужно узнать о юзере, отравившим сообщение, то мы просто берём UserId из сообщения и спрашиваем у библиотеки пользователей кто он такой. Опять же класс Message просто будет обладать аттрибутом (property) User, спрашивающим нужную информацию.

Андрей Светлов
А еще - о наследовании. У меня часто объект из базы данных на самом деле может относится к унаследованному типу. Для лучшего понимания данного тезиса стоит скачать sqlalchemy trunk и посмотреть в sqlalchemy\examples\polymorph\single.py. Пример хорошо иллюстрирует задачи наследования, актуальные для меня.
Посмотрю позже. Сейчас нужно спать…

Андрей Светлов
В моей интерпретации foreign key - “прозрачный” атрибут в доменном объекте, возвращающий объект, на который ссылается. Делая дополнительный запрос, если требуется.
Об этом я выше и говорил.

Андрей Светлов
Я попытаюсь научить сессию лениво обновлять все объекты, содержащиеся в ней, если приходит сообщение о том, что данный объект изменился. Лениво - значит удалить объект из кэша и перечитать его при следующем обращении.
Я, наверное, эту часть также организую. А в некоторых случаях выгодней прислать обновлённый объект вместе с сообщением об изменении. У меня есть довольно критичные к этому моменты.



Отредактировано (Май 4, 2008 00:56:01)

Офлайн

#9 Май 5, 2008 00:31:10

Андрей Светлов
От:
Зарегистрирован: 2007-05-15
Сообщения: 3137
Репутация: +  14  -
Профиль   Адрес электронной почты  

ORM

Нормально.
Дальше нужно более детальное рассмотрение, чтобы не говорить попусту.
Используем немного разные подходы (что естественно).



Офлайн

Board footer

Модераторировать

Powered by DjangoBB

Lo-Fi Version