...

пятница, 7 февраля 2014 г.

Снижение производительности SharePoint при увеличении уникальных Security Scopes на больших списках

Всем привет!

В этой статье мы решили поделиться своими жизненными наблюдениями за проблемой производительности больших списков в SharePoint.


Итак, мы достаточно часто сталкиваемся с ситуациями, когда есть список на портале SharePoint, в котором хранятся и обрабатываются, например, заявки пользователей. Дополнительно всегда возникает желание сделать так, чтобы все заявки хранились в одном месте, с другой стороны — чтобы права на эти элементы были только у пользователя, подавшего заявку, и у группы или групп пользователей, которые участвуют в ее обработке.


Сначала немного терминологии:


ACL (Access Control List) – упорядоченный список принципалов, которые определяют права на элемент или группу элементов.


Security Scopes – набор пар групп или пользователь + ACL.


Рекомендации Microsoft



Microsoft рекомендует соблюдать достаточно простые правила: избегать количества одновременных Security Scopes более 5 000 на одну библиотеку документов или на один список.

Почему так? Откуда взялось ограничение? Для начала ответим на вопрос, каким образом MS SharePoint обрабатывает запрос к списку.



  1. Делает обращение к БД содержимого и читает:

    a. Метаданные списка, т.е. определение списков полей и т.п.

    b. Computed fields, определения обработчиков событий и много чего еще.

    c. Все scope списка (точнее представления).

  2. На стороне front-end определяет: в scope попадает пользователь.

  3. Вытаскивает уже непосредственно данные.




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

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


С помощью этих SQL-запросов можно посчитать, сколько сейчас у вас Security Scopes:



SELECT [SiteId], [ScopeId], [RoleDefWebId], [WebId], [ScopeUrl], [Acl]
FROM [your Content DB].[dbo].[Perms] order by scopeurl


An even better query exposes the web URL



SELECT [fullurl],perms.[SiteId] , perms.[ScopeId], [RoleDefWebId], [WebId], [ScopeUrl], [Acl]
FROM [dbo].[Perms],[dbo].allwebs
where perms.WebId = allwebs.id
order by scopeurl


As an aside, you can see the number of security principals per scope, using this SQL:



select COUNT(ra.PrincipalId) as [Count],p.ScopeUrl from RoleAssignment ra with(nolock)
join Perms p with(nolock)
on p.SiteId = ra.SiteId and p.ScopeId = ra.ScopeId
group by p.ScopeUrl
order by p.ScopeUrl desc


Как Microsoft предлагает решать проблему:



  1. Использовать папки для инкапсуляции прав доступа на группы элементов с похожими правами.

  2. Разбивать на разные списки или библиотеки документов и использовать наследованные права.

  3. Никогда не выходить за лимит 5 000 уникальных права на листе.




К чему приводит нарушение этих рекомендаций:

  1. Снижение производительности на 20% на каждые 1 000 уникальных прав на листе.

  2. Программные ошибки при попытках разорвать права на элементе и выдать уникальные.




Для решений на базе MS SharePoint критически важно, чтобы ферма держала нагрузку, и вот почему.

Для разработчика нет поддержки транзакций. И наборы операций вида (т.е. если создать элемент, разорвать права, выдать на него права в MS SharePoint):



var item = list.AddItem(...);
// ...
item.Update();
item.BreakRoleInheritance(false);
item.RoleAssignments.Add(new SPRoleAssignment(...));




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

Понятно, что рекомендации 2 и 4 носят более камерный характер и не решают задачу хранения элементов вместе и уникальных прав, поэтому остановимся на первом как самом действенном способе – использование папок.


Наш пример



Для примера возьмем список из реально работающей системы, в которой на текущий момент 132 073 элементов:


В ней 3 590 Security Scopes – выглядит примерно вот так:



Внутри списка 721 папка...:



… на которые выданы права следующим образом:



По данным Google Analytics, среднее время загрузки страницы менее секунды — при уровне посещений именно этого списка за этот период порядка 968 в день, в рабочее время.



Наш подход



В соответствии с нашим подходом права раздаются вот так:

public static void ProcessItem(SPListItem item, SPFolder folder)
{
var file = item.Web.GetFile(item.UniqueId);
// Внимание не используйте item.File т.к. он во многих случаях будет null
// http://ift.tt/LDGR3i
file.MoveTo(string.Format("{0}/{1}", folder.Url, item[SPBuiltInFieldId.LinkFilename]));
}





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

class PermissionEventHandler : SPItemEventReceiver
{
public override void ItemUpdating(SPItemEventProperties properties)
{
var updatingAllowed = CheckUserHasPermission(properties.Web.CurrentUser, properties.ListItem);
// определить может ли пользователь изменять элемент
if (!updatingAllowed)
{
properties.Cancel = true;
properties.ErrorMessage = "Access denied";
}
}

public override void ItemAdding(SPItemEventProperties properties)
{
//...
}
//...
}




До изобретения этого подхода нам пришлось в течение 5 лет плотно заниматься SharePoint, съесть немало собак и кактусов. У него есть свои нюансы, но он абсолютно точно работает — опробовано уже на двух порталах.

Как всегда, рады были поделиться опытом!


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Комментариев нет:

Отправить комментарий