Blog

Connection Pooling – Der alte Hut

„No ManagedConnections available within configured blocking timeout […]”

Schon mal gelesen? Wenn nicht, bist Du entweder gut, oder die Last Eurer Systeme ist sehr gering. Bei mir war es im letzten Projekt bis vor kurzem Letzteres. :)
Die Nachricht wird vom JBoss AS in eine Exception verpackt und bedeutet sinngemäß, dass keine vom Container verwalteten Verbindungen mehr zur Verfügung stehen.
Wem klar ist, wie so etwas passieren und was man dagegen tun kann, der kann jetzt weitersurfen.
Schönen Tag noch. :)

Wie kann so etwas passieren?

Ein solcher Fall tritt ein, wenn sämtliche Verbindungen eines Pools in Transaktionen involviert sind – sie müssen nicht einmal in Verwendung sein.

Und die Folgen?

Im Prinzip gibt es zwei mögliche Folgen von „zu wenig Verbindungen“:

  1. Der Durchsatz pro Zeiteinheit wird verringert, weil das Beziehen von Verbindungen blockiert.
  2. Transaktionen brechen ab, weil innerhalb eines bestimmten Zeitfensters keine Verbindung verfügbar ist.

Mögliche Ursachen

Im Prinzip ist es falsch, im Plural zu sprechen, denn alles lässt sich auf einen einfachen Fakt reduzieren: Verbindungen werden zu lange verwendet. Was genau bedeutet „Verwendung“ in diesem Kontext? Dazu ein Code-Beispiel aus der EJB 3.x-Welt:

// ...snip
public class SomeServiceImpl implements SomeService {
   // ...snip
   @Resource(mappedName = "java:DefaultDS")
   protected DataSource dataSource;

   @Override
   public Something loadSomething(long id) {
       Connection c = null;
       try {
           c = dataSource.getConnection();
           // ...snip
       } finally {
           DbUtils.closeQuietly(c);
       }
   }
}

Zum Laden von Something benötigen wir eine (Datenbank-)Verbindung. Diese beziehen wir – überwacht vom Container – mit dataSource.getConnection() und geben sie mittels DbUtils.closeQuietly(c) wieder frei. Oder nicht?

<Fünf Minuten Pause, um über Resourcen-Verwaltung in Applikations-Servern nachzudenken.>

Die klare Antwort auf die Frage: Ja! Ja, wir geben sie nicht wieder frei! :) Eine Verbindung wird erst nach Abschluss der Transaktion wieder freigegeben, in der sie bezogen wurde. Voilà: Die Ursache. Handelt es sich bei der Transaktion um einen Langläufer, werden Resourcen unnötig lang gebunden.

Was tun?

Wenn wir uns das Code-Beispiel genauer ansehen, werden wir feststellen, dass der Standard- TransactionAttributeType.REQUIRED greift. Abhilfe schafft schon die Verwendung des alternativen TransactionAttributeType.NOT_SUPPORTED. Hierbei wird immer ohne Transaktion gearbeitet. Und die Magie dabei? Hiermit führt DbUtils.closeQuietly(c) effektiv zu einer Freigabe von c.

Das ist alles?

Ja und nein. Man muss immer bedenken, dass man mit TransactionAttributeType.NOT_SUPPORTED ggf. eine Transaktion aussetzt. Das kann – je nach Isolations-Level – zu unerwünschten Verhaltensweisen führen.

Geht das nicht besser?

Ja und nein. :) TransactionAttributeType.SUPPORTS ist noch ein interessanter Kandidat in diesem Kontext. Hiermit wird die Transaktion des Aufrufers übernommen, falls vorhanden, sonst arbeitet man einfach ohne (so wie oben beschrieben). Damit ist die Isolations-Problematik gelöst, aber natürlich auf Kosten einer anderen: Wenn der Aufrufer noch keine Daten modifiziert hat, bindet der Aufruf trotzdem die bezogene Verbindung an die Transaktion.

Und die übrigen TransactionAttributeTypes?

…sind in diesem Kontext nicht relevant.

Fazit

Ich wollte mit diesem Artikel aufzeigen, dass es nach wie vor durchaus sinnvoll ist, über Transaktionen im EJB-Umfeld nachzudenken – während der Entwicklung. ;)
Eine individuelle Bewertung, welcher TransactionAttributeType für eine EJB-Methode sinnvoll ist, kann sich lohnen, weil es manchmal den Durchsatz dramatisch verbessert. Aber Vorsicht ist geboten, denn man muss sich jedes Mal wieder zwei Fragen stellen:

  • Kann ich es mir erlauben, die laufende Transaktion auszusetzen?
  • Kann ich es mir erlauben, ohne Transaktion zu arbeiten?

Die Antworten hängen davon ab, was man in der Implementierung macht.

Über den Autor

Antwort hinterlassen