PooledConnection früher schließen oder abarbeitung queuen? wie vorgehen

Bitte aktiviere JavaScript!
Hi,

Ich habe hier in einem Projekt vor mir folgenden Code.
Der sieht eigentlich halbwegs sauber aus - aber das Problem ist das die Methode wohl zu oft aufgerufen wird.
Die Datenbank geht wegen der vielen Verbindungen in die Knie (oder eher auf den Boden)
Wenn dann der Finally-block abgearbeitet wird und die Verbindungen zurück gegeben werden ist die Datenbank aber schon hinüber und es bringt nichts mehr.

daher wollte ich euch mal fragen was man da machen kann am Code ohne alles auf den Kopf zu stellen.
Kann man die Verbindung früher schließen und nicht erst "nach dem return" und trotzdem so generisch bleiben?

Oder kann man innerhalb der Methode eine queue machen um es bis zu einer max Anzahl an Verbindungen zu zu lassen und dann eben blockiert?
Da dieser Bereich eigentlich nicht "mein" code ist wollte ich daran möglichst eingeschränkt Änderungen durchführen.

Ich hoffe ihr könnt mir ein paar tips geben


Code:
private <T> T executeQuery(String query, ResultContainer<T> resultContainer, PreSetter... preSetters) throws Exception{
        try {
            Connection c = null;
            try {
                PooledDataSource dataSource = atomicPooledDataSource.get(); // AtomicReference<PooledDataSource>
                if (dataSource == null) {
                    throw new Exception("No DB Connection");
                }
                c = dataSource.getConnection();
                PreparedStatement s = null;
                try {
                    int i = 1;
                    s = c.prepareStatement(query);
                    for (PreSetter preSetter : preSetters) {
                        i = preSetter.prepare(s, i);
                    }
                    ResultSet rs = null;
                    try {
                        rs = s.executeQuery();
                        return resultContainer.result(rs);
                    } finally {
                        if (rs != null) {
                            rs.close();
                        }
                    }
                } finally {
                    if (s != null) {
                        s.close();
                    }
                }
            } finally {
                if (c != null) {
                    c.close();
                }
            }
        } catch (SQLException e) {
            throw new Exception(e.getMessage(), e);
        }
    }
Code ist ein bisschen vereinfacht - aber es sollte alles da sein.
 
A

Anzeige




Vielleicht hilft dir unser Kurs hier weiter —> (hier klicken)
Code ist ein bisschen vereinfacht
Das ist vereinfachter Code? Dann möchte ich wirklich nicht die normale Variante sehen :p (oder vielleicht doch, ein bisschen Horror mag doch jeder...)

Die Datenbank geht wegen der vielen Verbindungen in die Knie (oder eher auf den Boden)
begrenz doch die Anzahl der möglichen Verbindungen? Wenn du ConnectionPools nutzt, sollte das doch problemlos möglich sein.
In der Methode braucht man dafür nichts ändern (und wenn du da doch was änderst: lösch sie und schreib sie in ~5 Zeilen neu)

Kann man die Verbindung früher schließen und nicht erst "nach dem return" und trotzdem so generisch bleiben?
Die Verbindung wird bereits so früh wie möglich geschlossen, nämlich direkt nachdem du sie benutzt. Vor'm benutzten kannst du sie logischerweise nicht schließen ;)
 
Das ist vereinfachter Code? Dann möchte ich wirklich nicht die normale Variante sehen :p (oder vielleicht doch, ein bisschen Horror mag doch jeder...)
Na zum glück is es dann nicht meiner ... der wäre vl noch schlimmer :D


begrenz doch die Anzahl der möglichen Verbindungen? Wenn du ConnectionPools nutzt, sollte das doch problemlos möglich sein.
In der Methode braucht man dafür nichts ändern (und wenn du da doch was änderst: lösch sie und schreib sie in ~5 Zeilen neu)
Genau dürfte es eine "ComboPooledDataSource()" sein.
Bei der ist wie ich sehe der default 15
https://www.mchange.com/projects/c3p0/#maxPoolSize
sie wird auch nicht anders konfiguriert so wie ich das sehe ... werde noch schauen ob vl andere config geladen wird (bin grad unterwegs und hab keinen zugriff).
Dher verstehe ich dass dann aber auch nicht. Wenn es nur 15 Verbindungen sein sollten- was ist den da los (kann mir nur vorstellen, das das ganze mehrfach instanziiert wird ... was aber auch nicht sein darf (in den logs sehe ich die Initialisierung nur ein mal)

Aber wenn die Limitierung funktionieren würde, würden dann die weiteren aufrufe von "executeQuery" nicht fehlschlagen?
Oder werden die Blockiert bis eine connection frei ist?

In 5 Zeilen? Glaub ich nicht ;)


Die Verbindung wird bereits so früh wie möglich geschlossen, nämlich direkt nachdem du sie benutzt. Vor'm benutzten kannst du sie logischerweise nicht schließen ;)
Hmmm dann dürfte es aber gar kein Problem geben - am Datenbank Server sehe ich aber das die Verbindungen nach oben gehen ... das verstehe ich dann nicht.
 
In 5 Zeilen? Glaub ich nicht
Puh, da musste ich doch ein paar extra Zeilenumbrüche einbauen, damit ich auf die 5 Zeilen komme:
Java:
private <T> T executeQuery(String query, 
        ResultContainer<T> resultContainer, 
        PreSetter... preSetters) throws Exception {
    executeQuery(atomicPooledDataSource.get(), query, resultContainer, preSetters);
}
SCNR :p
 
In 5 Zeilen? Glaub ich nicht ;)
7 ist gar nicht so weit entfernt von "etwa 5" (wenn man Klammern mal ignoriert...):p

Java:
private <T> T executeQuery(String query, ResultContainer<T> resultContainer, PreSetter... preSetters) throws SQLException {
    try (Connection c = Objects.requireNonNull(atomicPooledDataSource.get(), "No DB Connection").getConnection();
            PreparedStatement s = c.prepareStatement(query)) {
        int i = 1;
        for (PreSetter preSetter : preSetters) {
            i = preSetter.prepare(s, i);
        }
        return resultContainer.result(s.executeQuery());
    }
}
(Semantik bzgl Exceptions hat sich etwas geändert, da nicht mehr einfach Exception geworfen wird (aber das ist ja auch nicht grundlos Bad Practice....))

(Und dass Objects.requireNonNull lässt sich auch noch vermeiden, wenn man die Klasse drum herum etwas anders aufbaut. Entweder man verhindert null oder ergänzt einen extra Getter für die Connection)
 
Zuletzt bearbeitet:
Muss ich das verstehen?

Java:
private <T> T executeQuery(String query, ResultContainer<T> resultContainer, PreSetter... preSetters) throws SQLException {
    try (Connection c = Objects.requireNonNull(atomicPooledDataSource.get(), "No DB Connection").getConnection();
            PreparedStatement s = c.prepareStatement(query)) {
        int i = 1;
        for (PreSetter preSetter : preSetters) {
            i = preSetter.prepare(s, i);
        }
        return resultContainer.result(s.executeQuery());
    }
}
(Semantik bzgl Exceptions hat sich etwas geändert, da nicht mehr einfach Exception geworfen wird (aber das ist ja auch nicht grundlos Bad Practice....))
OK, sieht natürlich besser aus (wobei erst dann auffällt das resultContainer.result() und preSetter.prepare() exceptions wirft) - aber das probleme mit den connections ist ja nach wie vor das selbe (die connections werden nicht aufgeräumt).
Die limitation ändert sich nicht - und blockieren/warten lässt sich so auch nicht.
 
Muss ich das verstehen?
Nö.

wobei erst dann auffällt das resultContainer.result() und preSetter.prepare() exceptions wirft
Wie meinst Du das?

Die limitation ändert sich nicht - und blockieren/warten lässt sich so auch nicht.
In der Antwort ging es ja auch nur um die Reduzierung des Codes.

Zur Lösung hat Dir @mrBrown ja schon geschrieben, dass Du die Zahl der Verbindungen begrenzen kannst, da Du ja einen Connection Pool verwendest. Was passiert, wenn die maximale Anzahl der Verbindungen erreicht ist, kannst Du ebenfalls einstellen. Siehe dazu https://www.mchange.com/projects/c3p0/#configuring_recovery

Nachtrag: https://www.mchange.com/projects/c3p0/#checkoutTimeout
 
Ach jetzt hätte ich fast darauf vergessen.

Schade - hätte ich aber geren.


Wie meinst Du das?
Die Methoden können Exceptions werfen - aber zu dem grundproblem/dem Thema hier ist das eigentlich eh unwichtig/nebensächlich.


In der Antwort ging es ja auch nur um die Reduzierung des Codes.
Aso :)

Zur Lösung hat Dir @mrBrown ja schon geschrieben, dass Du die Zahl der Verbindungen begrenzen kannst, da Du ja einen Connection Pool verwendest. Was passiert, wenn die maximale Anzahl der Verbindungen erreicht ist, kannst Du ebenfalls einstellen. Siehe dazu https://www.mchange.com/projects/c3p0/#configuring_recovery

Nachtrag: https://www.mchange.com/projects/c3p0/#checkoutTimeout
Naja - das hat ja wie erwähnt nichts geracht ...

Aber das zugrundeliegende Problem dürfte sein, dass die ganze Klasse mehrfach von dem aufrufenden Projekt instanziiert wird - das entzieht sich aber meinem einfluss/einblick (und dürfte sich eben in der letzten version geändert haben).

Sprich meine ideen und eure hilfe sind wohl leider von einem falschen Rahmen-Setting ausgegangen, dass ich erst jetzt erkannt habe. momentan habe ich eine funktionierende version die eplizit die verbindung verwaltet (aufbaut & schließt).

Damit klappte es auch wenn ich keinen einfluss auf die aufrufende Instanz habe.

Sorry, dass mir erst so spät die eigentlich ausschlaggebenden Rahmenbedingungen aufgefallen sind und danke für den input.
 
Passende Stellenanzeigen aus deiner Region:

Oben