Какие запросы мешают вам использовать ORM?

whirlwind

TDD infected, paranoid
StUV

>сторонникам ORM в этом флейме никогда не приходилось работать над крупными проектами, критичными к высокой нагрузке

Уточни плз., что такое "крупный проект"? Может объем кода или количество таблиц, етц... Хочется сравнить с теми что имеются.
 

StUV

Rotaredom
ключевой момент:
критичными к высокой нагрузке
-~{}~ 02.10.06 10:14:

whirlwind
если говорить вообще - не касаясь веба...

я работал в поддержке/разработке (на мой взгляд) крупного проекта - около 1.3 Гб исходников на С++

в данном проекте на стадиии его проектирования году так в 98-99 некий молодой специалист очень увлекался всякими dao & orm...

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

whirlwind

TDD infected, paranoid
StUV еще раз без, плз, отклонений от темы разработки для веба на PHP, конкретно критериии, определяющие крупный проект.

PS. я правильно понял - ты для веба не программируешь или программируешь, но не считаешь свои разработки достаточно "крупными", раз затрудняешься дать определение крупного проекта для веба?
 

bkonst

.. хочется странного?...
fisher
Начинаем с построения модели:

Сущности:
- Country - страна
- Company - турфирма
- Route - маршрут
- Waypoint - путевая точка в маршруте
- Site - "примечательное место", через которое может проходить несколько маршрутов
- City - город
- POI - достопримечательность (Point of interest)

Заметим, что в этот момент ничего не мешает объединить Dinner, Supper и Waypoint, добавив, скажем, в Waypoint атрибуты-флаги; Dinner/Supper вынесены в отдельные сущности с прицелом на то, что у них могут появиться дополнительные атрибуты (наименование ресторана, предлагаемое меню, продолжительность отдыха и тому подобное).

Связи между сущностями:
Route:Country - 0..*:1
Route:Company - 0..*:1
Route:Waypoint - 1:1..*
Waypoint:Supper - 1:0..1
Waypoint:Dinner - 1:0..1
Waypoint:Site - 0..*:1
Site:City - 1:0..1
Site:pOI - 1:0..*

Атрибуты для разбираемой задачи (почти) несущественны. Будем считать, что сущность "город" ('City') имеет стрибут 'title' - имя города, а сущность "маршрут" ('Route') - атрибуты 'trips_per_month' - количество поездок в месяц и 'avg_group_size' - средний размер группы (понятно, что эти данные можно вычислять на основании записей о поездках; опять-таки, для поставленной задачи это не так важно). Этого достаточно для ответа на поставленные вопросы.

"Псевдокод" для задачи (1):
PHP:
$city_filter = new ORMFilterEquality('title', "Барселона");

$waypoint_filter = new ORMFilterCompositeAND();
$waypoint_filter->add_filter(new ORMFilterBoundObject("Supper", new ORMFilterTrue()));
$waypoint_filter->add_filter(new ORMFilterBoundObject("City", $city_filter));

$company_filter = new ORMFilterBoundObject("Waypoint", $waypoint_filter)

$manager = ORMMetaclass::_get_manager("Company");
$manager->itemsFiltered($filter);
Для задачи (2):
PHP:
$order = new ORMOrderCollection();
$order->add_rule(new ORMOrderBoundAggregateCount("Route", ORDER_DESC));

$section = new ORMQuerySectionByBoundObject("Country");
$section->limit(0, 5);

$manager = ORMMetaclass::_get_manager("Company");
$manager->itemsSectioned($section, $order);
и (2*):
PHP:
$order->add_rule(new ORMOrderBoundAggregateCustom(Route", "trips_per_month * avg_group_size", ORDER_DESC));
По поводу задач (2, 2*): лично я сделал несколько более простых запросов в цикле (выборка "лучших" внутри одной страны), вне зависимости от того, работаю с базой через ORM или SQL. Хотя бы потому, что оба известных мне варианта выборки "Top N в каждой группе одним запросом" выглядят довольно неуклюже.

StUV
Ключевой момент: есть разные проекты. Я уже объяснял это на предыдущей странице. Если вам всегда надо выжимать 3% производительности, не считаясь с затратами, то почему вы не пишете приложения исключительно на ассемблере?

-~{}~ 02.10.06 12:08:

fisher
ORMFilterBoundObject - нечто подобное есть в SQLAlchemy для Python - позволяет автоматически включать в запрос таблицы, описывающие связанные объекты, условия для связывания 1:N/N:M и условия для отбора по этим объектам. При "хорошей" модели (граф, задающий не более одного маршрута между сущностями) и "интеллектуальной" ORM такой объект может учитывать более одной связи - например, в
PHP:
$company_filter = new ORMFilterBoundObject("Waypoint", $waypoint_filter)
он неявным образом установит связи Company->Route и Route->Waypoint.
 

StUV

Rotaredom
whirlwind
если мы обсуждаем orm как технологию и задаемся вопросами вроде "имеет ли право orm жить в рамках проекта с долговременной поддержкой?" - то какая разница что это за проект - веб или прикладуха ?
разве сторонники orm в этом топике не выдвигают одним из главных плюсов этого подхода - хорошую переносимость и гибкость в расширении ?

-----------------

но, ок, если это так важно... - текущий проект:

связка apache/nginx/php/oracle

объем кода - около 2.5 Мб

с базой работает отдел pl/sql-девелоперов - т.е. в базу "руками" пхп-прогеры практически не лезут - вся работа через хранимые процедуры/функции

средняя требуемая по тз нагрузка - 100К уников в сутки

пхп-динамика выдерживает нагрузочное тестирование для 1000 одновременных пользователей - при этом веб-сервер и сервер БД работают в нормальном режиме и удерживает такую нагрузку для 5-6 параллельно работающих на них проектах

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

--------

далее, имхо, намного эффективнее по затратам использовать стандартизированный "низкоуровневый" интерфейс для работы с базой + 1-2 специалиста для реализации запросов для каждой субд, чем поддерживать orm api + документацию для него
 

whirlwind

TDD infected, paranoid
StUV ну и как по твоему, это показатель - объем кода? А что делает твой код? Что могут делать 100к уников на сайте с 2.5мб кода? По мне так большой проект, это проект не только с большим кол вом посещений или большим объемом кода, т.к. отдельный посетитель не использует 100% функциональных возможностей системы. Большая система - это система с большим количеством функциональных возможностей и зависимых объектов. А если это система посчета кликов, где в пару таблиц закидываются рефереры, инкрементируются счетчики, она написана на C/CPP, и объем хаков, которые повышают производительность, стремится в сторону бесконечности, и т.п (утрировано но тем не менее). Ничего удивительного что в таком проекте 2.5мб кода и каждый запрос там вылизан. Я действительно сомневаюсь что вы поддерживаете сложную биллинговую систему, в которой 10к уников в сутки делают хотя бы по одному заказу, а бедные менеджеры бесконечно генерят сложнейшие отчеты, в которых завязаны по пять-цатьпять таблиц. Может быть в каком нибудь SMS сервисе и оправдана такая оптимизация, но, я думаю многие согласятся, такие нагрузки есть далеко не везде.

-~{}~ 02.10.06 13:03:

пхп-динамика выдерживает нагрузочное тестирование для 1000 одновременных пользователей - при этом веб-сервер и сервер БД работают в нормальном режиме и удерживает такую нагрузку для 5-6 параллельно работающих на них проектах
PS. ты наверное забыл дать конфигурацию железа, или там у вас ZX спектрум стоит? ;)
 

StUV

Rotaredom
ты наверное забыл дать конфигурацию железа, или там у вас ZX спектрум стоит?
военная тайна =)

по поводу большой системы и большого набора функциональных возможностей + учитывая отказ от "гипер"-требований по производительности:

в предпоследнем своем посте я о такой системе и написал - т.е. мой личный опыт разработчика показал, что в системе со сложным функционалом красивое решение с применением orm красиво живет только до тех пор, пока с кодом работает тот, кто этот код написал и пока не требуется кардинального изменения в структуре данных
Когда мне пришлось добавлять в приложение новый функционал оказалось, что модуль доступа к данным сильно связан с многими участками приложения, в том числе с теми, которые напрямую не были связаны с расширением и человек этот код писавший уже года 3-4 как ушел в другое место.

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

Естественный мой вывод из этого - работа с plain-sql намного прозрачнее для разработчиков, чем с orm-api...

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

whirlwind

TDD infected, paranoid
Когда мне пришлось добавлять в приложение новый функционал оказалось, что модуль доступа к данным сильно связан с многими участками приложения, в том числе с теми, которые напрямую не были связаны с расширением и человек этот код писавший уже года 3-4 как ушел в другое место.

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

что модуль доступа к данным сильно связан с многими участками приложения, в том числе с теми, которые напрямую не были связаны с расширением и человек этот код писавший уже года 3-4 как ушел в другое место
Кривые руки разработчика не являются предпосылками для опускания или поднимания какой-либо технологии. Или ты будешь утверждать, что код всегда получается шоколадный, если не использовать ORM? :) Я тоже же мучаюсь с кодом, написанным дилетантами. И я тебя уверяю - кривой код без ORM выглядит так же гадко, как кривой код где используется ORM.
 

StUV

Rotaredom
Ну вот видишь, ты нигде не упоминаешь что вот этот конкретный ORM сделал мне гемороя.
баг был зашит где-то в глубинах иерархии классов, генерирующих этот запрос средствами orm
я нигде не говорил о том, что этот код был кривой, даже более того - я сказал, что код был красивый =)
только не расширяемый - ибо:

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

В тех местах где было возможно - такая иерархичная выборка "отключалась" и каждый класс просто "обладал" своим собственным plain-sql запросом и поверь, при возникновении бага с запросом было намного проще и быстрее однообразно поправить 3-4 типовых запроса в иерархии, чем найти баг в одном унифицированно строящемся запросе.

Возможно, когда-нибудь, появится единая теория orm с единым унифицированным синтаксисом и различные либы будут похожи друг на друга как различные диалекты sql - может быть тогда, я и пересмотрю свое отношение к orm.... но боюсь такая технология должна просто заменить сам sql, что вряд ли =)
 

whirlwind

TDD infected, paranoid
>когда запускается выборка - соответственно сложно отследить место, в котором генерится то или иное условие

Блин, ну вот скажи, я виноват в том, что недальновидные чуваки, которые подложили тебе такую собаку, не предусмотрели элементарных вещей, таких как модификация chunks запроса перед его выполнением? При чем здесь кривые руки и ORM? :) С тем же успехом можно взять какую нибудь непопулярную СУБД и на основе этой непопулярности сделать вывод что все СУБД - дерьмо и что их использовать не нужно.

У меня есть спецификация, я ее здесь выкладывал, там четко написано чего я хочу добиться и чего не хочу. И там чуть ли не первым пунктом написано - это инструмент, а не единственно верное и абсолютно правильно решение задач. Это так же означает, что любое телодвижение может быть заменено на какой то другой, более оптимальный вариант. Я этой спецификации стараюсь следовать. Вот глядя на нее скажи - при чем здесь кривые руки программеров, которые работали до тебя и как оно связано с тем якобы фактом, что ORM - пятая нога ваших приложений?
 

StUV

Rotaredom
whirlwind
я и не говорю, что "ORM в принципе есть зло"
только то, что есть класс задач, для которых этот метод малопригоден и связан с труднопредсказуемым повышением затрат впоследствии

имхо флейм из-за того, что очень сложно выделить этот класс задач =)

я высказал только свое отношение - не более
нравится - используйте ;)
 

Sad Spirit

мизантроп (Старожил PHPClub)
Команда форума
Автор оригинала: atv
Может кактить камень в сторону СУБД, чтобы расширили свой API, если не хотят быть вытесненными объектными БД.
Я уже писал про это выше, повторюсь.
Реляционные СУБД вытеснили своих предшественников не потому, что они там намного быстрее или намного удобнее. А потому, что в их основе лежит математическая теория, есть алгоритмы нормализации и т.п.
Поэтому пока ОО подход заключается в плясках с бубном и завываниях "ууу! рефакторинг! ооо! паттерны! ыыы! ORM!" вместо такой же по мощи теории, за будущее РСУБД можно быть спокойными. ;)
 

whirlwind

TDD infected, paranoid
Sad Spirit второй раз уже читаю

Реляционные СУБД вытеснили своих предшественников не потому, что они там намного быстрее или намного удобнее. А потому, что в их основе лежит математическая теория
по моему это надо в юмор.
 

Mich

Продвинутый новичёк
А я вот второй раз читаю и второй раз пропускаю... видно теория нормализации давит =)
 

bkonst

.. хочется странного?...
Sad Spirit

МАРТЫШКА И ОЧКИ

Мартышка к старости слаба глазами стала;
А у людей она слыхала,
Что это зло еще не так большой руки:
Лишь стоит завести Очки.
Очков с полдюжины себе она достала;
Вертит Очками так и сяк:
То к темю их прижмет, то их на хвост нанижет,
То их понюхает, то их полижет;
Очки не действуют никак.
"Тьфу пропасть! - говорит она, - и тот дурак,
Кто слушает людских всех врак:
Все про Очки лишь мне налгали;
А проку на волос нет в них".
Мартышка тут с досады и с печали
О камень так хватила их,
Что только брызги засверкали.
К несчастью, то ж бывает у людей:
Как ни полезна вещь, - цены не зная ей,
Невежда про нее свой толк все к худу клонит;
А ежели невежда познатней,
Так он ее еще и гонит.

Крылов И.А., 1815
 

Андрейка

Senior pomidor developer
bkonst
вы такой интересный псевдокод написали, просто заглядение... а можно глянуть на такое же, но более реальное (особенно в части прикручивания "модели" к "orm") в вашем же исполнении?
 

bkonst

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

... кстати, в "псевдокоде" смысловая ошибка - вместо
PHP:
$manager = ORMMetaclass::_get_manager("Company");
должно быть
PHP:
$manager = ORMMetaclass::_get_manager("Poi");
Иначе получаем список компаний, имеющих максимальное количество маршрутов, вместо списка достопримечательностей.

(*) а вы чего ждали? Если бы это уже было полностью написано, оттестировано и задокументировано - зачем бы я поднимал эту тему?
 

fisher

накатила суть
по поводу реализации - bkonst, а можно поподробнее про методы, кто что делает. и ещё - как возник этот код. просто опишите, как вы думали.

собственно, пример-то довольно простой, а уже
почти все ясно. что мы видим. совершенно классическое использование ORM, которое просто заключается в том, чтобы "автоматизировать" SQL, причем исхитриться сделать так, чтобы любой SQL у нас получался через магичское связывание, задание таких и сяких фильтров, и т.д. то есть думаем мы в терминах отношений, а потом отображаем это дело в наши объекты. так вот, уважаемые, всё, что я хочу вам показать - это что спирит был прав с самого начала, а вы беспомощно огрызаетесь, как дети ;) если есть что сказать в ответ - ну так дайте ссылки на научные работы, а? все мы люди неглупые, что-нибудь обязательно поймем.
 

Андрейка

Senior pomidor developer
ну еще для полного щастья б (псевдо)код от "Начинаем с построения модели" и до конца ее описания. или оно в теории и так работает?
 

bkonst

.. хочется странного?...
Андрейка
"Начинаем с построения модели" и до конца ее описания
Построение модели не зависит от использования/неиспользования ORM.
Рисую UML с сущностями и связями.

fisher
-> Конструктивная часть
Краткое описание псевдокода:
ORMFilter* - условия отбора.
ORMFilterEquality - условие "поле равно значению"
ORMFilterLessEqual - условие "поле меньше или равно значению"
ORMFilterCompositeAND - составное условие, для истинности которого необходима истинность всех частей
ORMFilterBoundObject - составное условие вида "существует связанный объект типа X, для которого выполняется условие F(X)"(*)

ORMOrderCollection - набор правил сортировки
ORMOrderBoundAggregateCount - правило сортировки "по количеству связанных объектов типа X"

ORMFilterComposite::add_filter - добавить условие в составное условие
ORMManager::itemsFiltered - найти все существующие объекты заданного типа X (тип неявно задается конкретным экземпляром ORMManager), удовлетворяющие заданным условиям

Как возник код:
0. сформировано описание модели
1. Берем исходный запрос на человеческом языке
найти все компании, которые возят народ с ночевкой в барселоне (у кого-то там отель и он хочет продавать места в отеле оптом именно этим компаниям)
и выделяем в нем условия отбора(**):
(Найти компании, у которых есть остановка на маршруте (Waypoint)), (на которой есть ужин и которая находится в городе), (который имеет имя Барселона)
2. Видим три группы условий (выделены скобками), применяемых к сущностям Company, Waypoint и City.
Записываем их:
PHP:
// город имеет имя Барселона
$city_filter = ORMFilterEquality('title', 'Барселона');

// (на точке [i]есть[/i] ужин) И (точка [i]находится в[/i] городе)
$waypoint_filter = ORMFilterCompositeAND();
$waypoint_filter->add_filter(new ORMFilterBoundObject("Supper", new ORMFilterTrue()));
$waypoint_filter->add_filter(new ORMFilterBoundObject("City", $city_filter));

// у компании [i]есть[/i] остановка на маршруте (Waypoint)
$company_filter = ORMFilterBoundObject("Waypoint", $waypoint_filter);
Всё.

(*) как я уже говорил ранее, это не будет работать с кратными связями между сущностями. Легко обходится.
(**) на этапе работы с естественным языком большую роль играет человеческий фактор; тот же запрос можно разобрать как
Найти компании, у которых есть ужин, который проводится в городе, который имеет имя Барселона
и получить более компактный псевдокод.
PHP:
$city_filter = new ORMFilterEquality('title', "Florenzia");
$company_filter = new ORMFilterBoundObject("Supper", 
                                           new ORMFilterBoundObject('City', $city_filter));

$manager = ORMMetaclass::_get_manager("Company");
var_dump($manager->itemsFiltered(0, null, $company_filter));
...
-> Флейм

По поводу высказываний Sad Spirit, процитирую себя:
не надо, пожалуйста, сравнивать ООП и реляционную алгебру. Они соотносятся примерно так же, как функциональное программирование и Windows API.
Я надеюсь, что неглупые люди, способные абстрактно мыслить - со второго повторения - поймут,
что объекты и взаимосвязи между ними легко описываются реляционной алгеброй,
что все НИР по теме "реляционная алгебра", таким образом, относятся и к ООП,
что SQL и есть "магическое связывание" и задание "таких и сяких фильтров" (см синтаксис FROM/WHERE/HAVING subclauses),
что "магическое связывание" в "псевдокоде" отсутствует, так как информация о связях содержится в описании модели и может быть использована ORM при формировании SQL-запроса,
что в ООП есть отношения между объектами,
что думать в терминах отношений между сущностями ООП не запрещает,
что ORM позиционируется (мной) как способ скрыть детали реализации (в частности, скрыть существование промежуточных таблиц, через которые производится связывание),
что такое использование ORM позволяет работать с транзитивным отношением "связан с", а не искать каждый раз путь между связанными объектами
и что болезненное отношение Sad Spirit к словам "паттерны" и "рефакторинг" будет более-менее обосновано в том случае, если он сможет указать алгоритмы, а не эвристики построения модели (а не приведения существующей модели к нормальной форме).

Задачу сравнить размер и читаемость SQL запроса, выполняющего задачу (2), с размером и читаемостью псевдокода оставляем на отдельный разбор.
 
Сверху