Skip to content

Commit ce3eb9c

Browse files
authored
[scylla] enable token aware LB by default, improve the docs (#1507)
* [scylla] token awareness is enabled by default See https://docs.datastax.com/en/developer/java-driver/3.10/manual/load_balancing/ : > If you don’t explicitly configure the policy, > you get the default, which is a datacenter-aware, > token-aware policy. * [scylla] driver: update to 3.10.2-scylla-1 * [scylla] log used consistency level * [scylla] doc: add latency correction section * [scylla] doc: dump and merge histograms * [scylla] doc: don't configure connections manually https://github.com/scylladb/java-driver/commits/3.10.2-scylla * [scylla] doc: details to sections 1,2,4,5
1 parent a2d5127 commit ce3eb9c

3 files changed

Lines changed: 113 additions & 60 deletions

File tree

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ LICENSE file.
143143
<rocksdb.version>6.2.2</rocksdb.version>
144144
<s3.version>1.10.20</s3.version>
145145
<seaweed.client.version>1.4.1</seaweed.client.version>
146-
<scylla.cql.version>3.10.1-scylla-0</scylla.cql.version>
146+
<scylla.cql.version>3.10.2-scylla-1</scylla.cql.version>
147147
<solr7.version>7.7.2</solr7.version>
148148
<tarantool.version>1.6.5</tarantool.version>
149149
<thrift.version>0.8.0</thrift.version>

scylla/README.md

Lines changed: 90 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,6 @@ Create a keyspace, and a table as mentioned above. Load data with:
5353
-p readproportion=0 -p updateproportion=0 \
5454
-p fieldcount=10 -p fieldlength=128 \
5555
-p insertstart=0 -p insertcount=1000000000 \
56-
-p cassandra.coreconnections=14 -p cassandra.maxconnections=14 \
5756
-p cassandra.username=cassandra -p cassandra.password=cassandra \
5857
-p scylla.hosts=ip1,ip2,ip3,...
5958

@@ -63,27 +62,32 @@ Use as following:
6362
-target 120000 -threads 840 -p recordcount=1000000000 \
6463
-p fieldcount=10 -p fieldlength=128 \
6564
-p operationcount=50000000 \
66-
-p scylla.coreconnections=280 -p scylla.maxconnections=280 \
6765
-p scylla.username=cassandra -p scylla.password=cassandra \
68-
-p scylla.hosts=ip1,ip2,ip3,... \
69-
-p scylla.tokenaware=true
66+
-p scylla.hosts=ip1,ip2,ip3,...
7067

7168
## On choosing meaningful configuration
7269

7370
### 1. Load target
7471

75-
You want to test how a database handles load. To get the performance picture
76-
you would want to look at the latency distribution and utilization under the
77-
constant load. To select load use `-target` to state desired throughput level.
72+
Suppose, you want to test how a database handles an OLTP load.
73+
74+
In this case, to get the performance picture you want to look at the latency
75+
distribution and utilization at the sustained throughput that is independent
76+
of the processing speed. This kind of system called an open-loop system.
77+
Use the `-target` flag to state desired requests arrival rate.
7878

7979
For example `-target 120000` means that we expect YCSB workers to generate
80-
120,000 requests per second (RPS, QPS or TPS) to the database.
80+
120,000 requests per second (RPS, QPS or TPS) overall to the database.
8181

82-
Why is this important? Because without setting target throughput you will be
83-
looking only on the system equilibrium point that in the face of constantly
84-
varying latency will not allow you to see either throughput, nor latency.
82+
Why is this important? First, we want to look at the latency at some sustained
83+
throughput target, not visa versa. Second, without a throughput target,
84+
the system+loader pair will converge to the closed-loop system that has completely
85+
different characteristics than what we wanted to measure. The load will settle
86+
at the system equilibrium point. You will be able to find the throughput that will depend
87+
on the number of loader threads (workers) but not the latency - only service time.
88+
This is not something we expected.
8589

86-
For more information check out these resources on the coordinated omission problem:
90+
For more information check out these resources on the coordinated omission problem.
8791

8892
See
8993
[[1]](http://highscalability.com/blog/2015/10/5/your-load-generator-is-probably-lying-to-you-take-the-red-pi.html)
@@ -92,7 +96,51 @@ See
9296
and [[this]](https://www.youtube.com/watch?v=lJ8ydIuPFeU)
9397
great talk by Gil Tene.
9498

95-
### 2. Parallelism factor and threads
99+
### 2. Latency correction
100+
101+
To measure latency, it is not enough to just set a target.
102+
The latencies must be measured with the correction as we apply
103+
a closed-class loader to the open-class problem. This is what YCSB
104+
calls an Intended operation.
105+
106+
Intended operations have points in time when they were intended to be executed
107+
according to the scheduler defined by the load target (--target). We must correct
108+
measurement if we did not manage to execute an operation in time.
109+
110+
The fair measurement consists of the operation latency and its correction
111+
to the point of its intended execution. Even if you don’t want to have
112+
a completely fair measurement, use “both”:
113+
114+
-p measurement.interval=both
115+
116+
Other options are “op” and “intended”. “op” is the default.
117+
118+
Another flag that affects measurement quality is the type of histogram
119+
“-p measurementtype” but for a long time, it uses “hdrhistogram” that
120+
must be fine for most use cases.
121+
122+
### 3. Latency percentiles and multiple loaders
123+
124+
Latencies percentiles can't be averaged. Don't fall into this trap.
125+
Neither averages nor p99 averages do not make any sense.
126+
127+
If you run a single loader instance look for P99 - 99 percentile.
128+
If you run multiple loaders dump result histograms with:
129+
130+
-p measurement.histogram.verbose=true
131+
132+
or
133+
134+
-p hdrhistogram.fileoutput=true
135+
-p hdrhistogram.output.path=file.hdr
136+
137+
merge them manually and extract required percentiles out of the
138+
joined result.
139+
140+
Remember that running multiple workloads may distort original
141+
workloads distributions they were intended to produce.
142+
143+
### 4. Parallelism factor and threads
96144

97145
Scylla utilizes [thread-per-core](https://www.scylladb.com/product/technology/) architecture design.
98146
That means that a Node consists of shards that are mapped to the CPU cores 1-per-core.
@@ -113,41 +161,45 @@ of shards, and the number of nodes in the cluster. For example:
113161

114162
=>
115163

116-
threads = K * shards * nodes = K * 14 * nodes
164+
threads = K * shards per node * nodes
117165

118166
for i3.4xlarge where
119167

120-
- K is parallelism factor >= 1,
121-
- Nodes is number of nodes in the cluster.
122-
123-
For example for 3 nodes `i3.4xlarge` and `-threads 840` means
124-
`K = 20`, `shards = 14`, and `threads = 14 * 20 * 3`.
168+
- K is parallelism factor:
125169

126-
Thus, the `K` - the parallelism factor must be selected in the first order. If you
127-
don't know what you want out of it start with 1.
170+
K >= Target Throughput / QPS per Worker / Shards per node / Nodes / Workers per shard >= 1
171+
where
172+
Target Throughput = --target
173+
QPS per Worker = 1000 [ms/second] / Latency in ms expected at target Percentile
174+
Shards per node = vCPU per cluster node - 2
175+
Nodes = a number of nodes in the cluster.
176+
Workers per shard = Target Throughput / Shards per node / Nodes / QPS per Worker
128177

129-
For picking desired parallelism factor it is useful to come from desired `target`
130-
parameter. It is better if the `target` is a multiple of `threads`.
178+
- Nodes is number of nodes in the cluster.
131179

132180
Another concern is that for high throughput scenarios you would probably
133181
want to keep shards incoming queues non-empty. For that your parallelism factor
134182
must be at least 2.
135183

136-
### 3. Number of connections
184+
### 5. Number of connections
137185

138-
Both `scylla.coreconnections` and `scylla.maxconnections` define limits
139-
per node. When you see `-p scylla.coreconnections=280 -p scylla.maxconnections=280`
140-
that means 280 connections per node.
186+
If you use original Cassandra drivers you need to pick the proper number
187+
of connections per host. Scylla drivers do not require this to be configured
188+
and by default create a connection per shard. For example if your node has
189+
16 vCPU and thus 14 shards Scylla drivers will pick to create 14 connections
190+
per host. An excess of connections may result in degraded latency.
141191

142-
Number of connections must be a multiple of:
192+
Database client protocol is asynchronous and allows queueing requests in
193+
a single connection. The default queue limit for local keys is 1024 and 256
194+
for remote ones. Current binding implementation do not require this.
143195

144-
- number of _shards_
145-
- parallelism factor `K`
196+
Both `scylla.coreconnections` and `scylla.maxconnections` define limits per node.
197+
When you see `-p scylla.coreconnections=14 -p scylla.maxconnections=14` that means
198+
14 connections per node.
146199

147-
For example, for `i3.4xlarge` that has 14 shards per node and `K = 20`
148-
it makes sense to pick `connections = shards * K = 14 * 20 = 280`.
200+
Pick the number of connections per host to be divisible by the number of _shards_.
149201

150-
### 4. Other considerations
202+
### 6. Other considerations
151203

152204
Consistency levels do not change consistency model or its strongness.
153205
Even with `-p scylla.writeconsistencylevel=ONE` the data will be written
@@ -159,16 +211,13 @@ latency picture a bit but would not affect utilization.
159211
Remember that you can't measure CPU utilization with Scylla by normal
160212
Unix tools. Check out Scylla own metrics to see real reactors utilization.
161213

162-
Always use [token aware](https://www.scylladb.com/2019/03/27/best-practices-for-scylla-applications/)
163-
load balancing `-p scylla.tokenaware=true`.
164-
165214
For best performance it is crucial to evenly load all available shards.
166215

167-
### 5. Expected performance target
216+
### 7. Expected performance target
168217

169218
You can expect about 12500 uOPS / core (shard), where uOPS are basic
170219
reads and writes operations post replication. Don't forget that usually
171-
`Core = 2 * vCPU` for HT systems.
220+
`Core = 2 vCPU` for HT systems.
172221

173222
For example if we insert a row with RF = 3 we can count at least 3 writes -
174223
1 write per each replica. That is 1 Transaction = 3 u operations.
@@ -235,12 +284,9 @@ of 3 nodes of i3.4xlarge (16 vCPU per node) and target of 120000 is:
235284
* Default is false
236285
* https://docs.scylladb.com/using-scylla/tracing/
237286

238-
- `scylla.tokenaware`
239-
- Enable token awareness
240-
- Default value is false.
241-
242-
- `scylla.tokenaware_local_dc`
243-
- Restrict Round Robin child policy with the local dc nodes
287+
* `scylla.local_dc`
288+
- Specify local datacenter for multi-dc setup.
289+
- By default uses LOCAL_QUORUM consistency level.
244290
- Default value is empty.
245291

246292
- `scylla.lwt`

scylla/src/main/java/site/ycsb/db/scylla/ScyllaCQLClient.java

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,10 @@
1616
package site.ycsb.db.scylla;
1717

1818
import com.datastax.driver.core.*;
19+
import com.datastax.driver.core.policies.DCAwareRoundRobinPolicy;
1920
import com.datastax.driver.core.policies.LoadBalancingPolicy;
20-
import com.datastax.driver.core.querybuilder.*;
2121
import com.datastax.driver.core.policies.TokenAwarePolicy;
22-
import com.datastax.driver.core.policies.DCAwareRoundRobinPolicy;
22+
import com.datastax.driver.core.querybuilder.*;
2323
import site.ycsb.ByteArrayByteIterator;
2424
import site.ycsb.ByteIterator;
2525
import site.ycsb.DB;
@@ -86,9 +86,7 @@ public class ScyllaCQLClient extends DB {
8686

8787
public static final String SCYLLA_LWT = "scylla.lwt";
8888

89-
public static final String TOKEN_AWARE = "scylla.tokenaware";
90-
public static final String TOKEN_AWARE_DEFAULT = "false";
91-
public static final String TOKEN_AWARE_LOCAL_DC = "scylla.tokenaware_local_dc";
89+
public static final String TOKEN_AWARE_LOCAL_DC = "scylla.local_dc";
9290

9391
public static final String TRACING_PROPERTY = "scylla.tracing";
9492
public static final String TRACING_PROPERTY_DEFAULT = "false";
@@ -162,17 +160,22 @@ public void init() throws DBException {
162160
.addContactPoints(hosts);
163161
}
164162

165-
if (Boolean.parseBoolean(getProperties().getProperty(TOKEN_AWARE, TOKEN_AWARE_DEFAULT))) {
166-
LoadBalancingPolicy child;
167-
String localDc = getProperties().getProperty(TOKEN_AWARE_LOCAL_DC);
168-
if (localDc != null && !localDc.isEmpty()) {
169-
child = DCAwareRoundRobinPolicy.builder().withLocalDc(localDc).build();
170-
LOGGER.info("Using shard awareness with local DC: {}\n", localDc);
171-
} else {
172-
child = DCAwareRoundRobinPolicy.builder().build();
173-
LOGGER.info("Using shard awareness\n");
163+
final String localDC = getProperties().getProperty(TOKEN_AWARE_LOCAL_DC);
164+
if (localDC != null && !localDC.isEmpty()) {
165+
final LoadBalancingPolicy local = DCAwareRoundRobinPolicy.builder().withLocalDc(localDC).build();
166+
final TokenAwarePolicy tokenAware = new TokenAwarePolicy(local);
167+
builder = builder.withLoadBalancingPolicy(tokenAware);
168+
169+
LOGGER.info("Using local datacenter with token awareness: {}\n", localDC);
170+
171+
// If was not overridden explicitly, set LOCAL_QUORUM
172+
if (getProperties().getProperty(READ_CONSISTENCY_LEVEL_PROPERTY) == null) {
173+
readConsistencyLevel = ConsistencyLevel.LOCAL_QUORUM;
174+
}
175+
176+
if (getProperties().getProperty(WRITE_CONSISTENCY_LEVEL_PROPERTY) == null) {
177+
writeConsistencyLevel = ConsistencyLevel.LOCAL_QUORUM;
174178
}
175-
builder = builder.withLoadBalancingPolicy(new TokenAwarePolicy(child));
176179
}
177180

178181
cluster = builder.build();
@@ -224,6 +227,10 @@ public void init() throws DBException {
224227
} else {
225228
LOGGER.info("Not using LWT\n");
226229
}
230+
231+
LOGGER.info("Read consistency: {}, Write consistency: {}\n",
232+
readConsistencyLevel.name(),
233+
writeConsistencyLevel.name());
227234
} catch (Exception e) {
228235
throw new DBException(e);
229236
}

0 commit comments

Comments
 (0)