playframework / playframework/play1
ERRORs on application shutdown when HikariCP is configured with multiple databases
Nobody has claimed this yet.
- Dominant language
- Java
- Stars
- 1.6k
- Forks
- 671
- Avg merge
- 12d 15h
- Merged PRs (30d)
- 1
Description
Play Version (1.5.x / etc)
Play-1.5.3
Operating System (Ubuntu 15.10 / MacOS 10.10 / Windows 10)
4.15.0-65-generic #74-Ubuntu SMP Tue Sep 17 17:06:04 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux
JDK (Oracle 1.8.0_72, OpenJDK 1.8.x, Azul Zing)
openjdk version "11.0.5" 2019-10-15
Expected Behavior
- Application does not emit "Couldn't destroy the datasource" ERRORs on shutdown in PROD mode when multiple databases are configured and HikariCP is the DataSource.
Actual Behavior
When your application shuts down in PROD mode (not DEV mode) n-many errors are logged, one for each configured database:
Couldn't destroy the datasource
These are emitted because DB.destroy() attempts to find and invoke a destroy method. The destroyMethod is discovered from ExtendedDataSource.getDestroyMethod(), which returns its destroyMethod field, which is set by DBPlugin:
// Current datasource. This is actually deprecated.
String destroyMethod = dbConfig.getProperty("db.destroyMethod", "");
DB.datasource = ds;
DB.destroyMethod = destroyMethod;
DB.ExtendedDatasource extDs = new DB.ExtendedDatasource(ds, destroyMethod);
If unset, db.destroyMethod is empty string. When the application shuts down in PROD mode, play.db.DB tries to destroy the data sources:
public static void destroy(String name) {
try {
ExtendedDatasource extDatasource = datasources.get(name);
if (extDatasource != null && extDatasource.getDestroyMethod() != null) {
Method close = extDatasource.datasource.getClass().getMethod(extDatasource.getDestroyMethod(), new Class[] {});
if (close != null) {
close.invoke(extDatasource.getDataSource(), new Object[] {});
datasources.remove(name);
DB.datasource = null;
Logger.trace("Datasource destroyed");
}
}
} catch (Throwable t) {
Logger.error("Couldn't destroy the datasource", t);
}
}
The conditional checks if the ExternalDataSource's destroyMethod is null, not empty string. Because the default is empty string, it tries to find and invoke a method named "" in the DataSource. This throws a java.lang.NoSuchMethodException, which is hidden because t is passed as a formatting argument to Logger.error(). The developer simply sees the error Couldn't destroy the datasource.
If the developer tries to avoid the ERRORs by explicitly setting the db.destroyMethod property, then they receive a WARN on startup:
Ignoring db.destroyMethod because running the in internal pool db.
This is because DBPlugin.changed() executes:
} else {
// Internal pool is c3p0, we should call the close() method to destroy it.
check(dbConfig, "internal pool", "db.destroyMethod");
dbConfig.put("db.destroyMethod", "close");
}
The check method emits the warning if the given property is already set. The scope of dbConfig is local to changed() (it does not replace the global db config).
Reproducible Test Case
Configure a Play! app to use HikariCP and multiple databases. Start the application in PROD mode, and then shut it down. This emits one
Couldn't destroy the datasource
Per configured database.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with play.db.DB.destroy(String) and the DBPlugin.changed() path described in the report, then reproduce the shutdown with HikariCP and multiple databases in PROD mode. Done means shutdown no longer logs one "Couldn't destroy the datasource" ERROR per configured database, while the existing internal-pool behavior remains intact.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java
- Domain
- database
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100