# WebApp MySQL Connection Loss



## Stefan2902 (15. Nov 2012)

Hallo,

Ich habe einen Server aufgesetzt und eine Java WebApp deployed. Leider verliert die Applikation nach zur einen Tag die Konnektivität zur Datenbank. Im catalina.out file finde ich diesbezüglich folgende Einträge:


```
SCHWERWIEGEND: Servlet.service() for servlet jsp threw exception
java.io.EOFException: Can not read response from server. Expected to read 4 bytes, read 0 bytes before connection was unexpectedly lost.

2012-11-15 06:11:15 - ERROR - de.stefan.tippspiel.Controller : Error in dispatch: javax.servlet.ServletException: com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure

The last packet successfully received from the server was 33.106.864 milliseconds ago.  The last packet sent successfully to the server was 1 milliseconds ago.
org.apache.jasper.JasperException: javax.servlet.ServletException: com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure

The last packet successfully received from the server was 33.106.864 milliseconds ago.  The last packet sent successfully to the server was 1 milliseconds ago.
```

Nach einer Recherche habe ich festgestellt, dass ich wohl das folgende JDBC Setting machen muss, um die maximale Idle Time zu reduzierent:



> I resolved above error by setting following property
> 
> jdbc.default.maxIdleTime=3600
> 
> ...



Mir ist nun überhaupt nicht klar, wo und wie ich das Setting machen kann. Ich habe einen Ubunutu Server 10.04 aufgesetzt. Hier habe ich einen Apache2 mit Tomcat7 und einer Mysql Datenbank.

Muss ich das Setting in irgendeiner Mysql Config Datei vornehmen, oder muss ich irgendein Pooling einrichten?

Gruß

Stefan


----------



## nillehammer (15. Nov 2012)

> I resolved above error by setting following property
> 
> jdbc.default.maxIdleTime=3600


Das ist ein Property der Connection, das bei Aufruf von DriverManager.getConnection() mitgegeben werden kann:

```
Connection conn = null;
Properties connectionProps = new Properties();
connectionProps.put("jdbc.default.maxIdleTime", 3600);
conn = DriverManager.getConnection( "<jdbcUrl>", connectionProps);
```

Für eine Lösung, wäre es hilfreich, wenn Du schreiben würdest, wie Du die Connections besorgst?
- Machst Du selbst in Deinem Code eine Connection auf und verwendest sie immer wieder?
- Oder holst Du Dir Connections über eine DataSource?

Der aus meiner Sicht sauberste Ansatz für Connections zu einer DB wäre eine DataSource, die vom Container (Tomcat) gemanaged wird und die Du Dir über JNDI holst. Im Tomcat gibt es einen Poolingmechanismus, der mit den von Dir beschriebenen Problemfällen gut umgehen kann. Das meiste ist Konfiguration des Tomcat. Infos findest Du hier:
Apache Tomcat 7 (7.0.32) - JNDI Resources HOW-TO
Apache Tomcat 7 (7.0.32) - JNDI Datasource HOW-TO


----------



## Stefan2902 (15. Nov 2012)

Vielen Dank für diese perfekte Antwort! Ich habe mich an deine Best Practise gehalten und nun folgenden JNDI Eintrag in der context.xml Datei vorgenommen:

[XML]
    <Resource
        name="jdbc/applikation"
        auth="Container"
        driverClassName="com.mysql.jdbc.Driver"
        maxActive="100"
        maxIdle="30"
        maxIdleTime="3600"
        maxPoolSize="15"
        maxWait="10000"
        minPoolSize="3"
        username="xxxx"
        password="xxxx"
        type="javax.sql.DataSource"
        url="jdbc:mysql://localhost:3306/applikation" />
[/XML] 

Im Java Code habe ich nun folgenden Verweis auf diese Resource:


```
public static Connection getConnection() {

		if (connection == null) {

			DataSource ds = null;

			// Obtain our environment naming context
			try {
				Context initCtx = new InitialContext();
				Context envCtx = (Context) initCtx.lookup("java:comp/env");
				// Look up our data source
				ds = (DataSource) envCtx.lookup("jdbc/applikation");

			} catch (NamingException e) {
				e.printStackTrace();
			}

			// Allocate and use a connection from the pool
			try {
				connection = ds.getConnection();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

		}
		return connection;

	}
```

In meinem Java Code verwende ich dann jeweils getConnection(); am Start vieler Methoden. Ich habe noch zwei kurze Fragen dazu:

a) Ist das Setzen des Parameters "maxIdleTime" ausreichend, um die Verbindungsabbrüche zu vermeiden?

b) Ist die Vorgehensweise, dass ich die Methode getConnection(); am Start vieler Methoden aufrufe best practise oder implementiert man die Datenbankkonnektivität normallerweise anders?

Gruß
Stefan


----------



## nillehammer (16. Nov 2012)

> a) Ist das Setzen des Parameters "maxIdleTime" ausreichend, um die Verbindungsabbrüche zu vermeiden?


Ich kann den Parameter "maxIdleTime" weder in der Doku zu den jdbc-Properties des Connector/J (MySQL :: MySQL 5.0 Reference Manual :: 20.3.5.1 Driver/Datasource Class Names, URL Syntax and Configuration Properties for Connector/J) noch in der Doku zu DBCP entdecken (DBCP - Configuration). Ich fürchte also nein. Außerdem sind einige der anderen Parameter widersprüchlich. Z.B. ist "maxPoolSize" kleiner als "maxActive". Diese werde müssten auch garnicht gesetzt werden, solange die Defaultwerte reichen. Lies Dir mal die DBCP-Seite durch. Da kann man den Pool so einstellen, dass er mit einer Query die Connection offen hält. Ich bin mit folgenden Werten auf einer Seite mit wenig Last gut zurecht gekommen:

```
initialSize">3
minIdle">3
validationQuery">SELECT 1 FROM eine_TABELLE_deiner_DB
testOnBorrow">false
testWhileIdle">true
poolPreparedStatements">true
maxOpenPreparedStatements">10
```



> b) Ist die Vorgehensweise, dass ich die Methode getConnection(); am Start vieler Methoden aufrufe best practise oder implementiert man die Datenbankkonnektivität normallerweise anders?


Das kann man schon so machen. Schließlich brauchst Du zum Erstellen von Queries ja immer ein Connection-Objekt. Aber so, wie Du es gemacht hast, ist etwas ungewöhnlich:

```
public static Connection getConnection() {
 
        if (connection == null) {
```
Lässt darauf schließen, dass "connection" eine static variable ist. Das ist kontraproduktiv. Mit einem Pool brauchst Du Dir die connection nirgends zu speichern.

```
DataSource ds = null;
 
            // Obtain our environment naming context
            try {
                Context initCtx = new InitialContext();
                Context envCtx = (Context) initCtx.lookup("java:comp/env");
                // Look up our data source
                ds = (DataSource) envCtx.lookup("jdbc/applikation");
 
            } catch (NamingException e) {
                e.printStackTrace();
            }
```
Der Teil sollte nur einmal laufen und nicht bei jedem Aufruf der Methode. Das ist unnötig.


----------



## Stefan2902 (16. Nov 2012)

Vielen Dank für die schnelle Unterstützung. Heute Morgen habe ich gemerkt, dass irgendetwas an den Parametern nicht stimmen kann, da heute Morgen die Applikation wieder nicht mehr zur Verfügung stand. Habe einfach eine leere, weiße Seite als Response bekommen. Komischerweise gab es im catalina.out File keinerlei Error Messages / Java Exceptions. Der letzte Eintrag war, dass der Server gestern ordnungsgemäß gestartet wurde. 

a) Daher wollte ich mal fragen, ob es noch andere Logfiles gibt, dir mir Aufschluss darüber geben können, warum die Seite heute keine Response geliefert hat?

Darüber hinaus habe ich nun die Settings wie folgt angepasst und bin gespannt, ob er Morgen wieder "stehen bleibt":

[XML]
    <Resource
        name="jdbc/applikation"
        auth="Container"
        driverClassName="com.mysql.jdbc.Driver"
        initialSize="3"
        maxOpenPreparedStatements="10"
        minIdle="3"
        password="xxxx"
        poolPreparedStatements="true"
        testOnBorrow="false"
        testWhileIdle="true"
        type="javax.sql.DataSource"
        url="jdbc:mysql://localhost:3306/applikation"
        username="xxxx"
        validationQuery="SELECT 1 FROM user" />
[/XML]

Auch was den Teil der Connection angeht hast du recht, dass ich die Connection als private static deklariert habe. Du sagst ja, dass ich mit einem Pool die Variable nicht Speichern muss. Bin mir aber nicht sicher, wie ich den Code anpassen muss, damit die Methode nur einmal läuft?

b) An welcher Stelle soll ich meine getConnection() denn im Servlet aufrufen und was ist der Rückgabewert? Sorry aber ich stehe da irgendwie auf dem Schlauch?!

Gruß

Stefan


----------



## nillehammer (16. Nov 2012)

> a) Daher wollte ich mal fragen, ob es noch andere Logfiles gibt, dir mir Aufschluss darüber geben können, warum die Seite heute keine Response geliefert hat?


Ich kenne Deine Anwendung jetzt nicht, aber normalerweise hat jede Anwendung ggf. eigene Logfiles. Wo die liegen, hängt von der Anwendung ab. Schaue auch mal in dieLogs des Apachen. Vielleicht hat er es nicht geschafft, den Request weiterzuleiten.


> ```
> validationQuery="SELECT 1 FROM user"
> ```


Setz die Query mal von Hand über das mysql-Tool ab. Nur zur Sicherheit, dass sie auch funktioniert. Du brauchst eine einfache Query, die (mindestens) eine Zeile zurückliefert.


> Auch was den Teil der Connection angeht hast du recht, dass ich die Connection als private static deklariert habe. Du sagst ja, dass ich mit einem Pool die Variable nicht Speichern muss. Bin mir aber nicht sicher, wie ich den Code anpassen muss, damit die Methode nur einmal läuft?
> 
> b) An welcher Stelle soll ich meine getConnection() denn im Servlet aufrufen und was ist der Rückgabewert? Sorry aber ich stehe da irgendwie auf dem Schlauch?!


Ich würde das Liefern von Connections als transparenten Service sehen. Für einen Service also zunächst die Interface-Definition:

```
public interface ConnectionSource {

   public Connection getConnection();
}
```
Dann die Implementierung. Ich mach DefaultImplementierungen, die keine eigenen Daten halten, gerne als Enum. Gibt's aber auch andere Meinungen zu. Als Klasse geht es natürlich auch. Dann könntest Du die DataSource sogar als Konstruktorparameter übergeben und den ganzen JDNI-Krams auch noch auslagern.

```
public enum ConnectionSourceImpl implements ConnectionSource{
  INSTANCE;

  private final DataSource ds;

  private ConnectionSourceImpl() {
    this.ds = initDataSource();
  }

  private final DataSource initDataSource() {
    try {
      Context initCtx = new InitialContext();
      Context envCtx = (Context) initCtx.lookup("java:comp/env");
       // Look up our data source
      this.ds = (DataSource) envCtx.lookup("jdbc/applikation");
    } catch (NamingException e) {
       // e.printStackTrace(); ist nicht so glücklich
       // Dein Programm funktioniert nicht ohne DataSource
       // also werden wir es durch das Weiterwerfen als RuntimeException
       // das Programm beenden
       throw new RuntimeException(e);
    }
  }

  public Connection getConnection() {
    try {
       return this.ds.getConnection();
    } catch (SQLException e) {
       // e.printStackTrace(); ist nicht so glücklich
       // Dein Programm funktioniert nicht ohne connection
       // also werden wir es durch das Weiterwerfen als RuntimeException
       // das Programm beenden
       throw new RuntimeException(e);
    }
  }
}
```

Und in Deinem Servlet dann:


```
// Hier mal direkt auf die public enum-Konstante zugegriffen
// Besser wäre, Wegkapseln und mit Factories oder Dependeny Injection zu arbeiten
private final ConnectionSource cs = ConnectionSourceImpl.INSTANCE;

doGet(...) {

   final Connection connection = this.cs.getConnection();
   ...
}
```


----------



## Stefan2902 (17. Nov 2012)

Vielen Dank für den Beispiel Code. Habe die Connection nun dementsprechend angepasst. Habe noch zwei kleine Fragen:

a) Wenn ich einen Connection Pool nun verwende, muss ich die Connection dann wieder nach jedem Gebrauch freigeben? Bin mir nicht sicher aber macht es nicht Sinn statement.close(), resultSet.close() und connection.close() auszuführen, um die Connection wieder freizugeben?

b) Leider habe ich mein Problem noch nicht lösen können, dass die Applikation nach ca. einem Tag nicht mehr läuft (weiße Response Seite). Im Catalina.out file finde ich folgende Einträge 


```
SCHWERWIEGEND: Servlet.service() for servlet jsp threw exception
java.net.SocketException: Broken pipe
	at java.net.SocketOutputStream.socketWrite0(Native Method)
	at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:109)
	at java.net.SocketOutputStream.write(SocketOutputStream.java:153)

...

16.11.2012 05:34:25 org.apache.catalina.loader.WebappClassLoader clearReferencesThreads
SCHWERWIEGEND: The web application [] appears to have started a thread named [Abandoned connection cleanup thread] but has failed to stop it. This is very likely to create a memory leak.
```

Hast du eine Idee, wie ich das fixen kann? Vielen Dank für die Unterstützung.

Gruß
Stefan


----------



## maki (17. Nov 2012)

Hast du mal probiert das testOnBorrow auf true zu setzen?

Rufst du auch immer Connection#close() auf?


----------



## Stefan2902 (19. Nov 2012)

Hallo,

Habe jetzt mal testOnBorrow auf "true" gesetzt. 

Was das Schließen der Connection angeht, so habe ich das nun wie folgt programmiert:


```
public class Klasse {

	private final static ConnectionSource cs = ConnectionSourceImpl.INSTANCE;


public static Methode1{

		final Connection connection = cs.getConnection();

                ..CODE..

		closeConnect(result, statement, connection);

}

public static Methode2{

		final Connection connection = cs.getConnection();

                ..CODE..

		closeConnect(result, statement, connection);

}

	public static void closeConnect(ResultSet resultSet, Statement statement, Connection connection) {

		// Close Resultset
		if (resultSet != null) {
			try {
				resultSet.close();
			} catch (SQLException e) {
				e.printStackTrace();
				log.info("ResultSet closed: " + e.getMessage());
			}
		}
		// Close Statement
		if (statement != null) {
			try {
				statement.close();
			} catch (SQLException e) {
				e.printStackTrace();
				log.info("Statement closed: " + e.getMessage());
			}
		}
		// Close Connection
		if (connection != null) {
			try {
				connection.close();
			} catch (SQLException e) {
				e.printStackTrace();
				log.info("Connection closed: " + e.getMessage());
			}
		}

	}
```

Macht das so Sinn?

Gruß
Stefan


----------



## nillehammer (19. Nov 2012)

Bei einem Connection Pool gibt es zwei Varianten:
1. testOnBorrow: Der Pool geht davon aus, dass Connections zwischenzeitlich "zu gehen" und prüft deswegen jedes Mal, bevor er eine Connection herausgibt. Funktioniert, hat aber bei Hochlast-Anwendungen den Nachteil, dass oft unnötige Test-Queries ausgeführt werden und, dass, ggf. relativ spät eine neue Connection (physisch) zur DB aufgemacht wird.
2. testWhileIdle: Der Pool testet periodisch in einem extra Thread alle Connections und hält sie damit offen. Das ist performancemäßig besser.

2. War die Variante, in die ich gehen wollte. Da braucht man kein testOnBorrow. Allerdings habe ich wohl einen Parameter vergessen: timeBetweenEvictionRunsMillis muss gesetzt sein (bspw. auf 3600), sonst läuft der Thread nämlich nicht.



> a) Wenn ich einen Connection Pool nun verwende, muss ich die Connection dann wieder nach jedem Gebrauch freigeben? Bin mir nicht sicher aber macht es nicht Sinn statement.close(), resultSet.close() und connection.close() auszuführen, um die Connection wieder freizugeben?


Du programmierst so, wie Du es ohne Pool machen würdest. Ein Connection.close() sorgt dafür, dass eine Connection dem Pool zurück gegeben wird.


----------



## Stefan2902 (20. Nov 2012)

Million Thanks for your advise. Solution:

testOnBorrow set to 'true' as well as proper memory management: stmt.close(), resultSet.close() and connection.close() fixed the issue.

Die Anwendung läuft nun stabil seit zwei Tagen!

Nochmals Danke!!


----------

