Backport ImmediateFlusher to cassandra-3.0 and cassandra-3.11

patch by Michael Burman and Sumanth Pasupuleti; reviewed by Benedict for CASSANDRA-14855
This commit is contained in:
Michael Burman 2018-05-08 15:40:54 +03:00 committed by Benedict Elliott Smith
parent 0c97908b2f
commit fff6eec290
6 changed files with 76 additions and 15 deletions

View File

@ -20,6 +20,7 @@
* Fix corrupted static collection deletions in 3.0 <-> 2.{1,2} messages (CASSANDRA-14568)
* Handle failures in parallelAllSSTableOperation (cleanup/upgradesstables/etc) (CASSANDRA-14657)
* Improve TokenMetaData cache populating performance avoid long locking (CASSANDRA-14660)
* Backport: Flush netty client messages immediately (not by default) (CASSANDRA-13651)
* Fix static column order for SELECT * wildcard queries (CASSANDRA-14638)
* sstableloader should use discovered broadcast address to connect intra-cluster (CASSANDRA-14522)
* Fix reading columns with non-UTF names from schema (CASSANDRA-14468)

View File

@ -1003,3 +1003,8 @@ windows_timer_interval: 1
# An interval of 0 disables any wait time, which is the behavior of former Cassandra versions.
#
# otc_backlog_expiration_interval_ms: 200
# Define use of legacy delayed flusher for replies to TCP connections. This will increase latency, but might be beneficial for
# legacy use-cases where only a single connection is used for each Cassandra node. Default is false.
# native_transport_flush_in_batches_legacy: false

View File

@ -149,6 +149,7 @@ public class Config
public Integer native_transport_max_frame_size_in_mb = 256;
public volatile Long native_transport_max_concurrent_connections = -1L;
public volatile Long native_transport_max_concurrent_connections_per_ip = -1L;
public boolean native_transport_flush_in_batches_legacy = false;
@Deprecated
public Integer thrift_max_message_length_in_mb = 16;

View File

@ -1499,6 +1499,11 @@ public class DatabaseDescriptor
conf.native_transport_max_concurrent_connections_per_ip = native_transport_max_concurrent_connections_per_ip;
}
public static boolean useNativeTransportLegacyFlusher()
{
return conf.native_transport_flush_in_batches_legacy;
}
public static double getCommitLogSyncBatchWindow()
{
return conf.commitlog_sync_batch_window_in_ms;
@ -2077,5 +2082,4 @@ public class DatabaseDescriptor
{
return conf.gc_warn_threshold_in_ms;
}
}

View File

@ -420,26 +420,38 @@ public abstract class Message
}
}
private static final class Flusher implements Runnable
private static abstract class Flusher implements Runnable
{
final EventLoop eventLoop;
final ConcurrentLinkedQueue<FlushItem> queued = new ConcurrentLinkedQueue<>();
final AtomicBoolean running = new AtomicBoolean(false);
final AtomicBoolean scheduled = new AtomicBoolean(false);
final HashSet<ChannelHandlerContext> channels = new HashSet<>();
final List<FlushItem> flushed = new ArrayList<>();
int runsSinceFlush = 0;
int runsWithNoWork = 0;
private Flusher(EventLoop eventLoop)
{
this.eventLoop = eventLoop;
}
void start()
{
if (!running.get() && running.compareAndSet(false, true))
if (!scheduled.get() && scheduled.compareAndSet(false, true))
{
this.eventLoop.execute(this);
}
}
public Flusher(EventLoop eventLoop)
{
this.eventLoop = eventLoop;
}
}
private static final class LegacyFlusher extends Flusher
{
int runsSinceFlush = 0;
int runsWithNoWork = 0;
private LegacyFlusher(EventLoop eventLoop)
{
super(eventLoop);
}
public void run()
{
@ -476,8 +488,8 @@ public abstract class Message
// either reschedule or cancel
if (++runsWithNoWork > 5)
{
running.set(false);
if (queued.isEmpty() || !running.compareAndSet(false, true))
scheduled.set(false);
if (queued.isEmpty() || !scheduled.compareAndSet(false, true))
return;
}
}
@ -486,11 +498,48 @@ public abstract class Message
}
}
private static final class ImmediateFlusher extends Flusher
{
private ImmediateFlusher(EventLoop eventLoop)
{
super(eventLoop);
}
public void run()
{
boolean doneWork = false;
FlushItem flush;
scheduled.set(false);
while (null != (flush = queued.poll()))
{
channels.add(flush.ctx);
flush.ctx.write(flush.response, flush.ctx.voidPromise());
flushed.add(flush);
doneWork = true;
}
if (doneWork)
{
for (ChannelHandlerContext channel : channels)
channel.flush();
for (FlushItem item : flushed)
item.sourceFrame.release();
channels.clear();
flushed.clear();
}
}
}
private static final ConcurrentMap<EventLoop, Flusher> flusherLookup = new ConcurrentHashMap<>();
public Dispatcher()
private final boolean useLegacyFlusher;
public Dispatcher(boolean useLegacyFlusher)
{
super(false);
this.useLegacyFlusher = useLegacyFlusher;
}
@Override
@ -538,7 +587,8 @@ public abstract class Message
Flusher flusher = flusherLookup.get(loop);
if (flusher == null)
{
Flusher alt = flusherLookup.putIfAbsent(loop, flusher = new Flusher(loop));
Flusher created = useLegacyFlusher ? new LegacyFlusher(loop) : new ImmediateFlusher(loop);
Flusher alt = flusherLookup.putIfAbsent(loop, flusher = created);
if (alt != null)
flusher = alt;
}

View File

@ -295,7 +295,7 @@ public class Server implements CassandraDaemon.Server
private static final Frame.Compressor frameCompressor = new Frame.Compressor();
private static final Frame.Encoder frameEncoder = new Frame.Encoder();
private static final Message.ExceptionHandler exceptionHandler = new Message.ExceptionHandler();
private static final Message.Dispatcher dispatcher = new Message.Dispatcher();
private static final Message.Dispatcher dispatcher = new Message.Dispatcher(DatabaseDescriptor.useNativeTransportLegacyFlusher());
private static final ConnectionLimitHandler connectionLimitHandler = new ConnectionLimitHandler();
private final Server server;