Remove Netty timed batching and instead do the batch during next

eventLoop invocation after a write has been enqueued. Old behavior can
be enabled with setting native_transport_flush_in_batches_legacy

patch by Michael Burman; reviewed by Benedict for CASSANDRA-13651
This commit is contained in:
Michael Burman 2018-05-08 15:40:54 +03:00 committed by Benedict Elliott Smith
parent ffde38a256
commit 96ef514917
6 changed files with 76 additions and 14 deletions

View File

@ -1,4 +1,5 @@
4.0
* Flush netty client messages immediately by default (CASSANDRA-13651)
* Improve read repair blocking behavior (CASSANDRA-10726)
* Add a virtual table to expose settings (CASSANDRA-14573)
* Fix up chunk cache handling of metrics (CASSANDRA-14628)

View File

@ -1222,3 +1222,7 @@ audit_logging_options:
# If enabled, diagnostic events can be helpful for troubleshooting operational issues. Emitted events contain details
# on internal state and temporal relationships across events, accessible by clients via JMX.
diagnostic_events_enabled: false
# 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

@ -152,6 +152,7 @@ public class Config
public int 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;
/**
* Max size of values in SSTables, in MegaBytes.

View File

@ -1866,6 +1866,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 getCommitLogSyncGroupWindow()
{
return conf.commitlog_sync_group_window_in_ms;
@ -2644,4 +2649,5 @@ public class DatabaseDescriptor
{
conf.corrupted_tombstone_strategy = strategy;
}
}

View File

@ -428,26 +428,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()
{
@ -484,8 +496,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;
}
}
@ -494,11 +506,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
@ -548,7 +597,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

@ -346,7 +346,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;