This is an automated email from the ASF dual-hosted git repository.
jshao pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/gravitino.git
The following commit(s) were added to refs/heads/main by this push:
new 8921672509 [#10253] Optimize driver degisteration logic for JDBC
catalog to avoid possible OOM problem (#10255)
8921672509 is described below
commit 892167250926a5c6c8694d497da4b0866cd98a27
Author: Qi Yu <[email protected]>
AuthorDate: Mon Mar 9 18:07:52 2026 +0800
[#10253] Optimize driver degisteration logic for JDBC catalog to avoid
possible OOM problem (#10255)
### What changes were proposed in this pull request?
This pull request refactors the JDBC driver deregistration logic across
multiple catalog implementations to centralize and standardize the
process. The main change is moving driver deregistration from individual
catalog subclasses to the base `JdbcCatalogOperations` class, reducing
code duplication and improving maintainability. Additionally, the pull
request updates related tests and adapts the OceanBase catalog to use a
more appropriate operations class.
### Why are the changes needed?
To avoid resource leakage
Fix: #10253
### Does this PR introduce _any_ user-facing change?
N/A
### How was this patch tested?
UTs
---------
Co-authored-by: Copilot <[email protected]>
---
.../operations/ClickHouseCatalogOperations.java | 15 +----
.../hologres/HologresCatalogOperations.java | 13 +---
.../catalog/oceanbase/OceanBaseCatalog.java | 4 +-
.../catalog/jdbc/JdbcCatalogOperations.java | 24 +++++++
.../MySQLProtocolCompatibleCatalogOperations.java | 13 ++--
.../catalog/jdbc/TestJdbcCatalogOperations.java | 78 ++++++++++++++++++++++
.../postgresql/PostgreSQLCatalogOperations.java | 13 +---
7 files changed, 120 insertions(+), 40 deletions(-)
diff --git
a/catalogs-contrib/catalog-jdbc-clickhouse/src/main/java/org/apache/gravitino/catalog/clickhouse/operations/ClickHouseCatalogOperations.java
b/catalogs-contrib/catalog-jdbc-clickhouse/src/main/java/org/apache/gravitino/catalog/clickhouse/operations/ClickHouseCatalogOperations.java
index 01631f3337..fb9a3d7140 100644
---
a/catalogs-contrib/catalog-jdbc-clickhouse/src/main/java/org/apache/gravitino/catalog/clickhouse/operations/ClickHouseCatalogOperations.java
+++
b/catalogs-contrib/catalog-jdbc-clickhouse/src/main/java/org/apache/gravitino/catalog/clickhouse/operations/ClickHouseCatalogOperations.java
@@ -21,6 +21,7 @@ package org.apache.gravitino.catalog.clickhouse.operations;
import java.sql.Driver;
import java.sql.DriverManager;
+import java.sql.SQLException;
import org.apache.gravitino.catalog.jdbc.JdbcCatalogOperations;
import
org.apache.gravitino.catalog.jdbc.converter.JdbcColumnDefaultValueConverter;
import org.apache.gravitino.catalog.jdbc.converter.JdbcExceptionConverter;
@@ -45,17 +46,7 @@ public class ClickHouseCatalogOperations extends
JdbcCatalogOperations {
}
@Override
- public void close() {
- super.close();
-
- // deregister ClickHouse JDBC driver to prevent memory leak in
long-running applications
- try {
- Driver clickhouseDriver =
-
DriverManager.getDriver("jdbc:clickhouse://dummy_address:8443/default");
- deregisterDriver(clickhouseDriver);
- } catch (java.sql.SQLException e) {
- // log and ignore
- System.err.println("Failed to deregister ClickHouse JDBC driver: " +
e.getMessage());
- }
+ protected Driver getDriver() throws SQLException {
+ return
DriverManager.getDriver("jdbc:clickhouse://dummy_address:8443/default");
}
}
diff --git
a/catalogs-contrib/catalog-jdbc-hologres/src/main/java/org/apache/gravitino/catalog/hologres/HologresCatalogOperations.java
b/catalogs-contrib/catalog-jdbc-hologres/src/main/java/org/apache/gravitino/catalog/hologres/HologresCatalogOperations.java
index 135e1bd02e..2c2442569e 100644
---
a/catalogs-contrib/catalog-jdbc-hologres/src/main/java/org/apache/gravitino/catalog/hologres/HologresCatalogOperations.java
+++
b/catalogs-contrib/catalog-jdbc-hologres/src/main/java/org/apache/gravitino/catalog/hologres/HologresCatalogOperations.java
@@ -20,6 +20,7 @@ package org.apache.gravitino.catalog.hologres;
import java.sql.Driver;
import java.sql.DriverManager;
+import java.sql.SQLException;
import org.apache.gravitino.catalog.jdbc.JdbcCatalogOperations;
import
org.apache.gravitino.catalog.jdbc.converter.JdbcColumnDefaultValueConverter;
import org.apache.gravitino.catalog.jdbc.converter.JdbcExceptionConverter;
@@ -50,15 +51,7 @@ public class HologresCatalogOperations extends
JdbcCatalogOperations {
}
@Override
- public void close() {
- super.close();
- try {
- // Unload the PostgreSQL driver, only unload the driver if it is loaded
by
- // IsolatedClassLoader. Hologres uses PostgreSQL JDBC driver.
- Driver pgDriver =
DriverManager.getDriver("jdbc:postgresql://dummy_address:12345/");
- deregisterDriver(pgDriver);
- } catch (Exception e) {
- LOG.warn("Failed to deregister PostgreSQL driver for Hologres", e);
- }
+ protected Driver getDriver() throws SQLException {
+ return DriverManager.getDriver("jdbc:postgresql://dummy_address:12345/");
}
}
diff --git
a/catalogs-contrib/catalog-jdbc-oceanbase/src/main/java/org/apache/gravitino/catalog/oceanbase/OceanBaseCatalog.java
b/catalogs-contrib/catalog-jdbc-oceanbase/src/main/java/org/apache/gravitino/catalog/oceanbase/OceanBaseCatalog.java
index 1231926441..200d501de5 100644
---
a/catalogs-contrib/catalog-jdbc-oceanbase/src/main/java/org/apache/gravitino/catalog/oceanbase/OceanBaseCatalog.java
+++
b/catalogs-contrib/catalog-jdbc-oceanbase/src/main/java/org/apache/gravitino/catalog/oceanbase/OceanBaseCatalog.java
@@ -20,7 +20,7 @@ package org.apache.gravitino.catalog.oceanbase;
import java.util.Map;
import org.apache.gravitino.catalog.jdbc.JdbcCatalog;
-import org.apache.gravitino.catalog.jdbc.JdbcCatalogOperations;
+import
org.apache.gravitino.catalog.jdbc.MySQLProtocolCompatibleCatalogOperations;
import
org.apache.gravitino.catalog.jdbc.converter.JdbcColumnDefaultValueConverter;
import org.apache.gravitino.catalog.jdbc.converter.JdbcTypeConverter;
import org.apache.gravitino.catalog.jdbc.operation.JdbcDatabaseOperations;
@@ -42,7 +42,7 @@ public class OceanBaseCatalog extends JdbcCatalog {
@Override
protected CatalogOperations newOps(Map<String, String> config) {
- return new JdbcCatalogOperations(
+ return new MySQLProtocolCompatibleCatalogOperations(
createExceptionConverter(),
createJdbcTypeConverter(),
createJdbcDatabaseOperations(),
diff --git
a/catalogs/catalog-jdbc-common/src/main/java/org/apache/gravitino/catalog/jdbc/JdbcCatalogOperations.java
b/catalogs/catalog-jdbc-common/src/main/java/org/apache/gravitino/catalog/jdbc/JdbcCatalogOperations.java
index b70abc354d..e9716749fa 100644
---
a/catalogs/catalog-jdbc-common/src/main/java/org/apache/gravitino/catalog/jdbc/JdbcCatalogOperations.java
+++
b/catalogs/catalog-jdbc-common/src/main/java/org/apache/gravitino/catalog/jdbc/JdbcCatalogOperations.java
@@ -102,6 +102,7 @@ public class JdbcCatalogOperations implements
CatalogOperations, SupportsSchemas
private final TableOperation tableOperation;
private DataSource dataSource;
+ private String jdbcUrl;
private final JdbcColumnDefaultValueConverter columnDefaultValueConverter;
@@ -170,6 +171,7 @@ public class JdbcCatalogOperations implements
CatalogOperations, SupportsSchemas
resultConf.putAll(gravitinoConfig);
JdbcConfig jdbcConfig = new JdbcConfig(resultConf);
+ this.jdbcUrl = jdbcConfig.getJdbcUrl();
this.dataSource = DataSourceUtils.createDataSource(jdbcConfig);
checkJDBCDriverVersion();
@@ -199,6 +201,28 @@ public class JdbcCatalogOperations implements
CatalogOperations, SupportsSchemas
metricsSystem.unregister(catalogMetricsSource);
}
DataSourceUtils.closeDataSource(dataSource);
+ try {
+ Driver driver = getDriver();
+ if (driver != null) {
+ deregisterDriver(driver);
+ }
+ } catch (SQLException e) {
+ LOG.warn("Failed to deregister JDBC driver", e);
+ }
+ }
+
+ /**
+ * Gets the JDBC driver for the provided JDBC URL.
+ *
+ * @return The JDBC driver instance if the JDBC URL is not blank; null if
the JDBC URL is blank.
+ * @throws SQLException if failing to get driver from JDBC URL.
+ */
+ protected Driver getDriver() throws SQLException {
+ if (StringUtils.isBlank(jdbcUrl)) {
+ return null;
+ }
+
+ return DriverManager.getDriver(jdbcUrl);
}
/**
diff --git
a/catalogs/catalog-jdbc-common/src/main/java/org/apache/gravitino/catalog/jdbc/MySQLProtocolCompatibleCatalogOperations.java
b/catalogs/catalog-jdbc-common/src/main/java/org/apache/gravitino/catalog/jdbc/MySQLProtocolCompatibleCatalogOperations.java
index b4ee565dce..2a5bddaa99 100644
---
a/catalogs/catalog-jdbc-common/src/main/java/org/apache/gravitino/catalog/jdbc/MySQLProtocolCompatibleCatalogOperations.java
+++
b/catalogs/catalog-jdbc-common/src/main/java/org/apache/gravitino/catalog/jdbc/MySQLProtocolCompatibleCatalogOperations.java
@@ -21,6 +21,7 @@ package org.apache.gravitino.catalog.jdbc;
import java.sql.Driver;
import java.sql.DriverManager;
+import java.sql.SQLException;
import
org.apache.gravitino.catalog.jdbc.converter.JdbcColumnDefaultValueConverter;
import org.apache.gravitino.catalog.jdbc.converter.JdbcExceptionConverter;
import org.apache.gravitino.catalog.jdbc.converter.JdbcTypeConverter;
@@ -70,13 +71,13 @@ public class MySQLProtocolCompatibleCatalogOperations
extends JdbcCatalogOperati
.getMethod("uncheckedShutdown")
.invoke(null);
LOG.info("AbandonedConnectionCleanupThread has been shutdown...");
-
- // Unload the MySQL driver, only Unload the driver if it is loaded by
- // IsolatedClassLoader.
- Driver mysqlDriver =
DriverManager.getDriver("jdbc:mysql://dumpy_address");
- deregisterDriver(mysqlDriver);
} catch (Exception e) {
- LOG.warn("Failed to shutdown AbandonedConnectionCleanupThread or
deregister MySQL driver", e);
+ LOG.warn("Failed to shutdown AbandonedConnectionCleanupThread", e);
}
}
+
+ @Override
+ protected Driver getDriver() throws SQLException {
+ return DriverManager.getDriver("jdbc:mysql://dummy_address");
+ }
}
diff --git
a/catalogs/catalog-jdbc-common/src/test/java/org/apache/gravitino/catalog/jdbc/TestJdbcCatalogOperations.java
b/catalogs/catalog-jdbc-common/src/test/java/org/apache/gravitino/catalog/jdbc/TestJdbcCatalogOperations.java
index 0b082d5916..d7f711e20f 100644
---
a/catalogs/catalog-jdbc-common/src/test/java/org/apache/gravitino/catalog/jdbc/TestJdbcCatalogOperations.java
+++
b/catalogs/catalog-jdbc-common/src/test/java/org/apache/gravitino/catalog/jdbc/TestJdbcCatalogOperations.java
@@ -20,6 +20,8 @@ package org.apache.gravitino.catalog.jdbc;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
+import java.sql.Driver;
+import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.HashMap;
import javax.sql.DataSource;
@@ -27,6 +29,8 @@ import org.apache.commons.dbcp2.BasicDataSource;
import org.apache.gravitino.Catalog;
import org.apache.gravitino.NameIdentifier;
import org.apache.gravitino.catalog.jdbc.config.JdbcConfig;
+import org.apache.gravitino.catalog.jdbc.converter.JdbcExceptionConverter;
+import org.apache.gravitino.catalog.jdbc.converter.JdbcTypeConverter;
import
org.apache.gravitino.catalog.jdbc.converter.SqliteColumnDefaultValueConverter;
import org.apache.gravitino.catalog.jdbc.converter.SqliteExceptionConverter;
import org.apache.gravitino.catalog.jdbc.converter.SqliteTypeConverter;
@@ -75,4 +79,78 @@ public class TestJdbcCatalogOperations {
Assertions.assertFalse(((BasicDataSource) dataSource).getTestOnBorrow());
((BasicDataSource) dataSource).close();
}
+
+ @Test
+ public void testCloseDeregisterDriver() throws SQLException {
+ TestableJdbcCatalogOperations catalogOperations =
+ new TestableJdbcCatalogOperations(
+ new SqliteExceptionConverter(),
+ new SqliteTypeConverter(),
+ new SqliteDatabaseOperations("/illegal/path"),
+ new SqliteTableOperations(),
+ new SqliteColumnDefaultValueConverter());
+
catalogOperations.setDriver(DriverManager.getDriver("jdbc:sqlite::memory:"));
+
+ Assertions.assertDoesNotThrow(catalogOperations::close);
+ Assertions.assertTrue(catalogOperations.isDeregisterCalled());
+ }
+
+ @Test
+ public void testCloseIgnoreGetDriverException() {
+ TestableJdbcCatalogOperations catalogOperations =
+ new TestableJdbcCatalogOperations(
+ new SqliteExceptionConverter(),
+ new SqliteTypeConverter(),
+ new SqliteDatabaseOperations("/illegal/path"),
+ new SqliteTableOperations(),
+ new SqliteColumnDefaultValueConverter());
+ catalogOperations.setThrowExceptionInGetDriver(true);
+
+ Assertions.assertDoesNotThrow(catalogOperations::close);
+ }
+
+ private static class TestableJdbcCatalogOperations extends
JdbcCatalogOperations {
+ private Driver driver;
+ private boolean deregisterCalled;
+ private boolean throwExceptionInGetDriver;
+
+ private TestableJdbcCatalogOperations(
+ JdbcExceptionConverter exceptionConverter,
+ JdbcTypeConverter jdbcTypeConverter,
+ SqliteDatabaseOperations databaseOperation,
+ SqliteTableOperations tableOperation,
+ SqliteColumnDefaultValueConverter columnDefaultValueConverter) {
+ super(
+ exceptionConverter,
+ jdbcTypeConverter,
+ databaseOperation,
+ tableOperation,
+ columnDefaultValueConverter);
+ }
+
+ @Override
+ protected Driver getDriver() throws SQLException {
+ if (throwExceptionInGetDriver) {
+ throw new SQLException("failed to get driver");
+ }
+ return driver;
+ }
+
+ @Override
+ public void deregisterDriver(Driver driver) {
+ this.deregisterCalled = true;
+ }
+
+ private void setDriver(Driver driver) {
+ this.driver = driver;
+ }
+
+ private void setThrowExceptionInGetDriver(boolean
throwExceptionInGetDriver) {
+ this.throwExceptionInGetDriver = throwExceptionInGetDriver;
+ }
+
+ private boolean isDeregisterCalled() {
+ return deregisterCalled;
+ }
+ }
}
diff --git
a/catalogs/catalog-jdbc-postgresql/src/main/java/org/apache/gravitino/catalog/postgresql/PostgreSQLCatalogOperations.java
b/catalogs/catalog-jdbc-postgresql/src/main/java/org/apache/gravitino/catalog/postgresql/PostgreSQLCatalogOperations.java
index 527a27072a..b04ac0dad0 100644
---
a/catalogs/catalog-jdbc-postgresql/src/main/java/org/apache/gravitino/catalog/postgresql/PostgreSQLCatalogOperations.java
+++
b/catalogs/catalog-jdbc-postgresql/src/main/java/org/apache/gravitino/catalog/postgresql/PostgreSQLCatalogOperations.java
@@ -20,6 +20,7 @@ package org.apache.gravitino.catalog.postgresql;
import java.sql.Driver;
import java.sql.DriverManager;
+import java.sql.SQLException;
import org.apache.gravitino.catalog.jdbc.JdbcCatalogOperations;
import
org.apache.gravitino.catalog.jdbc.converter.JdbcColumnDefaultValueConverter;
import org.apache.gravitino.catalog.jdbc.converter.JdbcExceptionConverter;
@@ -44,15 +45,7 @@ public class PostgreSQLCatalogOperations extends
JdbcCatalogOperations {
}
@Override
- public void close() {
- super.close();
- try {
- // Unload the PostgreSQL driver, only Unload the driver if it is loaded
by
- // IsolatedClassLoader.
- Driver pgDriver =
DriverManager.getDriver("jdbc:postgresql://dummy_address:12345/");
- deregisterDriver(pgDriver);
- } catch (Exception e) {
- LOG.warn("Failed to deregister PostgreSQL driver", e);
- }
+ protected Driver getDriver() throws SQLException {
+ return DriverManager.getDriver("jdbc:postgresql://dummy_address:12345/");
}
}