Execution Profiles
Execution profiles provide a mechanism to group together a set of configuration options and reuse them across different query executions. These options include:
- Load balancing policy
- Retry policy
- Speculative Execution policy
- Consistency level
- Serial consistency level
- Per-host request timeout
- Graph options
Execution profiles API is being introduced to help deal with the exploding number of configuration options, especially as the database platform evolves into more complex workloads.
The legacy configuration remains intact but it is recommended to set the available options via the new Execution Profiles API.
This page explains how Execution Profiles relate to existing settings, and shows how to use the new profiles for request execution.
Using Execution Profiles
When SLAs are defined there might be different SLAs for different parts of a system. When it comes to authentication, for example, the SLA for a log in might be different from the SLA for a new user registration. Let’s say that we need the following settings to meet the SLAs:
User journey | Speculative Execution Policy | Consistency |
---|---|---|
Log in | ConstantSpeculativeExecutionPolicy(100, 1) |
LOCAL_ONE |
Sign up | NoSpeculativeExecutionPolicy |
QUORUM |
Note that ConstantSpeculativeExecutionPolicy(100, 1)
means that at most 1
speculative execution will be launched after 100
ms of not receiving a response from a coordinator node. For more information on speculative executions, see this page.
Instead of manually adjusting the options on every request, you can create execution profiles:
var cluster =
DseCluster.Builder()
.AddContactPoint("127.0.0.1")
.WithExecutionProfiles(opts => opts
.WithProfile("default", profile => profile
.WithLoadBalancingPolicy(new DseLoadBalancingPolicy(localDc: "dc1")))
.WithProfile("login", profile => profile
.WithConsistencyLevel(ConsistencyLevel.LocalOne)
.WithSpeculativeExecutionPolicy(new ConstantSpeculativeExecutionPolicy(delay: 100, maxSpeculativeExecutions: 1)))
.WithProfile("signup", profile => profile
.WithConsistencyLevel(ConsistencyLevel.Quorum)
.WithSpeculativeExecutionPolicy(NoSpeculativeExecutionPolicy.Instance)))
.Build();
Note that both profiles (login
and signup
) will inherit the unspecified parameters from the default
profile. This means that in this case both profiles will use the token and datacenter aware load balancing policy with local datacenter dc1
.
Now each request only needs a profile name. Here’s an example with Mapper
.
// on startup
var session = cluster.Connect();
var mapper = new Mapper(session);
// on request
var cql = Cql.New("SELECT * FROM users WHERE username = ?", username).WithExecutionProfile("login");
var fetchResult = await mapper.FetchAsync<User>(cql).ConfigureAwait(false);
And here’s the same operation with a PreparedStatement
and DseSession.ExecuteAsync
:
// on startup
var session = cluster.Connect();
var ps = await session.PrepareAsync("SELECT * FROM users WHERE username = ?").ConfigureAwait(false);
// on request
var statement = ps.Bind(username);
var fetchResult = await session.ExecuteAsync(statement, "login").ConfigureAwait(false);
Mapping Legacy Parameters to Profiles
The name default
is reserved for the default execution profile. This profile will be the one that is going to be used whenever no profile is specified in a request.
You can change the default profile either by the legacy parameters on DseCluster.Builder
or by changing the execution profile itself with DseClusterBuilder.WithExecutionProfiles
.
The following code snippet illustrates two DseCluster
instances being built with the same configuration parameters:
-
cluster1
uses the legacy configuration -
cluster2
uses the new Execution Profile API for the same parameters ascluster1
var cluster2 =
DseCluster.Builder()
.AddContactPoint("127.0.0.1")
.WithQueryOptions(
new QueryOptions()
.SetConsistencyLevel(ConsistencyLevel.LocalQuorum)
.SetSerialConsistencyLevel(ConsistencyLevel.LocalSerial))
.WithLoadBalancingPolicy(lbp)
.WithSpeculativeExecutionPolicy(sep)
.WithRetryPolicy(rp)
.Build();
var cluster2 =
DseCluster.Builder()
.AddContactPoint("127.0.0.1")
.WithExecutionProfiles(opts => opts
.WithProfile("default", profile => profile
.WithConsistencyLevel(ConsistencyLevel.LocalQuorum)
.WithSerialConsistencyLevel(ConsistencyLevel.LocalSerial)
.WithLoadBalancingPolicy(lbp)
.WithSpeculativeExecutionPolicy(sep)
.WithRetryPolicy(rp)))
.Build();
Derived Execution Profiles
This is an advanced feature that might be useful in some cases. You can create derived profiles that inherit parameters from base profiles. A similar behavior is the way every execution profile inherits parameters from the default
profile.
Let’s say an application needs 2 execution profiles for its operations but it also implements datacenter failover. In this case it will need 2 more execution profiles that will basically be the same except for the localDc
parameter of DseLoadBalancingPolicy
and for the delay used in ISpeculativeExecutionPolicy
.
Scenario | Speculative Execution Policy | Consistency | Local Datacenter |
---|---|---|---|
default | ConstantSpeculativeExecutionPolicy(50, 1) |
LOCAL_ONE | dc1 |
local-quorum | ConstantSpeculativeExecutionPolicy(100, 1) |
LOCAL_QUORUM | dc1 |
remote-one | ConstantSpeculativeExecutionPolicy(2000, 1) |
LOCAL_ONE | dc2 |
remote-quorum | ConstantSpeculativeExecutionPolicy(2500, 1) |
LOCAL_QUORUM | dc2 |
Here is how this looks in code (note that local-quorum
is not created with WithDerivedProfile
, because the default
profile inheritance happens by default):
var cluster =
DseCluster.Builder()
.AddContactPoint("127.0.0.1")
.WithExecutionProfiles(opts => opts
.WithProfile("default", profile => profile
.WithLoadBalancingPolicy(new DseLoadBalancingPolicy(localDc: "dc1"))
.WithConsistencyLevel(ConsistencyLevel.LocalOne)
.WithSpeculativeExecutionPolicy(new ConstantSpeculativeExecutionPolicy(delay: 50, maxSpeculativeExecutions: 1)))
.WithProfile("local-quorum", profile => profile
.WithConsistencyLevel(ConsistencyLevel.LocalQuorum)
.WithSpeculativeExecutionPolicy(new ConstantSpeculativeExecutionPolicy(delay: 100, maxSpeculativeExecutions: 1)))
.WithProfile("remote-one", profile => profile
.WithLoadBalancingPolicy(new TokenAwarePolicy(new DCAwareRoundRobinPolicy(localDc: "dc2")))
.WithConsistencyLevel(ConsistencyLevel.LocalOne)
.WithSpeculativeExecutionPolicy(new ConstantSpeculativeExecutionPolicy(delay: 2000, maxSpeculativeExecutions: 1)))
.WithDerivedProfile("remote-quorum", "remote-one", profile => profile
.WithConsistencyLevel(ConsistencyLevel.LocalQuorum)
.WithSpeculativeExecutionPolicy(new ConstantSpeculativeExecutionPolicy(delay: 2500, maxSpeculativeExecutions: 1))))
.Build();
Accessing the defined profiles for a given Cluster
You can obtain IReadOnlyDictionary
of immutable IExecutionProfile
instances via the Configuration.ExecutionProfiles
property.
var profiles = cluster.Configuration.CassandraConfiguration.ExecutionProfiles;
Note that you can access the IDseCluster
instance through IDseSession
:
var profiles = session.Cluster.Configuration.ExecutionProfiles;