A significant part of sql server process memory has been paged out

En una ocasión, y al parecer fue provocado por una actualización de Windows Server 2012, el servicio de SQL Server 2012 mostraba los siguientes problemas:

  • Reducción en el desempeño, mostrado en la lentitud con que abrían las páginas que utilizaban SQL Server.
  • En Event Viewer, la aparición de este error: “A significant part of sql server process memory has been paged out. This may result in a performance degradation. Duration: 1534 seconds. Working set (KB): 20276, committed (KB): 136852, memory utilization: 14%%”
  • Después de algunos minutos, no abrían las páginas hospedadas en el servidor, ni siquiera aquellas que no utilizaban SQL Server.
  • Al loguearse al servidor con Remote Desktop, en ocasiones la lentitud era demasiada como para poder hacer algo. En otras ocasiones la conexión se atoraba mostrando únicamente una pantalla negra.

Una vez reiniciado el servidor, todo volvía a la normalidad, pero horas después se repetía el problema.

Encontré en esta página un procedimiento que resolvió el problema.

Pasos a seguir:

  • Haz clic en el botón “Start” con el botón derecho del mouse y selecciona “Run”.
  • Teclea “gpedit.msc” y oprime “enter”.
  • Verás la ventana del “Local Group Policy Editor”.
  • Abre esta llave: “Computer configuration > Windows Settings > Security Settings > Local Policies > User Rights Assignment”.
  • Haz doble clic en “Lock pages in memory”.
  • En “Local Security Setting” tienes que añadir la cuenta bajo la cual se ejecuta el servicio de SQL Server (Sqlservr.exe). Normalmente es “NT SERVICE\MSSQLSERVER”, pero si no estás seguro, utiliza estas instrucciones para encontrarla.
  • Cierra la ventana y reinicia el servicio de SQL Server.

En mi caso esto resolvió el problema, y debería funcionar para ti también. ¡Suerte!

Fuente:
https://support.microsoft.com/en-us/help/918483/how-to-reduce-paging-of-buffer-pool-memory-in-the-64-bit-version-of-sql-server

Cómo encontrar la cuenta bajo la cual corre SQL Server

El archivo mediante el cual se ejecuta el servicio de SQL Server es, normalmente, el siguiente:

C:\Program Files\Microsoft SQL Server\MSSQL11.MSSQLSERVER\MSSQL\Binn\sqlservr.exe

Y normalmente la cuenta bajo la cual se ejecuta ese archivo es:

NT Service\MSSQLSERVER

Sin embargo, en ocasiones necesitamos asegurarnos de que estos datos son correctos, y puedes hacerlo ejecutando esta sentencia mediante SQL Server Management Studio:

SELECT DSS.servicename,
 DSS.startup_type_desc,
 DSS.status_desc,
 DSS.last_startup_time,
 DSS.service_account,
 DSS.is_clustered,
 DSS.cluster_nodename,
 DSS.filename,
 DSS.startup_type,
 DSS.status,
 DSS.process_id
 FROM sys.dm_server_services AS DSS;

La sentencia te regresará varios datos, entre los cuales se encuentran:

  • servicename. Es el nombre del servicio.
  • service_account. Es la cuenta bajo la cual correo el servicio.
  • filename. Es el archivo mediante el cual se ejecuta el servicio.

Encontrarás un registro tanto para el servicio “SQL Server (MSSQLSERVER)” como para el servicio “SQL Server Agent (MSSQLSERVER)”.

Validation failed for one or more entities. See ‘EntityValidationErrors’ property for more details.

Al intentar insertar varios registros mediante mi Repository y después intentar guardar los cambios, me aparecía este error:

Validation failed for one or more entities. See 'EntityValidationErrors' property for more details.

Esto ocurre cuando estás tratando de insertar un registro que rompe las reglas de validación. Por ejemplo, estás tratando de insertar un valor en un campo que no coincide con su tipo de dato o estás ingresando un texto “nvarchar” de mayor longitud al permitido.

Para poder visualizar los errores de validación que se están generando, agrega un “try…catch” en el momento en que guardas, de esta forma:

try 
{
   miRepositorio.Guardar();
} 
catch (DbEntityValidationException dbEx) 
{
   foreach(var validationErrors in dbEx.EntityValidationErrors) 
   {
      foreach(var validationError in validationErrors.ValidationErrors) 
      {
         Trace.TraceInformation("Property: {0} Error: {1}",
             validationError.PropertyName,
             validationError.ErrorMessage);
      }
   }
}

Ahora corre tu aplicación en modo “debug” utilizando Visual Studio.
Abre la ventana “output” que encuentras en el menú Debug -> Windows -> Output.

La instrucción “Trace” va a escribir los errores en esta ventana. Es posible que el contenido de esta ventana esté lleno de información del “debugger”, si es así oprime CTRL-F y busca “Property:”, verás lo siguiente:

Visual Studio Trace

De esta forma podrás ver los errores que están apareciendo y modificar tu código para corregirlos.

Fuente.

There is already an open DataReader associated with this Command which must be closed first.

Tenía en mi repository este método:

public IEnumerable Lista()
{
   return context.Estudiante
          .OrderBy(o => o.ApellidoPaterno);
}

Y luego desde un controller llamaba este método así:

var estudiantes= repositorioEstudiante.Lista();

Más adelante tenía una línea donde solicitaba esta propiedad: estudiante.Campus

Como la tabla “Estudiante” tiene una relación de uno a muchos con la tabla “Campus” (un campus tiene muchos estudiantes, y un estudiante pertenece únicamente a un campus), entonces al solicitar esa propiedad aparecía este error:

There is already an open DataReader associated with this Command which must be closed first.

Esto ocurre porque debido a lazy loading el método Lista tiene todos los elementos del estudiante, pero no el del campus relacionado. Cuando intentas llamar la propiedad estudiante.Campus se intenta hacer una nueva conexión y por eso ocurre el error.

Para solucionarlo, debes indicar al método Lista que incluya de una vez todos los registros relacionados de campus.

Se hace así:

public IEnumerable Lista()
{
   return context.Estudiante
          .OrderBy(o => o.ApellidoPaterno)
          .Include(e => e.Campus);
}

De esta forma estás haciendo Eager Loading, lo cual genera una sentencia join en la base de datos que carga de una vez todos los registros. Al solicitar la propiedad estudiante.Campus entonces no estás haciendo otra conexión nueva, y desaparece el error.

Para aprender más sobre los tipos de carga que existen checa esta página en la parte donde dice “Lazy, Eager, and Explicit Loading of Related Data”.

Otra solución al problema es activar MARS, pero prefiero la que te puse en este artículo.

Cómo encontrar el Id de un registro recién insertado en una tabla de SQL Server

En una aplicación asp.net algunas veces necesitas encontrar el Id del registro recién insertado para utilizarlo en otra operación.

En este ejemplo voy a suponer que tienes un DAL y un BLL, es decir, un archivo con terminación .xsd y un archivo con terminación .vb.

En el “TableAdapter” de tu archivo xsd crea una nueva consulta y pon lo siguiente:

INSERT INTO [MiTabla] ([Campo1], [Campo2])
OUTPUT INSERTED.IdMiTabla
VALUES (@Campo1, @Campo2)

Nota la segunda línea, ésta hace que el valor de salida de tu consulta NO sea el número de registros afectados, sino el valor del Id del registro recién insertado.

Pero para que esto funcione falta una cosa más: en tu archivo xsd selecciona la consulta en tu “TableAdapter” y abre sus propiedades. Ahora establece la propiedad “ExecuteMode” a “Scalar”, así:

scalar

Listo, suponiendo que a tu “TableAdapter” le diste el nombre “InsertarYRegresarElUltimoId”, ahora en tu BLL utiliza un código similar a este:

Dim UltimoId As Integer = Adapter.InsertarYRegresarElUltimoId(Campo1, Campo2)

El valor guardado en la variable “UltimoId” será el correspondiente al Id recién insertado y ahora lo puedes usar para las demás operaciones que vas a hacer.