Los registros de eventos para mi aplicación .NET muestran que ocasionalmente interbloqueos mientras se lee desde el servidor Sql. Esto generalmente es muy raro ya que ya hemos optimizado nuestras consultas para evitar interbloqueos, pero a veces aún ocurren. En el pasado, hemos tenido algunos bloqueos que ocurren al llamar a la función ExecuteReader
en nuestra instancia SqlCommand
. Para solucionar este código de reintento hemos añadido simplemente a ejecutar la consulta, así:Cómo recuperarse de la excepción de interbloqueo durante SqlDataReader.Read()
//try to run the query five times if a deadlock happends
int DeadLockRetry = 5;
while (DeadLockRetry > 0)
{
try
{
return dbCommand.ExecuteReader();
}
catch (SqlException err)
{
//throw an exception if the error is not a deadlock or the deadlock still exists after 5 tries
if (err.Number != 1205 || --DeadLockRetry == 0)
throw;
}
}
Esto funcionó muy bien para el caso en que el punto muerto que sucedió durante la consulta inicial ejecutar, pero ahora estamos consiguiendo los puntos muertos mientras iterando a través de los resultados usando la función Read()
en el SqlDataReader
devuelto.
Una vez más, no estoy preocupado por la optimización de la consulta, sino simplemente tratando de recuperar en el raro caso de que se produzca un punto muerto. Estaba pensando en usar un proceso de reintento similar. Podría crear mi propia clase que hereda de SqlDataReader
que simplemente anula la función Read
con el código de reintento. De esta manera:
public class MyDataReader : SqlDataReader
{
public override bool Read()
{
int DeadLockRetry = 5;
while (DeadLockRetry > 0)
{
try
{
return base.Read();
}
catch (SqlException ex)
{
if (ex.ErrorCode != 1205 || --DeadLockRetry == 0)
throw;
}
}
return false;
}
}
¿Es este el enfoque correcto? Quiero estar seguro de que los registros no se saltan en el lector. ¿Volverá a intentar Read
después de un interbloqueo salte las filas? Además, ¿debo llamar al Thread.Sleep
entre reintentos para dar tiempo a la base de datos para salir del estado de punto muerto, o es suficiente? Este caso no es fácil de reproducir, así que me gustaría estar seguro antes de modificar cualquiera de mis códigos.
EDIT:
Conforme a lo solicitado, algo más de información acerca de mi situación: En un caso, tengo un proceso que realiza una consulta que carga una lista de ID de registros que necesitan ser actualizados. Luego recorro esa lista de identificadores usando la función Read
y ejecuto un proceso de actualización en ese registro, que eventualmente actualizará un valor para ese registro en la base de datos. (No, no hay forma de realizar la actualización en la consulta inicial, muchas otras cosas ocurren para cada registro que se devuelve). Este código ha estado funcionando bien por un tiempo, pero estamos ejecutando bastante código para cada registro, así que puedo imaginar que uno de esos procesos está creando un bloqueo en la tabla inicial que se está leyendo.
Después de pensarlo un poco, la sugerencia de Scottie de utilizar una estructura de datos para almacenar los resultados probablemente arreglaría esta situación. Podría almacenar los ID devueltos en un List<int>
y recorrerlos. De esa forma, los bloqueos en las filas se pueden eliminar de inmediato.
Sin embargo, todavía estaría interesado en saber si hay una forma general de recuperarse de interbloqueos en una lectura.
Podría dar más información sobre el uso por DB? Es extraño tener un punto muerto mientras se leen datos. Significa que su código ya tiene algunos bloqueos para otros datos y otra transacción ha bloqueado lo que está tratando de leer y está esperando los datos a los que ha accedido previamente. ¿Puedes llamar a ExecuteReader en otra transacción? – Timores