Статьи

Что случилось с NHibernate Pullout от Umbraco?

Меня попросили прокомментировать эту тему:

образ

Вот ссылка на полную статью , но вот суть:

Производительность .
Виновником v5 является наше использование NHibernate . Это не означает, что NHibernate является злом, просто он оказался неподходящим для нашего проекта (частично из-за нашей архитектуры и схемы БД, частично из-за отсутствия надлежащего опыта NHibernate в команде). Это плохо прирученный NHibernate, вызвавший множество операторов N + 1 SQL . Долгосрочное решение — заменить NHibernate на собственные оптимизированные вызовы SQL. но это большая операция, которую мы рассмотрим летом. Короткое групповое решение состоит в том, чтобы продолжить поиск как можно большего количества узких мест, как мы можем их настроить, в сочетании с введением провайдера кэширования на основе Lucene, который будет кэшировать запросы и результаты, которые выживут при перезапусках пула приложений (так же, как кэш v4 XML). Архитектура была подготовлена ​​для последнего, и мы будем иметь это к концу мая.

Еще одна вещь, которая вызывает огромные проблемы с производительностью, заключается в том, что мы наблюдаем тенденцию, когда люди вызывают ToList () для запросов бритвы, чтобы компенсировать отсутствие функций LINQ в нашей реализации. К сожалению, это приводит к тому, что большая часть (если не вся) базы данных загружается в память, что, естественно, вызывает большие проблемы с производительностью (и огромное количество вызовов SQL N + 1). Это приводит нас к …

И спустя некоторое время :

V5 не может быть исправлено. Нам нужно было полностью переписать весь бизнес-уровень, чтобы когда-либо сделать v5 продуктом, который люди ожидают от Umbraco

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

Теперь, чтобы быть справедливым, я смотрел на Умбрако несколько раз, но я никогда не использовал его и никогда не копал глубоко.

Вся проблема может быть подведена на этой странице , с сайта документации Umbraco. Это динамическая модель, которую Umbraco предоставляет представлениям для создания HTML. Из документов:

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

Каждый раз, когда вы предоставляете представлению способ генерации запросов, это большая проблема. В целях обсуждения я остановлюсь на следующем примере , также взятом из документов Umbraco:

{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}

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

Umbraco.GetDynamicContentById — это на самом деле UmbracoHelper.GetDynamicContentById, который выглядит следующим образом:

образ

Я имею в виду, я думаю, я мог бы просто остановиться здесь, посмотреть на первую строку, и она должна рассказать вам все Но давайте копать глубже. Вот как выглядит GetById:

образ

Note that with NHibernate, this is a worst practice, because it forces a query, rather than allow us to use the session cache and a more optimized code paths that Load or Get would use.

Also, note that this is generic on top of an IQueryable, so we have to go one step back and look at Cms().Content, which is implemented in:

 

образ

I just love how we dispose of the Unit of Work that we just created, before it is used by any client. But let us dig a bit deeper, okay?

образ

образ

образ

And we still haven’t found NHibernate! That is the point where I give up, I am pretty sure that Umbraco 5 is using NHibernate, I just can’t find how or where.

Note that in those conditions, it is pretty much settled that any optimizations that you can offer isn’t going to work, since you don’t have any good way to apply it, it is so hidden behind all those layers.

But I am pretty sure that it all end up with a Linq query on top of  a session.Query<Content>(), so I’ll assume that for now.

So we have this query in line 15, to load the object, then, on line 18, we issue what looks like a query:

if (startNode.Children.Where("umbracoNaviHide != @0", "True").Any())

We actually issue two queries here, one to load the Children collection, and the second in the Where(). It took a while to figure out what the Where was, but I am pretty sure it is:

образ

Which translate the string to a Linq query, which then have to be translated to a string again. To be perfectly honest, I have no idea if this is executed against the database ( I assume so ) or against the in memory collection.

Assuming it is against the database, then we issue the same query again, two lines below that.

And this is in a small sample, think about the implications for a more complex page. When you have most of you data access so abstracted, it is very hard to see what is going on, let alone do any optimizations. And when you are doing data access in the view, the very last thing that the developer has in his mind is optimizing data access.

This is just setting up for failure. Remember what I said about: replace NHibernate with native, optimized SQL calls, this is where it gets interesting. Just about every best practice of NHibernate was violated here, but the major problem is not the NHibernate usage. The major problem is that there is very little correlation between what is happening and the database. And the number of queries that are likely to be required to generate each page are truly enormous.

You don’t issues queries from the view, that is the #1 reason for SELECT N+1, and when you create a system where that seems to be the primary mode of operation, that is always going to cause you issues.

Replace NHibernate with optimized SQL calls, but you are still making tons of direct calls to the DB from the views.

I actually agree with the Umbraco decision, it should be re-written. But the starting point isn’t just the architectural complexity. The starting point should be the actual interface that you intend to give to the end developer.