Per-query keyspace
Sometimes it is convenient to send the keyspace separately from the query string, and without switching the whole session to that keyspace either. For example, you might have a multi-tenant setup where identical requests are executed against different keyspaces.
This feature is only available with Cassandra 4.0 or above (CASSANDRA-10145). Make sure you are using native protocol v5 or above to connect.
If you try against an older version, you will get an error:
Exception in thread "main" java.lang.IllegalArgumentException: Can't use per-request keyspace with protocol V4
Note: at the time of writing, Cassandra 4 is not released yet. If you want to test those examples
against the development version, keep in mind that native protocol v5 is still in beta, so you’ll
need to force it in the configuration: datastax-java-driver.protocol.version = V5
.
Basic usage
To use a per-query keyspace, set it on your statement instance:
CqlSession session = CqlSession.builder().build();
CqlIdentifier keyspace = CqlIdentifier.fromCql("test");
SimpleStatement statement =
SimpleStatement.newInstance("SELECT * FROM foo WHERE k = 1").setKeyspace(keyspace);
session.execute(statement);
You can do this on simple, prepared or batch statements.
If the session is connected to another keyspace, the per-query keyspace takes precedence:
CqlIdentifier keyspace1 = CqlIdentifier.fromCql("test1");
CqlIdentifier keyspace2 = CqlIdentifier.fromCql("test2");
CqlSession session = CqlSession.builder().withKeyspace(keyspace1).build();
// Will query test2.foo:
SimpleStatement statement =
SimpleStatement.newInstance("SELECT * FROM foo WHERE k = 1").setKeyspace(keyspace2);
session.execute(statement);
On the other hand, if a keyspace is hard-coded in the query, it takes precedence over the per-query keyspace:
// Will query test1.foo:
SimpleStatement statement =
SimpleStatement.newInstance("SELECT * FROM test1.foo WHERE k = 1").setKeyspace(keyspace2);
Bound statements
Bound statements can’t have a per-query keyspace; they only inherit the one that was set on the prepared statement:
CqlIdentifier keyspace = CqlIdentifier.fromCql("test");
PreparedStatement pst =
session.prepare(
SimpleStatement.newInstance("SELECT * FROM foo WHERE k = ?").setKeyspace(keyspace));
// Will query test.foo:
BoundStatement bs = pst.bind(1);
The rationale is that prepared statements hold metadata about the target table; if Cassandra allowed execution against different keyspaces, it would be under the assumption that all tables have the same exact schema, which could create issues if this turned out not to be true at runtime.
Therefore you’ll have to prepare against every target keyspace. A good strategy is to do this lazily with a cache. Here is a simple example using Guava:
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
LoadingCache<CqlIdentifier, PreparedStatement> cache =
CacheBuilder.newBuilder()
.build(
new CacheLoader<CqlIdentifier, PreparedStatement>() {
@Override
public PreparedStatement load(CqlIdentifier keyspace) throws Exception {
return session.prepare(
SimpleStatement.newInstance("SELECT * FROM foo WHERE k = ?")
.setKeyspace(keyspace));
}
});
CqlIdentifier keyspace = CqlIdentifier.fromCql("test");
BoundStatement bs = cache.get(keyspace).bind(1);
Relation to the routing keyspace
Statements have another keyspace-related method: Statement.setRoutingKeyspace()
. However, the
routing keyspace is only used for token-aware routing, as a hint to help the driver send requests
to the best replica. It does not affect the query string itself.
If you are using a per-query keyspace, the routing keyspace becomes obsolete: the driver will use the per-query keyspace as the routing keyspace.
SimpleStatement statement =
SimpleStatement.newInstance("SELECT * FROM foo WHERE k = 1")
.setKeyspace(keyspace)
.setRoutingKeyspace(keyspace); // NOT NEEDED: will be ignored
At some point in the future, when Cassandra 4 becomes prevalent and using a per-query keyspace is
the norm, we’ll probably deprecate setRoutingKeyspace()
.