Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support direct shard query routing in Spring JDBC #30988

Closed

Conversation

Anir-Ln
Copy link

@Anir-Ln Anir-Ln commented Aug 3, 2023

This PR adds Spring JDBC support for direct database operation routing in sharded databases. The main goal is to enable the routing of queries directly to specific shards. This functionality is achieved by acquiring a shard connection through the JDBC API DataSource.createConnectionBuilder().shardingKey(key).build().

Note: The sharding key is a value that determines which shard a particular data entry should be stored on or retrieved from within the sharded database.

The PR introduces two methods for executing direct shard queries:

  1. Using DirectShardCallbackTemplate: This method involves utilizing a callback that contains the queries to be executed using direct routing. Here's an example of how to use it:
ShardingKeyDataSourceAdapter dataSourceAdapter = new ShardingKeyDataSourceAdapter(dataSource);
DirectShardCallbackTemplate directShardTemplate = new DirectShardCallbackTemplate(dataSourceAdapter);

JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSourceAdapter);

directShardTemplate.execute(shardingKey, () -> {
    int id = jdbcTemplate.query(SQL1);
    jdbcTemplate.execute(SQL2, id);
});
  1. Using ShardingKeyProvider: This approach is suitable for cases where the sharding key value can be set independently from the queries being executed. Here's an example of how to use it:
ShardingKeyDataSourceAdapter dataSourceAdapter = new ShardingKeyDataSourceAdapter(dataSource);

dataSourceAdapter.setShardingKeyProvider(new ShardingKeyProvider() {
    public ShardingKey getShardingKey() throws SQLException {
        // Example: The sharding key is derived from the user name.
        String name = SecurityContextHolder.getContext().getAuthentication().getName();
        return dataSource.createShardingKeyBuilder().subKey(name, JDBCType.VARCHAR).build();
    }
    
    public ShardingKey getSuperShardingKey() throws SQLException {
        return null;
    }
});

JdbcTemplate shardingJdbcTemplate = new JdbcTemplate(dataSourceAdapter);

// The SQL query will be executed directly in the shard corresponding 
// to the sharding key returned from the ShardingKeyProvider.getShardingKey() method.
shardingJdbcTemplate.execute(SQL);

Mohamed Lahyane (Anir) added 4 commits August 3, 2023 11:04
This commit introduces a DataSource proxy, that changes the behavior of the getConnection method to use the `createConnectionBuilder()` api to acquire direct shard connections. The shard connection is acquired by specifying a `ShardingKey` that is correspondent to the wanted shard.

The sharding key can be specified either by setting the thread bound sharding key value, which will be used later by the `getConnection()` method, or by defining a `ShardingKeyProvider` which is used in the `getConnection` method to get the ShardingKey.

If both ways are used at the same time, the thread-bound sharding key is the one that will be used.

The goal of binding the sharding key to the current thread, is that we want to specify the sharding key at one level, and want it to be used later in another level, without the need to propagate it, for example set the thread-bound sharding key, and execute a `JdbcTemplate.query(SQL)` method that that will call `DataSource.getConnection()`, and as the sharding key is bound to the thread, it can be acquired to get the shard connection.
… shard.

This commit introduces a set of classes with the goal of providing an API to execute queries using direct shard routing. `DirectShardCallbackTemplate` provides `execute` methods that take a sharding key and a callback, so that the jdbc queries within that callback will be executed on the shard determined by the specified sharding key.
@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged or decided on label Aug 3, 2023
@sbrannen sbrannen added in: data Issues in data modules (jdbc, orm, oxm, tx) type: enhancement A general enhancement labels Aug 3, 2023
@sbrannen sbrannen changed the title Support direct shard query routing Support direct shard query routing in Spring JDBC Aug 3, 2023
@jhoeller
Copy link
Contributor

jhoeller commented Aug 3, 2023

While sharding would definitely be an interesting addition to the Spring JDBC module, there are concerns about transaction integration: The common connection pools out there do not support the JDBC 4.3 ConnectionBuilder API, and Spring's common transaction management assumes that it can operate on a given DataSource, with the same DataSource configured for JdbcTransactionManager as well as all JDBC data access code operating within Spring-managed transactions. I can see a ShardingKeyDataSourceAdapter being used with such managed transactions but only really with a sharding-enabled pool.

As for DirectShardCallbackTemplate, that is not really idiomatic since it exclusively works without transactions, in contrast to every other accessor in the Spring JDBC module. While there is nothing wrong with using such an arrangement in custom applications, I don't see it as a candidate for inclusion in Spring itself from the perspective of design and usage consistency.

@Anir-Ln
Copy link
Author

Anir-Ln commented Aug 18, 2023

Concerning the approach using a ShardingKeyProvider : since there is a concern regarding the connection pool's support for the JDBC 4.3 DataSource.createConnectionBuilder() API, and since JDBC provides two different ways to set the ShardingKey (either using the ConnectionBuilder API or using the Connection API) we can change the code so that if the connection pool does not support the JDBC 4.3 DataSource.createConnectionBuilder() API, the sharding key will be set in the Connection object. If neither of the APIs are implemented, it would mean that both the connection pool and the JDBC driver do not support sharding and there is no reason to use a ShardingKeyDataSourceAdapter.

@Override	
public Connection getConnection() throws SQLException {	
    try {
        return createConnectionBuilder().build();	
    }
    catch (SQLFeatureNotSupportedException ex) {
        Connection con = obtainDataSource().getConnection();
        con.setShardingKey(key);
	return con;
    }
}

Is that an acceptable solution for this issue ?

@meedbek
Copy link
Contributor

meedbek commented Oct 26, 2023

I will continue the work on this feature in a new PR #31506.

@Anir-Ln Anir-Ln closed this Oct 26, 2023
@snicoll snicoll added status: superseded An issue that has been superseded by another and removed status: waiting-for-triage An issue we've not yet triaged or decided on in: data Issues in data modules (jdbc, orm, oxm, tx) type: enhancement A general enhancement labels Oct 26, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: superseded An issue that has been superseded by another
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants