Merge branch 'cassandra-3.11' into trunk

This commit is contained in:
Ariel Weisberg 2017-02-13 13:33:11 -05:00
commit edcbef3e34
7 changed files with 114 additions and 9 deletions

View File

@ -247,6 +247,7 @@ Merged from 3.0:
* Correct log message for statistics of offheap memtable flush (CASSANDRA-12776) * Correct log message for statistics of offheap memtable flush (CASSANDRA-12776)
* Explicitly set locale for string validation (CASSANDRA-12541,CASSANDRA-12542,CASSANDRA-12543,CASSANDRA-12545) * Explicitly set locale for string validation (CASSANDRA-12541,CASSANDRA-12542,CASSANDRA-12543,CASSANDRA-12545)
Merged from 2.2: Merged from 2.2:
* Coalescing strategy sleeps too much and shouldn't be enabled by default (CASSANDRA-13090)
* Fix speculative retry bugs (CASSANDRA-13009) * Fix speculative retry bugs (CASSANDRA-13009)
* Fix handling of nulls and unsets in IN conditions (CASSANDRA-12981) * Fix handling of nulls and unsets in IN conditions (CASSANDRA-12981)
* Fix race causing infinite loop if Thrift server is stopped before it starts listening (CASSANDRA-12856) * Fix race causing infinite loop if Thrift server is stopped before it starts listening (CASSANDRA-12856)

View File

@ -1093,3 +1093,30 @@ back_pressure_strategy:
- high_ratio: 0.90 - high_ratio: 0.90
factor: 5 factor: 5
flow: FAST flow: FAST
# Coalescing Strategies #
# Coalescing multiples messages turns out to significantly boost message processing throughput (think doubling or more).
# On bare metal, the floor for packet processing throughput is high enough that many applications wont notice, but in
# virtualized environments, the point at which an application can be bound by network packet processing can be
# surprisingly low compared to the throughput of task processing that is possible inside a VM. Its not that bare metal
# doesnt benefit from coalescing messages, its that the number of packets a bare metal network interface can process
# is sufficient for many applications such that no load starvation is experienced even without coalescing.
# There are other benefits to coalescing network messages that are harder to isolate with a simple metric like messages
# per second. By coalescing multiple tasks together, a network thread can process multiple messages for the cost of one
# trip to read from a socket, and all the task submission work can be done at the same time reducing context switching
# and increasing cache friendliness of network message processing.
# See CASSANDRA-8692 for details.
# Strategy to use for coalescing messages in OutboundTcpConnection.
# Can be fixed, movingaverage, timehorizon (default), disabled.
# You can also specify a subclass of CoalescingStrategies.CoalescingStrategy by name.
# otc_coalescing_strategy: TIMEHORIZON
# How many microseconds to wait for coalescing. For fixed strategy this is the amount of time after the first
# message is received before it will be sent with any accompanying messages. For moving average this is the
# maximum amount of time that will be waited as well as the interval at which messages must arrive on average
# for coalescing to be enabled.
# otc_coalescing_window_us: 200
# Do not try to coalesce messages if we already got that many messages. This should be more than 2 and less than 128.
# otc_coalescing_enough_coalesced_messages: 8

View File

@ -283,12 +283,13 @@ public class Config
/* /*
* How many microseconds to wait for coalescing. For fixed strategy this is the amount of time after the first * How many microseconds to wait for coalescing. For fixed strategy this is the amount of time after the first
* messgae is received before it will be sent with any accompanying messages. For moving average this is the * message is received before it will be sent with any accompanying messages. For moving average this is the
* maximum amount of time that will be waited as well as the interval at which messages must arrive on average * maximum amount of time that will be waited as well as the interval at which messages must arrive on average
* for coalescing to be enabled. * for coalescing to be enabled.
*/ */
public static final int otc_coalescing_window_us_default = 200; public static final int otc_coalescing_window_us_default = 200;
public int otc_coalescing_window_us = otc_coalescing_window_us_default; public int otc_coalescing_window_us = otc_coalescing_window_us_default;
public int otc_coalescing_enough_coalesced_messages = 8;
public int windows_timer_interval = 0; public int windows_timer_interval = 0;

View File

@ -686,6 +686,12 @@ public class DatabaseDescriptor
{ {
throw new ConfigurationException("Error configuring back-pressure strategy: " + conf.back_pressure_strategy, ex); throw new ConfigurationException("Error configuring back-pressure strategy: " + conf.back_pressure_strategy, ex);
} }
if (conf.otc_coalescing_enough_coalesced_messages > 128)
throw new ConfigurationException("otc_coalescing_enough_coalesced_messages must be smaller than 128", false);
if (conf.otc_coalescing_enough_coalesced_messages <= 0)
throw new ConfigurationException("otc_coalescing_enough_coalesced_messages must be positive", false);
} }
private static String storagedirFor(String type) private static String storagedirFor(String type)
@ -2116,6 +2122,16 @@ public class DatabaseDescriptor
return conf.otc_coalescing_window_us; return conf.otc_coalescing_window_us;
} }
public static int getOtcCoalescingEnoughCoalescedMessages()
{
return conf.otc_coalescing_enough_coalesced_messages;
}
public static void setOtcCoalescingEnoughCoalescedMessages(int otc_coalescing_enough_coalesced_messages)
{
conf.otc_coalescing_enough_coalesced_messages = otc_coalescing_enough_coalesced_messages;
}
public static int getWindowsTimerInterval() public static int getWindowsTimerInterval()
{ {
return conf.windows_timer_interval; return conf.windows_timer_interval;

View File

@ -85,6 +85,8 @@ public class OutboundTcpConnection extends FastThreadLocalThread
//Size of 3 elements added to every message //Size of 3 elements added to every message
private static final int PROTOCOL_MAGIC_ID_TIMESTAMP_SIZE = 12; private static final int PROTOCOL_MAGIC_ID_TIMESTAMP_SIZE = 12;
public static final int MAX_COALESCED_MESSAGES = 128;
private static CoalescingStrategy newCoalescingStrategy(String displayName) private static CoalescingStrategy newCoalescingStrategy(String displayName)
{ {
return CoalescingStrategies.newCoalescingStrategy(DatabaseDescriptor.getOtcCoalescingStrategy(), return CoalescingStrategies.newCoalescingStrategy(DatabaseDescriptor.getOtcCoalescingStrategy(),
@ -200,7 +202,7 @@ public class OutboundTcpConnection extends FastThreadLocalThread
public void run() public void run()
{ {
final int drainedMessageSize = 128; final int drainedMessageSize = MAX_COALESCED_MESSAGES;
// keeping list (batch) size small for now; that way we don't have an unbounded array (that we never resize) // keeping list (batch) size small for now; that way we don't have an unbounded array (that we never resize)
final List<QueuedMessage> drainedMessages = new ArrayList<>(drainedMessageSize); final List<QueuedMessage> drainedMessages = new ArrayList<>(drainedMessageSize);

View File

@ -19,8 +19,11 @@ package org.apache.cassandra.utils;
import org.apache.cassandra.concurrent.NamedThreadFactory; import org.apache.cassandra.concurrent.NamedThreadFactory;
import org.apache.cassandra.config.Config; import org.apache.cassandra.config.Config;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.io.util.FileUtils; import org.apache.cassandra.io.util.FileUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File; import java.io.File;
import java.io.RandomAccessFile; import java.io.RandomAccessFile;
@ -40,6 +43,7 @@ import com.google.common.base.Preconditions;
public class CoalescingStrategies public class CoalescingStrategies
{ {
static protected final Logger logger = LoggerFactory.getLogger(CoalescingStrategies.class);
/* /*
* Log debug information at info level about what the average is and when coalescing is enabled/disabled * Log debug information at info level about what the average is and when coalescing is enabled/disabled
@ -89,15 +93,23 @@ public class CoalescingStrategies
{ {
long now = System.nanoTime(); long now = System.nanoTime();
final long timer = now + nanos; final long timer = now + nanos;
// We shouldn't loop if it's within a few % of the target sleep time if on a second iteration.
// See CASSANDRA-8692.
final long limit = timer - nanos / 16;
do do
{ {
LockSupport.parkNanos(timer - now); LockSupport.parkNanos(timer - now);
now = System.nanoTime();
} }
while (timer - (now = System.nanoTime()) > nanos / 16); while (now < limit);
} }
private static boolean maybeSleep(int messages, long averageGap, long maxCoalesceWindow, Parker parker) private static boolean maybeSleep(int messages, long averageGap, long maxCoalesceWindow, Parker parker)
{ {
// Do not sleep if there are still items in the backlog (CASSANDRA-13090).
if (messages >= DatabaseDescriptor.getOtcCoalescingEnoughCoalescedMessages())
return false;
// only sleep if we can expect to double the number of messages we're sending in the time interval // only sleep if we can expect to double the number of messages we're sending in the time interval
long sleep = messages * averageGap; long sleep = messages * averageGap;
if (sleep <= 0 || sleep > maxCoalesceWindow) if (sleep <= 0 || sleep > maxCoalesceWindow)
@ -335,7 +347,7 @@ public class CoalescingStrategies
if (input.drainTo(out, maxItems) == 0) if (input.drainTo(out, maxItems) == 0)
{ {
out.add(input.take()); out.add(input.take());
input.drainTo(out, maxItems - 1); input.drainTo(out, maxItems - out.size());
} }
for (Coalescable qm : out) for (Coalescable qm : out)
@ -418,15 +430,16 @@ public class CoalescingStrategies
if (input.drainTo(out, maxItems) == 0) if (input.drainTo(out, maxItems) == 0)
{ {
out.add(input.take()); out.add(input.take());
input.drainTo(out, maxItems - out.size());
} }
long average = notifyOfSample(out.get(0).timestampNanos()); long average = notifyOfSample(out.get(0).timestampNanos());
debugGap(average); debugGap(average);
maybeSleep(out.size(), average, maxCoalesceWindow, parker); if (maybeSleep(out.size(), average, maxCoalesceWindow, parker)) {
input.drainTo(out, maxItems - out.size());
}
input.drainTo(out, maxItems - out.size());
for (int ii = 1; ii < out.size(); ii++) for (int ii = 1; ii < out.size(); ii++)
notifyOfSample(out.get(ii).timestampNanos()); notifyOfSample(out.get(ii).timestampNanos());
} }
@ -455,11 +468,16 @@ public class CoalescingStrategies
@Override @Override
protected <C extends Coalescable> void coalesceInternal(BlockingQueue<C> input, List<C> out, int maxItems) throws InterruptedException protected <C extends Coalescable> void coalesceInternal(BlockingQueue<C> input, List<C> out, int maxItems) throws InterruptedException
{ {
int enough = DatabaseDescriptor.getOtcCoalescingEnoughCoalescedMessages();
if (input.drainTo(out, maxItems) == 0) if (input.drainTo(out, maxItems) == 0)
{ {
out.add(input.take()); out.add(input.take());
parker.park(coalesceWindow); input.drainTo(out, maxItems - out.size());
input.drainTo(out, maxItems - 1); if (out.size() < enough) {
parker.park(coalesceWindow);
input.drainTo(out, maxItems - out.size());
}
} }
debugTimestamps(out); debugTimestamps(out);
} }

View File

@ -17,10 +17,12 @@
*/ */
package org.apache.cassandra.utils; package org.apache.cassandra.utils;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.utils.CoalescingStrategies.Clock; import org.apache.cassandra.utils.CoalescingStrategies.Clock;
import org.apache.cassandra.utils.CoalescingStrategies.Coalescable; import org.apache.cassandra.utils.CoalescingStrategies.Coalescable;
import org.apache.cassandra.utils.CoalescingStrategies.CoalescingStrategy; import org.apache.cassandra.utils.CoalescingStrategies.CoalescingStrategy;
import org.apache.cassandra.utils.CoalescingStrategies.Parker; import org.apache.cassandra.utils.CoalescingStrategies.Parker;
import org.junit.BeforeClass;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -101,6 +103,12 @@ public class CoalescingStrategiesTest
Semaphore queueParked = new Semaphore(0); Semaphore queueParked = new Semaphore(0);
Semaphore queueRelease = new Semaphore(0); Semaphore queueRelease = new Semaphore(0);
@BeforeClass
public static void initDD()
{
DatabaseDescriptor.daemonInitialization();
}
@SuppressWarnings({ "serial" }) @SuppressWarnings({ "serial" })
@Before @Before
public void setUp() throws Exception public void setUp() throws Exception
@ -206,6 +214,38 @@ public class CoalescingStrategiesTest
} }
@Test
public void testFixedCoalescingStrategyEnough() throws Exception
{
int oldValue = DatabaseDescriptor.getOtcCoalescingEnoughCoalescedMessages();
DatabaseDescriptor.setOtcCoalescingEnoughCoalescedMessages(1);
try {
cs = newStrategy("FIXED", 200);
//Test that when a stream of messages continues arriving it keeps sending until all are drained
//It does this because it is already awake and sending messages
add(42);
add(42);
cs.coalesce(input, output, 128);
assertEquals(2, output.size());
assertNull(parker.parks.poll());
clear();
runBlocker(queueParked);
add(42);
add(42);
add(42);
release();
assertEquals(3, output.size());
assertNull(parker.parks.poll());
}
finally {
DatabaseDescriptor.setOtcCoalescingEnoughCoalescedMessages(oldValue);
}
}
@Test @Test
public void testDisabledCoalescingStrateg() throws Exception public void testDisabledCoalescingStrateg() throws Exception
{ {