2009-06-11 33 views
8

Tenemos una aplicación de flujo de trabajo de Java que utiliza una base de datos Oracle para rastrear sus pasos e interacciones con otros servicios. Durante una ejecución del flujo de trabajo, se realizan varias inserciones/actualizaciones/selecciones y ocasionalmente la selección no devolverá los datos actualizados, aunque la confirmación de inserción/actualización se ejecutó antes de que se completara correctamente. Después de que se produzcan errores en el flujo de trabajo (debido a los datos incorrectos), si volvemos atrás y verificamos la base de datos a través de una aplicación de terceros, se mostrarán los datos nuevos/actualizados. Parece haber un desfase entre el momento en que se cumplen nuestros compromisos y cuándo son visibles. Esto ocurre en aproximadamente el 2% de todas las ejecuciones de flujo de trabajo y aumenta durante el uso intensivo de la base de datos.Oracle lag entre commit y select

Nuestro equipo de soporte de base de datos sugirió cambiar un parámetro max-commit-propagation-delay a 0, ya que de manera predeterminada era 700. Esto parecía ser una solución posible pero al final no solucionó nuestro problema.

La aplicación se ejecuta en WebSphere y la base de datos Oracle está configurada como un origen de datos JDBC. Estamos usando Oracle 10.1g. La aplicación está escrita en Java 1.5.

Cualquier ayuda sería apreciada.

edición: ejemplo de código

DataSource ds; // spring configured 

String sql = "INSERT INTO " + currentTable + " (" + stepId + ',' + stepEntryId + ", " + stepStepId + ", " + stepActionId + ", " + stepOwner + ", " + stepStartDate + ", " + stepDueDate + ", " + stepFinishDate + ", " + stepStatus + ", " + stepCaller + ") VALUES (?, ?, ?, null, ?, ?, ?, null, ?, null)"; 

Connection conn = ds.getConnection(); 
PreparedStatement stmt = conn.prepareStatement(sql); 
// set values 
stmt.executeUpdate(); 
// close connections 

// later on in the code... 
Connection conn = ds.getConnection(); 
PreparedStatement stmt = null; 
ResultSet rset = null; 

String sql = "SELECT " + stepId + ", " + stepStepId + ", " + stepActionId + ", " + stepOwner + ", " + stepStartDate + ", " + stepDueDate + ", " + stepFinishDate + ", " + stepStatus + ", " + stepCaller + " FROM " + currentTable + " WHERE " + stepEntryId + " = ?"; 
stmt = conn.prepareStatement(sql); 

stmt.setLong(1, entryId); 

rset = stmt.executeQuery(); 
//close connections 
+0

En [Documentación de Oracle] (http://download.oracle.com/docs/cd/B14117_01/server.101/b10755/initparams115.htm), parece que el parámetro 'max_commit_propagation_delay' solo se aplica a la configuración de RAC. ¿Te estás conectando a una instancia de RAC? –

+0

¿Los datos se están comprometiendo como parte de una transacción? O la lectura? –

Respuesta

1

son el uso usando un ORM? podría estar seleccionando desde la memoria caché y no formando la base de datos después del cambio.

+0

No estamos usando un ORM, las tablas y las sentencias SQL son realmente básicas y solo se utilizan para almacenar una cantidad pequeña de información de seguimiento. – Andrew

+0

publique un fragmento de código si es posible ... –

+1

si las sugerencias de @Steve Broberg no funcionan, puede intentar usar la misma conexión –

6

De manera predeterminada, el comportamiento que describió debería ser imposible: los cambios realizados en una transacción comprometida estarán disponibles inmediatamente para todas las sesiones. Sin embargo, hay excepciones:

  1. ¿Está utilizando alguna de las opciones ESCRIBIR en el comando COMPRAR? Si no lo está, confirme el valor de su parámetro de inicialización COMMIT_WRITE. Si cualquiera de los dos está utilizando el "ESCRIBIR EL LOTE" o especialmente "ESCRIBIR EL LOTE NOWAIT", podría estarse abriendo a problemas de concurrencia. "WRITE BATCH NOWAIT" normalmente se usaría en casos donde la velocidad de sus transacciones de escritura es más importante que los posibles problemas de concurrencia. Si el parámetro de inicialización está usando las variantes de "escritura", puede anular sobre una base de transacción mediante la especificación de la cláusula de inmediato en su commit (see COMMIT)

  2. es la transacción que está intentando leer los datos que invocan SET TRANSACTION antes a la otra transacción comprometida? El uso de SET TRANSACTION para especificar el nivel de SERIALIZACIÓN READ ONLY o SERIALIZABLE resultará en la que la transacción no ver los cambios que se producen a partir de otras sesiones comprometidos que se produjeron después de la invocación de SET TRANSACTION (see SET TRANSACTION)

edición: Veo que usted está usando una clase DataSource. No estoy familiarizado con esta clase, supongo que es un recurso para compartir la conexión. Me doy cuenta de que su diseño de aplicación actual puede no facilitar el uso del mismo objeto de conexión en todo su flujo de trabajo (los pasos pueden estar diseñados para funcionar de forma independiente, y no construyó en una instalación para pasar un objeto de conexión de un paso al siguiente), pero debe verificar que los objetos de conexión que se devuelven al objeto DataSource estén "limpios", especialmente con respecto a las transacciones abiertas. Es posible que no esté invocando SET TRANSACTION en su código, pero otro consumidor de DataSource en otro lugar puede estar haciéndolo y devolviendo la conexión a la fuente de datos con la sesión todavía en modo SERIALIZABLE o READ ONLY. Cuando se comparte la conexión, es imperativo que todas las conexiones se deshagan antes de entregárselos a un nuevo consumidor.

Si usted no tiene control o visibilidad al comportamiento de la clase origen de datos, es posible que desee probar la ejecución de un ROLLBACK en la conexión recién adquirida para asegurar que ya se ha establecido ninguna transacción persistente.

+0

+1 !!! Ni siquiera había considerado MAYOR COMPROMISO aparte de ESPERAR INMEDIATAMENTE o que el nivel de aislamiento de la transacción no era LEÍDO COMPROMETIDO. – spencer7593

+0

Disculpa la tardanza en contactarte. No creo que esté usando ninguna opción de escritura para la confirmación. Solo estoy usando lo que sea que la clase de conexión java sea la predeterminada, lo mismo ocurre con la configuración de aislamiento de transacción. Aquí hay algunos enlaces de documentación para las clases de Java que estoy usando: http://java.sun.com/j2se/1.5.0/docs/api/javax/sql/DataSource.html http: //java.sun .com/j2se/1.5.0/docs/api/java/sql/Connection.html http://java.sun.com/j2se/1.5.0/docs/guide/jdbc/getstart/connection.html – Andrew

4

Si el equipo de DBA intentó modificar el parámetro max_commit_propagation_delay, probablemente signifique que se está conectando a una instancia de RAC (i-e: varios servidores distintos que acceden a una sola base de datos).

En ese caso, cuando cierre y vuelva a abrir la conexión en su código java, existe la posibilidad de que un servidor diferente le responda. El parámetro de retraso significa que hay un marco de tiempo pequeño cuando las dos instancias no estarán exactamente en el mismo punto en el tiempo. La respuesta que está recibiendo es consistente con un punto en el tiempo, pero puede no ser la más reciente.

Según lo propuesto por KM, la solución más fácil sería mantener la conexión abierta después de la confirmación.

Como alternativa, también podría agregar un retraso después de haber cerrado la conexión si es práctico (si esto es un trabajo por lotes y el tiempo de respuesta no es crítico, por ejemplo).

0

Esto suena como un problema con RAC, con conexiones a dos instancias diferentes y el SCN no está sincronizado.

Como solución alternativa, considere no cerrar la conexión de la base de datos y obtener una nueva, sino reutilizar la misma conexión.

Si eso no es posible, agregue un reintento a la consulta que intenta recuperar la fila insertada. Si la fila no se devuelve, espere un poco y vuelva a intentar la consulta. Ponlo en un ciclo, después de un número específico de reintentos, entonces puedes fallar.

[ANEXO]

En su respuesta, Steve Broberg (1!) Plantea ideas interesantes. No había considerado:

  • la COMMIT podría ser otra cosa que IMMEDIATE WAIT
  • el nivel de aislamiento podría ser cualquier cosa que no sea LEER COMPROMETIDOS

hice considerar la posibilidad de consulta de flashback, y lo descartó sin mencionarlo, ya que no hay una razón aparente de que el OP esté usando la retrospectiva y no hay evidencia de tal cosa en el fragmento de código.)

[/ ADDENDUM]

0

Una posible solución puede ser utilizar la transacción JTA. Mantiene su conexión abierta "detrás de la escena" en múltiples conns jdbc abiertas/cerradas. Tal vez mantendrá su conexión en el mismo servidor y evitará este problema de sincronización.

UserTransaction transaction = (UserTransaction)new InitialContext().lookup("java:comp/UserTransaction"); 
transaction.begin(); 
// doing multiple open/close cxs 
transaction.commit(); 
0

El fragmento de código en realidad no incluía la confirmación.

Si está asumiendo/confiando en la conexión cercana que realiza la confirmación, puede no ser síncrona (es decir, la java puede informar que la conexión está cerrada cuando le dice a Oracle que cierre la conexión, lo que significa que puede estar antes de la commit es completado por Oracle).

+0

http : //java.sun.com/j2se/1.5.0/docs/api/java/sql/Connection.html De forma predeterminada, un objeto de conexión está en modo de confirmación automática, lo que significa que confirma automáticamente los cambios después de ejecutar cada uno declaración. – Andrew

+0

Yuck. Pero lo mismo puede aplicarse, ya que no tienes control sobre cómo/cuándo se está actuando el compromiso. Además de eso, hay una penalización de rendimiento (y posiblemente consecuencias de integridad de datos) al comprometer innecesariamente. Desactívela, comprométase explícitamente y vea si el problema desaparece. –

0

No veo confirmación en su código.Son las declaraciones más importantes en una aplicación así que me gustaría tenerlas escritas explícitamente cada vez, sin depender de close() o tal.

También es posible que la confirmación automática se establezca en verdadero de manera predeterminada en su (s) conexión (es) lo que explicaría exactamente el comportamiento (se confirma después de cada inserción/actualización).

Puede comprobar que se compromete exactamente donde lo desea, p. al final de la transacción y no antes?

Si hay confirmaciones cuando está parcialmente finalizado, entonces tiene una condición de carrera entre sus hilos que también explicaría por qué hay más problemas cuando la carga es más grande.

+0

Tengo la confirmación automática establecida en verdadero, aunque no estoy seguro de cómo eso explicaría el comportamiento que estoy viendo. Realizo una inserción y executeUpdate() que se confirma automáticamente, luego realizo una selección y esa fila insertada no se encuentra. No veo cómo puede ocurrir una condición de carrera aquí, teniendo en cuenta que executeUpdate() no volverá hasta que se haya confirmado. – Andrew

0

"aunque la confirmación de inserción/actualización se ejecutó antes de que se completara correctamente".

Esto me sugiere que está emitiendo una confirmación(), y luego espera volver a leer exactamente los mismos datos (es lectura repetible).

Esto me sugiere que no debe comprometerse. Siempre que desee asegurarse de que NINGUNA OTRA TAREA pueda modificar NINGUNO de los datos que ESPERA EXPLÍCITAMENTE que permanezcan estables, no puede darse el lujo de liberar bloqueos (que es lo que hace commit).

Tenga en cuenta que mientras mantiene un bloqueo en algún recurso, se acumularán otros subprocesos "esperando a que ese recurso esté disponible". La probabilidad de que esa pila no esté vacía en el momento de soltar su bloqueo aumenta a medida que aumenta la carga general del sistema. Y lo que su DBMS concluirá cuando (finalmente) emita "commit", es concluir que, "oye, wow, este tipo finalmente ha terminado con este recurso, así que ahora puedo dejar que los otros tipos que esperan lo intenten con eso (¡Y NO HAY NADA para evitar que "su cosa" sea una actualización!) ".

Quizás haya problemas que ver con el aislamiento instantáneo de Oracle que estoy pasando por alto. Disculpas si es así.

Cuestiones relacionadas