Oracle se situe entre commit et select

Nous avons une application de stream de travail Java qui utilise une firebase database Oracle pour suivre ses étapes et ses interactions avec d’autres services. Au cours d’un workflow, plusieurs insert / update / selects sont effectués et parfois, select ne renvoie pas les données mises à jour, même si la validation insert / update ayant été exécutée avant son achèvement. Une fois les erreurs de stream de travail terminées (en raison de données incorrectes), si nous revenons en arrière et vérifions la firebase database via une application tierce, les données nouvelles / mises à jour s’afficheront. Il semble y avoir un décalage entre le moment où nos commits passent et quand ils sont visibles. Cela se produit dans environ 2% de tous les stream de travail exécutés et augmente au cours d’une utilisation intensive de la firebase database.

Notre équipe de support de firebase database a suggéré de changer le paramètre max-commit-propagation-delay en 0, car il est réglé par défaut sur 700. Cela semblait être une solution possible mais n’a finalement pas résolu notre problème.

L’application s’exécute sur WebSphere et la firebase database Oracle est configurée en tant que source de données JDBC. Nous utilisons Oracle 10.1g. L’application est écrite en Java 1.5.

Toute aide serait appréciée.

edit: exemple de code

DataSource ds; // spring configured Ssortingng 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; Ssortingng 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 

Par défaut, le comportement que vous avez décrit devrait être impossible: les modifications apscopes à une transaction validée deviennent immédiatement disponibles pour toutes les sessions. Cependant, il y a des exceptions:

  1. Utilisez-vous l’une des options WRITE de la commande COMMIT? Sinon, confirmez la valeur de votre paramètre d’initialisation COMMIT_WRITE. Si l’un utilise le “WRITE BATCH” ou en particulier “WRITE BATCH NOWAIT”, vous pourriez vous exposer à des problèmes de concurrence. “WRITE BATCH NOWAIT” est généralement utilisé dans les cas où la rapidité de vos transactions d’écriture est plus importante que d’éventuels problèmes de simultanéité. Si votre paramètre d’initialisation utilise les variantes “WRITE”, vous pouvez le remplacer par transaction en spécifiant la clause IMMEDIATE dans vos commits ( voir COMMIT ).

  2. La transaction qui tente de lire les données en appelant SET TRANSACTION avant la validation de l’autre transaction? L’utilisation de SET TRANSACTION pour spécifier SERIALIZATION LEVEL READ ONLY ou SERIALIZABLE aura pour effet que la transaction ne verra plus de modifications des autres sessions validées survenues après l’appel de SET TRANSACTION ( voir SET TRANSACTION ).

edit: Je vois que vous utilisez une classe DataSource. Je ne connais pas bien cette classe – je suppose que c’est une ressource de partage de connexion. Je me rends compte que la conception actuelle de votre application ne facilite peut-être pas l’utilisation du même object de connexion tout au long de votre stream de travail (les étapes peuvent être conçues pour fonctionner de manière autonome, et vous n’avez pas intégré de fonction permettant de passer d’un object de connexion à un autre). next), mais vous devez vérifier que les objects de connexion renvoyés à l’object DataSource sont “propres”, notamment en ce qui concerne les transactions en cours. Il est possible que vous n’appeliez pas SET TRANSACTION dans votre code, mais qu’un autre utilisateur de DataSource le fasse ailleurs, et que la connexion soit renvoyée à la source de données avec la session toujours en mode SERIALIZABLE ou READ ONLY. Lors du partage de connexion, il est impératif que toutes les connexions soient annulées avant de les transférer à un nouveau consommateur.

Si vous n’avez aucun contrôle ou visibilité sur le comportement de la classe DataSource, vous pouvez essayer d’exécuter un ROLLBACK sur la connexion nouvellement acquise pour vous assurer qu’il n’y a pas de transaction en attente déjà établie.

Si l’équipe DBA a essayé de modifier le paramètre max_commit_propagation_delay , cela signifie probablement que vous vous connectez à une instance RAC (c’est-à-dire: plusieurs serveurs distincts accédant à une seule firebase database).

Dans ce cas, lorsque vous fermez et rouvrez la connexion dans votre code Java, il est possible que vous receviez une réponse sur un serveur différent. Le paramètre delay signifie qu’il y a un court laps de temps où les deux instances ne seront pas exactement au même moment. La réponse que vous obtenez correspond à un moment précis mais n’est peut-être pas la plus récente.

Comme proposé par KM, la solution la plus simple serait de garder la connexion ouverte après le commit.

Sinon, vous pouvez également append un délai après avoir fermé la connexion si cela est pratique (s’il s’agit d’un travail par lots et que le temps de réponse n’est pas critique, par exemple).

utilisez-vous en utilisant un ORM? il se peut qu’il choisisse dans le cache et ne forme pas la firebase database après le changement.

Une solution de contournement possible consiste à utiliser une transaction JTA. Il maintient votre connexion ouverte “en coulisse” sur plusieurs connexions jdbc ouvertes / fermées. Peut-être que cela maintiendra votre connexion sur le même serveur et évitera ce problème de synchronisation.

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

Cela ressemble à un problème avec RAC, avec des connexions à deux instances différentes et le SCN est désynchronisé.

En guise de solution de contournement, envisagez de ne pas fermer la connexion à la firebase database et d’en obtenir une nouvelle, mais de réutiliser la même connexion.

Si cela ne fonctionne pas, ajoutez une nouvelle tentative à la requête qui tente de récupérer la ligne insérée. Si la ligne n’est pas renvoyée, attendez un peu, puis relancez la requête. Mettez cela dans une boucle, après un nombre de tentatives spécifié, vous pouvez alors échouer.

[ADDENDA]

Dans sa réponse, Steve Broberg (+1!) Soulève des idées intéressantes. Je n’avais pas envisagé:

  • le COMMIT peut être autre chose IMMEDIATE WAIT
  • le niveau d’isolation de transaction peut être autre que READ COMMITTED

J’ai effectivement envisagé la possibilité d’une requête flashback et l’ai écartée sans la mentionner, car il n’y a aucune raison apparente pour que l’OP utilise une requête flashback, et aucune preuve d’une telle chose dans l’extrait de code.)

[/ADDENDA]

L’extrait de code n’incluait pas réellement le commit.

Si vous supposez / comptez sur la connexion fermée effectuant la validation, celle-ci risque de ne pas être synchrone (c.-à-d. Que le java peut signaler la connexion comme fermée quand il dit à Oracle de fermer la connexion, ce qui signifie que la validation doit être terminée avant Oracle).

Je ne vois pas de commit dans votre code. Ce sont les déclarations les plus importantes dans une telle application, je voudrais donc les écrire explicitement à chaque fois, sans s’appuyer sur close () ou autre.

Vous pouvez également avoir autocommit défini sur true par défaut sur votre (vos) connexion (s), ce qui expliquerait exactement le comportement (il est validé après chaque insertion / mise à jour).

Pouvez-vous vérifier que vous avez des commits exactement où vous le souhaitez, par exemple à la fin de la transaction et pas avant?

S’il y a des commits lorsque vous avez partiellement terminé, vous avez une condition de concurrence critique entre vos threads, ce qui expliquerait également pourquoi il y a plus de problèmes lorsque la charge est plus importante.

“Même si la validation insert / update ayant été exécutée avant son achèvement avec succès.”

Cela me suggère que vous émettez un commit () et que vous vous attendez ensuite à lire exactement les mêmes données à nouveau (c’est une lecture reproductible).

Cela me suggère que vous ne devriez pas vous engager. Tant que vous voulez vous assurer que AUCUNE AUTRE TÂCHE n’est capable de modifier TOUTES les données que vous vous attendez EXPLICITE à restr stables, vous ne pouvez pas vous permettre de libérer des verrous (ce que fait la validation).

Notez que pendant que vous gardez un verrou sur une ressource, d’autres threads s’emstacknt “en attente de la disponibilité de cette ressource”. La probabilité que cette stack soit non vide au moment où vous libérez votre verrou augmente au fur et à mesure que la charge générale du système augmente. Et ce que votre SGBD conclura lorsque vous (enfin) émettez “commit”, est de conclure que “heu wow, ce mec a enfin fini avec cette ressource. Je peux maintenant laisser tous les autres attendre et essayer.” leur truc avec ça (ET il n’ya RIEN d’empêcher que “leur truc” soit une mise à jour!) “.

Il y a peut-être des problèmes liés à l’isolement instantané d’Oracle que je néglige. Excuses si oui.