Archive

Posts Tagged ‘Performance’

Alto consumo CPU e memória noderunner.exe – SharePoint 2013

February 1, 2018 Leave a comment

CENÁRIO:

Olá!

Apesar de ter montando uma máquina virtual de desenvolvimento com um hardware razoável (gastar menos $$) sofri com consumo excessivo de CPU e memória em um servidor com SharePoint 2013, SQL Server 2008 R2 e Visual Studio 2017 instalado.

ERRO:

Não é um erro, mas é um comportamento pesado para um ambiente de desenvolvimento.

Picture1

SOLUÇÃO:

Analisando um pouco o comportamento do servidor para descobrir o “vilão” da jogada… notei que o processo “noderunner.exe” estava consumindo muita memória e CPU considerável.

Picture2

Noderunner.exe é um componente do serviço de busca do SharePoint 2013.

Para ambientes de Dev, podemos reduzir o consumo desse serviço. Principalmente se não estamos usando a busca do SharePoint 2013 no desenvolvimento. Inclusive, desabilitar o serviço.

No caso, eu precisava de um “pouco” de busca… :/

Então, utilizei os passos a seguir para reduzir o impacto da busca no consumo de recursos do servidor.

  1. Rode o cmdlet Powershell abaixo para reduzir o consumo de CPU

Set-SPEnterpriseSearchService -PerformanceLevel Reduced

  1. Altere o parâmetro “memoryLimitMegabytes” do arquivo “exe.config”. Por padrão, a configuração é “0” – ilimitado.

C:\Program Files\Microsoft Office Servers\15.0\Search\Runtime\1.0\noderunner.exe.config

Picture3

  1. Reinicie o serviço de busca do SharePoint com o cmdlet a seguir

Restart-Service SPSearchHostController

Picture4

Agora o servidor ganhou “algum” fôlego.

Informações adicionais: https://docs.microsoft.com/en-us/powershell/module/sharepoint-server/Set-SPEnterpriseSearchService?view=sharepoint-ps

Reduced: Total number of threads = number of processors, Max Threads/host = number of processors

Partly Reduced: Total number of threads = 4 times the number of processors , Max Threads/host = 16 time the number of processors

Maximum: Total number of threads = number of processors

Abraço!

 

Fazendo Joins com CAML Query

April 20, 2012 2 comments

Olá!

Cenário:

O SharePoint 2010 introduziu uma novidade na maneira de realizar queries usando as já conhecidas CAML Queries. Agora podemos utilizar Joins (e campos projetados) em nossas CAML queries, economizando esforço e até ganhando performance nas queries para alcançar alguns objetivos que antes, no SharePoint 2007, não havia o que fazer.

No MSDN encontramos bastante documentação a respeito, mas ainda um tanto confuso. Pelo menos eu achei.

http://msdn.microsoft.com/en-us/library/ee539975.aspx – List Joins and Projections

SOLUÇÃO:

Primeiramente vamos preparar o ambiente que vamos fazer nossos testes.

· Crie duas listas em seu SharePoint, uma lista chamada Eventos (do tipo Personalizada ou Custom List) e outra lista chamada Contatos (do tipo Contatos ou Contacts)

image

image

Nas lista de Eventos, crie uma coluna do tipo Loopkup (consulta) para a lista de Contatos, chamada Responsavel (sem acento).

image

Vá na lista de Contatos e adicione alguns registros:

image

Volte a lista de Eventos e também adicione alguns registros, associando contatos aos Eventos:

image

Pronto, agora temos o ambiente preparado. Iremos listar todos os eventos e mostrar o título do Evento, Nome e SobreNome (informação que está na outra lista) com apenas uma única query.

static void Main(string[] args)
{
ShowJoin();
}

private static void ShowJoin()
{
using (SPSite site = new SPSite("http://notebook02/sites/treinamento"))
{
using (SPWeb web = site.OpenWeb())
{
SPQuery query = new SPQuery();

query.Query = @"<OrderBy>
<FieldRef Name='Title' />
</OrderBy>"
;

query.Joins = "<Join Type='INNER' ListAlias='Contatos'>" +
"<Eq>" +
"<FieldRef Name='Responsavel' RefType='Id'/>" +
"<FieldRef List='Contatos' Name='ID'/>" +
"</Eq>" +
"</Join>";

query.ProjectedFields =
"<Field Name='Nome' Type='Lookup' " +
"List='Contatos' ShowField='FirstName'/>" +
"<Field Name='SobreNome' Type='Lookup' " +
"List='Contatos' ShowField='Title'/>";

query.ViewFields = "<FieldRef Name='Title'/>" +
"<FieldRef Name='Nome'/>" +
"<FieldRef Name='SobreNome'/>";

SPList listaEventos = web.Lists["Eventos"];

SPListItemCollection items = listaEventos.GetItems(query);

if (items.Count > 0)
{
foreach (SPListItem item in items)
{
SPFieldLookupValue dseLastName =
new SPFieldLookupValue(item["SobreNome"].ToString());
SPFieldLookupValue dseFirstName =
new SPFieldLookupValue(item["Nome"].ToString());

Console.WriteLine("Nome do Evento: {0}, SobreNome do Reponsável: {1}, Nome do Responsável: {2}",
item.Title,
dseLastName.LookupValue,
dseFirstName.LookupValue);
}

Console.ReadLine();
}
}
}
}

Explicando:

SPQuery query = new SPQuery();

query.Query = @"<OrderBy>
<FieldRef Name='Title' />
</OrderBy>"
;

Até aqui normal, a mesma query que sempre utilizamos. Nesse caso, apenas ordenando pelo título do evento.

query.Joins = "<Join Type='INNER' ListAlias='Contatos'>" +
"<Eq>" +
"<FieldRef Name='Responsavel' RefType='Id'/>" +
"<FieldRef List='Contatos' Name='ID'/>" +
"</Eq>" +
"</Join>";

Aqui montamos a condição do Join que iremos fazer com a lista Contatos (lembrando que nossa query roda na lista Eventos).

query.ProjectedFields =
"<Field Name='Nome' Type='Lookup' " +
"List='Contatos' ShowField='FirstName'/>" +
"<Field Name='SobreNome' Type='Lookup' " +
"List='Contatos' ShowField='Title'/>";

Aqui projetamos os campos da lista Contatos na “lista” Eventos. Na verdade, os campos são projetados apenas no SPListItemCollection retornado na query. Damos um alias (Name) para o campo e vinculamos ao nome interno (ShowField) do campo na lista Contatos. Então, nossos itens retornados terão as colunas projetadas Nome e SobreNome, da lista Contatos, juntamente com todas as colunas da lista de Eventos.

query.ViewFields = "<FieldRef Name='Title'/>" +
"<FieldRef Name='Nome'/>" +
"<FieldRef Name='SobreNome'/>";

Aqui explicitamos apenas os campos que queremos retornados na query. Ou seja, a query trará APENAS os campos relacionados abaixo. Bom procedimento quando queremos performance, afinal, por que trazer todos os campos se apenas vamos usar alguns?

Percebam que usei os alias criados anteriormente e não mais o nomes reais da lista Contatos (que seriam Title, para LastName, e FirstName).

Ao mandar rodar, temos:

image

Campos de outra lista projetados no resultado da query. E o melhor, podemos fazer query na outra lista já nessa mesma query! Exemplo:

query.Query = @"<Where>
<Eq>
<FieldRef Name='Nome' />
<Value Type='Text'>Thiago</Value>
</Eq>
</Where>
<OrderBy>
<FieldRef Name='Title' />
</OrderBy>"
;

O campo Nome, que usei na query, não “existe” em nenhuma das listas. É o alias, ou campo projetado, criado aqui:

query.ProjectedFields =
"<Field Name='Nome' Type='Lookup' " +
"List='Contatos' ShowField='FirstName'/>" +
"<Field Name='SobreNome' Type='Lookup' " +
"List='Contatos' ShowField='Title'/>";

Vamos testar! De acordo com a Query, é para trazer ordenado pelo título do evento, apenas os eventos que o Nome do responsável seja igual a Thiago.

image

É isso aí, até mais! Smile

Abraço!

Executando Queries de Alta Performance com SPSiteDataQuery

April 20, 2012 1 comment

Olá! Smile

Cenário:

Meu grande amigo postou recentemente uma série de posts sobre como o SharePoint pode armazenar listas com grandes quantidades de informação:

http://rodrigoromano.net/2012/04/12/trabalhando-com-listas-grandesparte-i/ – Trabalhando com listas “grandes”–Parte I

http://rodrigoromano.net/2012/04/18/trabalhando-com-listas-grandesparte-2/ – Trabalhando com listas “grandes” – Parte 2

Para auxiliar esse processo, o Object Model do SharePoint possui um objeto excepcional para executar queries de alta performance. Esse objeto é o SPSiteDataQuery (http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.spsitedataquery.aspx).

Diferente do objeto comum de queries, o SPQuery (http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.spquery.aspx) que só pode ser executado em uma única lista (SPList) por vez, o SPSiteDataQuery pode executar queries em mais de uma lista e em mais de um site. Ou seja, ele é cross-site e cross-lista, e a performance é absurdamente melhor. Então, se puder, utilize o SPSiteDataQuery quando for retornar informações que obedecem mesma condição em várias listas/sites.

SOLUÇÃO:

Para utilizar o SPSiteDataQuery em nossas queries, precisamos definir algumas propriedades:

SPSiteDataQuery.Webs: Define o escopo da query. Por padrão (string.Empty), irá buscar apenas no site (SPWeb) em que rodamos o SPSiteDataQuery.

· "<Webs Scope=’SiteCollection’ />": Define o escopo para buscar em todos os sites da coleção de sites (SPSite).

· "<Webs Scope=’Recursive’ />": Define o escopo para buscar no site (SPWeb) que está rodando o SPSiteDataQuery e em todos seus subsites.

SPSiteDataQuery.Lists: Define quais listas incluir na query.

· Podemos utilizar o template da lista (Ex: "<Lists ServerTemplate=\"105\" />"; – Esse exemplo busca apenas as listas com template 105 ou seja, as listas de Contatos do SharePoint) ou GUIDs das listas que queremos pesquisar (Ex: "<Lists><List ID=’416E2D08-46F1-4029-9729-C1B73850BF12′ /><List ID=’923CD349-6A71-46DE-9A2D-7F6703EC0756’/></Lists>").

SPSiteDataQuery.ViewFields: Definimos quais fields queremos retornar na query. Por padrão, se não especificarmos nenhum campo, será retornado WebId, ListId e ID de cada item encontrado.

· Ex: "<FieldRef Name=’Title’ Type=’Text’ /><FieldRef Name=’ID’ Type=’Counter’/><FieldRef Name=’Author’ Type=’User’/><FieldRef Name=’Created’ Type=’DateTime’/><FieldRef Name=’Modified’ Type=’DateTime’/>";

E finalmente,

SPSiteDataQuery.Query: Definimos a condição que os itens retornados devem obedecer, ou simplesmente ordenamos os resultados.

· Ex: “<OrderBy><FieldRef Name=’Modified’ Ascending=’False’/></OrderBy>”;

Opcionalmente, podemos limitar a quantidade de items retornados com SPSiteDataQuery.RowLimit.

Na íntegra:

 

protected override void CreateChildControls()
{
using (SPSite site = new SPSite(SPContext.Current.Web.Url))
{
using (SPWeb web = site.OpenWeb())
{
try
{
SPSiteDataQuery q = new SPSiteDataQuery();
q.Lists = "<Lists><List ID='416E2D08-46F1-4029-9729-C1B73850BF12' /><List ID='923CD349-6A71-46DE-9A2D-7F6703EC0756'/></Lists>";
q.Webs = "";
q.Query = "<OrderBy><FieldRef Name='Modified' Ascending='False'/></OrderBy>";
q.ViewFields = @"<FieldRef Name='Title' Type='Text' /><FieldRef Name='ID' Type='Counter'/><FieldRef Name='Author' Type='User'/><FieldRef Name='Created' Type='DateTime'/><FieldRef Name='Modified' Type='DateTime'/>";
q.RowLimit = 10;

DataTable dt = web.GetSiteData(q);
DataView dv = new DataView(dt);

GridView grid = new GridView();
grid.DataSource = dv;
grid.AutoGenerateColumns = false;
grid.Width = Unit.Percentage(100);
grid.CssClass = "whatsnew";

BoundField boundField = new BoundField();
boundField.HeaderText = "Nome";
boundField.DataField = "Title";
grid.Columns.Add(boundField);

boundField = new BoundField();
boundField.HeaderText = "Criado Por";
boundField.DataField = "Author";
grid.Columns.Add(boundField);

this.Controls.Add(grid);
}
catch (Exception ex)
{
ULSLogging.LogErrorInULS(ex, "What's New");
}
}
}
}

 

Abraço!