Skip to content

Commit

Permalink
Support connecting to HiveServer2 through database connection pools o…
Browse files Browse the repository at this point in the history
…ther than HikariCP
  • Loading branch information
linghengqian committed Nov 21, 2024
1 parent 0ea667a commit 9097e11
Show file tree
Hide file tree
Showing 6 changed files with 178 additions and 37 deletions.
1 change: 1 addition & 0 deletions RELEASE-NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
1. Doc: Adds documentation for HiveServer2 support - [#33717](https://github.com/apache/shardingsphere/pull/33717)
1. DistSQL: Check inline expression when create sharding table rule with inline sharding algorithm - [#33735](https://github.com/apache/shardingsphere/pull/33735)
1. Infra: Support setting `hive_conf_list`, `hive_var_list` and `sess_var_list` for jdbcURL when connecting to HiveServer2 - [#33749](https://github.com/apache/shardingsphere/pull/33749)
1. Infra: Support connecting to HiveServer2 through database connection pools other than HikariCP - [#33762](https://github.com/apache/shardingsphere/pull/33762)

### Bug Fixes

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -233,16 +233,6 @@ ShardingSphere 仅针对 HiveServer2 `4.0.1` 进行集成测试。
HiveServer2 JDBC Driver `4.0.1` 不支持 Hadoop `3.4.1`
参考 https://github.com/apache/hive/pull/5500

### 数据库连接池限制

由于 `org.apache.hive.jdbc.DatabaseMetaData` 未实现 `java.sql.DatabaseMetaData#getURL()`
ShardingSphere 在`org.apache.shardingsphere.infra.database.DatabaseTypeEngine#getStorageType(javax.sql.DataSource)`处做了模糊处理,
因此用户暂时仅可通过 `com.zaxxer.hikari.HikariDataSource` 的数据库连接池连接 HiveServer2。

若用户需要通过 `com.alibaba.druid.pool.DruidDataSource` 的数据库连接池连接 HiveServer2,
用户应当考虑在 Hive 的主分支实现 `java.sql.DatabaseMetaData#getURL()`
而不是尝试修改 ShardingSphere 的内部类。

### SQL 限制

ShardingSphere JDBC DataSource 尚不支持执行 HiveServer2 的 `SET` 语句,`CREATE TABLE` 语句和 `TRUNCATE TABLE` 语句。
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -237,16 +237,6 @@ Users can only use Hadoop `3.3.6` as the underlying Hadoop dependency of HiveSer
HiveServer2 JDBC Driver `4.0.1` does not support Hadoop `3.4.1`,
Reference https://github.com/apache/hive/pull/5500.

### Database connection pool limitation

Since `org.apache.hive.jdbc.DatabaseMetaData` does not implement `java.sql.DatabaseMetaData#getURL()`,
ShardingSphere has done fuzzy processing at `org.apache.shardingsphere.infra.database.DatabaseTypeEngine#getStorageType(javax.sql.DataSource)`,
so users can only connect to HiveServer2 through the database connection pool of `com.zaxxer.hikari.HikariDataSource` for the time being.

If users need to connect to HiveServer2 through the database connection pool of `com.alibaba.druid.pool.DruidDataSource`,
users should consider implementing `java.sql.DatabaseMetaData#getURL()` in the main branch of Hive,
rather than trying to modify the internal classes of ShardingSphere.

### SQL Limitations

ShardingSphere JDBC DataSource does not yet support executing HiveServer2's `SET` statement,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,13 @@
import org.apache.shardingsphere.infra.config.props.ConfigurationPropertyKey;
import org.apache.shardingsphere.infra.database.core.type.DatabaseType;
import org.apache.shardingsphere.infra.database.core.type.DatabaseTypeFactory;
import org.apache.shardingsphere.infra.datasource.pool.CatalogSwitchableDataSource;
import org.apache.shardingsphere.infra.datasource.pool.hikari.metadata.HikariDataSourcePoolFieldMetaData;
import org.apache.shardingsphere.infra.datasource.pool.hikari.metadata.HikariDataSourcePoolMetaData;
import org.apache.shardingsphere.infra.exception.core.external.sql.type.wrapper.SQLWrapperException;
import org.apache.shardingsphere.infra.spi.type.typed.TypedSPILoader;
import org.apache.shardingsphere.infra.util.reflection.ReflectionUtils;

import javax.sql.DataSource;
import java.lang.reflect.InvocationTargetException;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.Collection;
Expand Down Expand Up @@ -117,27 +115,29 @@ public static Map<String, DatabaseType> getStorageTypes(final DatabaseConfigurat

/**
* Get storage type.
* Similar to apache/hive 4.0.0's `org.apache.hive.jdbc.HiveDatabaseMetaData`, it does not implement {@link java.sql.DatabaseMetaData#getURL()}.
* So use {@link CatalogSwitchableDataSource#getUrl()} and {@link ReflectionUtils#getFieldValue(Object, String)} to try fuzzy matching.
* Similar to apache/hive 4.0.1's `org.apache.hive.jdbc.HiveDatabaseMetaData`, it does not implement {@link DatabaseMetaData#getURL()}.
* So use {@link java.sql.Wrapper#isWrapperFor(Class)} to try fuzzy matching.
*
* @param dataSource data source
* @return storage type
* @throws SQLWrapperException SQL wrapper exception
* @throws RuntimeException Runtime exception
*/
public static DatabaseType getStorageType(final DataSource dataSource) {
try (Connection connection = dataSource.getConnection()) {
return DatabaseTypeFactory.get(connection.getMetaData().getURL());
} catch (final SQLFeatureNotSupportedException sqlFeatureNotSupportedException) {
if (dataSource instanceof CatalogSwitchableDataSource) {
return DatabaseTypeFactory.get(((CatalogSwitchableDataSource) dataSource).getUrl());
try (Connection connection = dataSource.getConnection()) {
Class<?> hiveConnectionClass = Class.forName("org.apache.hive.jdbc.HiveConnection");
if (connection.isWrapperFor(hiveConnectionClass)) {
Object hiveConnection = connection.unwrap(hiveConnectionClass);
String connectedUrl = (String) hiveConnectionClass.getMethod("getConnectedUrl").invoke(hiveConnection);
return DatabaseTypeFactory.get(connectedUrl);
}
throw new SQLWrapperException(sqlFeatureNotSupportedException);
} catch (final SQLException | ClassNotFoundException | NoSuchMethodException | InvocationTargetException | IllegalAccessException exception) {
throw new RuntimeException(exception);
}
if (dataSource.getClass().getName().equals(new HikariDataSourcePoolMetaData().getType())) {
HikariDataSourcePoolFieldMetaData dataSourcePoolFieldMetaData = new HikariDataSourcePoolFieldMetaData();
String jdbcUrlFieldName = ReflectionUtils.<String>getFieldValue(dataSource, dataSourcePoolFieldMetaData.getJdbcUrlFieldName())
.orElseThrow(() -> new SQLWrapperException(sqlFeatureNotSupportedException));
return DatabaseTypeFactory.get(jdbcUrlFieldName);
}
throw new SQLWrapperException(sqlFeatureNotSupportedException);
} catch (final SQLException ex) {
throw new SQLWrapperException(ex);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
* limitations under the License.
*/

package org.apache.shardingsphere.test.natived.jdbc.databases;
package org.apache.shardingsphere.test.natived.jdbc.databases.hive;

import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
Expand Down Expand Up @@ -45,7 +45,7 @@
@SuppressWarnings({"SqlDialectInspection", "SqlNoDataSourceInspection"})
@EnabledInNativeImage
@Testcontainers
class HiveTest {
class HiveSimpleTest {

@SuppressWarnings("resource")
@Container
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.apache.shardingsphere.test.natived.jdbc.databases.hive;

import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import org.apache.curator.test.InstanceSpec;
import org.apache.shardingsphere.test.natived.commons.TestShardingService;
import org.awaitility.Awaitility;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.EnabledInNativeImage;
import org.testcontainers.containers.FixedHostPortGenericContainer;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.Network;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;

import javax.sql.DataSource;
import java.nio.file.Paths;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.time.Duration;
import java.util.Properties;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.nullValue;

@SuppressWarnings({"SqlDialectInspection", "SqlNoDataSourceInspection", "resource", "deprecation"})
@EnabledInNativeImage
@Testcontainers
class HiveZookeeperServiceDiscoveryTest {

private static final int RANDOM_PORT = InstanceSpec.getRandomPort();

private static final Network NETWORK = Network.newNetwork();

@Container
private static final GenericContainer<?> ZOOKEEPER_CONTAINER = new GenericContainer<>("zookeeper:3.9.3-jre-17")
.withNetwork(NETWORK)
.withNetworkAliases("foo")
.withExposedPorts(2181);

/**
* TODO Maybe we should be able to find a better solution than {@link InstanceSpec#getRandomPort()} to use a random available port on the host.
* It is not a good practice to use {@link FixedHostPortGenericContainer}.
*/
@SuppressWarnings("unused")
@Container
private static final GenericContainer<?> HIVE_SERVER2_CONTAINER = new FixedHostPortGenericContainer<>("apache/hive:4.0.1")
.withNetwork(NETWORK)
.withEnv("SERVICE_NAME", "hiveserver2")
.withEnv("SERVICE_OPTS", "-Dhive.server2.support.dynamic.service.discovery=true" + " "
+ "-Dhive.zookeeper.quorum=" + ZOOKEEPER_CONTAINER.getNetworkAliases().get(0) + ":2181" + " "
+ "-Dhive.server2.thrift.bind.host=0.0.0.0" + " "
+ "-Dhive.server2.thrift.port=" + RANDOM_PORT)
.withExposedPorts(10002)
.withFixedExposedPort(RANDOM_PORT, RANDOM_PORT)
.dependsOn(ZOOKEEPER_CONTAINER);

private static final String SYSTEM_PROP_KEY_PREFIX = "fixture.test-native.yaml.database.hive.";

// Due to https://issues.apache.org/jira/browse/HIVE-28317 , the `initFile` parameter of HiveServer2 JDBC Driver must be an absolute path.
private static final String ABSOLUTE_PATH = Paths.get("src/test/resources/test-native/sql/test-native-databases-hive.sql").toAbsolutePath().normalize().toString();

private final String jdbcUrlSuffix = ";serviceDiscoveryMode=zooKeeper;zooKeeperNamespace=hiveserver2";

private String jdbcUrlPrefix;

private TestShardingService testShardingService;

@BeforeAll
static void beforeAll() {
assertThat(System.getProperty(SYSTEM_PROP_KEY_PREFIX + "ds0.jdbc-url"), is(nullValue()));
assertThat(System.getProperty(SYSTEM_PROP_KEY_PREFIX + "ds1.jdbc-url"), is(nullValue()));
assertThat(System.getProperty(SYSTEM_PROP_KEY_PREFIX + "ds2.jdbc-url"), is(nullValue()));
}

@AfterAll
static void afterAll() {
NETWORK.close();
System.clearProperty(SYSTEM_PROP_KEY_PREFIX + "ds0.jdbc-url");
System.clearProperty(SYSTEM_PROP_KEY_PREFIX + "ds1.jdbc-url");
System.clearProperty(SYSTEM_PROP_KEY_PREFIX + "ds2.jdbc-url");
}

/**
* TODO Need to fix `shardingsphere-parser-sql-hive` module to use {@link TestShardingService#cleanEnvironment()}
* after {@link TestShardingService#processSuccessInHive()}.
*
* @throws SQLException An exception that provides information on a database access error or other errors.
*/
@Test
void assertShardingInLocalTransactions() throws SQLException {
jdbcUrlPrefix = "jdbc:hive2://" + ZOOKEEPER_CONTAINER.getHost() + ":" + ZOOKEEPER_CONTAINER.getMappedPort(2181) + "/";
DataSource dataSource = createDataSource();
testShardingService = new TestShardingService(dataSource);
testShardingService.processSuccessInHive();
}

/**
* TODO Need to fix `shardingsphere-parser-sql-hive` module to use `initEnvironment()` before {@link TestShardingService#processSuccessInHive()}.
*
* @throws SQLException An exception that provides information on a database access error or other errors.
*/
@SuppressWarnings("unused")
private void initEnvironment() throws SQLException {
testShardingService.getOrderRepository().createTableIfNotExistsInHive();
testShardingService.getOrderItemRepository().createTableIfNotExistsInHive();
testShardingService.getAddressRepository().createTableIfNotExistsInHive();
testShardingService.getOrderRepository().truncateTable();
testShardingService.getOrderItemRepository().truncateTable();
testShardingService.getAddressRepository().truncateTable();
}

private Connection openConnection() throws SQLException {
Properties props = new Properties();
return DriverManager.getConnection(jdbcUrlPrefix + jdbcUrlSuffix, props);
}

private DataSource createDataSource() throws SQLException {
Awaitility.await().atMost(Duration.ofMinutes(1L)).ignoreExceptions().until(() -> {
openConnection().close();
return true;
});
try (
Connection connection = openConnection();
Statement statement = connection.createStatement()) {
statement.executeUpdate("CREATE DATABASE demo_ds_0");
statement.executeUpdate("CREATE DATABASE demo_ds_1");
statement.executeUpdate("CREATE DATABASE demo_ds_2");
}
HikariConfig config = new HikariConfig();
config.setDriverClassName("org.apache.shardingsphere.driver.ShardingSphereDriver");
config.setJdbcUrl("jdbc:shardingsphere:classpath:test-native/yaml/jdbc/databases/hive.yaml?placeholder-type=system_props");
System.setProperty(SYSTEM_PROP_KEY_PREFIX + "ds0.jdbc-url", jdbcUrlPrefix + "demo_ds_0" + ";initFile=" + ABSOLUTE_PATH + jdbcUrlSuffix);
System.setProperty(SYSTEM_PROP_KEY_PREFIX + "ds1.jdbc-url", jdbcUrlPrefix + "demo_ds_1" + ";initFile=" + ABSOLUTE_PATH + jdbcUrlSuffix);
System.setProperty(SYSTEM_PROP_KEY_PREFIX + "ds2.jdbc-url", jdbcUrlPrefix + "demo_ds_2" + ";initFile=" + ABSOLUTE_PATH + jdbcUrlSuffix);
return new HikariDataSource(config);
}
}

0 comments on commit 9097e11

Please sign in to comment.