0

I keep randomly getting this error every once in a while: "java.sql.SQLSyntaxErrorException: User '{key}' has exceeded the 'max_user_connections' resource (current value: 10)".

I have tried googling help for this, but all I can find is:

  1. "increase the max connections limit" (which can't be done in free clearDB)
  2. "adjust maxActive amount" or "release old connections" (both of which I can't find how to do it in Spring Boot)

Here's what my code looks like:

// application.properties
# Connect to heroku ClearDB MySql database
spring.datasource.url=jdbc:mysql://{heroku_url}?reconnect=true
spring.datasource.username={user}
spring.datasource.password={password}

# Hibernate ddl auto (create, create-drop, update)
spring.jpa.hibernate.ddl-auto=update

#MySQL DIALECT
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
spring.jpa.open-in-view=false
server.port=8080
@Configuration
public class DatabaseConfig {
    @Value("${spring.datasource.url}")
    private String dbUrl;

    @Bean
    public DataSource dataSource() {
        HikariConfig config = new HikariConfig();
        config.setJdbcUrl(dbUrl);

        return new HikariDataSource(config);
    }
}

EDIT 1: I was following PauMAVA's instructions as best as I could and I came up with this code, which for some reason fails:

@Configuration
public class DatabaseConfig {
    @Value("${spring.datasource.url}")
    private String dbUrl;

    public static DataSource ds;

    @Bean
    public DataSource dataSource() {
        HikariConfig config = new HikariConfig();
        config.setJdbcUrl(dbUrl);

        DataSource ds = new HikariDataSource(config);
        DatabaseConfig.ds = ds;

        return ds;
    }
}
// Main class
public static void main(String[] args) {
    SpringApplication.run(BloggerApplication.class, args);

    Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
        public void run() {
            DataSource ds = DatabaseConfig.ds;
            if (ds != null) {
                try {
                    ds.getConnection().close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }, "Shutdown-thread"));
}
Crossoni
  • 193
  • 1
  • 10
  • 1
    try lowering your max pool size using the `setMaximumPoolSize(5)` in your `HikariConfig`. The default max pool size of Hikari is 10 which is also the max limit of your ClearDB. Also avoid long running queries. – Ryan Guamos Apr 30 '20 at 12:48
  • @RyanGuamos It seems that worked, I have now relaunched the backend over 10 times and fetched data each time and there's been no errors at all. I just wish that there were some way how I could test that it really is fixed now, it has always worked correctly for some time after every change I have made to the HikariConfig. – Crossoni Apr 30 '20 at 13:37
  • you might also consider increasing the connection timeout of your `HikariConfig` which is by default is 30 seconds so the query could wait more than 30 seconds if the connection already reached the max pool size – Ryan Guamos Apr 30 '20 at 13:46
  • @RyanGuamos There is one problem if I increase the connection timeout, I kept getting this warning on default time: `"WARN 7364 - Possibly consider using a shorter maxLifetime value"` so I had to change it to this: `config.setMaxLifetime(30000);`, or is connection timeout different than maxLifetime? – Crossoni Apr 30 '20 at 13:57
  • 1
    `maxLifetime` is diffirent from `connectionTimeout` You may refer on their official documentation [https://github.com/brettwooldridge/HikariCP#configuration-knobs-baby](https://github.com/brettwooldridge/HikariCP#configuration-knobs-baby) – Ryan Guamos Apr 30 '20 at 14:05

2 Answers2

1

Whenever you create a connection object in you code, it is advisable to close the same in finally block. This way the number of connections do not get exhausted.

Hope this helps!

Ritika Goel
  • 285
  • 3
  • 19
  • But I have no idea where I am opening the connection. I don't call the "datasource" method anywhere, I only use normal RestController methods, for example: @RequestMapping(value = "/blogposts", method= RequestMethod.GET) public Iterable fetchBlogposts() { return blogDatabase.findAll(); } – Crossoni Apr 30 '20 at 13:01
0

You should close the DataSource on application termination so that no unused connections remain open.

public void close(DataSource ds) {
    if(ds != null) {
        ds.close();
    }
}

But do this only on program termination as stated here.

To use the data source later (on closing) you can register the DataSource as a Field in your class:

private DataSource ds;

@Bean
public DataSource dataSource() {
    HikariConfig config = new HikariConfig();
    config.setJdbcUrl(dbUrl);
    DataSource ds = new HikariDataSource(config);
    this.ds = ds;
    return ds;
}

If you are going to have more than one data source you can make a List based approach:

private List<DataSource> activeDataSources = new ArrayList<>();

public DataSource dataSource() {
    HikariConfig config = new HikariConfig();
    config.setJdbcUrl(dbUrl);
    DataSource ds = new HikariDataSource(config);
    this.activeDataSources.add(ds);
    return ds;
}

public void closeAllDataSources() {
    for(DataSource ds: this.activeDataSources) {
        if(ds != null) {
            ds.close();
        }
    }
    this.activeDataSources = new ArrayList<>();
}

To execute a function on program close refer to this.

PauMAVA
  • 1,163
  • 14
  • 23
  • Do I need to include an actuator module in my project to call that close method when the backend is terminated or is there a better way? And how do I get access to that DataSource so I can provide it as the parameter? – Crossoni Apr 30 '20 at 13:17
  • I've added the information you required. Hope it helps :) – PauMAVA Apr 30 '20 at 13:55
  • I feel so stupid, but I just can't seem to figure out how you want me to implement this. I am not accessing DataSource from anywhere in my code (as far as I know), but I edited my question to show you how I understood your instructions. I feel like I am missing some key aspect on this. – Crossoni Apr 30 '20 at 14:35
  • You don't need to explicitly close the connection because in Spring boot, if you add the spring data dependency, it will automatically configure a Transaction Management. It automatically closes the connection after the transaction by defining `@Transactional` in the class level or method level. – Ryan Guamos Apr 30 '20 at 15:11
  • @RyanGuamos Ahh so if I add `@Transactional` annotation to my RestController classes, they will be automatically closed? Do I need the `spring-boot-starter-data-rest` dependency or is `spring-boot-starter-data-jpa` enough? – Crossoni Apr 30 '20 at 15:20
  • 1
    @Crossoni Just the spring-boot-starter-data-jpa. When you use any of the repository interface available in the spring data, their default implementation has a `@Transactional` annotation so need to add it in any layers of your application unless you need to override it or do some transaction management in your service layer. – Ryan Guamos Apr 30 '20 at 15:47