PageRenderTime 57ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/src/java/org/apache/cassandra/service/paxos/ContentionStrategy.java

https://github.com/thelastpickle/cassandra
Java | 651 lines | 489 code | 89 blank | 73 comment | 55 complexity | d87d912aa1c0d5c75735fcecfc82ed0e MD5 | raw file
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one
  3. * or more contributor license agreements. See the NOTICE file
  4. * distributed with this work for additional information
  5. * regarding copyright ownership. The ASF licenses this file
  6. * to you under the Apache License, Version 2.0 (the
  7. * "License"); you may not use this file except in compliance
  8. * with the License. You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing, software
  13. * distributed under the License is distributed on an "AS IS" BASIS,
  14. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. * See the License for the specific language governing permissions and
  16. * limitations under the License.
  17. */
  18. package org.apache.cassandra.service.paxos;
  19. import com.google.common.annotations.VisibleForTesting;
  20. import com.google.common.base.Preconditions;
  21. import com.google.common.collect.ImmutableMap;
  22. import com.codahale.metrics.Snapshot;
  23. import org.apache.cassandra.config.DatabaseDescriptor;
  24. import org.apache.cassandra.db.ConsistencyLevel;
  25. import org.apache.cassandra.db.DecoratedKey;
  26. import org.apache.cassandra.schema.TableMetadata;
  27. import org.apache.cassandra.tracing.Tracing;
  28. import org.apache.cassandra.utils.ByteBufferUtil;
  29. import org.apache.cassandra.utils.NoSpamLogger;
  30. import org.slf4j.Logger;
  31. import org.slf4j.LoggerFactory;
  32. import java.util.concurrent.ThreadLocalRandom;
  33. import java.util.concurrent.TimeUnit;
  34. import java.util.concurrent.atomic.AtomicReference;
  35. import java.util.function.DoubleSupplier;
  36. import java.util.function.LongBinaryOperator;
  37. import java.util.function.Supplier;
  38. import java.util.regex.Matcher;
  39. import java.util.regex.Pattern;
  40. import static java.lang.Double.parseDouble;
  41. import static java.lang.Integer.parseInt;
  42. import static java.lang.Math.*;
  43. import static java.util.Arrays.stream;
  44. import static java.util.concurrent.TimeUnit.*;
  45. import static org.apache.cassandra.config.DatabaseDescriptor.*;
  46. import static org.apache.cassandra.metrics.ClientRequestsMetricsHolder.casReadMetrics;
  47. import static org.apache.cassandra.metrics.ClientRequestsMetricsHolder.casWriteMetrics;
  48. import static org.apache.cassandra.utils.Clock.Global.nanoTime;
  49. import static org.apache.cassandra.utils.Clock.waitUntil;
  50. /**
  51. * <p>A strategy for making back-off decisions for Paxos operations that fail to make progress because of other paxos operations.
  52. * The strategy is defined by four factors: <ul>
  53. * <li> {@link #min}
  54. * <li> {@link #max}
  55. * <li> {@link #minDelta}
  56. * <li> {@link #waitRandomizer}
  57. * </ul>
  58. *
  59. * <p>The first three represent time periods, and may be defined dynamically based on a simple calculation over: <ul>
  60. * <li> {@code pX()} recent experienced latency distribution for successful operations,
  61. * e.g. {@code p50(rw)} the maximum of read and write median latencies,
  62. * {@code p999(r)} the 99.9th percentile of read latencies
  63. * <li> {@code attempts} the number of failed attempts made by the operation so far
  64. * <li> {@code constant} a user provided floating point constant
  65. * </ul>
  66. *
  67. * <p>Their calculation may take any of these forms
  68. * <li> constant {@code $constant$[mu]s}
  69. * <li> dynamic constant {@code pX() * constant}
  70. * <li> dynamic linear {@code pX() * constant * attempts}
  71. * <li> dynamic exponential {@code pX() * constant ^ attempts}
  72. *
  73. * <p>Furthermore, the dynamic calculations can be bounded with a min/max, like so:
  74. * {@code min[mu]s <= dynamic expr <= max[mu]s}
  75. *
  76. * e.g.
  77. * <li> {@code 10ms <= p50(rw)*0.66}
  78. * <li> {@code 10ms <= p95(rw)*1.8^attempts <= 100ms}
  79. * <li> {@code 5ms <= p50(rw)*0.5}
  80. *
  81. * <p>These calculations are put together to construct a range from which we draw a random number.
  82. * The period we wait for {@code X} will be drawn so that {@code min <= X < max}.
  83. *
  84. * <p>With the constraint that {@code max} must be {@code minDelta} greater than {@code min},
  85. * but no greater than its expression-defined maximum. {@code max} will be increased up until
  86. * this point, after which {@code min} will be decreased until this gap is imposed.
  87. *
  88. * <p>The {@link #waitRandomizer} property specifies the manner in which a random value is drawn from the range.
  89. * It is defined using one of the following specifiers:
  90. * <li> uniform
  91. * <li> exp($power$) or exponential($power$)
  92. * <li> qexp($power$) or qexponential($power$) or quantizedexponential($power$)
  93. *
  94. * The uniform specifier is self-explanatory, selecting all values in the range with equal probability.
  95. * The exponential specifier draws values towards the end of the range with higher probability, raising
  96. * a floating point number in the range [0..1.0) to the power provided, and translating the resulting value
  97. * to a uniform value in the range.
  98. * The quantized exponential specifier partitions the range into {@code attempts} buckets, then applies the pure
  99. * exponential approach to draw values from [0..attempts), before drawing a uniform value from the corresponding bucket
  100. *
  101. * <p>Finally, there is also a {@link #traceAfterAttempts} property that permits initiating tracing of operations
  102. * that experience a certain minimum number of failed paxos rounds due to contention. A setting of 0 or 1 will initiate
  103. * a trace session after the first failed ballot.
  104. */
  105. public class ContentionStrategy
  106. {
  107. private static final Logger logger = LoggerFactory.getLogger(ContentionStrategy.class);
  108. private static final Pattern BOUND = Pattern.compile(
  109. "(?<const>0|[0-9]+[mu]s)" +
  110. "|((?<min>0|[0-9]+[mu]s) *<= *)?" +
  111. "(p(?<perc>[0-9]+)\\((?<rw>r|w|rw|wr)\\)|(?<constbase>0|[0-9]+[mu]s))" +
  112. "\\s*([*]\\s*(?<mod>[0-9.]+)?\\s*(?<modkind>[*^]\\s*attempts)?)?" +
  113. "( *<= *(?<max>0|[0-9]+[mu]s))?");
  114. private static final Pattern TIME = Pattern.compile(
  115. "0|([0-9]+)ms|([0-9]+)us");
  116. private static final Pattern RANDOMIZER = Pattern.compile(
  117. "uniform|exp(onential)?[(](?<exp>[0-9.]+)[)]|q(uantized)?exp(onential)?[(](?<qexp>[0-9.]+)[)]");
  118. private static final String DEFAULT_WAIT_RANDOMIZER = "qexp(1.5)"; // at least 0ms, and at least 66% of median latency
  119. private static final String DEFAULT_MIN = "0 <= p50(rw)*0.66"; // at least 0ms, and at least 66% of median latency
  120. private static final String DEFAULT_MAX = "10ms <= p95(rw)*1.8^attempts <= 100ms"; // p95 latency with exponential back-off at rate of 1.8^attempts
  121. private static final String DEFAULT_MIN_DELTA = "5ms <= p50(rw)*0.5"; // at least 5ms, and at least 50% of median latency
  122. private static volatile ContentionStrategy current;
  123. // Factories can be useful for testing purposes, to supply custom implementations of selectors and modifiers.
  124. final static LatencySelectorFactory selectors = new LatencySelectorFactory(){};
  125. final static LatencyModifierFactory modifiers = new LatencyModifierFactory(){};
  126. final static WaitRandomizerFactory randomizers = new WaitRandomizerFactory(){};
  127. static
  128. {
  129. current = new ContentionStrategy(defaultWaitRandomizer(), defaultMinWait(), defaultMaxWait(), defaultMinDelta(), Integer.MAX_VALUE);
  130. }
  131. static interface LatencyModifierFactory
  132. {
  133. default LatencyModifier identity() { return (l, a) -> l; }
  134. default LatencyModifier multiply(double constant) { return (l, a) -> saturatedCast(l * constant); }
  135. default LatencyModifier multiplyByAttempts(double multiply) { return (l, a) -> saturatedCast(l * multiply * a); }
  136. default LatencyModifier multiplyByAttemptsExp(double base) { return (l, a) -> saturatedCast(l * pow(base, a)); }
  137. }
  138. static interface LatencySupplier
  139. {
  140. abstract long get(double percentile);
  141. }
  142. static interface LatencySelector
  143. {
  144. abstract long select(LatencySupplier readLatencyHistogram, LatencySupplier writeLatencyHistogram);
  145. }
  146. static interface LatencySelectorFactory
  147. {
  148. default LatencySelector constant(long latency) { return (read, write) -> latency; }
  149. default LatencySelector read(double percentile) { return (read, write) -> read.get(percentile); }
  150. default LatencySelector write(double percentile) { return (read, write) -> write.get(percentile); }
  151. default LatencySelector maxReadWrite(double percentile) { return (read, write) -> max(read.get(percentile), write.get(percentile)); }
  152. }
  153. static interface LatencyModifier
  154. {
  155. long modify(long latency, int attempts);
  156. }
  157. static interface WaitRandomizer
  158. {
  159. abstract long wait(long min, long max, int attempts);
  160. }
  161. static interface WaitRandomizerFactory
  162. {
  163. default LongBinaryOperator uniformLongSupplier() { return (min, max) -> ThreadLocalRandom.current().nextLong(min, max); } // DO NOT USE METHOD HANDLES (want to fetch afresh each time)
  164. default DoubleSupplier uniformDoubleSupplier() { return () -> ThreadLocalRandom.current().nextDouble(); }
  165. default WaitRandomizer uniform() { return new Uniform(uniformLongSupplier()); }
  166. default WaitRandomizer exponential(double power) { return new Exponential(uniformLongSupplier(), uniformDoubleSupplier(), power); }
  167. default WaitRandomizer quantizedExponential(double power) { return new QuantizedExponential(uniformLongSupplier(), uniformDoubleSupplier(), power); }
  168. static class Uniform implements WaitRandomizer
  169. {
  170. final LongBinaryOperator uniformLong;
  171. public Uniform(LongBinaryOperator uniformLong)
  172. {
  173. this.uniformLong = uniformLong;
  174. }
  175. @Override
  176. public long wait(long min, long max, int attempts)
  177. {
  178. return uniformLong.applyAsLong(min, max);
  179. }
  180. }
  181. static abstract class AbstractExponential implements WaitRandomizer
  182. {
  183. final LongBinaryOperator uniformLong;
  184. final DoubleSupplier uniformDouble;
  185. final double power;
  186. public AbstractExponential(LongBinaryOperator uniformLong, DoubleSupplier uniformDouble, double power)
  187. {
  188. this.uniformLong = uniformLong;
  189. this.uniformDouble = uniformDouble;
  190. this.power = power;
  191. }
  192. }
  193. static class Exponential extends AbstractExponential
  194. {
  195. public Exponential(LongBinaryOperator uniformLong, DoubleSupplier uniformDouble, double power)
  196. {
  197. super(uniformLong, uniformDouble, power);
  198. }
  199. @Override
  200. public long wait(long min, long max, int attempts)
  201. {
  202. if (attempts == 1)
  203. return uniformLong.applyAsLong(min, max);
  204. double p = uniformDouble.getAsDouble();
  205. long delta = max - min;
  206. delta *= Math.pow(p, power);
  207. return max - delta;
  208. }
  209. }
  210. static class QuantizedExponential extends AbstractExponential
  211. {
  212. public QuantizedExponential(LongBinaryOperator uniformLong, DoubleSupplier uniformDouble, double power)
  213. {
  214. super(uniformLong, uniformDouble, power);
  215. }
  216. @Override
  217. public long wait(long min, long max, int attempts)
  218. {
  219. long quanta = (max - min) / attempts;
  220. if (attempts == 1 || quanta == 0)
  221. return uniformLong.applyAsLong(min, max);
  222. double p = uniformDouble.getAsDouble();
  223. int base = (int) (attempts * Math.pow(p, power));
  224. return max - ThreadLocalRandom.current().nextLong(quanta * base, quanta * (base + 1));
  225. }
  226. }
  227. }
  228. static class SnapshotAndTime
  229. {
  230. final long validUntil;
  231. final Snapshot snapshot;
  232. SnapshotAndTime(long validUntil, Snapshot snapshot)
  233. {
  234. this.validUntil = validUntil;
  235. this.snapshot = snapshot;
  236. }
  237. }
  238. static class TimeLimitedLatencySupplier extends AtomicReference<SnapshotAndTime> implements LatencySupplier
  239. {
  240. final Supplier<Snapshot> snapshotSupplier;
  241. final long validForNanos;
  242. TimeLimitedLatencySupplier(Supplier<Snapshot> snapshotSupplier, long time, TimeUnit units)
  243. {
  244. this.snapshotSupplier = snapshotSupplier;
  245. this.validForNanos = units.toNanos(time);
  246. }
  247. private Snapshot getSnapshot()
  248. {
  249. long now = nanoTime();
  250. SnapshotAndTime cur = get();
  251. if (cur != null && cur.validUntil > now)
  252. return cur.snapshot;
  253. Snapshot newSnapshot = snapshotSupplier.get();
  254. SnapshotAndTime next = new SnapshotAndTime(now + validForNanos, newSnapshot);
  255. if (compareAndSet(cur, next))
  256. return next.snapshot;
  257. return accumulateAndGet(next, (a, b) -> a.validUntil > b.validUntil ? a : b).snapshot;
  258. }
  259. @Override
  260. public long get(double percentile)
  261. {
  262. return (long)getSnapshot().getValue(percentile);
  263. }
  264. }
  265. static class Bound
  266. {
  267. final long min, max, onFailure;
  268. final LatencyModifier modifier;
  269. final LatencySelector selector;
  270. final LatencySupplier reads, writes;
  271. Bound(long min, long max, long onFailure, LatencyModifier modifier, LatencySelector selector)
  272. {
  273. Preconditions.checkArgument(min<=max, "min (%s) must be less than or equal to max (%s)", min, max);
  274. this.min = min;
  275. this.max = max;
  276. this.onFailure = onFailure;
  277. this.modifier = modifier;
  278. this.selector = selector;
  279. this.reads = new TimeLimitedLatencySupplier(casReadMetrics.latency::getSnapshot, 10L, SECONDS);
  280. this.writes = new TimeLimitedLatencySupplier(casWriteMetrics.latency::getSnapshot, 10L, SECONDS);
  281. }
  282. long get(int attempts)
  283. {
  284. try
  285. {
  286. long base = selector.select(reads, writes);
  287. return max(min, min(max, modifier.modify(base, attempts)));
  288. }
  289. catch (Throwable t)
  290. {
  291. NoSpamLogger.getLogger(logger, 1L, MINUTES).info("", t);
  292. return onFailure;
  293. }
  294. }
  295. public String toString()
  296. {
  297. return "Bound{" +
  298. "min=" + min +
  299. ", max=" + max +
  300. ", onFailure=" + onFailure +
  301. ", modifier=" + modifier +
  302. ", selector=" + selector +
  303. '}';
  304. }
  305. }
  306. final WaitRandomizer waitRandomizer;
  307. final Bound min, max, minDelta;
  308. final int traceAfterAttempts;
  309. public ContentionStrategy(String waitRandomizer, String min, String max, String minDelta, int traceAfterAttempts)
  310. {
  311. this.waitRandomizer = parseWaitRandomizer(waitRandomizer);
  312. this.min = parseBound(min, true);
  313. this.max = parseBound(max, false);
  314. this.minDelta = parseBound(minDelta, true);
  315. this.traceAfterAttempts = traceAfterAttempts;
  316. }
  317. public enum Type
  318. {
  319. READ("Contended Paxos Read"), WRITE("Contended Paxos Write"), REPAIR("Contended Paxos Repair");
  320. final String traceTitle;
  321. final String lowercase;
  322. Type(String traceTitle)
  323. {
  324. this.traceTitle = traceTitle;
  325. this.lowercase = name().toLowerCase();
  326. }
  327. }
  328. private long computeWaitUntilForContention(int attempts, TableMetadata table, DecoratedKey partitionKey, ConsistencyLevel consistency, Type type)
  329. {
  330. if (attempts >= traceAfterAttempts && !Tracing.isTracing())
  331. {
  332. Tracing.instance.newSession(Tracing.TraceType.QUERY);
  333. Tracing.instance.begin(type.traceTitle,
  334. ImmutableMap.of(
  335. "keyspace", table.keyspace,
  336. "table", table.name,
  337. "partitionKey", table.partitionKeyType.getString(partitionKey.getKey()),
  338. "consistency", consistency.name(),
  339. "kind", type.lowercase
  340. ));
  341. logger.info("Tracing contended paxos {} for key {} on {}.{} with trace id {}",
  342. type.lowercase,
  343. ByteBufferUtil.bytesToHex(partitionKey.getKey()),
  344. table.keyspace, table.name,
  345. Tracing.instance.getSessionId());
  346. }
  347. long minWaitMicros = min.get(attempts);
  348. long maxWaitMicros = max.get(attempts);
  349. long minDeltaMicros = minDelta.get(attempts);
  350. if (minWaitMicros + minDeltaMicros > maxWaitMicros)
  351. {
  352. maxWaitMicros = minWaitMicros + minDeltaMicros;
  353. if (maxWaitMicros > this.max.max)
  354. {
  355. maxWaitMicros = this.max.max;
  356. minWaitMicros = max(this.min.min, min(this.min.max, maxWaitMicros - minDeltaMicros));
  357. }
  358. }
  359. long wait = waitRandomizer.wait(minWaitMicros, maxWaitMicros, attempts);
  360. return nanoTime() + wait;
  361. }
  362. private boolean doWaitForContention(long deadline, int attempts, TableMetadata table, DecoratedKey partitionKey, ConsistencyLevel consistency, Type type)
  363. {
  364. long until = computeWaitUntilForContention(attempts, table, partitionKey, consistency, type);
  365. if (until >= deadline)
  366. return false;
  367. try
  368. {
  369. waitUntil(until);
  370. }
  371. catch (InterruptedException e)
  372. {
  373. Thread.currentThread().interrupt();
  374. return false;
  375. }
  376. return true;
  377. }
  378. static boolean waitForContention(long deadline, int attempts, TableMetadata table, DecoratedKey partitionKey, ConsistencyLevel consistency, Type type)
  379. {
  380. return current.doWaitForContention(deadline, attempts, table, partitionKey, consistency, type);
  381. }
  382. static long waitUntilForContention(int attempts, TableMetadata table, DecoratedKey partitionKey, ConsistencyLevel consistency, Type type)
  383. {
  384. return current.computeWaitUntilForContention(attempts, table, partitionKey, consistency, type);
  385. }
  386. static class ParsedStrategy
  387. {
  388. final String waitRandomizer, min, max, minDelta;
  389. final ContentionStrategy strategy;
  390. ParsedStrategy(String waitRandomizer, String min, String max, String minDelta, ContentionStrategy strategy)
  391. {
  392. this.waitRandomizer = waitRandomizer;
  393. this.min = min;
  394. this.max = max;
  395. this.minDelta = minDelta;
  396. this.strategy = strategy;
  397. }
  398. }
  399. @VisibleForTesting
  400. static ParsedStrategy parseStrategy(String spec)
  401. {
  402. String[] args = spec.split(",");
  403. String waitRandomizer = find(args, "random");
  404. String min = find(args, "min");
  405. String max = find(args, "max");
  406. String minDelta = find(args, "delta");
  407. String trace = find(args, "trace");
  408. if (waitRandomizer == null) waitRandomizer = defaultWaitRandomizer();
  409. if (min == null) min = defaultMinWait();
  410. if (max == null) max = defaultMaxWait();
  411. if (minDelta == null) minDelta = defaultMinDelta();
  412. int traceAfterAttempts = trace == null ? current.traceAfterAttempts: Integer.parseInt(trace);
  413. ContentionStrategy strategy = new ContentionStrategy(waitRandomizer, min, max, minDelta, traceAfterAttempts);
  414. return new ParsedStrategy(waitRandomizer, min, max, minDelta, strategy);
  415. }
  416. public static void setStrategy(String spec)
  417. {
  418. ParsedStrategy parsed = parseStrategy(spec);
  419. current = parsed.strategy;
  420. setPaxosContentionWaitRandomizer(parsed.waitRandomizer);
  421. setPaxosContentionMinWait(parsed.min);
  422. setPaxosContentionMaxWait(parsed.max);
  423. setPaxosContentionMinDelta(parsed.minDelta);
  424. }
  425. public static String getStrategySpec()
  426. {
  427. return "min=" + defaultMinWait()
  428. + ",max=" + defaultMaxWait()
  429. + ",delta=" + defaultMinDelta()
  430. + ",random=" + defaultWaitRandomizer()
  431. + ",trace=" + current.traceAfterAttempts;
  432. }
  433. private static String find(String[] args, String param)
  434. {
  435. return stream(args).filter(s -> s.startsWith(param + '='))
  436. .map(s -> s.substring(param.length() + 1))
  437. .findFirst().orElse(null);
  438. }
  439. private static LatencySelector parseLatencySelector(Matcher m, LatencySelectorFactory selectors)
  440. {
  441. String perc = m.group("perc");
  442. if (perc == null)
  443. return selectors.constant(parseInMicros(m.group("constbase")));
  444. double percentile = parseDouble("0." + perc);
  445. String rw = m.group("rw");
  446. if (rw.length() == 2)
  447. return selectors.maxReadWrite(percentile);
  448. else if ("r".equals(rw))
  449. return selectors.read(percentile);
  450. else
  451. return selectors.write(percentile);
  452. }
  453. private static LatencyModifier parseLatencyModifier(Matcher m, LatencyModifierFactory modifiers)
  454. {
  455. String mod = m.group("mod");
  456. if (mod == null)
  457. return modifiers.identity();
  458. double modifier = parseDouble(mod);
  459. String modkind = m.group("modkind");
  460. if (modkind == null)
  461. return modifiers.multiply(modifier);
  462. if (modkind.startsWith("*"))
  463. return modifiers.multiplyByAttempts(modifier);
  464. else if (modkind.startsWith("^"))
  465. return modifiers.multiplyByAttemptsExp(modifier);
  466. else
  467. throw new IllegalArgumentException("Unrecognised attempt modifier: " + modkind);
  468. }
  469. static long saturatedCast(double v)
  470. {
  471. if (v > Long.MAX_VALUE)
  472. return Long.MAX_VALUE;
  473. return (long) v;
  474. }
  475. static WaitRandomizer parseWaitRandomizer(String input)
  476. {
  477. return parseWaitRandomizer(input, randomizers);
  478. }
  479. static WaitRandomizer parseWaitRandomizer(String input, WaitRandomizerFactory randomizers)
  480. {
  481. Matcher m = RANDOMIZER.matcher(input);
  482. if (!m.matches())
  483. throw new IllegalArgumentException(input + " does not match" + RANDOMIZER);
  484. String exp;
  485. exp = m.group("exp");
  486. if (exp != null)
  487. return randomizers.exponential(Double.parseDouble(exp));
  488. exp = m.group("qexp");
  489. if (exp != null)
  490. return randomizers.quantizedExponential(Double.parseDouble(exp));
  491. return randomizers.uniform();
  492. }
  493. static Bound parseBound(String input, boolean isMin)
  494. {
  495. return parseBound(input, isMin, selectors, modifiers);
  496. }
  497. @VisibleForTesting
  498. static Bound parseBound(String input, boolean isMin, LatencySelectorFactory selectors, LatencyModifierFactory modifiers)
  499. {
  500. Matcher m = BOUND.matcher(input);
  501. if (!m.matches())
  502. throw new IllegalArgumentException(input + " does not match " + BOUND);
  503. String maybeConst = m.group("const");
  504. if (maybeConst != null)
  505. {
  506. long v = parseInMicros(maybeConst);
  507. return new Bound(v, v, v, modifiers.identity(), selectors.constant(v));
  508. }
  509. long min = parseInMicros(m.group("min"), 0);
  510. long max = parseInMicros(m.group("max"), maxQueryTimeoutMicros() / 2);
  511. return new Bound(min, max, isMin ? min : max, parseLatencyModifier(m, modifiers), parseLatencySelector(m, selectors));
  512. }
  513. private static long parseInMicros(String input, long orElse)
  514. {
  515. if (input == null)
  516. return orElse;
  517. return parseInMicros(input);
  518. }
  519. private static long parseInMicros(String input)
  520. {
  521. Matcher m = TIME.matcher(input);
  522. if (!m.matches())
  523. throw new IllegalArgumentException(input + " does not match " + TIME);
  524. String text;
  525. if (null != (text = m.group(1)))
  526. return parseInt(text) * 1000;
  527. else if (null != (text = m.group(2)))
  528. return parseInt(text);
  529. else
  530. return 0;
  531. }
  532. @VisibleForTesting
  533. static String defaultWaitRandomizer()
  534. {
  535. return orElse(DatabaseDescriptor::getPaxosContentionWaitRandomizer, DEFAULT_WAIT_RANDOMIZER);
  536. }
  537. @VisibleForTesting
  538. static String defaultMinWait()
  539. {
  540. return orElse(DatabaseDescriptor::getPaxosContentionMinWait, DEFAULT_MIN);
  541. }
  542. @VisibleForTesting
  543. static String defaultMaxWait()
  544. {
  545. return orElse(DatabaseDescriptor::getPaxosContentionMaxWait, DEFAULT_MAX);
  546. }
  547. @VisibleForTesting
  548. static String defaultMinDelta()
  549. {
  550. return orElse(DatabaseDescriptor::getPaxosContentionMinDelta, DEFAULT_MIN_DELTA);
  551. }
  552. @VisibleForTesting
  553. static long maxQueryTimeoutMicros()
  554. {
  555. return max(max(getCasContentionTimeout(MICROSECONDS), getWriteRpcTimeout(MICROSECONDS)), getReadRpcTimeout(MICROSECONDS));
  556. }
  557. private static String orElse(Supplier<String> get, String orElse)
  558. {
  559. String result = get.get();
  560. return result != null ? result : orElse;
  561. }
  562. }