MySQL "Too many connections" auch nach schliessen der Connections.

Zakon

Bekanntes Mitglied
Hallo zusammen,

ich programmiere mir gerade eine Webapp mit Vaadin.
Für die Datenbank anbindung benutze ich HikariPool bzw HakariDataSource und zum ausführen der Stmt jdbTemplate.

Folgendes Problem:
Wenn ich die Seite ein paar mal hinterheinander neu lade, bekomme ich eine:

"Data source rejected establishment of connection, message from server: "Too many connections"

[CODE lang="java" title="DatabaseConfiguration"]@Configuration
public class DatabaseConfig {
private HikariDataSource dataSource;

@Bean
public DataSource configureDatasource() {
return DataSourceBuilder.create()
.url("jdbc:mysql://localhost:3306/music")
.username("root")
.password("root")
.build();
}

protected HikariDataSource getDatasource(){
if(dataSource == null || dataSource.isClosed()){
dataSource = new HikariDataSource();
dataSource.setDataSource(configureDatasource());
dataSource.setMaximumPoolSize(10);
dataSource.setMaxLifetime(30000);
return dataSource;
}else{
return dataSource;
}
}
}
[/CODE]
[CODE lang="java" title="DatabaseService"] public DatabaseService() {
jdbcTemplate.setDataSource(databaseConfig.getDatasource());
}

//GET
/**
* Get the UserPlaylist from Database
*
* @return List of Songs
*/
public UserPlaylist getUserPLaylist(String id) {
UserPlaylist userplaylist =new UserPlaylist(jdbcTemplate.query(
getUserplaylistQuerry, new Object[]{id}, new BeanPropertyRowMapper<>(Song.class)));
closeConnection();
return userplaylist;
}

private void closeConnection(){
try {
jdbcTemplate.getDataSource().getConnection().close();
} catch (SQLException e) {
e.printStackTrace();
}
}
[/CODE]


Ich schliesse eigenlich nach jedem Statement die Connection. Meine MaxLifeTime beträgt auch nur 30sekunden.
Bedeutet jede Connection sollte nach 30 sekunden sowieso geschlossen werden.
Ich bekomme jedoch immer die Fehlermeldung "Too many Connections".
Mir ist bewusst das es von der Datenbank kommt und nicht von meinem Connection Pool jedoch verstehe ich nicht warum die Connections so lange aufrecht bleiben.

Vielleicht noch Interessant zu wissen nach jedem Seiten reloade erstellt Hikari ein neuen Pool. Siehe anhang.

Zusatzfrage:
Brauche ich überhaupt mehrere Connections wenn das ganze auf einem Server läuft? Oder gehen dann meine User alle über "eine" connection bzw einem Connection Pool auf die Datenbank?
 

Anhänge

  • Screenshot 2022-01-10 203921.png
    Screenshot 2022-01-10 203921.png
    82,5 KB · Aufrufe: 0
Zuletzt bearbeitet:
Ja, hikari behält immer eine Menge Sessions alive. Default ässig glaube ich sogar die max pool size.

Edit
minimumIdle
This property controls the minimum number of idle connections that HikariCP tries to maintain in the pool. If the idle connections dip below this value and total connections in the pool are less than maximumPoolSize, HikariCP will make a best effort to add additional connections quickly and efficiently. However, for maximum performance and responsiveness to spike demands, we recommend not setting this value and instead allowing HikariCP to act as a fixed size connection pool. Default: same as maximumPoolSize
 
Ja stimmt. Habe das mit den connection close() auch hinbekommen nur dann zerstöre ich mir die ConnectionPool logik. Knannst du mir denn erklären warum ich bei jedem Refresh der Seite in ein neuen Pool gewoirfen werde? Warum komme ich nicht in den alten?
 
Dazu müsste ich mehr vom Code sehen, aber du benutzt doch Spring, wenn ich das Recht sehe. Dann könntst du das Erstellen der Datasource auch Spring überlassen. Version 2.x mit spring-data-jdbc verwendet default mässig hikari als Connection Pool.

Siehe: https://www.baeldung.com/spring-boot-hikari und in der Spring Doku.

Das explizite schliessen der Connection dürfte mMn auch nicht nötig sein. Aber ich muss auch anfügen, dass ich ausser Spring Batch eher auf der JEE schiene unterwegs bin. Evtl hat jemand schon mehr Erfahrung mit Webapps und Spring gemacht und kann noch etwas ergänzen/korrigieren
 
Bei einem Connection Pool wird close() doch auch benötigt. Nur eben schließt das nicht die Vonnection sondern markiert die Vonnection als Verfügbar.

Wäre auch ein bescheidenes Design, wenn AutoVlosable nicht mehr zu schließen wären.
 
Danke erstmal für eure Hilfe. Ich habe jetzt alles nochmla neu geschrieben und mich an die Documentation gehalten. Das Problem mit den ConenctionPool besteht jedoch immer noch.
Wenn ich die Seite neu lade, erwarte ich eigtnlich, dass ich in meinen alten Connection Pool zurück komme oder wenigstens der alte geschlossen wird.
Tatsächlich werden aber bei jedem seiten refresh 2 Connection Pools gestartet und initialisiert. (Siehe Anhang oben)
[CODE lang="java" title="DatabaseService"]public class DatabaseService {
private DatabaseConfig databaseConfig = new DatabaseConfig();
public DatabaseService() {
jdbcTemplate.setDataSource(databaseConfig.customDatasource());
}

//GET
/**
* Get the UserPlaylist from Database
*
* @return List of Songs
*/
public UserPlaylist getUserPLaylist(String id) {
UserPlaylist userPlaylist = new UserPlaylist(jdbcTemplate.query(
getUserplaylistQuerry, new Object[]{id}, new BeanPropertyRowMapper<>(Song.class)));
closeConnection();
return userPlaylist;
}
}[/CODE]
[CODE lang="java" title="DatabaseConfig"]@Configuration
public class DatabaseConfig {

@Bean(name="customDataSource")
@ConfigurationProperties("spring.datasource")
public DataSource customDatasource(){
return DataSourceBuilder.create()
.url("jdbc:mysql://localhost:3306/music")
.username("root")
.password("root")
.build();
}
}[/CODE]
[CODE lang="java" title="PlaylistLogic"]public class PlaylistLogic {
@Autowired
private DatabaseService databaseService;

//Objects
UserPlaylist userPlaylist;
User user;

public PlaylistLogic() {
this.databaseService = new DatabaseService();
user = (User) UI.getCurrent().getSession().getAttribute("user");
}
/**
* Get the UserPlaylist from Database
* @return UserPlaylist
*/
public UserPlaylist getUserPlaylist(String userId){
return userPlaylist = databaseService.getUserPLaylist(userId);
}
}
[/CODE]
[CODE lang="java" title="CreatePlaylistView"]public class CreatePlaylistView extends VerticalLayout implements HasUrlParameter<String> {
/**
* URL Parameter
* @param event
* @param parameter
*/
@Override
public void setParameter(BeforeEvent event,
@OptionalParameter String parameter) {
if(parameter == null || parameter.isEmpty()){
//Pass User to Startpage
UI.getCurrent().navigate(StartView.class);
UI.getCurrent().getPage().reload();
}else{
//User from Datenbank
this.userLogic = new UserLogic();

user = userLogic.getUser(parameter);

this.playlistLogic = new PlaylistLogic();
this.resultlistLogic = new ResultlistLogic();

userPlaylist = playlistLogic.getUserPlaylist(user.getId());

playlistGrid.setItems(userPlaylist);
playlistHeader.setText(user.getName());
}
}
}[/CODE]

Das wäre jetzt ein kompletter Weg von Oberfläche zur Datenbank. Hoffe das reicht. 🙂
 
this.databaseService = new DatabaseService();
Das hier ist auf jeden Fall nicht korrekt. Spring erstellt dir diese Klasse via Depemdency Injection. Du solltest sie selber nicht erstellen müssen.

PlaylistLogic müsste auch ein SpringBean sein und dann ins UI injected werden. So wie es jetzt ist, wirst du wohl pro Request alle Klassen erstellen.
 
Hoffe das reicht.
Nein, das reicht nicht.
In der Klasse DatabaseService rufst du eine Methode closeConnection() auf, die in der Klasse nicht definiert ist. Sie ist allerdings in deinem ursprünglichen Code definiert. Aber glaubst du wirklich, dass wir den gebotenen Code mit dem ursprünglichen Code kombinieren? Und im ursprünglichen Code wird in closeConnetion aufgerufen: jdbcTemplate.getDataSource().getConnection().close(); den Code von jdbcTemplate (ist das eine Klasse (die werden normalerweise groß geschrieben) oder ein Member?) zeigst du uns auch nicht.
 

Zurück
Oben