Merge branch 'cassandra-4.1' into cassandra-5.0

This commit is contained in:
Alex Petrov 2024-05-31 11:24:37 +02:00
commit 617a75843c
141 changed files with 2923 additions and 964 deletions

View File

@ -5686,7 +5686,7 @@ Merged from 1.0:
* avoid recursion in leveled compaction (CASSANDRA-4587)
* increase stack size under Java7 to 180K
* Log(info) schema changes (CASSANDRA-4547)
* Change nodetool setcachecapcity to manipulate global caches (CASSANDRA-4563)
* Change nodetool setcachecapacity to manipulate global caches (CASSANDRA-4563)
* (cql3) fix setting compaction strategy (CASSANDRA-4597)
* fix broken system.schema_* timestamps on system startup (CASSANDRA-4561)
* fix wrong skip of cache saving (CASSANDRA-4533)

View File

@ -47,14 +47,13 @@ import org.apache.cassandra.db.marshal.TupleType;
import org.apache.cassandra.exceptions.RequestExecutionException;
import org.apache.cassandra.schema.SchemaConstants;
import org.apache.cassandra.service.ClientState;
import org.apache.cassandra.transport.Dispatcher;
import org.apache.cassandra.transport.messages.ResultMessage;
import org.apache.cassandra.utils.ByteBufferUtil;
import org.apache.cassandra.utils.MBeanWrapper;
import org.apache.cassandra.utils.Pair;
import static org.apache.cassandra.service.QueryState.forInternalCalls;
import static org.apache.cassandra.utils.Clock.Global.nanoTime;
public class CIDRGroupsMappingManager implements CIDRGroupsMappingManagerMBean
{
@ -89,7 +88,7 @@ public class CIDRGroupsMappingManager implements CIDRGroupsMappingManagerMBean
@VisibleForTesting
ResultMessage.Rows select(SelectStatement statement, QueryOptions options)
{
return statement.execute(forInternalCalls(), options, nanoTime());
return statement.execute(forInternalCalls(), options, Dispatcher.RequestTime.forImmediateExecution());
}
UntypedResultSet process(String query, ConsistencyLevel cl) throws RequestExecutionException

View File

@ -41,12 +41,12 @@ import org.apache.cassandra.exceptions.RequestExecutionException;
import org.apache.cassandra.schema.SchemaConstants;
import org.apache.cassandra.service.ClientState;
import org.apache.cassandra.service.reads.range.RangeCommands;
import org.apache.cassandra.transport.Dispatcher;
import org.apache.cassandra.transport.messages.ResultMessage;
import org.apache.cassandra.utils.ByteBufferUtil;
import org.apache.cassandra.utils.MBeanWrapper;
import static org.apache.cassandra.service.QueryState.forInternalCalls;
import static org.apache.cassandra.utils.Clock.Global.nanoTime;
/**
* Provides functionality to list/update/drop CIDR permissions of a role
@ -76,7 +76,7 @@ public class CIDRPermissionsManager implements CIDRPermissionsManagerMBean, Auth
@VisibleForTesting
ResultMessage.Rows select(SelectStatement statement, QueryOptions options)
{
return statement.execute(forInternalCalls(), options, nanoTime());
return statement.execute(forInternalCalls(), options, Dispatcher.RequestTime.forImmediateExecution());
}
@VisibleForTesting

View File

@ -46,12 +46,11 @@ import org.apache.cassandra.schema.SchemaConstants;
import org.apache.cassandra.cql3.statements.SelectStatement;
import org.apache.cassandra.service.ClientState;
import org.apache.cassandra.service.QueryState;
import org.apache.cassandra.transport.Dispatcher;
import org.apache.cassandra.transport.messages.ResultMessage;
import org.apache.cassandra.utils.ByteBufferUtil;
import org.apache.cassandra.utils.Pair;
import static org.apache.cassandra.utils.Clock.Global.nanoTime;
/**
* CassandraAuthorizer is an IAuthorizer implementation that keeps
* user permissions internally in C* using the system_auth.role_permissions
@ -421,7 +420,7 @@ public class CassandraAuthorizer implements IAuthorizer
ResultMessage.Rows select(SelectStatement statement, QueryOptions options)
{
return statement.execute(QueryState.forInternalCalls(), options, nanoTime());
return statement.execute(QueryState.forInternalCalls(), options, Dispatcher.RequestTime.forImmediateExecution());
}
/**
@ -439,7 +438,7 @@ public class CassandraAuthorizer implements IAuthorizer
QueryProcessor.instance.processBatch(statement,
QueryState.forInternalCalls(),
BatchQueryOptions.withoutPerStatementVariables(options),
nanoTime());
Dispatcher.RequestTime.forImmediateExecution());
}
public static ConsistencyLevel authWriteConsistencyLevel()

View File

@ -39,11 +39,11 @@ import org.apache.cassandra.db.marshal.UTF8Type;
import org.apache.cassandra.exceptions.ConfigurationException;
import org.apache.cassandra.schema.SchemaConstants;
import org.apache.cassandra.service.ClientState;
import org.apache.cassandra.transport.Dispatcher;
import org.apache.cassandra.transport.messages.ResultMessage;
import org.apache.cassandra.utils.ByteBufferUtil;
import static org.apache.cassandra.service.QueryState.forInternalCalls;
import static org.apache.cassandra.utils.Clock.Global.nanoTime;
public class CassandraNetworkAuthorizer implements INetworkAuthorizer
{
@ -61,7 +61,7 @@ public class CassandraNetworkAuthorizer implements INetworkAuthorizer
@VisibleForTesting
ResultMessage.Rows select(SelectStatement statement, QueryOptions options)
{
return statement.execute(forInternalCalls(), options, nanoTime());
return statement.execute(forInternalCalls(), options, Dispatcher.RequestTime.forImmediateExecution());
}
/**

View File

@ -45,6 +45,7 @@ import org.apache.cassandra.exceptions.*;
import org.apache.cassandra.service.ClientState;
import org.apache.cassandra.service.StorageProxy;
import org.apache.cassandra.service.StorageService;
import org.apache.cassandra.transport.Dispatcher;
import org.apache.cassandra.transport.messages.ResultMessage;
import org.apache.cassandra.utils.ByteBufferUtil;
import org.apache.cassandra.utils.NoSpamLogger;
@ -52,7 +53,6 @@ import org.mindrot.jbcrypt.BCrypt;
import static org.apache.cassandra.config.CassandraRelevantProperties.AUTH_BCRYPT_GENSALT_LOG2_ROUNDS;
import static org.apache.cassandra.service.QueryState.forInternalCalls;
import static org.apache.cassandra.utils.Clock.Global.nanoTime;
/**
* Responsible for the creation, maintenance and deletion of roles
@ -678,7 +678,7 @@ public class CassandraRoleManager implements IRoleManager
@VisibleForTesting
ResultMessage.Rows select(SelectStatement statement, QueryOptions options)
{
return statement.execute(forInternalCalls(), options, nanoTime());
return statement.execute(forInternalCalls(), options, Dispatcher.RequestTime.forImmediateExecution());
}
@Override

View File

@ -43,12 +43,12 @@ import org.apache.cassandra.exceptions.AuthenticationException;
import org.apache.cassandra.exceptions.ConfigurationException;
import org.apache.cassandra.service.ClientState;
import org.apache.cassandra.service.QueryState;
import org.apache.cassandra.transport.Dispatcher;
import org.apache.cassandra.transport.messages.ResultMessage;
import org.apache.cassandra.utils.ByteBufferUtil;
import org.mindrot.jbcrypt.BCrypt;
import static org.apache.cassandra.auth.CassandraRoleManager.consistencyForRoleRead;
import static org.apache.cassandra.utils.Clock.Global.nanoTime;
/**
* PasswordAuthenticator is an IAuthenticator implementation
@ -198,7 +198,7 @@ public class PasswordAuthenticator implements IAuthenticator, AuthCache.BulkLoad
@VisibleForTesting
ResultMessage.Rows select(SelectStatement statement, QueryOptions options)
{
return statement.execute(QueryState.forInternalCalls(), options, nanoTime());
return statement.execute(QueryState.forInternalCalls(), options, Dispatcher.RequestTime.forImmediateExecution());
}
public Set<DataResource> protectedResources()

View File

@ -36,6 +36,7 @@ import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.Iterables;
import com.google.common.util.concurrent.RateLimiter;
import org.apache.cassandra.concurrent.ScheduledExecutorPlus;
import org.apache.cassandra.transport.Dispatcher;
import org.apache.cassandra.utils.TimeUUID;
import org.apache.cassandra.utils.concurrent.Future;
import org.slf4j.Logger;
@ -82,7 +83,6 @@ import static org.apache.cassandra.cql3.QueryProcessor.executeInternal;
import static org.apache.cassandra.cql3.QueryProcessor.executeInternalWithPaging;
import static org.apache.cassandra.net.Verb.MUTATION_REQ;
import static org.apache.cassandra.utils.Clock.Global.currentTimeMillis;
import static org.apache.cassandra.utils.Clock.Global.nanoTime;
public class BatchlogManager implements BatchlogManagerMBean
{
@ -503,7 +503,7 @@ public class BatchlogManager implements BatchlogManagerMBean
ReplicaPlan.ForWrite replicaPlan = new ReplicaPlan.ForWrite(keyspace, liveAndDown.replicationStrategy(),
ConsistencyLevel.ONE, liveRemoteOnly.pending(), liveRemoteOnly.all(), liveRemoteOnly.all(), liveRemoteOnly.all());
ReplayWriteResponseHandler<Mutation> handler = new ReplayWriteResponseHandler<>(replicaPlan, mutation, nanoTime());
ReplayWriteResponseHandler<Mutation> handler = new ReplayWriteResponseHandler<>(replicaPlan, mutation, Dispatcher.RequestTime.forImmediateExecution());
Message<Mutation> message = Message.outWithFlag(MUTATION_REQ, mutation, MessageFlag.CALL_BACK_ON_FAILURE);
for (Replica replica : liveRemoteOnly.all())
MessagingService.instance().sendWriteWithCallback(message, replica, handler);
@ -527,9 +527,9 @@ public class BatchlogManager implements BatchlogManagerMBean
private final Set<InetAddressAndPort> undelivered = Collections.newSetFromMap(new ConcurrentHashMap<>());
// TODO: should we be hinting here, since presumably batch log will retry? Maintaining historical behaviour for the moment.
ReplayWriteResponseHandler(ReplicaPlan.ForWrite replicaPlan, Supplier<Mutation> hintOnFailure, long queryStartNanoTime)
ReplayWriteResponseHandler(ReplicaPlan.ForWrite replicaPlan, Supplier<Mutation> hintOnFailure, Dispatcher.RequestTime requestTime)
{
super(replicaPlan, null, WriteType.UNLOGGED_BATCH, hintOnFailure, queryStartNanoTime);
super(replicaPlan, null, WriteType.UNLOGGED_BATCH, hintOnFailure, requestTime);
Iterables.addAll(undelivered, replicaPlan.contacts().endpoints());
}

View File

@ -17,6 +17,9 @@
*/
package org.apache.cassandra.concurrent;
import java.util.concurrent.Callable;
import org.apache.cassandra.utils.MonotonicClock;
import org.apache.cassandra.utils.Shared;
import static org.apache.cassandra.utils.Shared.Recursive.INTERFACES;
@ -29,6 +32,11 @@ import static org.apache.cassandra.utils.Shared.Scope.SIMULATION;
@Shared(scope = SIMULATION, inner = INTERFACES)
public interface DebuggableTask
{
public default long elapsedSinceCreation()
{
return MonotonicClock.Global.approxTime.now() - creationTimeNanos();
}
public long creationTimeNanos();
public long startTimeNanos();
@ -36,6 +44,7 @@ public interface DebuggableTask
public String description();
interface RunnableDebuggableTask extends Runnable, DebuggableTask {}
interface CallableDebuggableTask<T> extends Callable<T>, DebuggableTask {}
/**
* Wraps a {@link DebuggableTask} to include the name of the thread running it.

View File

@ -89,6 +89,34 @@ public class FutureTask<V> extends AsyncFuture<V> implements RunnableFuture<V>
public static <T> Callable<T> callable(Runnable run)
{
if (run instanceof DebuggableTask.RunnableDebuggableTask)
{
return new DebuggableTask.CallableDebuggableTask<T>()
{
final RunnableDebuggableTask task = (RunnableDebuggableTask) run;
public T call()
{
run.run();
return null;
}
public long creationTimeNanos()
{
return task.creationTimeNanos();
}
public long startTimeNanos()
{
return task.startTimeNanos();
}
public String description()
{
return task.description();
}
};
}
return new Callable<T>()
{
public T call()

View File

@ -79,4 +79,9 @@ public interface ResizableThreadPool
{
return -1;
}
default long oldestTaskQueueTime()
{
return 0;
}
}

View File

@ -84,6 +84,21 @@ public class SEPExecutor implements LocalAwareExecutorPlus, SEPExecutorMBean
completedTasks.incrementAndGet();
}
@Override
public long oldestTaskQueueTime()
{
Runnable task = tasks.peek();
if (!(task instanceof FutureTask))
return 0L;
FutureTask<?> futureTask = (FutureTask<?>) task;
DebuggableTask debuggableTask = futureTask.debuggableTask();
if (debuggableTask == null)
return 0L;
return debuggableTask.elapsedSinceCreation();
}
@Override
public int getMaxTasksQueued()
{

View File

@ -1283,4 +1283,24 @@ public class Config
public double severity_during_decommission = 0;
public StorageCompatibilityMode storage_compatibility_mode;
/**
* Which timestamp should be used to represent a base for replica-side timeouts.
* Client-side timeout is always based on the QUEUE, and is controlled by native_transport_timeout.
*/
public enum CQLStartTime
{
REQUEST, // uses a timestamp that represent the start of processing of the request
QUEUE // uses a timestamp that represents when the request was enqueued
}
public volatile CQLStartTime cql_start_time = CQLStartTime.REQUEST;
public boolean native_transport_throw_on_overload = false;
public double native_transport_queue_max_item_age_threshold = Double.MAX_VALUE;
public DurationSpec.LongMillisecondsBound native_transport_min_backoff_on_queue_overload = new DurationSpec.LongMillisecondsBound("10ms");
public DurationSpec.LongMillisecondsBound native_transport_max_backoff_on_queue_overload = new DurationSpec.LongMillisecondsBound("200ms");
// 3.x Cassandra Driver has its "read" timeout set to 12 seconds, default matches this.
public DurationSpec.LongMillisecondsBound native_transport_timeout = new DurationSpec.LongMillisecondsBound("12s");
public boolean enforce_native_deadline_for_hints = false;
}

View File

@ -990,6 +990,15 @@ public class DatabaseDescriptor
throw new ConfigurationException(String.format("Invalid configuration. Heap dump is enabled but cannot create heap dump output path: %s.", conf.heap_dump_path != null ? conf.heap_dump_path : "null"));
conf.sai_options.validate();
if (conf.native_transport_min_backoff_on_queue_overload.toMilliseconds() <= 0)
throw new ConfigurationException("native_transport_min_backoff_on_queue_overload should be strictly positive");
if (conf.native_transport_min_backoff_on_queue_overload.toMilliseconds() >= conf.native_transport_max_backoff_on_queue_overload.toMilliseconds())
throw new ConfigurationException(String.format("native_transport_min_backoff_on_queue_overload should be strictly less than native_transport_max_backoff_on_queue_overload, but %s >= %s",
conf.native_transport_min_backoff_on_queue_overload,
conf.native_transport_max_backoff_on_queue_overload));
}
@VisibleForTesting
@ -2238,6 +2247,100 @@ public class DatabaseDescriptor
getTruncateRpcTimeout(unit));
}
public static Config.CQLStartTime getCQLStartTime()
{
return conf.cql_start_time;
}
public static void setCQLStartTime(Config.CQLStartTime value)
{
conf.cql_start_time = value;
}
/**
* How much time the item is allowed to spend in (currently only Native) queue, compared to {@link #nativeTransportIdleTimeout()},
* before backpressure starts being applied.
*
* For example, setting this value to 0.5 means and having the largest of read/range/write/counter timeouts to 10 seconds
* means that if any item spends more than 5 seconds in the queue, backpressure will be applied to the socket associated
* with this queue.
*
* Set to 0 or any negative value to fully disable.
*/
public static double getNativeTransportQueueMaxItemAgeThreshold()
{
return conf.native_transport_queue_max_item_age_threshold;
}
public static void setNativeTransportMaxQueueItemAgeThreshold(double threshold)
{
conf.native_transport_queue_max_item_age_threshold = threshold;
}
public static long getNativeTransportMinBackoffOnQueueOverload(TimeUnit timeUnit)
{
return conf.native_transport_min_backoff_on_queue_overload.to(timeUnit);
}
public static long getNativeTransportMaxBackoffOnQueueOverload(TimeUnit timeUnit)
{
return conf.native_transport_max_backoff_on_queue_overload.to(timeUnit);
}
public static void setNativeTransportBackoffOnQueueOverload(long minBackoffMillis,
long maxBackoffMillis,
TimeUnit timeUnit)
{
if (minBackoffMillis <= 0)
throw new IllegalArgumentException("native_transport_min_backoff_on_queue_overload should be positive");
if (minBackoffMillis >= maxBackoffMillis)
throw new IllegalArgumentException(String.format("native_transport_max_backoff_on_queue_overload should be greater than native_transport_min_backoff_on_queue_overload, but %s >= %s", minBackoffMillis, maxBackoffMillis));
conf.native_transport_min_backoff_on_queue_overload = new DurationSpec.LongMillisecondsBound(minBackoffMillis, timeUnit);
conf.native_transport_max_backoff_on_queue_overload = new DurationSpec.LongMillisecondsBound(maxBackoffMillis, timeUnit);
}
private static long native_transport_timeout_nanos_cached = -1;
public static long getNativeTransportTimeout(TimeUnit timeUnit)
{
if (timeUnit == TimeUnit.NANOSECONDS)
{
if (native_transport_timeout_nanos_cached == -1)
native_transport_timeout_nanos_cached = conf.native_transport_timeout.to(TimeUnit.NANOSECONDS);
return native_transport_timeout_nanos_cached;
}
return conf.native_transport_timeout.to(timeUnit);
}
public static void setNativeTransportTimeout(long dealine, TimeUnit timeUnit)
{
conf.native_transport_timeout = new DurationSpec.LongMillisecondsBound(dealine, timeUnit);
}
public static boolean getEnforceNativeDeadlineForHints()
{
return conf.enforce_native_deadline_for_hints;
}
public static void setEnforceNativeDeadlineForHints(boolean value)
{
conf.enforce_native_deadline_for_hints = value;
}
public static boolean getNativeTransportThrowOnOverload()
{
return conf.native_transport_throw_on_overload;
}
public static void setNativeTransportThrowOnOverload(boolean throwOnOverload)
{
conf.native_transport_throw_on_overload = throwOnOverload;
}
public static long getPingTimeout(TimeUnit unit)
{
return unit.convert(getBlockForPeersTimeoutInSeconds(), TimeUnit.SECONDS);

View File

@ -24,6 +24,7 @@ import org.apache.cassandra.audit.AuditLogContext;
import org.apache.cassandra.cql3.functions.Function;
import org.apache.cassandra.service.ClientState;
import org.apache.cassandra.service.QueryState;
import org.apache.cassandra.transport.Dispatcher;
import org.apache.cassandra.transport.messages.ResultMessage;
public interface CQLStatement
@ -75,9 +76,9 @@ public interface CQLStatement
*
* @param state the current query state
* @param options options for this query (consistency, variables, pageSize, ...)
* @param queryStartNanoTime the timestamp returned by System.nanoTime() when this statement was received
* @param requestTime request enqueue / and start times;
*/
public ResultMessage execute(QueryState state, QueryOptions options, long queryStartNanoTime);
public ResultMessage execute(QueryState state, QueryOptions options, Dispatcher.RequestTime requestTime);
/**
* Variant of execute used for internal query against the system tables, and thus only query the local node.

View File

@ -24,6 +24,7 @@ import org.apache.cassandra.config.CassandraRelevantProperties;
import org.apache.cassandra.cql3.statements.BatchStatement;
import org.apache.cassandra.service.ClientState;
import org.apache.cassandra.service.QueryState;
import org.apache.cassandra.transport.Dispatcher;
import org.apache.cassandra.transport.messages.ResultMessage;
import org.apache.cassandra.utils.MD5Digest;
@ -41,13 +42,14 @@ public class CustomPayloadMirroringQueryHandler implements QueryHandler
return queryProcessor.parse(query, state, options);
}
@Override
public ResultMessage process(CQLStatement statement,
QueryState state,
QueryOptions options,
Map<String, ByteBuffer> customPayload,
long queryStartNanoTime)
Dispatcher.RequestTime requestTime)
{
ResultMessage result = queryProcessor.process(statement, state, options, customPayload, queryStartNanoTime);
ResultMessage result = queryProcessor.process(statement, state, options, customPayload, requestTime);
result.setCustomPayload(customPayload);
return result;
}
@ -64,24 +66,26 @@ public class CustomPayloadMirroringQueryHandler implements QueryHandler
return queryProcessor.getPrepared(id);
}
@Override
public ResultMessage processPrepared(CQLStatement statement,
QueryState state,
QueryOptions options,
Map<String, ByteBuffer> customPayload,
long queryStartNanoTime)
Dispatcher.RequestTime requestTime)
{
ResultMessage result = queryProcessor.processPrepared(statement, state, options, customPayload, queryStartNanoTime);
ResultMessage result = queryProcessor.processPrepared(statement, state, options, customPayload, requestTime);
result.setCustomPayload(customPayload);
return result;
}
@Override
public ResultMessage processBatch(BatchStatement statement,
QueryState state,
BatchQueryOptions options,
Map<String, ByteBuffer> customPayload,
long queryStartNanoTime)
Dispatcher.RequestTime requestTime)
{
ResultMessage result = queryProcessor.processBatch(statement, state, options, customPayload, queryStartNanoTime);
ResultMessage result = queryProcessor.processBatch(statement, state, options, customPayload, requestTime);
result.setCustomPayload(customPayload);
return result;
}

View File

@ -25,6 +25,7 @@ import org.apache.cassandra.exceptions.RequestExecutionException;
import org.apache.cassandra.exceptions.RequestValidationException;
import org.apache.cassandra.service.ClientState;
import org.apache.cassandra.service.QueryState;
import org.apache.cassandra.transport.Dispatcher;
import org.apache.cassandra.transport.messages.ResultMessage;
import org.apache.cassandra.utils.MD5Digest;
@ -36,7 +37,7 @@ public interface QueryHandler
QueryState state,
QueryOptions options,
Map<String, ByteBuffer> customPayload,
long queryStartNanoTime) throws RequestExecutionException, RequestValidationException;
Dispatcher.RequestTime requestTime) throws RequestExecutionException, RequestValidationException;
ResultMessage.Prepared prepare(String query,
ClientState clientState,
@ -48,13 +49,13 @@ public interface QueryHandler
QueryState state,
QueryOptions options,
Map<String, ByteBuffer> customPayload,
long queryStartNanoTime) throws RequestExecutionException, RequestValidationException;
Dispatcher.RequestTime requestTime) throws RequestExecutionException, RequestValidationException;
ResultMessage processBatch(BatchStatement statement,
QueryState state,
BatchQueryOptions options,
Map<String, ByteBuffer> customPayload,
long queryStartNanoTime) throws RequestExecutionException, RequestValidationException;
Dispatcher.RequestTime requestTime) throws RequestExecutionException, RequestValidationException;
public static class Prepared
{

View File

@ -67,6 +67,7 @@ import org.apache.cassandra.metrics.CQLMetrics;
import org.apache.cassandra.service.*;
import org.apache.cassandra.service.pager.QueryPager;
import org.apache.cassandra.tracing.Tracing;
import org.apache.cassandra.transport.Dispatcher;
import org.apache.cassandra.transport.ProtocolVersion;
import org.apache.cassandra.transport.messages.ResultMessage;
import org.apache.cassandra.utils.*;
@ -243,7 +244,7 @@ public class QueryProcessor implements QueryHandler
}
}
public ResultMessage processStatement(CQLStatement statement, QueryState queryState, QueryOptions options, long queryStartNanoTime)
public ResultMessage processStatement(CQLStatement statement, QueryState queryState, QueryOptions options, Dispatcher.RequestTime requestTime)
throws RequestExecutionException, RequestValidationException
{
logger.trace("Process {} @CL.{}", statement, options.getConsistency());
@ -253,7 +254,7 @@ public class QueryProcessor implements QueryHandler
ResultMessage result = options.getConsistency() == ConsistencyLevel.NODE_LOCAL
? processNodeLocalStatement(statement, queryState, options)
: statement.execute(queryState, options, queryStartNanoTime);
: statement.execute(queryState, options, requestTime);
return result == null ? new ResultMessage.Void() : result;
}
@ -314,12 +315,12 @@ public class QueryProcessor implements QueryHandler
}
}
public static ResultMessage process(String queryString, ConsistencyLevel cl, QueryState queryState, long queryStartNanoTime)
public static ResultMessage process(String queryString, ConsistencyLevel cl, QueryState queryState, Dispatcher.RequestTime requestTime)
throws RequestExecutionException, RequestValidationException
{
QueryOptions options = QueryOptions.forInternalCalls(cl, Collections.<ByteBuffer>emptyList());
CQLStatement statement = instance.parse(queryString, queryState, options);
return instance.process(statement, queryState, options, queryStartNanoTime);
return instance.process(statement, queryState, options, requestTime);
}
public CQLStatement parse(String queryString, QueryState queryState, QueryOptions options)
@ -331,12 +332,12 @@ public class QueryProcessor implements QueryHandler
QueryState state,
QueryOptions options,
Map<String, ByteBuffer> customPayload,
long queryStartNanoTime) throws RequestExecutionException, RequestValidationException
Dispatcher.RequestTime requestTime) throws RequestExecutionException, RequestValidationException
{
return process(statement, state, options, queryStartNanoTime);
return process(statement, state, options, requestTime);
}
public ResultMessage process(CQLStatement prepared, QueryState queryState, QueryOptions options, long queryStartNanoTime)
public ResultMessage process(CQLStatement prepared, QueryState queryState, QueryOptions options, Dispatcher.RequestTime requestTime)
throws RequestExecutionException, RequestValidationException
{
options.prepare(prepared.getBindVariables());
@ -346,7 +347,7 @@ public class QueryProcessor implements QueryHandler
if (!queryState.getClientState().isInternal)
metrics.regularStatementsExecuted.inc();
return processStatement(prepared, queryState, options, queryStartNanoTime);
return processStatement(prepared, queryState, options, requestTime);
}
public static CQLStatement parseStatement(String queryStr, ClientState clientState) throws RequestValidationException
@ -364,7 +365,7 @@ public class QueryProcessor implements QueryHandler
QueryState queryState = QueryState.forInternalCalls();
QueryOptions options = QueryOptions.forInternalCalls(cl, values);
CQLStatement statement = instance.parse(query, queryState, options);
ResultMessage result = instance.process(statement, queryState, options, nanoTime());
ResultMessage result = instance.process(statement, queryState, options, Dispatcher.RequestTime.forImmediateExecution());
if (result instanceof ResultMessage.Rows)
return UntypedResultSet.create(((ResultMessage.Rows)result).result);
else
@ -526,7 +527,7 @@ public class QueryProcessor implements QueryHandler
try
{
Prepared prepared = prepareInternal(query);
ResultMessage result = prepared.statement.execute(state, makeInternalOptionsWithNowInSec(prepared.statement, state.getNowInSeconds(), values, cl), nanoTime());
ResultMessage result = prepared.statement.execute(state, makeInternalOptionsWithNowInSec(prepared.statement, state.getNowInSeconds(), values, cl), Dispatcher.RequestTime.forImmediateExecution());
if (result instanceof ResultMessage.Rows)
return UntypedResultSet.create(((ResultMessage.Rows)result).result);
else
@ -586,12 +587,12 @@ public class QueryProcessor implements QueryHandler
* Note that this only make sense for Selects so this only accept SELECT statements and is only useful in rare
* cases.
*/
public static UntypedResultSet executeInternalWithNow(long nowInSec, long queryStartNanoTime, String query, Object... values)
public static UntypedResultSet executeInternalWithNow(long nowInSec, Dispatcher.RequestTime requestTime, String query, Object... values)
{
Prepared prepared = prepareInternal(query);
assert prepared.statement instanceof SelectStatement;
SelectStatement select = (SelectStatement)prepared.statement;
ResultMessage result = select.executeInternal(internalQueryState(), makeInternalOptionsWithNowInSec(prepared.statement, nowInSec, values), nowInSec, queryStartNanoTime);
ResultMessage result = select.executeInternal(internalQueryState(), makeInternalOptionsWithNowInSec(prepared.statement, nowInSec, values), nowInSec, requestTime);
assert result instanceof ResultMessage.Rows;
return UntypedResultSet.create(((ResultMessage.Rows)result).result);
}
@ -791,17 +792,18 @@ public class QueryProcessor implements QueryHandler
return new ResultMessage.Prepared(statementId, resultMetadata.getResultMetadataId(), preparedMetadata, resultMetadata);
}
@Override
public ResultMessage processPrepared(CQLStatement statement,
QueryState state,
QueryOptions options,
Map<String, ByteBuffer> customPayload,
long queryStartNanoTime)
Dispatcher.RequestTime requestTime)
throws RequestExecutionException, RequestValidationException
{
return processPrepared(statement, state, options, queryStartNanoTime);
return processPrepared(statement, state, options, requestTime);
}
public ResultMessage processPrepared(CQLStatement statement, QueryState queryState, QueryOptions options, long queryStartNanoTime)
public ResultMessage processPrepared(CQLStatement statement, QueryState queryState, QueryOptions options, Dispatcher.RequestTime requestTime)
throws RequestExecutionException, RequestValidationException
{
List<ByteBuffer> variables = options.getValues();
@ -820,27 +822,27 @@ public class QueryProcessor implements QueryHandler
}
metrics.preparedStatementsExecuted.inc();
return processStatement(statement, queryState, options, queryStartNanoTime);
return processStatement(statement, queryState, options, requestTime);
}
public ResultMessage processBatch(BatchStatement statement,
QueryState state,
BatchQueryOptions options,
Map<String, ByteBuffer> customPayload,
long queryStartNanoTime)
Dispatcher.RequestTime requestTime)
throws RequestExecutionException, RequestValidationException
{
return processBatch(statement, state, options, queryStartNanoTime);
return processBatch(statement, state, options, requestTime);
}
public ResultMessage processBatch(BatchStatement batch, QueryState queryState, BatchQueryOptions options, long queryStartNanoTime)
public ResultMessage processBatch(BatchStatement batch, QueryState queryState, BatchQueryOptions options, Dispatcher.RequestTime requestTime)
throws RequestExecutionException, RequestValidationException
{
ClientState clientState = queryState.getClientState().cloneWithKeyspaceIfSet(options.getKeyspace());
batch.authorize(clientState);
batch.validate();
batch.validate(clientState);
return batch.execute(queryState, options, queryStartNanoTime);
return batch.execute(queryState, options, requestTime);
}
public static CQLStatement getStatement(String queryStr, ClientState clientState)

View File

@ -48,12 +48,11 @@ import org.apache.cassandra.schema.ColumnMetadata;
import org.apache.cassandra.schema.TableMetadata;
import org.apache.cassandra.service.ClientState;
import org.apache.cassandra.service.pager.QueryPager;
import org.apache.cassandra.transport.Dispatcher;
import org.apache.cassandra.utils.AbstractIterator;
import org.apache.cassandra.utils.FBUtilities;
import org.apache.cassandra.utils.TimeUUID;
import static org.apache.cassandra.utils.Clock.Global.nanoTime;
/** a utility for doing internal cql-based queries */
public abstract class UntypedResultSet implements Iterable<UntypedResultSet.Row>
{
@ -291,7 +290,7 @@ public abstract class UntypedResultSet implements Iterable<UntypedResultSet.Row>
if (pager.isExhausted())
return endOfData();
try (PartitionIterator iter = pager.fetchPage(pageSize, cl, clientState, nanoTime()))
try (PartitionIterator iter = pager.fetchPage(pageSize, cl, clientState, Dispatcher.RequestTime.forImmediateExecution()))
{
currentPage = select.process(iter, nowInSec, true, clientState).rows.iterator();
}

View File

@ -26,6 +26,7 @@ import org.apache.cassandra.exceptions.RequestValidationException;
import org.apache.cassandra.exceptions.UnauthorizedException;
import org.apache.cassandra.service.ClientState;
import org.apache.cassandra.service.QueryState;
import org.apache.cassandra.transport.Dispatcher;
import org.apache.cassandra.transport.messages.ResultMessage;
public abstract class AuthenticationStatement extends CQLStatement.Raw implements CQLStatement
@ -35,7 +36,8 @@ public abstract class AuthenticationStatement extends CQLStatement.Raw implement
return this;
}
public ResultMessage execute(QueryState state, QueryOptions options, long queryStartNanoTime)
@Override
public ResultMessage execute(QueryState state, QueryOptions options, Dispatcher.RequestTime requestTime)
throws RequestExecutionException, RequestValidationException
{
return execute(state.getClientState());
@ -43,6 +45,7 @@ public abstract class AuthenticationStatement extends CQLStatement.Raw implement
public abstract ResultMessage execute(ClientState state) throws RequestExecutionException, RequestValidationException;
@Override
public ResultMessage executeLocally(QueryState state, QueryOptions options)
{
// executeLocally is for local query only, thus altering users doesn't make sense and is not supported

View File

@ -26,6 +26,7 @@ import org.apache.cassandra.exceptions.RequestExecutionException;
import org.apache.cassandra.exceptions.RequestValidationException;
import org.apache.cassandra.service.ClientState;
import org.apache.cassandra.service.QueryState;
import org.apache.cassandra.transport.Dispatcher;
import org.apache.cassandra.transport.messages.ResultMessage;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
@ -37,7 +38,8 @@ public abstract class AuthorizationStatement extends CQLStatement.Raw implements
return this;
}
public ResultMessage execute(QueryState state, QueryOptions options, long queryStartNanoTime)
@Override
public ResultMessage execute(QueryState state, QueryOptions options, Dispatcher.RequestTime requestTime)
throws RequestValidationException, RequestExecutionException
{
return execute(state.getClientState());

View File

@ -45,6 +45,7 @@ import org.apache.cassandra.metrics.BatchMetrics;
import org.apache.cassandra.metrics.ClientRequestSizeMetrics;
import org.apache.cassandra.service.*;
import org.apache.cassandra.tracing.Tracing;
import org.apache.cassandra.transport.Dispatcher;
import org.apache.cassandra.transport.messages.ResultMessage;
import org.apache.cassandra.utils.FBUtilities;
import org.apache.cassandra.utils.NoSpamLogger;
@ -53,7 +54,6 @@ import org.apache.cassandra.utils.Pair;
import static java.util.function.Predicate.isEqual;
import static org.apache.cassandra.cql3.statements.RequestValidations.checkFalse;
import static org.apache.cassandra.utils.Clock.Global.nanoTime;
/**
* A <code>BATCH</code> statement parsed from a CQL query.
@ -272,7 +272,7 @@ public class BatchStatement implements CQLStatement
boolean local,
long batchTimestamp,
long nowInSeconds,
long queryStartNanoTime)
Dispatcher.RequestTime requestTime)
{
if (statements.isEmpty())
return Collections.emptyList();
@ -309,7 +309,7 @@ public class BatchStatement implements CQLStatement
}
QueryOptions statementOptions = options.forStatement(i);
long timestamp = attrs.getTimestamp(batchTimestamp, statementOptions);
statement.addUpdates(collector, partitionKeys.get(i), state, statementOptions, local, timestamp, nowInSeconds, queryStartNanoTime);
statement.addUpdates(collector, partitionKeys.get(i), state, statementOptions, local, timestamp, nowInSeconds, requestTime);
}
if (tablesWithZeroGcGs != null)
@ -397,12 +397,12 @@ public class BatchStatement implements CQLStatement
}
public ResultMessage execute(QueryState queryState, QueryOptions options, long queryStartNanoTime)
public ResultMessage execute(QueryState queryState, QueryOptions options, Dispatcher.RequestTime requestTime)
{
return execute(queryState, BatchQueryOptions.withoutPerStatementVariables(options), queryStartNanoTime);
return execute(queryState, BatchQueryOptions.withoutPerStatementVariables(options), requestTime);
}
public ResultMessage execute(QueryState queryState, BatchQueryOptions options, long queryStartNanoTime)
public ResultMessage execute(QueryState queryState, BatchQueryOptions options, Dispatcher.RequestTime requestTime)
{
long timestamp = options.getTimestamp(queryState);
long nowInSeconds = options.getNowInSeconds(queryState);
@ -420,18 +420,18 @@ public class BatchStatement implements CQLStatement
statements.get(i).validateDiskUsage(options.forStatement(i), clientState);
if (hasConditions)
return executeWithConditions(options, queryState, queryStartNanoTime);
return executeWithConditions(options, queryState, requestTime);
if (updatesVirtualTables)
executeInternalWithoutCondition(queryState, options, queryStartNanoTime);
executeInternalWithoutCondition(queryState, options, requestTime);
else
executeWithoutConditions(getMutations(clientState, options, false, timestamp, nowInSeconds, queryStartNanoTime),
options.getConsistency(), queryStartNanoTime);
executeWithoutConditions(getMutations(clientState, options, false, timestamp, nowInSeconds, requestTime),
options.getConsistency(), requestTime);
return new ResultMessage.Void();
}
private void executeWithoutConditions(List<? extends IMutation> mutations, ConsistencyLevel cl, long queryStartNanoTime) throws RequestExecutionException, RequestValidationException
private void executeWithoutConditions(List<? extends IMutation> mutations, ConsistencyLevel cl, Dispatcher.RequestTime requestTime) throws RequestExecutionException, RequestValidationException
{
if (mutations.isEmpty())
return;
@ -442,7 +442,7 @@ public class BatchStatement implements CQLStatement
updatePartitionsPerBatchMetrics(mutations.size());
boolean mutateAtomic = (isLogged() && mutations.size() > 1);
StorageProxy.mutateWithTriggers(mutations, cl, mutateAtomic, queryStartNanoTime);
StorageProxy.mutateWithTriggers(mutations, cl, mutateAtomic, requestTime);
ClientRequestSizeMetrics.recordRowAndColumnCountMetrics(mutations);
}
@ -457,7 +457,7 @@ public class BatchStatement implements CQLStatement
}
}
private ResultMessage executeWithConditions(BatchQueryOptions options, QueryState state, long queryStartNanoTime)
private ResultMessage executeWithConditions(BatchQueryOptions options, QueryState state, Dispatcher.RequestTime requestTime)
{
Pair<CQL3CasRequest, Set<ColumnMetadata>> p = makeCasRequest(options, state);
CQL3CasRequest casRequest = p.left;
@ -474,7 +474,7 @@ public class BatchStatement implements CQLStatement
options.getConsistency(),
state.getClientState(),
options.getNowInSeconds(state),
queryStartNanoTime))
requestTime))
{
return new ResultMessage.Rows(ModificationStatement.buildCasResultSet(ksName,
tableName,
@ -563,16 +563,16 @@ public class BatchStatement implements CQLStatement
if (hasConditions)
return executeInternalWithConditions(batchOptions, queryState);
executeInternalWithoutCondition(queryState, batchOptions, nanoTime());
executeInternalWithoutCondition(queryState, batchOptions, Dispatcher.RequestTime.forImmediateExecution());
return new ResultMessage.Void();
}
private ResultMessage executeInternalWithoutCondition(QueryState queryState, BatchQueryOptions batchOptions, long queryStartNanoTime)
private ResultMessage executeInternalWithoutCondition(QueryState queryState, BatchQueryOptions batchOptions, Dispatcher.RequestTime requestTime)
{
long timestamp = batchOptions.getTimestamp(queryState);
long nowInSeconds = batchOptions.getNowInSeconds(queryState);
for (IMutation mutation : getMutations(queryState.getClientState(), batchOptions, true, timestamp, nowInSeconds, queryStartNanoTime))
for (IMutation mutation : getMutations(queryState.getClientState(), batchOptions, true, timestamp, nowInSeconds, requestTime))
mutation.apply();
return null;
}

View File

@ -46,6 +46,7 @@ import org.apache.cassandra.service.ClientState;
import org.apache.cassandra.service.QueryState;
import org.apache.cassandra.service.StorageService;
import org.apache.cassandra.service.pager.PagingState;
import org.apache.cassandra.transport.Dispatcher;
import org.apache.cassandra.transport.messages.ResultMessage;
import org.apache.cassandra.utils.ByteBufferUtil;
import org.apache.cassandra.utils.FBUtilities;
@ -124,7 +125,7 @@ public abstract class DescribeStatement<T> extends CQLStatement.Raw implements C
}
@Override
public final ResultMessage execute(QueryState state, QueryOptions options, long queryStartNanoTime) throws RequestValidationException, RequestExecutionException
public final ResultMessage execute(QueryState state, QueryOptions options, Dispatcher.RequestTime requestTime) throws RequestValidationException, RequestExecutionException
{
return executeLocally(state, options);
}

View File

@ -59,6 +59,7 @@ import org.apache.cassandra.service.disk.usage.DiskUsageBroadcaster;
import org.apache.cassandra.service.paxos.Ballot;
import org.apache.cassandra.service.paxos.BallotGenerator;
import org.apache.cassandra.service.paxos.Commit.Proposal;
import org.apache.cassandra.transport.Dispatcher;
import org.apache.cassandra.transport.messages.ResultMessage;
import org.apache.cassandra.triggers.TriggerExecutor;
import org.apache.cassandra.utils.FBUtilities;
@ -68,7 +69,6 @@ import org.apache.cassandra.utils.Pair;
import static org.apache.cassandra.cql3.statements.RequestValidations.checkFalse;
import static org.apache.cassandra.cql3.statements.RequestValidations.checkNull;
import static org.apache.cassandra.service.paxos.Ballot.Flag.NONE;
import static org.apache.cassandra.utils.Clock.Global.nanoTime;
/*
* Abstract parent class of individual modifications, i.e. INSERT, UPDATE and DELETE.
@ -422,7 +422,7 @@ public abstract class ModificationStatement implements CQLStatement.SingleKeyspa
boolean local,
ConsistencyLevel cl,
long nowInSeconds,
long queryStartNanoTime)
Dispatcher.RequestTime requestTime)
{
if (!requiresRead())
return null;
@ -457,7 +457,7 @@ public abstract class ModificationStatement implements CQLStatement.SingleKeyspa
}
}
try (PartitionIterator iter = group.execute(cl, null, queryStartNanoTime))
try (PartitionIterator iter = group.execute(cl, null, requestTime))
{
return asMaterializedMap(iter);
}
@ -488,7 +488,7 @@ public abstract class ModificationStatement implements CQLStatement.SingleKeyspa
&& getRestrictions().isColumnRange();
}
public ResultMessage execute(QueryState queryState, QueryOptions options, long queryStartNanoTime)
public ResultMessage execute(QueryState queryState, QueryOptions options, Dispatcher.RequestTime requestTime)
throws RequestExecutionException, RequestValidationException
{
if (options.getConsistency() == null)
@ -498,15 +498,15 @@ public abstract class ModificationStatement implements CQLStatement.SingleKeyspa
queryState.getClientState());
return hasConditions()
? executeWithCondition(queryState, options, queryStartNanoTime)
: executeWithoutCondition(queryState, options, queryStartNanoTime);
? executeWithCondition(queryState, options, requestTime)
: executeWithoutCondition(queryState, options, requestTime);
}
private ResultMessage executeWithoutCondition(QueryState queryState, QueryOptions options, long queryStartNanoTime)
private ResultMessage executeWithoutCondition(QueryState queryState, QueryOptions options, Dispatcher.RequestTime requestTime)
throws RequestExecutionException, RequestValidationException
{
if (isVirtual())
return executeInternalWithoutCondition(queryState, options, queryStartNanoTime);
return executeInternalWithoutCondition(queryState, options, requestTime);
ConsistencyLevel cl = options.getConsistency();
if (isCounter())
@ -523,10 +523,10 @@ public abstract class ModificationStatement implements CQLStatement.SingleKeyspa
false,
options.getTimestamp(queryState),
options.getNowInSeconds(queryState),
queryStartNanoTime);
requestTime);
if (!mutations.isEmpty())
{
StorageProxy.mutateWithTriggers(mutations, cl, false, queryStartNanoTime);
StorageProxy.mutateWithTriggers(mutations, cl, false, requestTime);
if (!SchemaConstants.isSystemKeyspace(metadata.keyspace))
ClientRequestSizeMetrics.recordRowAndColumnCountMetrics(mutations);
@ -535,7 +535,7 @@ public abstract class ModificationStatement implements CQLStatement.SingleKeyspa
return null;
}
private ResultMessage executeWithCondition(QueryState queryState, QueryOptions options, long queryStartNanoTime)
private ResultMessage executeWithCondition(QueryState queryState, QueryOptions options, Dispatcher.RequestTime requestTime)
{
CQL3CasRequest request = makeCasRequest(queryState, options);
@ -547,7 +547,7 @@ public abstract class ModificationStatement implements CQLStatement.SingleKeyspa
options.getConsistency(),
queryState.getClientState(),
options.getNowInSeconds(queryState),
queryStartNanoTime))
requestTime))
{
return new ResultMessage.Rows(buildCasResultSet(result, queryState, options));
}
@ -680,15 +680,15 @@ public abstract class ModificationStatement implements CQLStatement.SingleKeyspa
{
return hasConditions()
? executeInternalWithCondition(queryState, options)
: executeInternalWithoutCondition(queryState, options, nanoTime());
: executeInternalWithoutCondition(queryState, options, Dispatcher.RequestTime.forImmediateExecution());
}
public ResultMessage executeInternalWithoutCondition(QueryState queryState, QueryOptions options, long queryStartNanoTime)
public ResultMessage executeInternalWithoutCondition(QueryState queryState, QueryOptions options, Dispatcher.RequestTime requestTime)
throws RequestValidationException, RequestExecutionException
{
long timestamp = options.getTimestamp(queryState);
long nowInSeconds = options.getNowInSeconds(queryState);
for (IMutation mutation : getMutations(queryState.getClientState(), options, true, timestamp, nowInSeconds, queryStartNanoTime))
for (IMutation mutation : getMutations(queryState.getClientState(), options, true, timestamp, nowInSeconds, requestTime))
mutation.apply();
return null;
}
@ -741,12 +741,12 @@ public abstract class ModificationStatement implements CQLStatement.SingleKeyspa
boolean local,
long timestamp,
long nowInSeconds,
long queryStartNanoTime)
Dispatcher.RequestTime requestTime)
{
List<ByteBuffer> keys = buildPartitionKeyNames(options, state);
HashMultiset<ByteBuffer> perPartitionKeyCounts = HashMultiset.create(keys);
SingleTableUpdatesCollector collector = new SingleTableUpdatesCollector(metadata, updatedColumns, perPartitionKeyCounts);
addUpdates(collector, keys, state, options, local, timestamp, nowInSeconds, queryStartNanoTime);
addUpdates(collector, keys, state, options, local, timestamp, nowInSeconds, requestTime);
return collector.toMutations(state);
}
@ -757,7 +757,7 @@ public abstract class ModificationStatement implements CQLStatement.SingleKeyspa
boolean local,
long timestamp,
long nowInSeconds,
long queryStartNanoTime)
Dispatcher.RequestTime requestTime)
{
if (hasSlices())
{
@ -775,7 +775,7 @@ public abstract class ModificationStatement implements CQLStatement.SingleKeyspa
local,
timestamp,
nowInSeconds,
queryStartNanoTime);
requestTime);
for (ByteBuffer key : keys)
{
Validation.validateKey(metadata(), key);
@ -795,7 +795,7 @@ public abstract class ModificationStatement implements CQLStatement.SingleKeyspa
if (restrictions.hasClusteringColumnsRestrictions() && clusterings.isEmpty())
return;
UpdateParameters params = makeUpdateParameters(keys, clusterings, state, options, local, timestamp, nowInSeconds, queryStartNanoTime);
UpdateParameters params = makeUpdateParameters(keys, clusterings, state, options, local, timestamp, nowInSeconds, requestTime);
for (ByteBuffer key : keys)
{
@ -835,7 +835,7 @@ public abstract class ModificationStatement implements CQLStatement.SingleKeyspa
boolean local,
long timestamp,
long nowInSeconds,
long queryStartNanoTime)
Dispatcher.RequestTime requestTime)
{
if (clusterings.contains(Clustering.STATIC_CLUSTERING))
return makeUpdateParameters(keys,
@ -846,7 +846,7 @@ public abstract class ModificationStatement implements CQLStatement.SingleKeyspa
local,
timestamp,
nowInSeconds,
queryStartNanoTime);
requestTime);
return makeUpdateParameters(keys,
new ClusteringIndexNamesFilter(clusterings, false),
@ -856,7 +856,7 @@ public abstract class ModificationStatement implements CQLStatement.SingleKeyspa
local,
timestamp,
nowInSeconds,
queryStartNanoTime);
requestTime);
}
private UpdateParameters makeUpdateParameters(Collection<ByteBuffer> keys,
@ -867,7 +867,7 @@ public abstract class ModificationStatement implements CQLStatement.SingleKeyspa
boolean local,
long timestamp,
long nowInSeconds,
long queryStartNanoTime)
Dispatcher.RequestTime requestTime)
{
// Some lists operation requires reading
Map<DecoratedKey, Partition> lists =
@ -877,7 +877,7 @@ public abstract class ModificationStatement implements CQLStatement.SingleKeyspa
local,
options.getConsistency(),
nowInSeconds,
queryStartNanoTime);
requestTime);
return new UpdateParameters(metadata(),
updatedColumns(),

View File

@ -76,6 +76,7 @@ import org.apache.cassandra.service.StorageProxy;
import org.apache.cassandra.service.pager.AggregationQueryPager;
import org.apache.cassandra.service.pager.PagingState;
import org.apache.cassandra.service.pager.QueryPager;
import org.apache.cassandra.transport.Dispatcher;
import org.apache.cassandra.transport.ProtocolVersion;
import org.apache.cassandra.transport.messages.ResultMessage;
import org.apache.cassandra.utils.ByteBufferUtil;
@ -92,7 +93,6 @@ import static org.apache.cassandra.cql3.statements.RequestValidations.checkNull;
import static org.apache.cassandra.cql3.statements.RequestValidations.checkTrue;
import static org.apache.cassandra.cql3.statements.RequestValidations.invalidRequest;
import static org.apache.cassandra.utils.ByteBufferUtil.UNSET_BYTE_BUFFER;
import static org.apache.cassandra.utils.Clock.Global.nanoTime;
/**
* Encapsulates a completely parsed SELECT query, including the target
@ -277,7 +277,7 @@ public class SelectStatement implements CQLStatement.SingleKeyspaceCqlStatement
Guardrails.allowFilteringEnabled.ensureEnabled(state);
}
public ResultMessage.Rows execute(QueryState state, QueryOptions options, long queryStartNanoTime)
public ResultMessage.Rows execute(QueryState state, QueryOptions options, Dispatcher.RequestTime requestTime)
{
ConsistencyLevel cl = options.getConsistency();
checkNotNull(cl, "Invalid empty consistency level");
@ -337,7 +337,7 @@ public class SelectStatement implements CQLStatement.SingleKeyspaceCqlStatement
if (aggregationSpec == null && (pageSize <= 0 || (query.limits().count() <= pageSize) || query.isTopK()))
{
rows = execute(query, options, state.getClientState(), selectors, nowInSec, userLimit, null, queryStartNanoTime, unmask);
rows = execute(query, options, state.getClientState(), selectors, nowInSec, userLimit, null, requestTime, unmask);
}
else
{
@ -351,7 +351,7 @@ public class SelectStatement implements CQLStatement.SingleKeyspaceCqlStatement
nowInSec,
userLimit,
aggregationSpec,
queryStartNanoTime,
requestTime,
unmask);
}
if (!SchemaConstants.isSystemKeyspace(table.keyspace))
@ -418,10 +418,10 @@ public class SelectStatement implements CQLStatement.SingleKeyspaceCqlStatement
long nowInSec,
int userLimit,
AggregationSpecification aggregationSpec,
long queryStartNanoTime,
Dispatcher.RequestTime requestTime,
boolean unmask)
{
try (PartitionIterator data = query.execute(options.getConsistency(), state, queryStartNanoTime))
try (PartitionIterator data = query.execute(options.getConsistency(), state, requestTime))
{
return processResults(data, options, selectors, nowInSec, userLimit, aggregationSpec, unmask, state);
}
@ -463,7 +463,7 @@ public class SelectStatement implements CQLStatement.SingleKeyspaceCqlStatement
return pager.state();
}
public abstract PartitionIterator fetchPage(int pageSize, long queryStartNanoTime);
public abstract PartitionIterator fetchPage(int pageSize, Dispatcher.RequestTime requestTime);
public static class NormalPager extends Pager
{
@ -477,9 +477,9 @@ public class SelectStatement implements CQLStatement.SingleKeyspaceCqlStatement
this.clientState = clientState;
}
public PartitionIterator fetchPage(int pageSize, long queryStartNanoTime)
public PartitionIterator fetchPage(int pageSize, Dispatcher.RequestTime requestTime)
{
return pager.fetchPage(pageSize, consistency, clientState, queryStartNanoTime);
return pager.fetchPage(pageSize, consistency, clientState, requestTime);
}
}
@ -493,7 +493,7 @@ public class SelectStatement implements CQLStatement.SingleKeyspaceCqlStatement
this.executionController = executionController;
}
public PartitionIterator fetchPage(int pageSize, long queryStartNanoTime)
public PartitionIterator fetchPage(int pageSize, Dispatcher.RequestTime requestTime)
{
return pager.fetchPageInternal(pageSize, executionController);
}
@ -508,7 +508,7 @@ public class SelectStatement implements CQLStatement.SingleKeyspaceCqlStatement
long nowInSec,
int userLimit,
AggregationSpecification aggregationSpec,
long queryStartNanoTime,
Dispatcher.RequestTime requestTime,
boolean unmask)
{
Guardrails.pageSize.guard(pageSize, table(), false, state.getClientState());
@ -536,7 +536,7 @@ public class SelectStatement implements CQLStatement.SingleKeyspaceCqlStatement
+ " you must either remove the ORDER BY or the IN and sort client side, or disable paging for this query");
ResultMessage.Rows msg;
try (PartitionIterator page = pager.fetchPage(pageSize, queryStartNanoTime))
try (PartitionIterator page = pager.fetchPage(pageSize, requestTime))
{
msg = processResults(page, options, selectors, nowInSec, userLimit, aggregationSpec, unmask, state.getClientState());
}
@ -570,13 +570,13 @@ public class SelectStatement implements CQLStatement.SingleKeyspaceCqlStatement
public ResultMessage.Rows executeLocally(QueryState state, QueryOptions options) throws RequestExecutionException, RequestValidationException
{
return executeInternal(state, options, options.getNowInSeconds(state), nanoTime());
return executeInternal(state, options, options.getNowInSeconds(state), Dispatcher.RequestTime.forImmediateExecution());
}
public ResultMessage.Rows executeInternal(QueryState state,
QueryOptions options,
long nowInSec,
long queryStartNanoTime)
Dispatcher.RequestTime requestTime)
{
int userLimit = getLimit(options);
int userPerPartitionLimit = getPerPartitionLimit(options);
@ -614,7 +614,7 @@ public class SelectStatement implements CQLStatement.SingleKeyspaceCqlStatement
nowInSec,
userLimit,
aggregationSpec,
queryStartNanoTime,
requestTime,
unmask);
}
}

View File

@ -34,6 +34,7 @@ import org.apache.cassandra.schema.TableMetadata;
import org.apache.cassandra.service.ClientState;
import org.apache.cassandra.service.QueryState;
import org.apache.cassandra.service.StorageProxy;
import org.apache.cassandra.transport.Dispatcher;
import org.apache.cassandra.transport.messages.ResultMessage;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
@ -61,7 +62,8 @@ public class TruncateStatement extends QualifiedStatement implements CQLStatemen
Guardrails.dropTruncateTableEnabled.ensureEnabled(state);
}
public ResultMessage execute(QueryState state, QueryOptions options, long queryStartNanoTime) throws InvalidRequestException, TruncateException
@Override
public ResultMessage execute(QueryState state, QueryOptions options, Dispatcher.RequestTime requestTime) throws InvalidRequestException, TruncateException
{
try
{

View File

@ -25,6 +25,7 @@ import org.apache.cassandra.cql3.QueryOptions;
import org.apache.cassandra.cql3.QueryProcessor;
import org.apache.cassandra.exceptions.InvalidRequestException;
import org.apache.cassandra.exceptions.UnauthorizedException;
import org.apache.cassandra.transport.Dispatcher;
import org.apache.cassandra.transport.messages.ResultMessage;
import org.apache.cassandra.service.ClientState;
import org.apache.cassandra.service.QueryState;
@ -32,7 +33,6 @@ import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import static org.apache.cassandra.cql3.statements.RequestValidations.checkTrue;
import static org.apache.cassandra.utils.Clock.Global.nanoTime;
public class UseStatement extends CQLStatement.Raw implements CQLStatement
{
@ -59,7 +59,8 @@ public class UseStatement extends CQLStatement.Raw implements CQLStatement
checkTrue(DatabaseDescriptor.getUseStatementsEnabled(), "USE statements prohibited. (see use_statements_enabled in cassandra.yaml)");
}
public ResultMessage execute(QueryState state, QueryOptions options, long queryStartNanoTime) throws InvalidRequestException
@Override
public ResultMessage execute(QueryState state, QueryOptions options, Dispatcher.RequestTime requestTime) throws InvalidRequestException
{
QueryProcessor.metrics.useStatementsExecuted.inc();
state.getClientState().setKeyspace(keyspace);
@ -70,7 +71,7 @@ public class UseStatement extends CQLStatement.Raw implements CQLStatement
{
// In production, internal queries are exclusively on the system keyspace and 'use' is thus useless
// but for some unit tests we need to set the keyspace (e.g. for tests with DROP INDEX)
return execute(state, options, nanoTime());
return execute(state, options, Dispatcher.RequestTime.forImmediateExecution());
}
@Override

View File

@ -34,6 +34,7 @@ import org.apache.cassandra.schema.Keyspaces.KeyspacesDiff;
import org.apache.cassandra.service.ClientState;
import org.apache.cassandra.service.ClientWarn;
import org.apache.cassandra.service.QueryState;
import org.apache.cassandra.transport.Dispatcher;
import org.apache.cassandra.transport.Event.SchemaChange;
import org.apache.cassandra.transport.messages.ResultMessage;
@ -55,7 +56,8 @@ abstract public class AlterSchemaStatement implements CQLStatement.SingleKeyspac
this.state = state;
}
public ResultMessage execute(QueryState state, QueryOptions options, long queryStartNanoTime)
@Override
public ResultMessage execute(QueryState state, QueryOptions options, Dispatcher.RequestTime requestTime)
{
return execute(state, false);
}

View File

@ -25,8 +25,7 @@ import org.apache.cassandra.net.IVerbHandler;
import org.apache.cassandra.net.Message;
import org.apache.cassandra.net.MessagingService;
import org.apache.cassandra.service.StorageProxy;
import static org.apache.cassandra.utils.Clock.Global.nanoTime;
import org.apache.cassandra.transport.Dispatcher;
public class CounterMutationVerbHandler implements IVerbHandler<CounterMutation>
{
@ -36,7 +35,6 @@ public class CounterMutationVerbHandler implements IVerbHandler<CounterMutation>
public void doVerb(final Message<CounterMutation> message)
{
long queryStartNanoTime = nanoTime();
final CounterMutation cm = message.payload;
logger.trace("Applying forwarded {}", cm);
@ -51,6 +49,6 @@ public class CounterMutationVerbHandler implements IVerbHandler<CounterMutation>
StorageProxy.applyCounterMutationOnLeader(cm,
localDataCenter,
() -> MessagingService.instance().send(message.emptyResponse(), message.from()),
queryStartNanoTime);
Dispatcher.RequestTime.forImmediateExecution());
}
}

View File

@ -22,7 +22,9 @@ import org.apache.cassandra.locator.InetAddressAndPort;
import org.apache.cassandra.net.*;
import org.apache.cassandra.tracing.Tracing;
import static java.util.concurrent.TimeUnit.NANOSECONDS;
import static org.apache.cassandra.db.commitlog.CommitLogSegment.ENTRY_OVERHEAD_SIZE;
import static org.apache.cassandra.utils.MonotonicClock.Global.approxTime;
public class MutationVerbHandler implements IVerbHandler<Mutation>
{
@ -41,6 +43,13 @@ public class MutationVerbHandler implements IVerbHandler<Mutation>
public void doVerb(Message<Mutation> message)
{
if (approxTime.now() > message.expiresAtNanos())
{
Tracing.trace("Discarding mutation from {} (timed out)", message.from());
MessagingService.instance().metrics.recordDroppedMessage(message, message.elapsedSinceCreated(NANOSECONDS), NANOSECONDS);
return;
}
message.payload.validateSize(MessagingService.current_version, ENTRY_OVERHEAD_SIZE);
// Check if there were any forwarding headers in this message

View File

@ -54,6 +54,7 @@ import org.apache.cassandra.schema.TableMetadata;
import org.apache.cassandra.service.ClientState;
import org.apache.cassandra.service.StorageProxy;
import org.apache.cassandra.tracing.Tracing;
import org.apache.cassandra.transport.Dispatcher;
/**
* A read command that selects a (part of a) range of partitions.
@ -304,9 +305,9 @@ public class PartitionRangeReadCommand extends ReadCommand implements PartitionR
return dataRange.isReversed();
}
public PartitionIterator execute(ConsistencyLevel consistency, ClientState state, long queryStartNanoTime) throws RequestExecutionException
public PartitionIterator execute(ConsistencyLevel consistency, ClientState state, Dispatcher.RequestTime requestTime) throws RequestExecutionException
{
return StorageProxy.getRangeSlice(this, consistency, queryStartNanoTime);
return StorageProxy.getRangeSlice(this, consistency, requestTime);
}
protected void recordLatency(TableMetrics metric, long latencyNanos)
@ -549,7 +550,7 @@ public class PartitionRangeReadCommand extends ReadCommand implements PartitionR
}
@Override
public PartitionIterator execute(ConsistencyLevel consistency, ClientState state, long queryStartNanoTime) throws RequestExecutionException
public PartitionIterator execute(ConsistencyLevel consistency, ClientState state, Dispatcher.RequestTime requestTime) throws RequestExecutionException
{
return executeInternal(executionController());
}

View File

@ -69,6 +69,7 @@ import org.apache.cassandra.service.ActiveRepairService;
import org.apache.cassandra.service.ClientWarn;
import org.apache.cassandra.tracing.Tracing;
import org.apache.cassandra.utils.CassandraUInt;
import org.apache.cassandra.transport.Dispatcher;
import org.apache.cassandra.utils.FBUtilities;
import org.apache.cassandra.utils.ObjectSizes;
import org.apache.cassandra.utils.TimeUUID;
@ -76,8 +77,8 @@ import org.apache.cassandra.utils.TimeUUID;
import static com.google.common.collect.Iterables.any;
import static com.google.common.collect.Iterables.filter;
import static org.apache.cassandra.utils.Clock.Global.nanoTime;
import static org.apache.cassandra.utils.MonotonicClock.Global.approxTime;
import static org.apache.cassandra.db.partitions.UnfilteredPartitionIterators.MergeListener.NOOP;
import static org.apache.cassandra.utils.MonotonicClock.Global.approxTime;
/**
* General interface for storage-engine read commands (common to both range and
@ -789,14 +790,19 @@ public abstract class ReadCommand extends AbstractReadQuery
/**
* Creates a message for this command.
*/
public Message<ReadCommand> createMessage(boolean trackRepairedData)
public Message<ReadCommand> createMessage(boolean trackRepairedData, Dispatcher.RequestTime requestTime)
{
Message<ReadCommand> msg = trackRepairedData
? Message.outWithFlags(verb(), this, MessageFlag.CALL_BACK_ON_FAILURE, MessageFlag.TRACK_REPAIRED_DATA)
: Message.outWithFlag(verb(), this, MessageFlag.CALL_BACK_ON_FAILURE);
List<MessageFlag> flags = new ArrayList<>(3);
flags.add(MessageFlag.CALL_BACK_ON_FAILURE);
if (trackWarnings)
msg = msg.withFlag(MessageFlag.TRACK_WARNINGS);
return msg;
flags.add(MessageFlag.TRACK_WARNINGS);
if (trackRepairedData)
flags.add(MessageFlag.TRACK_REPAIRED_DATA);
return Message.outWithFlags(verb(),
this,
requestTime,
flags);
}
protected abstract boolean intersects(SSTableReader sstable);

View File

@ -26,6 +26,7 @@ import org.apache.cassandra.schema.TableMetadata;
import org.apache.cassandra.service.ClientState;
import org.apache.cassandra.service.pager.QueryPager;
import org.apache.cassandra.service.pager.PagingState;
import org.apache.cassandra.transport.Dispatcher;
import org.apache.cassandra.transport.ProtocolVersion;
import org.apache.cassandra.utils.FBUtilities;
@ -48,7 +49,7 @@ public interface ReadQuery
return ReadExecutionController.empty();
}
public PartitionIterator execute(ConsistencyLevel consistency, ClientState state, long queryStartNanoTime) throws RequestExecutionException
public PartitionIterator execute(ConsistencyLevel consistency, ClientState state, Dispatcher.RequestTime requestTime) throws RequestExecutionException
{
return EmptyIterators.partition();
}
@ -141,9 +142,10 @@ public interface ReadQuery
*
* @param consistency the consistency level to achieve for the query.
* @param state client state
* @param state request enqueue / and start times
* @return the result of the query.
*/
public PartitionIterator execute(ConsistencyLevel consistency, ClientState state, long queryStartNanoTime) throws RequestExecutionException;
public PartitionIterator execute(ConsistencyLevel consistency, ClientState state, Dispatcher.RequestTime requestTime) throws RequestExecutionException;
/**
* Execute the query for internal queries (that is, it basically executes the query locally).

View File

@ -77,6 +77,7 @@ import org.apache.cassandra.service.CacheService;
import org.apache.cassandra.service.ClientState;
import org.apache.cassandra.service.StorageProxy;
import org.apache.cassandra.tracing.Tracing;
import org.apache.cassandra.transport.Dispatcher;
import org.apache.cassandra.utils.FBUtilities;
import org.apache.cassandra.utils.btree.BTreeSet;
@ -462,12 +463,12 @@ public class SinglePartitionReadCommand extends ReadCommand implements SinglePar
}
@Override
public PartitionIterator execute(ConsistencyLevel consistency, ClientState state, long queryStartNanoTime) throws RequestExecutionException
public PartitionIterator execute(ConsistencyLevel consistency, ClientState state, Dispatcher.RequestTime requestTime) throws RequestExecutionException
{
if (clusteringIndexFilter.isEmpty(metadata().comparator))
return EmptyIterators.partition();
return StorageProxy.read(Group.one(this), consistency, queryStartNanoTime);
return StorageProxy.read(Group.one(this), consistency, requestTime);
}
protected void recordLatency(TableMetrics metric, long latencyNanos)
@ -1273,9 +1274,9 @@ public class SinglePartitionReadCommand extends ReadCommand implements SinglePar
new Group(commands, limits);
}
public PartitionIterator execute(ConsistencyLevel consistency, ClientState state, long queryStartNanoTime) throws RequestExecutionException
public PartitionIterator execute(ConsistencyLevel consistency, ClientState state, Dispatcher.RequestTime requestTime) throws RequestExecutionException
{
return StorageProxy.read(this, consistency, queryStartNanoTime);
return StorageProxy.read(this, consistency, requestTime);
}
}
@ -1287,13 +1288,13 @@ public class SinglePartitionReadCommand extends ReadCommand implements SinglePar
}
@Override
public PartitionIterator execute(ConsistencyLevel consistency, ClientState state, long queryStartNanoTime) throws RequestExecutionException
public PartitionIterator execute(ConsistencyLevel consistency, ClientState state, Dispatcher.RequestTime requestTime) throws RequestExecutionException
{
if (queries.size() == 1)
return queries.get(0).execute(consistency, state, queryStartNanoTime);
return queries.get(0).execute(consistency, state, requestTime);
return PartitionIterators.concat(queries.stream()
.map(q -> q.execute(consistency, state, queryStartNanoTime))
.map(q -> q.execute(consistency, state, requestTime))
.collect(Collectors.toList()));
}
}
@ -1366,7 +1367,7 @@ public class SinglePartitionReadCommand extends ReadCommand implements SinglePar
}
@Override
public PartitionIterator execute(ConsistencyLevel consistency, ClientState state, long queryStartNanoTime) throws RequestExecutionException
public PartitionIterator execute(ConsistencyLevel consistency, ClientState state, Dispatcher.RequestTime requestTime) throws RequestExecutionException
{
return executeInternal(executionController());
}

View File

@ -38,6 +38,7 @@ import org.apache.cassandra.schema.TableId;
import org.apache.cassandra.schema.TableMetadata;
import org.apache.cassandra.schema.TableMetadataRef;
import org.apache.cassandra.service.StorageProxy;
import org.apache.cassandra.transport.Dispatcher;
import org.apache.cassandra.utils.FBUtilities;
import org.apache.cassandra.utils.btree.BTree;
import org.apache.cassandra.utils.btree.BTreeSet;
@ -149,7 +150,7 @@ public class TableViews extends AbstractCollection<View>
// Read modified rows
long nowInSec = FBUtilities.nowInSeconds();
long queryStartNanoTime = nanoTime();
Dispatcher.RequestTime requestTime = Dispatcher.RequestTime.forImmediateExecution();
SinglePartitionReadCommand command = readExistingRowsCommand(update, views, nowInSec);
if (command == null)
return;
@ -166,7 +167,7 @@ public class TableViews extends AbstractCollection<View>
Keyspace.openAndGetStore(update.metadata()).metric.viewReadTime.update(nanoTime() - start, TimeUnit.NANOSECONDS);
if (!mutations.isEmpty())
StorageProxy.mutateMV(update.partitionKey().getKey(), mutations, writeCommitLog, baseComplete, queryStartNanoTime);
StorageProxy.mutateMV(update.partitionKey().getKey(), mutations, writeCommitLog, baseComplete, requestTime);
}

View File

@ -56,11 +56,11 @@ import org.apache.cassandra.gms.Gossiper;
import org.apache.cassandra.io.sstable.ReducingKeyIterator;
import org.apache.cassandra.io.sstable.format.SSTableReader;
import org.apache.cassandra.service.StorageProxy;
import org.apache.cassandra.transport.Dispatcher;
import org.apache.cassandra.utils.FBUtilities;
import org.apache.cassandra.utils.TimeUUID;
import org.apache.cassandra.utils.concurrent.Refs;
import static org.apache.cassandra.utils.Clock.Global.nanoTime;
import static org.apache.cassandra.utils.TimeUUID.Generator.nextTimeUUID;
public class ViewBuilderTask extends CompactionInfo.Holder implements Callable<Long>
@ -114,7 +114,7 @@ public class ViewBuilderTask extends CompactionInfo.Holder implements Callable<L
.generateViewUpdates(Collections.singleton(view), data, empty, nowInSec, true);
AtomicLong noBase = new AtomicLong(Long.MAX_VALUE);
mutations.forEachRemaining(m -> StorageProxy.mutateMV(key.getKey(), m, true, noBase, nanoTime()));
mutations.forEachRemaining(m -> StorageProxy.mutateMV(key.getKey(), m, true, noBase, Dispatcher.RequestTime.forImmediateExecution()));
}
}

View File

@ -43,11 +43,11 @@ import org.apache.cassandra.metrics.CIDRAuthorizerMetrics;
import org.apache.cassandra.schema.SchemaConstants;
import org.apache.cassandra.schema.TableMetadata;
import org.apache.cassandra.service.ClientState;
import org.apache.cassandra.transport.Dispatcher;
import org.apache.cassandra.transport.messages.ResultMessage;
import org.apache.cassandra.utils.MBeanWrapper;
import static org.apache.cassandra.service.QueryState.forInternalCalls;
import static org.apache.cassandra.utils.Clock.Global.nanoTime;
/**
* Virtual tables capturing metrics related to CIDR filtering
@ -196,7 +196,7 @@ public class CIDRFilteringMetricsTable implements CIDRFilteringMetricsTableMBean
QueryOptions options = QueryOptions.forInternalCalls(CassandraAuthorizer.authReadConsistencyLevel(),
Collections.emptyList());
ResultMessage.Rows rows = statement.execute(forInternalCalls(), options, nanoTime());
ResultMessage.Rows rows = statement.execute(forInternalCalls(), options, Dispatcher.RequestTime.forImmediateExecution());
return UntypedResultSet.create(rows.result);
}

View File

@ -46,6 +46,7 @@ import org.apache.cassandra.service.ClientState;
import org.apache.cassandra.service.DatacenterSyncWriteResponseHandler;
import org.apache.cassandra.service.DatacenterWriteResponseHandler;
import org.apache.cassandra.service.WriteResponseHandler;
import org.apache.cassandra.transport.Dispatcher;
import org.apache.cassandra.utils.FBUtilities;
import org.cliffc.high_scale_lib.NonBlockingHashMap;
@ -136,32 +137,32 @@ public abstract class AbstractReplicationStrategy
Runnable callback,
WriteType writeType,
Supplier<Mutation> hintOnFailure,
long queryStartNanoTime)
Dispatcher.RequestTime requestTime)
{
return getWriteResponseHandler(replicaPlan, callback, writeType, hintOnFailure,
queryStartNanoTime, DatabaseDescriptor.getIdealConsistencyLevel());
requestTime, DatabaseDescriptor.getIdealConsistencyLevel());
}
public <T> AbstractWriteResponseHandler<T> getWriteResponseHandler(ReplicaPlan.ForWrite replicaPlan,
Runnable callback,
WriteType writeType,
Supplier<Mutation> hintOnFailure,
long queryStartNanoTime,
Dispatcher.RequestTime requestTime,
ConsistencyLevel idealConsistencyLevel)
{
AbstractWriteResponseHandler<T> resultResponseHandler;
if (replicaPlan.consistencyLevel().isDatacenterLocal())
{
// block for in this context will be localnodes block.
resultResponseHandler = new DatacenterWriteResponseHandler<T>(replicaPlan, callback, writeType, hintOnFailure, queryStartNanoTime);
resultResponseHandler = new DatacenterWriteResponseHandler<T>(replicaPlan, callback, writeType, hintOnFailure, requestTime);
}
else if (replicaPlan.consistencyLevel() == ConsistencyLevel.EACH_QUORUM && (this instanceof NetworkTopologyStrategy))
{
resultResponseHandler = new DatacenterSyncWriteResponseHandler<T>(replicaPlan, callback, writeType, hintOnFailure, queryStartNanoTime);
resultResponseHandler = new DatacenterSyncWriteResponseHandler<T>(replicaPlan, callback, writeType, hintOnFailure, requestTime);
}
else
{
resultResponseHandler = new WriteResponseHandler<T>(replicaPlan, callback, writeType, hintOnFailure, queryStartNanoTime);
resultResponseHandler = new WriteResponseHandler<T>(replicaPlan, callback, writeType, hintOnFailure, requestTime);
}
//Check if tracking the ideal consistency level is configured
@ -181,7 +182,7 @@ public abstract class AbstractReplicationStrategy
callback,
writeType,
hintOnFailure,
queryStartNanoTime,
requestTime,
idealConsistencyLevel);
resultResponseHandler.setIdealCLResponseHandler(idealHandler);
}

View File

@ -18,13 +18,23 @@
*/
package org.apache.cassandra.metrics;
import java.util.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import com.google.common.annotations.VisibleForTesting;
import com.codahale.metrics.Gauge;
import com.codahale.metrics.Histogram;
import com.codahale.metrics.Meter;
import com.codahale.metrics.Reservoir;
import com.codahale.metrics.Timer;
import org.apache.cassandra.transport.*;
import static org.apache.cassandra.metrics.CassandraMetricsRegistry.Metrics;
@ -41,15 +51,17 @@ public final class ClientMetrics
private Meter authSuccess;
private Meter authFailure;
private AtomicInteger pausedConnections;
@SuppressWarnings({ "unused", "FieldCanBeLocal" })
private Gauge<Integer> pausedConnectionsGauge;
private Meter connectionPaused;
private Meter requestDiscarded;
private Meter requestDispatched;
private Meter timedOutBeforeProcessing;
private Meter protocolException;
private Meter unknownException;
private Timer queueTime;
private ClientMetrics()
{
@ -65,11 +77,22 @@ public final class ClientMetrics
authFailure.mark();
}
public void pauseConnection() { pausedConnections.incrementAndGet(); }
@VisibleForTesting
public int getNumberOfPausedConnections()
{
return (int) connectionPaused.getCount();
}
public void pauseConnection()
{
connectionPaused.mark();
pausedConnections.incrementAndGet();
}
public void unpauseConnection() { pausedConnections.decrementAndGet(); }
public void markRequestDiscarded() { requestDiscarded.mark(); }
public void markRequestDispatched() { requestDispatched.mark(); }
public void markTimedOutBeforeProcessing() { timedOutBeforeProcessing.mark(); }
public List<ConnectedClient> allConnectedClients()
{
@ -120,13 +143,16 @@ public final class ClientMetrics
pausedConnections = new AtomicInteger();
pausedConnectionsGauge = registerGauge("PausedConnections", pausedConnections::get);
connectionPaused = registerMeter("ConnectionPaused");
requestDiscarded = registerMeter("RequestDiscarded");
requestDispatched = registerMeter("RequestDispatched");
timedOutBeforeProcessing = registerMeter("TimedOutBeforeProcessing");
protocolException = registerMeter("ProtocolException");
unknownException = registerMeter("UnknownException");
initialized = true;
queueTime = registerTimer("Queued");
}
private int countConnectedClients()
@ -191,4 +217,14 @@ public final class ClientMetrics
{
return Metrics.meter(factory.createMetricName(name));
}
public Timer registerTimer(String name)
{
return Metrics.timer(factory.createMetricName(name));
}
public void queueTime(long value, TimeUnit unit)
{
queueTime.update(value, unit);
}
}

View File

@ -40,6 +40,7 @@ public class ThreadPoolMetrics
public static final String TOTAL_BLOCKED_TASKS = "TotalBlockedTasks";
public static final String MAX_POOL_SIZE = "MaxPoolSize";
public static final String MAX_TASKS_QUEUED = "MaxTasksQueued";
public static final String OLDEST_TASK_QUEUE_TIME = "OldestTaskQueueTime";
/** Number of active tasks. */
public final Gauge<Integer> activeTasks;
@ -62,6 +63,9 @@ public class ThreadPoolMetrics
/** Maximum number of threads before it will start queuing tasks */
public final Gauge<Integer> maxPoolSize;
/** For how long the oldest task in the queue was queued */
public final Gauge<Long> oldestTaskQueueTime;
/** Maximum number of tasks queued before a task get blocked */
public final Gauge<Integer> maxTasksQueued;
@ -87,6 +91,7 @@ public class ThreadPoolMetrics
completedTasks = executor::getCompletedTaskCount;
maxPoolSize = executor::getMaximumPoolSize;
maxTasksQueued = executor::getMaxTasksQueued;
oldestTaskQueueTime = executor::oldestTaskQueueTime;
}
public ThreadPoolMetrics register()
@ -98,6 +103,7 @@ public class ThreadPoolMetrics
Metrics.register(makeMetricName(path, poolName, TOTAL_BLOCKED_TASKS), totalBlocked);
Metrics.register(makeMetricName(path, poolName, MAX_POOL_SIZE), maxPoolSize);
Metrics.register(makeMetricName(path, poolName, MAX_TASKS_QUEUED), maxTasksQueued);
Metrics.register(makeMetricName(path, poolName, OLDEST_TASK_QUEUE_TIME), oldestTaskQueueTime);
return Metrics.register(this);
}
@ -110,6 +116,7 @@ public class ThreadPoolMetrics
Metrics.remove(makeMetricName(path, poolName, TOTAL_BLOCKED_TASKS));
Metrics.remove(makeMetricName(path, poolName, MAX_POOL_SIZE));
Metrics.remove(makeMetricName(path, poolName, MAX_TASKS_QUEUED));
Metrics.remove(makeMetricName(path, poolName, OLDEST_TASK_QUEUE_TIME));
Metrics.remove(this);
}

View File

@ -403,24 +403,24 @@ public class InboundMessageHandler extends AbstractMessageHandler
{
/**
* Actually handle the message. Runs on the appropriate {@link Stage} for the {@link Verb}.
*
* <p>
* Small messages will come pre-deserialized. Large messages will be deserialized on the stage,
* just in time, and only then processed.
*/
public void run()
{
Header header = header();
long currentTimeNanos = approxTime.now();
boolean expired = approxTime.isAfter(currentTimeNanos, header.expiresAtNanos);
long approxStartTimeNanos = approxTime.now();
boolean expired = approxTime.isAfter(approxStartTimeNanos, header.expiresAtNanos);
boolean processed = false;
try
{
callbacks.onExecuting(size(), header, currentTimeNanos - header.createdAtNanos, NANOSECONDS);
callbacks.onExecuting(size(), header, approxStartTimeNanos - header.createdAtNanos, NANOSECONDS);
if (expired)
{
callbacks.onExpired(size(), header, currentTimeNanos - header.createdAtNanos, NANOSECONDS);
callbacks.onExpired(size(), header, approxStartTimeNanos - header.createdAtNanos, NANOSECONDS);
return;
}
@ -441,7 +441,7 @@ public class InboundMessageHandler extends AbstractMessageHandler
releaseResources();
callbacks.onExecuted(size(), header, approxTime.now() - currentTimeNanos, NANOSECONDS);
callbacks.onExecuted(size(), header, approxTime.now() - approxStartTimeNanos, NANOSECONDS);
}
}

View File

@ -22,6 +22,7 @@ import java.nio.ByteBuffer;
import java.util.Collections;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
@ -42,6 +43,7 @@ import org.apache.cassandra.io.util.DataOutputPlus;
import org.apache.cassandra.locator.InetAddressAndPort;
import org.apache.cassandra.tracing.Tracing;
import org.apache.cassandra.tracing.Tracing.TraceType;
import org.apache.cassandra.transport.Dispatcher;
import org.apache.cassandra.utils.MonotonicClockTranslation;
import org.apache.cassandra.utils.NoSpamLogger;
import org.apache.cassandra.utils.TimeUUID;
@ -217,6 +219,23 @@ public class Message<T>
return outWithParam(nextId(), verb, 0, payload, flag2.addTo(flag1.addTo(0)), null, null);
}
public static <T> Message<T> outWithFlags(Verb verb, T payload, Dispatcher.RequestTime requestTime, List<MessageFlag> flags)
{
assert !verb.isResponse();
int encodedFlags = 0;
for (MessageFlag flag : flags)
encodedFlags = flag.addTo(encodedFlags);
return new Message<T>(new Header(nextId(),
verb,
getBroadcastAddressAndPort(),
requestTime.startedAtNanos(),
requestTime.computeDeadline(verb.expiresAfterNanos()),
encodedFlags,
buildParams(null, null)),
payload);
}
@VisibleForTesting
static <T> Message<T> outWithParam(long id, Verb verb, T payload, ParamType paramType, Object paramValue)
{
@ -832,15 +851,13 @@ public class Message<T>
Map<ParamType, Object> params = extractParams(buf, index, version);
long createdAtNanos = calculateCreationTimeNanos(createdAtMillis, timeSnapshot, currentTimeNanos);
long expiresAtNanos = getExpiresAtNanos(createdAtNanos, currentTimeNanos, TimeUnit.MILLISECONDS.toNanos(expiresInMillis));
long expiresAtNanos = getExpiresAtNanos(createdAtNanos, TimeUnit.MILLISECONDS.toNanos(expiresInMillis));
return new Header(id, verb, from, createdAtNanos, expiresAtNanos, flags, params);
}
private static long getExpiresAtNanos(long createdAtNanos, long currentTimeNanos, long expirationPeriodNanos)
private static long getExpiresAtNanos(long createdAtNanos, long expirationPeriodNanos)
{
if (!DatabaseDescriptor.hasCrossNodeTimeout() || createdAtNanos > currentTimeNanos)
createdAtNanos = currentTimeNanos;
return createdAtNanos + expirationPeriodNanos;
}
@ -862,7 +879,7 @@ public class Message<T>
long currentTimeNanos = approxTime.now();
MonotonicClockTranslation timeSnapshot = approxTime.translate();
long creationTimeNanos = calculateCreationTimeNanos(in.readInt(), timeSnapshot, currentTimeNanos);
long expiresAtNanos = getExpiresAtNanos(creationTimeNanos, currentTimeNanos, TimeUnit.MILLISECONDS.toNanos(in.readUnsignedVInt()));
long expiresAtNanos = getExpiresAtNanos(creationTimeNanos, TimeUnit.MILLISECONDS.toNanos(in.readUnsignedVInt()));
Verb verb = Verb.fromId(in.readUnsignedVInt32());
int flags = in.readUnsignedVInt32();
Map<ParamType, Object> params = deserializeParams(in, version);
@ -901,6 +918,10 @@ public class Message<T>
@VisibleForTesting
static long calculateCreationTimeNanos(int messageTimestampMillis, MonotonicClockTranslation timeSnapshot, long currentTimeNanos)
{
// We do not trust external time source, so we override their value with current time
if (!DatabaseDescriptor.hasCrossNodeTimeout())
return currentTimeNanos;
long currentTimeMillis = timeSnapshot.toMillisSinceEpoch(currentTimeNanos);
// Reconstruct the message construction time sent by the remote host (we sent only the lower 4 bytes, assuming the
// higher 4 bytes wouldn't change between the sender and receiver)

View File

@ -45,6 +45,7 @@ import org.apache.cassandra.net.Verb;
import org.apache.cassandra.repair.messages.FailSession;
import org.apache.cassandra.repair.messages.RepairMessage;
import org.apache.cassandra.repair.state.ParticipateState;
import org.apache.cassandra.transport.Dispatcher;
import org.apache.cassandra.utils.*;
import org.apache.cassandra.utils.concurrent.Future;
import org.apache.commons.lang3.time.DurationFormatUtils;
@ -551,7 +552,7 @@ public class RepairCoordinator implements Runnable, ProgressEventNotifier, Repai
QueryOptions options = QueryOptions.forInternalCalls(ConsistencyLevel.ONE, Lists.newArrayList(sessionIdBytes,
tminBytes,
tmaxBytes));
ResultMessage.Rows rows = statement.execute(forInternalCalls(), options, ctx.clock().nanoTime());
ResultMessage.Rows rows = statement.execute(forInternalCalls(), options, new Dispatcher.RequestTime(ctx.clock().nanoTime()));
UntypedResultSet result = UntypedResultSet.create(rows.result);
for (UntypedResultSet.Row r : result)

View File

@ -25,45 +25,42 @@ import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import javax.annotation.Nullable;
import org.apache.cassandra.db.ConsistencyLevel;
import org.apache.cassandra.db.Mutation;
import org.apache.cassandra.locator.EndpointsForToken;
import org.apache.cassandra.locator.ReplicaPlan;
import org.apache.cassandra.locator.ReplicaPlan.ForWrite;
import org.apache.cassandra.utils.concurrent.Condition;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.cassandra.db.ColumnFamilyStore;
import org.apache.cassandra.db.ConsistencyLevel;
import org.apache.cassandra.db.IMutation;
import org.apache.cassandra.db.Mutation;
import org.apache.cassandra.db.WriteType;
import org.apache.cassandra.exceptions.RequestFailureReason;
import org.apache.cassandra.exceptions.WriteFailureException;
import org.apache.cassandra.exceptions.WriteTimeoutException;
import org.apache.cassandra.locator.EndpointsForToken;
import org.apache.cassandra.locator.InetAddressAndPort;
import org.apache.cassandra.net.RequestCallback;
import org.apache.cassandra.locator.ReplicaPlan;
import org.apache.cassandra.locator.ReplicaPlan.ForWrite;
import org.apache.cassandra.net.Message;
import org.apache.cassandra.net.RequestCallback;
import org.apache.cassandra.transport.Dispatcher;
import org.apache.cassandra.utils.concurrent.Condition;
import org.apache.cassandra.utils.concurrent.UncheckedInterruptedException;
import static java.lang.Long.MAX_VALUE;
import static java.lang.Math.min;
import static java.util.concurrent.TimeUnit.MICROSECONDS;
import static java.util.concurrent.TimeUnit.NANOSECONDS;
import static org.apache.cassandra.utils.Clock.Global.nanoTime;
import static java.util.stream.Collectors.toList;
import static org.apache.cassandra.config.DatabaseDescriptor.getCounterWriteRpcTimeout;
import static org.apache.cassandra.config.DatabaseDescriptor.getWriteRpcTimeout;
import static org.apache.cassandra.db.WriteType.COUNTER;
import static org.apache.cassandra.locator.Replicas.countInOurDc;
import static org.apache.cassandra.schema.Schema.instance;
import static org.apache.cassandra.service.StorageProxy.WritePerformer;
import static org.apache.cassandra.utils.Clock.Global.nanoTime;
import static org.apache.cassandra.utils.concurrent.Condition.newOneTimeCondition;
import static org.apache.cassandra.locator.Replicas.countInOurDc;
public abstract class AbstractWriteResponseHandler<T> implements RequestCallback<T>
{
@ -80,7 +77,7 @@ public abstract class AbstractWriteResponseHandler<T> implements RequestCallback
AtomicIntegerFieldUpdater.newUpdater(AbstractWriteResponseHandler.class, "failures");
private volatile int failures = 0;
private final Map<InetAddressAndPort, RequestFailureReason> failureReasonByEndpoint;
private final long queryStartNanoTime;
private final Dispatcher.RequestTime requestTime;
private @Nullable final Supplier<Mutation> hintOnFailure;
/**
@ -99,17 +96,17 @@ public abstract class AbstractWriteResponseHandler<T> implements RequestCallback
/**
* @param callback A callback to be called when the write is successful.
* @param hintOnFailure
* @param queryStartNanoTime
* @param requestTime
*/
protected AbstractWriteResponseHandler(ForWrite replicaPlan, Runnable callback, WriteType writeType,
Supplier<Mutation> hintOnFailure, long queryStartNanoTime)
Supplier<Mutation> hintOnFailure, Dispatcher.RequestTime requestTime)
{
this.replicaPlan = replicaPlan;
this.callback = callback;
this.writeType = writeType;
this.hintOnFailure = hintOnFailure;
this.failureReasonByEndpoint = new ConcurrentHashMap<>();
this.queryStartNanoTime = queryStartNanoTime;
this.requestTime = requestTime;
}
public void get() throws WriteTimeoutException, WriteFailureException
@ -154,10 +151,11 @@ public abstract class AbstractWriteResponseHandler<T> implements RequestCallback
public final long currentTimeoutNanos()
{
long now = nanoTime();
long requestTimeout = writeType == COUNTER
? getCounterWriteRpcTimeout(NANOSECONDS)
: getWriteRpcTimeout(NANOSECONDS);
return requestTimeout - (nanoTime() - queryStartNanoTime);
return requestTime.computeTimeout(now, requestTimeout);
}
/**
@ -292,7 +290,7 @@ public abstract class AbstractWriteResponseHandler<T> implements RequestCallback
if (blockFor() + n > candidateReplicaCount())
signal();
if (hintOnFailure != null && StorageProxy.shouldHint(replicaPlan.lookup(from)))
if (hintOnFailure != null && StorageProxy.shouldHint(replicaPlan.lookup(from)) && requestTime.shouldSendHints())
StorageProxy.submitHint(hintOnFailure.get(), replicaPlan.lookup(from), null);
}
@ -320,7 +318,7 @@ public abstract class AbstractWriteResponseHandler<T> implements RequestCallback
}
else
{
replicaPlan.keyspace().metric.idealCLWriteLatency.addNano(nanoTime() - queryStartNanoTime);
replicaPlan.keyspace().metric.idealCLWriteLatency.addNano(nanoTime() - requestTime.startedAtNanos());
}
}
}
@ -354,7 +352,8 @@ public abstract class AbstractWriteResponseHandler<T> implements RequestCallback
writePerformer.apply(mutation, replicaPlan.withContacts(uncontacted),
(AbstractWriteResponseHandler<IMutation>) this,
localDC);
localDC,
requestTime);
}
}
catch (InterruptedException e)

View File

@ -25,6 +25,7 @@ import org.apache.cassandra.exceptions.WriteFailureException;
import org.apache.cassandra.exceptions.WriteTimeoutException;
import org.apache.cassandra.locator.InetAddressAndPort;
import org.apache.cassandra.net.Message;
import org.apache.cassandra.transport.Dispatcher;
public class BatchlogResponseHandler<T> extends AbstractWriteResponseHandler<T>
{
@ -34,9 +35,9 @@ public class BatchlogResponseHandler<T> extends AbstractWriteResponseHandler<T>
private static final AtomicIntegerFieldUpdater<BatchlogResponseHandler> requiredBeforeFinishUpdater
= AtomicIntegerFieldUpdater.newUpdater(BatchlogResponseHandler.class, "requiredBeforeFinish");
public BatchlogResponseHandler(AbstractWriteResponseHandler<T> wrapped, int requiredBeforeFinish, BatchlogCleanup cleanup, long queryStartNanoTime)
public BatchlogResponseHandler(AbstractWriteResponseHandler<T> wrapped, int requiredBeforeFinish, BatchlogCleanup cleanup, Dispatcher.RequestTime requestTime)
{
super(wrapped.replicaPlan, wrapped.callback, wrapped.writeType, null, queryStartNanoTime);
super(wrapped.replicaPlan, wrapped.callback, wrapped.writeType, null, requestTime);
this.wrapped = wrapped;
this.requiredBeforeFinish = requiredBeforeFinish;
this.cleanup = cleanup;

View File

@ -111,6 +111,12 @@ public class CassandraDaemon
return instance;
}
@VisibleForTesting
public NativeTransportService nativeTransportService()
{
return nativeTransportService;
}
static {
// Need to register metrics before instrumented appender is created(first access to LoggerFactory).
SharedMetricRegistries.getOrCreate("logback-metrics").addListener(new MetricRegistryListener.Base()
@ -818,12 +824,16 @@ public class CassandraDaemon
StorageService.instance.setRpcReady(true);
}
@Deprecated(since = "5.0.0")
public void stopNativeTransport()
{
stopNativeTransport(false);
}
public void stopNativeTransport(boolean force)
{
if (nativeTransportService != null)
{
nativeTransportService.stop();
}
nativeTransportService.stop(force);
}
public boolean isNativeTransportRunning()

View File

@ -31,6 +31,7 @@ import org.apache.cassandra.locator.ReplicaPlan;
import org.apache.cassandra.net.Message;
import org.apache.cassandra.db.ConsistencyLevel;
import org.apache.cassandra.db.WriteType;
import org.apache.cassandra.transport.Dispatcher;
/**
* This class blocks for a quorum of responses _in all datacenters_ (CL.EACH_QUORUM).
@ -46,10 +47,10 @@ public class DatacenterSyncWriteResponseHandler<T> extends AbstractWriteResponse
Runnable callback,
WriteType writeType,
Supplier<Mutation> hintOnFailure,
long queryStartNanoTime)
Dispatcher.RequestTime requestTime)
{
// Response is been managed by the map so make it 1 for the superclass.
super(replicaPlan, callback, writeType, hintOnFailure, queryStartNanoTime);
super(replicaPlan, callback, writeType, hintOnFailure, requestTime);
assert replicaPlan.consistencyLevel() == ConsistencyLevel.EACH_QUORUM;
if (replicaPlan.replicationStrategy() instanceof NetworkTopologyStrategy)

View File

@ -23,6 +23,7 @@ import org.apache.cassandra.locator.InOurDc;
import org.apache.cassandra.locator.InetAddressAndPort;
import org.apache.cassandra.locator.ReplicaPlan;
import org.apache.cassandra.net.Message;
import org.apache.cassandra.transport.Dispatcher;
import java.util.function.Predicate;
import java.util.function.Supplier;
@ -38,9 +39,9 @@ public class DatacenterWriteResponseHandler<T> extends WriteResponseHandler<T>
Runnable callback,
WriteType writeType,
Supplier<Mutation> hintOnFailure,
long queryStartNanoTime)
Dispatcher.RequestTime requestTime)
{
super(replicaPlan, callback, writeType, hintOnFailure, queryStartNanoTime);
super(replicaPlan, callback, writeType, hintOnFailure, requestTime);
assert replicaPlan.consistencyLevel().isDatacenterLocal();
}

View File

@ -139,7 +139,12 @@ public class NativeTransportService
*/
public void stop()
{
servers.forEach(Server::stop);
stop(false);
}
public void stop(boolean force)
{
servers.forEach((s) -> s.stop(force));
}
/**

View File

@ -21,6 +21,7 @@ import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@ -136,6 +137,7 @@ import org.apache.cassandra.service.reads.ReadCallback;
import org.apache.cassandra.service.reads.range.RangeCommands;
import org.apache.cassandra.service.reads.repair.ReadRepair;
import org.apache.cassandra.tracing.Tracing;
import org.apache.cassandra.transport.Dispatcher;
import org.apache.cassandra.triggers.TriggerExecutor;
import org.apache.cassandra.utils.Clock;
import org.apache.cassandra.utils.FBUtilities;
@ -220,10 +222,10 @@ public class StorageProxy implements StorageProxyMBean
MBeanWrapper.instance.registerMBean(instance, MBEAN_NAME);
HintsService.instance.registerMBean();
standardWritePerformer = (mutation, targets, responseHandler, localDataCenter) ->
standardWritePerformer = (mutation, targets, responseHandler, localDataCenter, requestTime) ->
{
assert mutation instanceof Mutation;
sendToHintedReplicas((Mutation) mutation, targets, responseHandler, localDataCenter, Stage.MUTATION);
sendToHintedReplicas((Mutation) mutation, targets, responseHandler, localDataCenter, Stage.MUTATION, requestTime);
};
/*
@ -232,19 +234,19 @@ public class StorageProxy implements StorageProxyMBean
* but on the latter case, the verb handler already run on the COUNTER_MUTATION stage, so we must not execute the
* underlying on the stage otherwise we risk a deadlock. Hence two different performer.
*/
counterWritePerformer = (mutation, targets, responseHandler, localDataCenter) ->
counterWritePerformer = (mutation, targets, responseHandler, localDataCenter, requestTime) ->
{
EndpointsForToken selected = targets.contacts().withoutSelf();
Replicas.temporaryAssertFull(selected); // TODO CASSANDRA-14548
counterWriteTask(mutation, targets.withContacts(selected), responseHandler, localDataCenter).run();
counterWriteTask(mutation, targets.withContacts(selected), responseHandler, localDataCenter, requestTime).run();
};
counterWriteOnCoordinatorPerformer = (mutation, targets, responseHandler, localDataCenter) ->
counterWriteOnCoordinatorPerformer = (mutation, targets, responseHandler, localDataCenter, requestTime) ->
{
EndpointsForToken selected = targets.contacts().withoutSelf();
Replicas.temporaryAssertFull(selected); // TODO CASSANDRA-14548
Stage.COUNTER_MUTATION.executor()
.execute(counterWriteTask(mutation, targets.withContacts(selected), responseHandler, localDataCenter));
.execute(counterWriteTask(mutation, targets.withContacts(selected), responseHandler, localDataCenter, requestTime));
};
@ -310,7 +312,7 @@ public class StorageProxy implements StorageProxyMBean
ConsistencyLevel consistencyForCommit,
ClientState clientState,
long nowInSeconds,
long queryStartNanoTime)
Dispatcher.RequestTime requestTime)
throws UnavailableException, IsBootstrappingException, RequestFailureException, RequestTimeoutException, InvalidRequestException, CasWriteUnknownResultException
{
if (DatabaseDescriptor.getPartitionDenylistEnabled() && DatabaseDescriptor.getDenylistWritesEnabled() && !partitionDenylist.isKeyPermitted(keyspaceName, cfName, key.getKey()))
@ -322,7 +324,7 @@ public class StorageProxy implements StorageProxyMBean
return Paxos.useV2()
? Paxos.cas(key, request, consistencyForPaxos, consistencyForCommit, clientState)
: legacyCas(keyspaceName, cfName, key, request, consistencyForPaxos, consistencyForCommit, clientState, nowInSeconds, queryStartNanoTime);
: legacyCas(keyspaceName, cfName, key, request, consistencyForPaxos, consistencyForCommit, clientState, nowInSeconds, requestTime);
}
public static RowIterator legacyCas(String keyspaceName,
@ -333,10 +335,9 @@ public class StorageProxy implements StorageProxyMBean
ConsistencyLevel consistencyForCommit,
ClientState clientState,
long nowInSeconds,
long queryStartNanoTime)
Dispatcher.RequestTime requestTime)
throws UnavailableException, IsBootstrappingException, RequestFailureException, RequestTimeoutException, InvalidRequestException
{
final long startTimeForMetrics = nanoTime();
try
{
TableMetadata metadata = Schema.instance.validateTable(keyspaceName, cfName);
@ -345,11 +346,11 @@ public class StorageProxy implements StorageProxyMBean
{
// read the current values and check they validate the conditions
Tracing.trace("Reading existing values for CAS precondition");
SinglePartitionReadCommand readCommand = (SinglePartitionReadCommand) request.readCommand(nowInSeconds);
SinglePartitionReadCommand readCommand = request.readCommand(nowInSeconds);
ConsistencyLevel readConsistency = consistencyForPaxos == ConsistencyLevel.LOCAL_SERIAL ? ConsistencyLevel.LOCAL_QUORUM : ConsistencyLevel.QUORUM;
FilteredPartition current;
try (RowIterator rowIter = readOne(readCommand, readConsistency, queryStartNanoTime))
try (RowIterator rowIter = readOne(readCommand, readConsistency, requestTime))
{
current = FilteredPartition.create(rowIter);
}
@ -388,7 +389,7 @@ public class StorageProxy implements StorageProxyMBean
consistencyForPaxos,
consistencyForCommit,
consistencyForCommit,
queryStartNanoTime,
requestTime,
casWriteMetrics,
updateProposer);
@ -430,7 +431,9 @@ public class StorageProxy implements StorageProxyMBean
}
finally
{
final long latency = nanoTime() - startTimeForMetrics;
// We track latency based on request processing time, since the amount of time that request spends in the queue
// is not a representative metric of replica performance.
long latency = nanoTime() - requestTime.startedAtNanos();
casWriteMetrics.addNano(latency);
writeMetricsForLevel(consistencyForPaxos).addNano(latency);
}
@ -466,7 +469,7 @@ public class StorageProxy implements StorageProxyMBean
* {@link ConsistencyLevel#LOCAL_SERIAL}).
* @param consistencyForReplayCommits the consistency for the commit phase of "replayed" in-progress operations.
* @param consistencyForCommit the consistency for the commit phase of _this_ operation update.
* @param queryStartNanoTime the nano time for the start of the query this is part of. This is the base time for
* @param requestTime the nano time for the start of the query this is part of. This is the base time for
* timeouts.
* @param casMetrics the metrics to update for this operation.
* @param createUpdateProposal method called after a successful 'prepare' phase to obtain 1) the actual update of
@ -481,7 +484,7 @@ public class StorageProxy implements StorageProxyMBean
ConsistencyLevel consistencyForPaxos,
ConsistencyLevel consistencyForReplayCommits,
ConsistencyLevel consistencyForCommit,
long queryStartNanoTime,
Dispatcher.RequestTime requestTime,
CASClientRequestMetrics casMetrics,
Function<Ballot, Pair<PartitionUpdate, RowIterator>> createUpdateProposal)
throws UnavailableException, IsBootstrappingException, RequestFailureException, RequestTimeoutException, InvalidRequestException
@ -496,12 +499,13 @@ public class StorageProxy implements StorageProxyMBean
consistencyForCommit.validateForCasCommit(latestRs);
long timeoutNanos = DatabaseDescriptor.getCasContentionTimeout(NANOSECONDS);
while (nanoTime() - queryStartNanoTime < timeoutNanos)
long deadline = requestTime.computeDeadline(timeoutNanos);
while (nanoTime() < deadline)
{
// for simplicity, we'll do a single liveness check at the start of each attempt
ReplicaPlan.ForPaxosWrite replicaPlan = ReplicaPlans.forPaxos(keyspace, key, consistencyForPaxos);
latestRs = replicaPlan.replicationStrategy();
PaxosBallotAndContention pair = beginAndRepairPaxos(queryStartNanoTime,
PaxosBallotAndContention pair = beginAndRepairPaxos(requestTime,
key,
metadata,
replicaPlan,
@ -519,14 +523,14 @@ public class StorageProxy implements StorageProxyMBean
Commit proposal = Commit.newProposal(ballot, proposalPair.left);
Tracing.trace("CAS precondition is met; proposing client-requested updates for {}", ballot);
if (proposePaxos(proposal, replicaPlan, true, queryStartNanoTime))
if (proposePaxos(proposal, replicaPlan, true, requestTime))
{
// We skip committing accepted updates when they are empty. This is an optimization which works
// because we also skip replaying those same empty update in beginAndRepairPaxos (see the longer
// comment there). As empty update are somewhat common (serial reads and non-applying CAS propose
// them), this is worth bothering.
if (!proposal.update.isEmpty())
commitPaxos(proposal, consistencyForCommit, true, queryStartNanoTime);
commitPaxos(proposal, consistencyForCommit, true, requestTime);
RowIterator result = proposalPair.right;
if (result != null)
Tracing.trace("CAS did not apply");
@ -569,7 +573,7 @@ public class StorageProxy implements StorageProxyMBean
* @return the Paxos ballot promised by the replicas if no in-progress requests were seen and a quorum of
* nodes have seen the mostRecentCommit. Otherwise, return null.
*/
private static PaxosBallotAndContention beginAndRepairPaxos(long queryStartNanoTime,
private static PaxosBallotAndContention beginAndRepairPaxos(Dispatcher.RequestTime requestTime,
DecoratedKey key,
TableMetadata metadata,
ReplicaPlan.ForPaxosWrite paxosPlan,
@ -582,7 +586,8 @@ public class StorageProxy implements StorageProxyMBean
PrepareCallback summary = null;
int contentions = 0;
while (nanoTime() - queryStartNanoTime < timeoutNanos)
long deadline = requestTime.computeDeadline(timeoutNanos);
while (nanoTime() < deadline)
{
// We want a timestamp that is guaranteed to be unique for that node (so that the ballot is globally unique), but if we've got a prepare rejected
// already we also want to make sure we pick a timestamp that has a chance to be promised, i.e. one that is greater that the most recently known
@ -598,7 +603,7 @@ public class StorageProxy implements StorageProxyMBean
{
Tracing.trace("Preparing {}", ballot);
Commit toPrepare = Commit.newPrepare(key, metadata, ballot);
summary = preparePaxos(toPrepare, paxosPlan, queryStartNanoTime);
summary = preparePaxos(toPrepare, paxosPlan, requestTime);
if (!summary.promised)
{
Tracing.trace("Some replicas have already promised a higher ballot than ours; aborting");
@ -634,9 +639,9 @@ public class StorageProxy implements StorageProxyMBean
Tracing.trace("Finishing incomplete paxos round {}", inProgress);
casMetrics.unfinishedCommit.inc();
Commit refreshedInProgress = Commit.newProposal(ballot, inProgress.update);
if (proposePaxos(refreshedInProgress, paxosPlan, false, queryStartNanoTime))
if (proposePaxos(refreshedInProgress, paxosPlan, false, requestTime))
{
commitPaxos(refreshedInProgress, consistencyForCommit, false, queryStartNanoTime);
commitPaxos(refreshedInProgress, consistencyForCommit, false, requestTime);
}
else
{
@ -686,10 +691,10 @@ public class StorageProxy implements StorageProxyMBean
MessagingService.instance().send(message, target);
}
private static PrepareCallback preparePaxos(Commit toPrepare, ReplicaPlan.ForPaxosWrite replicaPlan, long queryStartNanoTime)
private static PrepareCallback preparePaxos(Commit toPrepare, ReplicaPlan.ForPaxosWrite replicaPlan, Dispatcher.RequestTime requestTime)
throws WriteTimeoutException
{
PrepareCallback callback = new PrepareCallback(toPrepare.update.partitionKey(), toPrepare.update.metadata(), replicaPlan.requiredParticipants(), replicaPlan.consistencyLevel(), queryStartNanoTime);
PrepareCallback callback = new PrepareCallback(toPrepare.update.partitionKey(), toPrepare.update.metadata(), replicaPlan.requiredParticipants(), replicaPlan.consistencyLevel(), requestTime);
Message<Commit> message = Message.out(PAXOS_PREPARE_REQ, toPrepare);
boolean hasLocalRequest = false;
@ -730,10 +735,10 @@ public class StorageProxy implements StorageProxyMBean
* When {@param backoffIfPartial} is true, the proposer backs off when seeing the proposal being accepted by some but not a quorum.
* The result of the cooresponding CAS in uncertain as the accepted proposal may or may not be spread to other nodes in later rounds.
*/
private static boolean proposePaxos(Commit proposal, ReplicaPlan.ForPaxosWrite replicaPlan, boolean backoffIfPartial, long queryStartNanoTime)
private static boolean proposePaxos(Commit proposal, ReplicaPlan.ForPaxosWrite replicaPlan, boolean backoffIfPartial, Dispatcher.RequestTime requestTime)
throws WriteTimeoutException, CasWriteUnknownResultException
{
ProposeCallback callback = new ProposeCallback(replicaPlan.contacts().size(), replicaPlan.requiredParticipants(), !backoffIfPartial, replicaPlan.consistencyLevel(), queryStartNanoTime);
ProposeCallback callback = new ProposeCallback(replicaPlan.contacts().size(), replicaPlan.requiredParticipants(), !backoffIfPartial, replicaPlan.consistencyLevel(), requestTime);
Message<Commit> message = Message.out(PAXOS_PROPOSE_REQ, proposal);
for (Replica replica : replicaPlan.contacts())
{
@ -767,7 +772,7 @@ public class StorageProxy implements StorageProxyMBean
return false;
}
private static void commitPaxos(Commit proposal, ConsistencyLevel consistencyLevel, boolean allowHints, long queryStartNanoTime) throws WriteTimeoutException
private static void commitPaxos(Commit proposal, ConsistencyLevel consistencyLevel, boolean allowHints, Dispatcher.RequestTime requestTime) throws WriteTimeoutException
{
boolean shouldBlock = consistencyLevel != ConsistencyLevel.ANY;
Keyspace keyspace = Keyspace.open(proposal.update.metadata().keyspace);
@ -780,7 +785,7 @@ public class StorageProxy implements StorageProxyMBean
if (shouldBlock)
{
AbstractReplicationStrategy rs = replicaPlan.replicationStrategy();
responseHandler = rs.getWriteResponseHandler(replicaPlan, null, WriteType.SIMPLE, proposal::makeMutation, queryStartNanoTime);
responseHandler = rs.getWriteResponseHandler(replicaPlan, null, WriteType.SIMPLE, proposal::makeMutation, requestTime);
}
Message<Commit> message = Message.outWithFlag(PAXOS_COMMIT_REQ, proposal, MessageFlag.CALL_BACK_ON_FAILURE);
@ -794,7 +799,7 @@ public class StorageProxy implements StorageProxyMBean
if (shouldBlock)
{
if (replica.isSelf())
commitPaxosLocal(replica, message, responseHandler);
commitPaxosLocal(replica, message, responseHandler, requestTime);
else
MessagingService.instance().sendWriteWithCallback(message, replica, responseHandler);
}
@ -825,9 +830,9 @@ public class StorageProxy implements StorageProxyMBean
* submit a fake one that executes immediately on the mutation stage, but generates the necessary backpressure
* signal for hints
*/
private static void commitPaxosLocal(Replica localReplica, final Message<Commit> message, final AbstractWriteResponseHandler<?> responseHandler)
private static void commitPaxosLocal(Replica localReplica, final Message<Commit> message, final AbstractWriteResponseHandler<?> responseHandler, Dispatcher.RequestTime requestTime)
{
PAXOS_COMMIT_REQ.stage.maybeExecuteImmediately(new LocalMutationRunnable(localReplica)
PAXOS_COMMIT_REQ.stage.maybeExecuteImmediately(new LocalMutationRunnable(localReplica, requestTime)
{
public void runMayThrow()
{
@ -867,16 +872,14 @@ public class StorageProxy implements StorageProxyMBean
*
* @param mutations the mutations to be applied across the replicas
* @param consistencyLevel the consistency level for the operation
* @param queryStartNanoTime the value of nanoTime() when the query started to be processed
* @param requestTime object holding times when request got enqueued and started execution
*/
public static void mutate(List<? extends IMutation> mutations, ConsistencyLevel consistencyLevel, long queryStartNanoTime)
public static void mutate(List<? extends IMutation> mutations, ConsistencyLevel consistencyLevel, Dispatcher.RequestTime requestTime)
throws UnavailableException, OverloadedException, WriteTimeoutException, WriteFailureException
{
Tracing.trace("Determining replicas for mutation");
final String localDataCenter = DatabaseDescriptor.getEndpointSnitch().getLocalDatacenter();
long startTime = nanoTime();
List<AbstractWriteResponseHandler<IMutation>> responseHandlers = new ArrayList<>(mutations.size());
WriteType plainWriteType = mutations.size() <= 1 ? WriteType.SIMPLE : WriteType.UNLOGGED_BATCH;
@ -885,9 +888,9 @@ public class StorageProxy implements StorageProxyMBean
for (IMutation mutation : mutations)
{
if (mutation instanceof CounterMutation)
responseHandlers.add(mutateCounter((CounterMutation)mutation, localDataCenter, queryStartNanoTime));
responseHandlers.add(mutateCounter((CounterMutation)mutation, localDataCenter, requestTime));
else
responseHandlers.add(performWrite(mutation, consistencyLevel, localDataCenter, standardWritePerformer, null, plainWriteType, queryStartNanoTime));
responseHandlers.add(performWrite(mutation, consistencyLevel, localDataCenter, standardWritePerformer, null, plainWriteType, requestTime));
}
// upgrade to full quorum any failed cheap quorums
@ -943,7 +946,9 @@ public class StorageProxy implements StorageProxyMBean
}
finally
{
long latency = nanoTime() - startTime;
// We track latency based on request processing time, since the amount of time that request spends in the queue
// is not a representative metric of replica performance.
long latency = nanoTime() - requestTime.startedAtNanos();
writeMetrics.addNano(latency);
writeMetricsForLevel(consistencyLevel).addNano(latency);
updateCoordinatorWriteLatencyTableMetric(mutations, latency);
@ -999,9 +1004,9 @@ public class StorageProxy implements StorageProxyMBean
* @param mutations the mutations to be applied across the replicas
* @param writeCommitLog if commitlog should be written
* @param baseComplete time from epoch in ms that the local base mutation was(or will be) completed
* @param queryStartNanoTime the value of nanoTime() when the query started to be processed
* @param requestTime object holding times when request got enqueued and started execution
*/
public static void mutateMV(ByteBuffer dataKey, Collection<Mutation> mutations, boolean writeCommitLog, AtomicLong baseComplete, long queryStartNanoTime)
public static void mutateMV(ByteBuffer dataKey, Collection<Mutation> mutations, boolean writeCommitLog, AtomicLong baseComplete, Dispatcher.RequestTime requestTime)
throws UnavailableException, OverloadedException, WriteTimeoutException
{
Tracing.trace("Determining replicas for mutation");
@ -1032,7 +1037,7 @@ public class StorageProxy implements StorageProxyMBean
//Since the base -> view replication is 1:1 we only need to store the BL locally
ReplicaPlan.ForWrite replicaPlan = ReplicaPlans.forLocalBatchlogWrite();
BatchlogCleanup cleanup = new BatchlogCleanup(mutations.size(),
() -> asyncRemoveFromBatchlog(replicaPlan, batchUUID));
() -> asyncRemoveFromBatchlog(replicaPlan, batchUUID, requestTime));
// add a handler for each mutation - includes checking availability, but doesn't initiate any writes, yet
for (Mutation mutation : mutations)
@ -1087,7 +1092,7 @@ public class StorageProxy implements StorageProxyMBean
baseComplete,
WriteType.BATCH,
cleanup,
queryStartNanoTime));
requestTime));
}
}
@ -1097,7 +1102,7 @@ public class StorageProxy implements StorageProxyMBean
// Perform remote writes
if (!wrappers.isEmpty())
asyncWriteBatchedMutations(wrappers, localDataCenter, Stage.VIEW_MUTATION);
asyncWriteBatchedMutations(wrappers, localDataCenter, Stage.VIEW_MUTATION, requestTime);
}
}
finally
@ -1110,7 +1115,7 @@ public class StorageProxy implements StorageProxyMBean
public static void mutateWithTriggers(List<? extends IMutation> mutations,
ConsistencyLevel consistencyLevel,
boolean mutateAtomically,
long queryStartNanoTime)
Dispatcher.RequestTime requestTime)
throws WriteTimeoutException, WriteFailureException, UnavailableException, OverloadedException, InvalidRequestException
{
if (DatabaseDescriptor.getPartitionDenylistEnabled() && DatabaseDescriptor.getDenylistWritesEnabled())
@ -1143,13 +1148,13 @@ public class StorageProxy implements StorageProxyMBean
writeMetricsForLevel(consistencyLevel).mutationSize.update(size);
if (augmented != null)
mutateAtomically(augmented, consistencyLevel, updatesView, queryStartNanoTime);
mutateAtomically(augmented, consistencyLevel, updatesView, requestTime);
else
{
if (mutateAtomically || updatesView)
mutateAtomically((Collection<Mutation>) mutations, consistencyLevel, updatesView, queryStartNanoTime);
mutateAtomically((Collection<Mutation>) mutations, consistencyLevel, updatesView, requestTime);
else
mutate(mutations, consistencyLevel, queryStartNanoTime);
mutate(mutations, consistencyLevel, requestTime);
}
}
@ -1162,12 +1167,12 @@ public class StorageProxy implements StorageProxyMBean
* @param mutations the Mutations to be applied across the replicas
* @param consistency_level the consistency level for the operation
* @param requireQuorumForRemove at least a quorum of nodes will see update before deleting batchlog
* @param queryStartNanoTime the value of nanoTime() when the query started to be processed
* @param requestTime object holding times when request got enqueued and started execution
*/
public static void mutateAtomically(Collection<Mutation> mutations,
ConsistencyLevel consistency_level,
boolean requireQuorumForRemove,
long queryStartNanoTime)
Dispatcher.RequestTime requestTime)
throws UnavailableException, OverloadedException, WriteTimeoutException
{
Tracing.trace("Determining replicas for atomic batch");
@ -1198,7 +1203,7 @@ public class StorageProxy implements StorageProxyMBean
final TimeUUID batchUUID = nextTimeUUID();
BatchlogCleanup cleanup = new BatchlogCleanup(mutations.size(),
() -> asyncRemoveFromBatchlog(replicaPlan, batchUUID));
() -> asyncRemoveFromBatchlog(replicaPlan, batchUUID, requestTime));
// add a handler for each mutation - includes checking availability, but doesn't initiate any writes, yet
for (Mutation mutation : mutations)
@ -1208,16 +1213,16 @@ public class StorageProxy implements StorageProxyMBean
batchConsistencyLevel,
WriteType.BATCH,
cleanup,
queryStartNanoTime);
requestTime);
// exit early if we can't fulfill the CL at this time.
wrappers.add(wrapper);
}
// write to the batchlog
syncWriteToBatchlog(mutations, replicaPlan, batchUUID, queryStartNanoTime);
syncWriteToBatchlog(mutations, replicaPlan, batchUUID, requestTime);
// now actually perform the writes and wait for them to complete
syncWriteBatchedMutations(wrappers, Stage.MUTATION);
syncWriteBatchedMutations(wrappers, Stage.MUTATION, requestTime);
}
catch (UnavailableException e)
{
@ -1271,13 +1276,13 @@ public class StorageProxy implements StorageProxyMBean
}
}
private static void syncWriteToBatchlog(Collection<Mutation> mutations, ReplicaPlan.ForWrite replicaPlan, TimeUUID uuid, long queryStartNanoTime)
private static void syncWriteToBatchlog(Collection<Mutation> mutations, ReplicaPlan.ForWrite replicaPlan, TimeUUID uuid, Dispatcher.RequestTime requestTime)
throws WriteTimeoutException, WriteFailureException
{
WriteResponseHandler<?> handler = new WriteResponseHandler(replicaPlan,
WriteType.BATCH_LOG,
null,
queryStartNanoTime);
WriteResponseHandler<?> handler = new WriteResponseHandler<>(replicaPlan,
WriteType.BATCH_LOG,
null,
requestTime);
Batch batch = Batch.createLocal(uuid, FBUtilities.timestampMicros(), mutations);
Message<Batch> message = Message.out(BATCH_STORE_REQ, batch);
@ -1286,14 +1291,14 @@ public class StorageProxy implements StorageProxyMBean
logger.trace("Sending batchlog store request {} to {} for {} mutations", batch.id, replica, batch.size());
if (replica.isSelf())
performLocally(Stage.MUTATION, replica, () -> BatchlogManager.store(batch), handler, "Batchlog store");
performLocally(Stage.MUTATION, replica, () -> BatchlogManager.store(batch), handler, "Batchlog store", requestTime);
else
MessagingService.instance().sendWithCallback(message, replica.endpoint(), handler);
}
handler.get();
}
private static void asyncRemoveFromBatchlog(ReplicaPlan.ForWrite replicaPlan, TimeUUID uuid)
private static void asyncRemoveFromBatchlog(ReplicaPlan.ForWrite replicaPlan, TimeUUID uuid, Dispatcher.RequestTime requestTime)
{
Message<TimeUUID> message = Message.out(Verb.BATCH_REMOVE_REQ, uuid);
for (Replica target : replicaPlan.contacts())
@ -1302,13 +1307,13 @@ public class StorageProxy implements StorageProxyMBean
logger.trace("Sending batchlog remove request {} to {}", uuid, target);
if (target.isSelf())
performLocally(Stage.MUTATION, target, () -> BatchlogManager.remove(uuid), "Batchlog remove");
performLocally(Stage.MUTATION, target, () -> BatchlogManager.remove(uuid), "Batchlog remove", requestTime);
else
MessagingService.instance().send(message, target.endpoint());
}
}
private static void asyncWriteBatchedMutations(List<WriteResponseHandlerWrapper> wrappers, String localDataCenter, Stage stage)
private static void asyncWriteBatchedMutations(List<WriteResponseHandlerWrapper> wrappers, String localDataCenter, Stage stage, Dispatcher.RequestTime requestTime)
{
for (WriteResponseHandlerWrapper wrapper : wrappers)
{
@ -1317,7 +1322,7 @@ public class StorageProxy implements StorageProxyMBean
try
{
sendToHintedReplicas(wrapper.mutation, replicas, wrapper.handler, localDataCenter, stage);
sendToHintedReplicas(wrapper.mutation, replicas, wrapper.handler, localDataCenter, stage, requestTime);
}
catch (OverloadedException | WriteTimeoutException e)
{
@ -1326,7 +1331,7 @@ public class StorageProxy implements StorageProxyMBean
}
}
private static void syncWriteBatchedMutations(List<WriteResponseHandlerWrapper> wrappers, Stage stage)
private static void syncWriteBatchedMutations(List<WriteResponseHandlerWrapper> wrappers, Stage stage, Dispatcher.RequestTime requestTime)
throws WriteTimeoutException, OverloadedException
{
String localDataCenter = DatabaseDescriptor.getEndpointSnitch().getLocalDatacenter();
@ -1335,7 +1340,7 @@ public class StorageProxy implements StorageProxyMBean
{
EndpointsForToken sendTo = wrapper.handler.replicaPlan.liveAndDown();
Replicas.temporaryAssertFull(sendTo); // TODO: CASSANDRA-14549
sendToHintedReplicas(wrapper.mutation, wrapper.handler.replicaPlan.withContacts(sendTo), wrapper.handler, localDataCenter, stage);
sendToHintedReplicas(wrapper.mutation, wrapper.handler.replicaPlan.withContacts(sendTo), wrapper.handler, localDataCenter, stage, requestTime);
}
for (WriteResponseHandlerWrapper wrapper : wrappers)
@ -1354,7 +1359,7 @@ public class StorageProxy implements StorageProxyMBean
* given the list of write endpoints (either standardWritePerformer for
* standard writes or counterWritePerformer for counter writes).
* @param callback an optional callback to be run if and when the write is
* @param queryStartNanoTime the value of nanoTime() when the query started to be processed
* @param requestTime object holding times when request got enqueued and started execution
*/
public static AbstractWriteResponseHandler<IMutation> performWrite(IMutation mutation,
ConsistencyLevel consistencyLevel,
@ -1362,7 +1367,7 @@ public class StorageProxy implements StorageProxyMBean
WritePerformer performer,
Runnable callback,
WriteType writeType,
long queryStartNanoTime)
Dispatcher.RequestTime requestTime)
{
String keyspaceName = mutation.getKeyspaceName();
Keyspace keyspace = Keyspace.open(keyspaceName);
@ -1376,9 +1381,9 @@ public class StorageProxy implements StorageProxyMBean
writeMetrics.remoteRequests.mark();
AbstractReplicationStrategy rs = replicaPlan.replicationStrategy();
AbstractWriteResponseHandler<IMutation> responseHandler = rs.getWriteResponseHandler(replicaPlan, callback, writeType, mutation.hintOnFailure(), queryStartNanoTime);
AbstractWriteResponseHandler<IMutation> responseHandler = rs.getWriteResponseHandler(replicaPlan, callback, writeType, mutation.hintOnFailure(), requestTime);
performer.apply(mutation, replicaPlan, responseHandler, localDataCenter);
performer.apply(mutation, replicaPlan, responseHandler, localDataCenter, requestTime);
return responseHandler;
}
@ -1388,7 +1393,7 @@ public class StorageProxy implements StorageProxyMBean
ConsistencyLevel batchConsistencyLevel,
WriteType writeType,
BatchlogResponseHandler.BatchlogCleanup cleanup,
long queryStartNanoTime)
Dispatcher.RequestTime requestTime)
{
Keyspace keyspace = Keyspace.open(mutation.getKeyspaceName());
Token tk = mutation.key().getToken();
@ -1401,8 +1406,8 @@ public class StorageProxy implements StorageProxyMBean
writeMetrics.remoteRequests.mark();
AbstractReplicationStrategy rs = replicaPlan.replicationStrategy();
AbstractWriteResponseHandler<IMutation> writeHandler = rs.getWriteResponseHandler(replicaPlan, null, writeType, mutation, queryStartNanoTime);
BatchlogResponseHandler<IMutation> batchHandler = new BatchlogResponseHandler<>(writeHandler, batchConsistencyLevel.blockFor(rs), cleanup, queryStartNanoTime);
AbstractWriteResponseHandler<IMutation> writeHandler = rs.getWriteResponseHandler(replicaPlan, null, writeType, mutation, requestTime);
BatchlogResponseHandler<IMutation> batchHandler = new BatchlogResponseHandler<>(writeHandler, batchConsistencyLevel.blockFor(rs), cleanup, requestTime);
return new WriteResponseHandlerWrapper(batchHandler, mutation);
}
@ -1417,7 +1422,7 @@ public class StorageProxy implements StorageProxyMBean
AtomicLong baseComplete,
WriteType writeType,
BatchlogResponseHandler.BatchlogCleanup cleanup,
long queryStartNanoTime)
Dispatcher.RequestTime requestTime)
{
Keyspace keyspace = Keyspace.open(mutation.getKeyspaceName());
ReplicaPlan.ForWrite replicaPlan = ReplicaPlans.forWrite(keyspace, consistencyLevel, liveAndDown, ReplicaPlans.writeAll);
@ -1425,8 +1430,8 @@ public class StorageProxy implements StorageProxyMBean
AbstractWriteResponseHandler<IMutation> writeHandler = replicationStrategy.getWriteResponseHandler(replicaPlan, () -> {
long delay = Math.max(0, currentTimeMillis() - baseComplete.get());
viewWriteMetrics.viewWriteLatency.update(delay, MILLISECONDS);
}, writeType, mutation, queryStartNanoTime);
BatchlogResponseHandler<IMutation> batchHandler = new ViewWriteMetricsWrapped(writeHandler, batchConsistencyLevel.blockFor(replicationStrategy), cleanup, queryStartNanoTime);
}, writeType, mutation, requestTime);
BatchlogResponseHandler<IMutation> batchHandler = new ViewWriteMetricsWrapped(writeHandler, batchConsistencyLevel.blockFor(replicationStrategy), cleanup, requestTime);
return new WriteResponseHandlerWrapper(batchHandler, mutation);
}
@ -1464,7 +1469,8 @@ public class StorageProxy implements StorageProxyMBean
ReplicaPlan.ForWrite plan,
AbstractWriteResponseHandler<IMutation> responseHandler,
String localDataCenter,
Stage stage)
Stage stage,
Dispatcher.RequestTime requestTime)
throws OverloadedException
{
// this dc replicas:
@ -1502,7 +1508,12 @@ public class StorageProxy implements StorageProxyMBean
{
// belongs on a different server
if (message == null)
message = Message.outWithFlag(MUTATION_REQ, mutation, MessageFlag.CALL_BACK_ON_FAILURE);
{
message = Message.outWithFlags(MUTATION_REQ,
mutation,
requestTime,
Collections.singletonList(MessageFlag.CALL_BACK_ON_FAILURE));
}
String dc = DatabaseDescriptor.getEndpointSnitch().getDatacenter(destination);
@ -1542,13 +1553,13 @@ public class StorageProxy implements StorageProxyMBean
}
}
if (endpointsToHint != null)
if (endpointsToHint != null && requestTime.shouldSendHints())
submitHint(mutation, EndpointsForToken.copyOf(mutation.key().getToken(), endpointsToHint), responseHandler);
if (insertLocal)
{
Preconditions.checkNotNull(localReplica);
performLocally(stage, localReplica, mutation::apply, responseHandler, mutation);
performLocally(stage, localReplica, mutation::apply, responseHandler, mutation, requestTime);
}
if (localDc != null)
@ -1623,9 +1634,9 @@ public class StorageProxy implements StorageProxyMBean
return select.get(ThreadLocalRandom.current().nextInt(0, select.size()));
}
private static void performLocally(Stage stage, Replica localReplica, final Runnable runnable, String description)
private static void performLocally(Stage stage, Replica localReplica, final Runnable runnable, String description, Dispatcher.RequestTime requestTime)
{
stage.maybeExecuteImmediately(new LocalMutationRunnable(localReplica)
stage.maybeExecuteImmediately(new LocalMutationRunnable(localReplica, requestTime)
{
public void runMayThrow()
{
@ -1653,9 +1664,9 @@ public class StorageProxy implements StorageProxyMBean
});
}
private static void performLocally(Stage stage, Replica localReplica, final Runnable runnable, final RequestCallback<?> handler, Object description)
private static void performLocally(Stage stage, Replica localReplica, final Runnable runnable, final RequestCallback<?> handler, Object description, Dispatcher.RequestTime requestTime)
{
stage.maybeExecuteImmediately(new LocalMutationRunnable(localReplica)
stage.maybeExecuteImmediately(new LocalMutationRunnable(localReplica, requestTime)
{
public void runMayThrow()
{
@ -1702,13 +1713,13 @@ public class StorageProxy implements StorageProxyMBean
* quicker response and because the WriteResponseHandlers don't make it easy to send back an error. We also always gather
* the write latencies at the coordinator node to make gathering point similar to the case of standard writes.
*/
public static AbstractWriteResponseHandler<IMutation> mutateCounter(CounterMutation cm, String localDataCenter, long queryStartNanoTime) throws UnavailableException, OverloadedException
public static AbstractWriteResponseHandler<IMutation> mutateCounter(CounterMutation cm, String localDataCenter, Dispatcher.RequestTime requestTime) throws UnavailableException, OverloadedException
{
Replica replica = findSuitableReplica(cm.getKeyspaceName(), cm.key(), localDataCenter, cm.consistency());
if (replica.isSelf())
{
return applyCounterMutationOnCoordinator(cm, localDataCenter, queryStartNanoTime);
return applyCounterMutationOnCoordinator(cm, localDataCenter, requestTime);
}
else
{
@ -1727,7 +1738,7 @@ public class StorageProxy implements StorageProxyMBean
// Forward the actual update to the chosen leader replica
AbstractWriteResponseHandler<IMutation> responseHandler = new WriteResponseHandler<>(ReplicaPlans.forForwardingCounterWrite(keyspace, tk, replica),
WriteType.COUNTER, null, queryStartNanoTime);
WriteType.COUNTER, null, requestTime);
Tracing.trace("Enqueuing counter update to {}", replica);
Message message = Message.outWithFlag(Verb.COUNTER_MUTATION_REQ, cm, MessageFlag.CALL_BACK_ON_FAILURE);
@ -1785,26 +1796,27 @@ public class StorageProxy implements StorageProxyMBean
// Must be called on a replica of the mutation. This replica becomes the
// leader of this mutation.
public static AbstractWriteResponseHandler<IMutation> applyCounterMutationOnLeader(CounterMutation cm, String localDataCenter, Runnable callback, long queryStartNanoTime)
public static AbstractWriteResponseHandler<IMutation> applyCounterMutationOnLeader(CounterMutation cm, String localDataCenter, Runnable callback, Dispatcher.RequestTime requestTime)
throws UnavailableException, OverloadedException
{
return performWrite(cm, cm.consistency(), localDataCenter, counterWritePerformer, callback, WriteType.COUNTER, queryStartNanoTime);
return performWrite(cm, cm.consistency(), localDataCenter, counterWritePerformer, callback, WriteType.COUNTER, requestTime);
}
// Same as applyCounterMutationOnLeader but must with the difference that it use the MUTATION stage to execute the write (while
// applyCounterMutationOnLeader assumes it is on the MUTATION stage already)
public static AbstractWriteResponseHandler<IMutation> applyCounterMutationOnCoordinator(CounterMutation cm, String localDataCenter, long queryStartNanoTime)
public static AbstractWriteResponseHandler<IMutation> applyCounterMutationOnCoordinator(CounterMutation cm, String localDataCenter, Dispatcher.RequestTime requestTime)
throws UnavailableException, OverloadedException
{
return performWrite(cm, cm.consistency(), localDataCenter, counterWriteOnCoordinatorPerformer, null, WriteType.COUNTER, queryStartNanoTime);
return performWrite(cm, cm.consistency(), localDataCenter, counterWriteOnCoordinatorPerformer, null, WriteType.COUNTER, requestTime);
}
private static Runnable counterWriteTask(final IMutation mutation,
final ReplicaPlan.ForWrite replicaPlan,
final AbstractWriteResponseHandler<IMutation> responseHandler,
final String localDataCenter)
final String localDataCenter,
final Dispatcher.RequestTime requestTime)
{
return new DroppableRunnable(Verb.COUNTER_MUTATION_REQ)
return new DroppableRunnable(Verb.COUNTER_MUTATION_REQ, requestTime)
{
@Override
public void runMayThrow() throws OverloadedException, WriteTimeoutException
@ -1813,7 +1825,7 @@ public class StorageProxy implements StorageProxyMBean
Mutation result = ((CounterMutation) mutation).applyCounterMutation();
responseHandler.onResponse(null);
sendToHintedReplicas(result, replicaPlan, responseHandler, localDataCenter, Stage.COUNTER_MUTATION);
sendToHintedReplicas(result, replicaPlan, responseHandler, localDataCenter, Stage.COUNTER_MUTATION, requestTime);
}
};
}
@ -1826,17 +1838,17 @@ public class StorageProxy implements StorageProxyMBean
return true;
}
public static RowIterator readOne(SinglePartitionReadCommand command, ConsistencyLevel consistencyLevel, long queryStartNanoTime)
public static RowIterator readOne(SinglePartitionReadCommand command, ConsistencyLevel consistencyLevel, Dispatcher.RequestTime requestTime)
throws UnavailableException, IsBootstrappingException, ReadFailureException, ReadTimeoutException, InvalidRequestException
{
return PartitionIterators.getOnlyElement(read(SinglePartitionReadCommand.Group.one(command), consistencyLevel, queryStartNanoTime), command);
return PartitionIterators.getOnlyElement(read(SinglePartitionReadCommand.Group.one(command), consistencyLevel, requestTime), command);
}
/**
* Performs the actual reading of a row out of the StorageService, fetching
* a specific set of column names from a given column family.
*/
public static PartitionIterator read(SinglePartitionReadCommand.Group group, ConsistencyLevel consistencyLevel, long queryStartNanoTime)
public static PartitionIterator read(SinglePartitionReadCommand.Group group, ConsistencyLevel consistencyLevel, Dispatcher.RequestTime requestTime)
throws UnavailableException, IsBootstrappingException, ReadFailureException, ReadTimeoutException, InvalidRequestException
{
if (!isSafeToPerformRead(group.queries))
@ -1862,8 +1874,8 @@ public class StorageProxy implements StorageProxyMBean
}
return consistencyLevel.isSerialConsistency()
? readWithPaxos(group, consistencyLevel, queryStartNanoTime)
: readRegular(group, consistencyLevel, queryStartNanoTime);
? readWithPaxos(group, consistencyLevel, requestTime)
: readRegular(group, consistencyLevel, requestTime);
}
public static boolean isSafeToPerformRead(List<SinglePartitionReadCommand> queries)
@ -1876,21 +1888,20 @@ public class StorageProxy implements StorageProxyMBean
return !StorageService.instance.isBootstrapMode();
}
private static PartitionIterator readWithPaxos(SinglePartitionReadCommand.Group group, ConsistencyLevel consistencyLevel, long queryStartNanoTime)
private static PartitionIterator readWithPaxos(SinglePartitionReadCommand.Group group, ConsistencyLevel consistencyLevel, Dispatcher.RequestTime requestTime)
throws InvalidRequestException, UnavailableException, ReadFailureException, ReadTimeoutException
{
return Paxos.useV2()
? Paxos.read(group, consistencyLevel)
: legacyReadWithPaxos(group, consistencyLevel, queryStartNanoTime);
? Paxos.read(group, consistencyLevel, requestTime)
: legacyReadWithPaxos(group, consistencyLevel, requestTime);
}
private static PartitionIterator legacyReadWithPaxos(SinglePartitionReadCommand.Group group, ConsistencyLevel consistencyLevel, long queryStartNanoTime)
private static PartitionIterator legacyReadWithPaxos(SinglePartitionReadCommand.Group group, ConsistencyLevel consistencyLevel, Dispatcher.RequestTime requestTime)
throws InvalidRequestException, UnavailableException, ReadFailureException, ReadTimeoutException
{
if (group.queries.size() > 1)
throw new InvalidRequestException("SERIAL/LOCAL_SERIAL consistency may only be requested for one partition at a time");
long start = nanoTime();
SinglePartitionReadCommand command = group.queries.get(0);
TableMetadata metadata = command.metadata();
DecoratedKey key = command.partitionKey();
@ -1921,7 +1932,7 @@ public class StorageProxy implements StorageProxyMBean
consistencyLevel,
consistencyForReplayCommitsOrFetch,
ConsistencyLevel.ANY,
start,
requestTime,
casReadMetrics,
updateProposer);
}
@ -1934,7 +1945,7 @@ public class StorageProxy implements StorageProxyMBean
throw new ReadFailureException(consistencyLevel, e.received, e.blockFor, false, e.failureReasonByEndpoint);
}
result = fetchRows(group.queries, consistencyForReplayCommitsOrFetch, queryStartNanoTime);
result = fetchRows(group.queries, consistencyForReplayCommitsOrFetch, requestTime);
}
catch (UnavailableException e)
{
@ -1968,7 +1979,9 @@ public class StorageProxy implements StorageProxyMBean
}
finally
{
long latency = nanoTime() - start;
// We track latency based on request processing time, since the amount of time that request spends in the queue
// is not a representative metric of replica performance.
long latency = nanoTime() - requestTime.startedAtNanos();
readMetrics.addNano(latency);
casReadMetrics.addNano(latency);
readMetricsForLevel(consistencyLevel).addNano(latency);
@ -1978,13 +1991,13 @@ public class StorageProxy implements StorageProxyMBean
return result;
}
private static PartitionIterator readRegular(SinglePartitionReadCommand.Group group, ConsistencyLevel consistencyLevel, long queryStartNanoTime)
@SuppressWarnings("resource")
private static PartitionIterator readRegular(SinglePartitionReadCommand.Group group, ConsistencyLevel consistencyLevel, Dispatcher.RequestTime requestTime)
throws UnavailableException, ReadFailureException, ReadTimeoutException
{
long start = nanoTime();
try
{
PartitionIterator result = fetchRows(group.queries, consistencyLevel, queryStartNanoTime);
PartitionIterator result = fetchRows(group.queries, consistencyLevel, requestTime);
// Note that the only difference between the command in a group must be the partition key on which
// they applied.
boolean enforceStrictLiveness = group.queries.get(0).metadata().enforceStrictLiveness();
@ -2021,7 +2034,9 @@ public class StorageProxy implements StorageProxyMBean
}
finally
{
long latency = nanoTime() - start;
// We track latency based on request processing time, since the amount of time that request spends in the queue
// is not a representative metric of replica performance.
long latency = nanoTime() - requestTime.startedAtNanos();
readMetrics.addNano(latency);
readMetricsForLevel(consistencyLevel).addNano(latency);
// TODO avoid giving every command the same latency number. Can fix this in CASSADRA-5329
@ -2075,7 +2090,9 @@ public class StorageProxy implements StorageProxyMBean
* 4. If the digests (if any) match the data return the data
* 5. else carry out read repair by getting data from all the nodes.
*/
private static PartitionIterator fetchRows(List<SinglePartitionReadCommand> commands, ConsistencyLevel consistencyLevel, long queryStartNanoTime)
private static PartitionIterator fetchRows(List<SinglePartitionReadCommand> commands,
ConsistencyLevel consistencyLevel,
Dispatcher.RequestTime requestTime)
throws UnavailableException, ReadFailureException, ReadTimeoutException
{
int cmdCount = commands.size();
@ -2086,7 +2103,9 @@ public class StorageProxy implements StorageProxyMBean
// for type of speculation we'll use in this read
for (int i=0; i<cmdCount; i++)
{
reads[i] = AbstractReadExecutor.getReadExecutor(commands.get(i), consistencyLevel, queryStartNanoTime);
reads[i] = AbstractReadExecutor.getReadExecutor(commands.get(i),
consistencyLevel,
requestTime);
if (reads[i].hasLocalRead())
readMetrics.localRequests.mark();
@ -2149,14 +2168,14 @@ public class StorageProxy implements StorageProxyMBean
private final ReadCallback handler;
private final boolean trackRepairedStatus;
public LocalReadRunnable(ReadCommand command, ReadCallback handler)
public LocalReadRunnable(ReadCommand command, ReadCallback handler, Dispatcher.RequestTime requestTime)
{
this(command, handler, false);
this(command, handler, requestTime, false);
}
public LocalReadRunnable(ReadCommand command, ReadCallback handler, boolean trackRepairedStatus)
public LocalReadRunnable(ReadCommand command, ReadCallback handler, Dispatcher.RequestTime requestTime, boolean trackRepairedStatus)
{
super(Verb.READ_REQ);
super(Verb.READ_REQ, requestTime);
this.command = command;
this.handler = handler;
this.trackRepairedStatus = trackRepairedStatus;
@ -2169,7 +2188,8 @@ public class StorageProxy implements StorageProxyMBean
MessageParams.reset();
boolean readRejected = false;
command.setMonitoringTime(approxCreationTimeNanos, false, verb.expiresAfterNanos(), DatabaseDescriptor.getSlowQueryTimeout(NANOSECONDS));
long deadline = requestTime.computeDeadline(verb.expiresAfterNanos());
command.setMonitoringTime(requestTime.startedAtNanos(), false, deadline - requestTime.startedAtNanos(), DatabaseDescriptor.getSlowQueryTimeout(NANOSECONDS));
ReadResponse response;
try (ReadExecutionController controller = command.executionController(trackRepairedStatus);
@ -2198,12 +2218,14 @@ public class StorageProxy implements StorageProxyMBean
}
else
{
MessagingService.instance().metrics.recordSelfDroppedMessage(verb, MonotonicClock.Global.approxTime.now() - approxCreationTimeNanos, NANOSECONDS);
// We track latency based on request processing time
MessagingService.instance().metrics.recordSelfDroppedMessage(verb, MonotonicClock.Global.preciseTime.now() - requestTime.startedAtNanos(), NANOSECONDS);
handler.onFailure(FBUtilities.getBroadcastAddressAndPort(), RequestFailureReason.UNKNOWN);
}
if (!readRejected)
MessagingService.instance().latencySubscribers.add(FBUtilities.getBroadcastAddressAndPort(), MonotonicClock.Global.approxTime.now() - approxCreationTimeNanos, NANOSECONDS);
// We track latency based on request processing time
MessagingService.instance().latencySubscribers.add(FBUtilities.getBroadcastAddressAndPort(), MonotonicClock.Global.preciseTime.now() - requestTime.startedAtNanos(), NANOSECONDS);
}
catch (Throwable t)
{
@ -2223,13 +2245,13 @@ public class StorageProxy implements StorageProxyMBean
@Override
public long creationTimeNanos()
{
return approxCreationTimeNanos;
return requestTime.enqueuedAtNanos();
}
@Override
public long startTimeNanos()
{
return approxStartTimeNanos;
return requestTime.startedAtNanos();
}
@Override
@ -2241,7 +2263,7 @@ public class StorageProxy implements StorageProxyMBean
public static PartitionIterator getRangeSlice(PartitionRangeReadCommand command,
ConsistencyLevel consistencyLevel,
long queryStartNanoTime)
Dispatcher.RequestTime requestTime)
{
if (DatabaseDescriptor.getPartitionDenylistEnabled() && DatabaseDescriptor.getDenylistRangeReadsEnabled())
{
@ -2255,7 +2277,7 @@ public class StorageProxy implements StorageProxyMBean
tokens));
}
}
return RangeCommands.partitions(command, consistencyLevel, queryStartNanoTime);
return RangeCommands.partitions(command, consistencyLevel, requestTime);
}
public Map<String, List<String>> getSchemaVersions()
@ -2523,7 +2545,8 @@ public class StorageProxy implements StorageProxyMBean
public void apply(IMutation mutation,
ReplicaPlan.ForWrite targets,
AbstractWriteResponseHandler<IMutation> responseHandler,
String localDataCenter) throws OverloadedException;
String localDataCenter,
Dispatcher.RequestTime requestTime) throws OverloadedException;
}
/**
@ -2531,9 +2554,9 @@ public class StorageProxy implements StorageProxyMBean
*/
private static class ViewWriteMetricsWrapped extends BatchlogResponseHandler<IMutation>
{
public ViewWriteMetricsWrapped(AbstractWriteResponseHandler<IMutation> writeHandler, int i, BatchlogCleanup cleanup, long queryStartNanoTime)
public ViewWriteMetricsWrapped(AbstractWriteResponseHandler<IMutation> writeHandler, int i, BatchlogCleanup cleanup, Dispatcher.RequestTime requestTime)
{
super(writeHandler, i, cleanup, queryStartNanoTime);
super(writeHandler, i, cleanup, requestTime);
viewWriteMetrics.viewReplicasAttempted.inc(candidateReplicaCount());
}
@ -2549,25 +2572,22 @@ public class StorageProxy implements StorageProxyMBean
*/
private static abstract class DroppableRunnable implements Runnable
{
protected final long approxCreationTimeNanos;
protected volatile long approxStartTimeNanos;
final Verb verb;
public DroppableRunnable(Verb verb)
final Dispatcher.RequestTime requestTime;
public DroppableRunnable(Verb verb, Dispatcher.RequestTime requestTime)
{
this.approxCreationTimeNanos = MonotonicClock.Global.approxTime.now();
this.verb = verb;
this.requestTime = requestTime;
}
public final void run()
{
approxStartTimeNanos = MonotonicClock.Global.approxTime.now();
long expirationTimeNanos = verb.expiresAtNanos(approxCreationTimeNanos);
if (approxStartTimeNanos > expirationTimeNanos)
long nowNanos = MonotonicClock.Global.preciseTime.now();
long deadline = requestTime.computeDeadline(verb.expiresAfterNanos());
if (nowNanos > deadline)
{
long timeTakenNanos = approxStartTimeNanos - approxCreationTimeNanos;
MessagingService.instance().metrics.recordSelfDroppedMessage(verb, timeTakenNanos, NANOSECONDS);
long elapsed = nowNanos - requestTime.startedAtNanos();
MessagingService.instance().metrics.recordSelfDroppedMessage(verb, elapsed, NANOSECONDS);
return;
}
try
@ -2589,35 +2609,37 @@ public class StorageProxy implements StorageProxyMBean
*/
private static abstract class LocalMutationRunnable implements RunnableDebuggableTask
{
private final long approxCreationTimeNanos = MonotonicClock.Global.approxTime.now();
private volatile long approxStartTimeNanos;
private final Replica localReplica;
private final Dispatcher.RequestTime requestTime;
LocalMutationRunnable(Replica localReplica)
LocalMutationRunnable(Replica localReplica, Dispatcher.RequestTime requestTime)
{
this.localReplica = localReplica;
this.requestTime = requestTime;
}
public final void run()
{
final Verb verb = verb();
approxStartTimeNanos = MonotonicClock.Global.approxTime.now();
long expirationTimeNanos = verb.expiresAtNanos(approxCreationTimeNanos);
if (approxStartTimeNanos > expirationTimeNanos)
long now = MonotonicClock.Global.approxTime.now();
long deadline = requestTime.computeDeadline(verb.expiresAfterNanos());
if (now > deadline)
{
long timeTakenNanos = approxStartTimeNanos - approxCreationTimeNanos;
long timeTakenNanos = now - startTimeNanos();
MessagingService.instance().metrics.recordSelfDroppedMessage(Verb.MUTATION_REQ, timeTakenNanos, NANOSECONDS);
HintRunnable runnable = new HintRunnable(EndpointsForToken.of(localReplica.range().right, localReplica))
if (requestTime.shouldSendHints())
{
protected void runMayThrow() throws Exception
HintRunnable runnable = new HintRunnable(EndpointsForToken.of(localReplica.range().right, localReplica))
{
LocalMutationRunnable.this.runMayThrow();
}
};
submitHint(runnable);
protected void runMayThrow() throws Exception
{
LocalMutationRunnable.this.runMayThrow();
}
};
submitHint(runnable);
}
return;
}
@ -2634,13 +2656,13 @@ public class StorageProxy implements StorageProxyMBean
@Override
public long creationTimeNanos()
{
return approxCreationTimeNanos;
return requestTime.enqueuedAtNanos();
}
@Override
public long startTimeNanos()
{
return approxStartTimeNanos;
return requestTime.startedAtNanos();
}
@Override
@ -2738,14 +2760,14 @@ public class StorageProxy implements StorageProxyMBean
}
}
public static Future<Void> submitHint(Mutation mutation, Replica target, AbstractWriteResponseHandler<IMutation> responseHandler)
public static void submitHint(Mutation mutation, Replica target, AbstractWriteResponseHandler<IMutation> responseHandler)
{
return submitHint(mutation, EndpointsForToken.of(target.range().right, target), responseHandler);
submitHint(mutation, EndpointsForToken.of(target.range().right, target), responseHandler);
}
public static Future<Void> submitHint(Mutation mutation,
EndpointsForToken targets,
AbstractWriteResponseHandler<IMutation> responseHandler)
private static void submitHint(Mutation mutation,
EndpointsForToken targets,
AbstractWriteResponseHandler<IMutation> responseHandler)
{
Replicas.assertFull(targets); // hints should not be written for transient replicas
HintRunnable runnable = new HintRunnable(targets)
@ -2774,7 +2796,7 @@ public class StorageProxy implements StorageProxyMBean
}
};
return submitHint(runnable);
submitHint(runnable);
}
private static Future<Void> submitHint(HintRunnable runnable)

View File

@ -595,13 +595,13 @@ public class StorageService extends NotificationBroadcasterSupport implements IE
}
}
public void stopNativeTransport()
public void stopNativeTransport(boolean force)
{
if (daemon == null)
{
throw new IllegalStateException("No configured daemon");
}
daemon.stopNativeTransport();
daemon.stopNativeTransport(force);
}
public boolean isNativeTransportRunning()
@ -7452,4 +7452,71 @@ public class StorageService extends NotificationBroadcasterSupport implements IE
if (!skipNotificationListeners)
super.addNotificationListener(listener, filter, handback);
}
@Override
public double getNativeTransportQueueMaxItemAgeThreshold()
{
return DatabaseDescriptor.getNativeTransportQueueMaxItemAgeThreshold();
}
@Override
public void setNativeTransportQueueMaxItemAgeThreshold(double threshold)
{
DatabaseDescriptor.setNativeTransportMaxQueueItemAgeThreshold(threshold);
}
@Override
public long getNativeTransportMinBackoffOnQueueOverloadInMillis()
{
return DatabaseDescriptor.getNativeTransportMinBackoffOnQueueOverload(MILLISECONDS);
}
@Override
public long getNativeTransportMaxBackoffOnQueueOverloadInMillis()
{
return DatabaseDescriptor.getNativeTransportMaxBackoffOnQueueOverload(MILLISECONDS);
}
@Override
public void setNativeTransportBackoffOnQueueOverloadInMillis(long min, long max)
{
DatabaseDescriptor.setNativeTransportBackoffOnQueueOverload(min, max, MILLISECONDS);
}
@Override
public boolean getNativeTransportThrowOnOverload()
{
return DatabaseDescriptor.getNativeTransportThrowOnOverload();
}
@Override
public void setNativeTransportThrowOnOverload(boolean throwOnOverload)
{
DatabaseDescriptor.setNativeTransportThrowOnOverload(throwOnOverload);
}
@Override
public long getNativeTransportTimeoutMillis()
{
return DatabaseDescriptor.getNativeTransportTimeout(MILLISECONDS);
}
@Override
public void setNativeTransportTimeoutMillis(long deadlineMillis)
{
DatabaseDescriptor.setNativeTransportTimeout(deadlineMillis, MILLISECONDS);
}
@Override
public boolean getEnforceNativeDeadlineForHints()
{
return DatabaseDescriptor.getEnforceNativeDeadlineForHints();
}
@Override
public void setEnforceNativeDeadlineForHints(boolean value)
{
DatabaseDescriptor.setEnforceNativeDeadlineForHints(value);
}
}

View File

@ -665,7 +665,8 @@ public interface StorageServiceMBean extends NotificationEmitter
// to determine if initialization has completed
public boolean isInitialized();
public void stopNativeTransport();
@Deprecated(since = "5.0") public default void stopNativeTransport() { stopNativeTransport(false); }
public void stopNativeTransport(boolean force);
public void startNativeTransport();
public boolean isNativeTransportRunning();
public void enableNativeTransportOldProtocolVersions();
@ -1262,4 +1263,20 @@ public interface StorageServiceMBean extends NotificationEmitter
public void setSkipStreamDiskSpaceCheck(boolean value);
public boolean getSkipStreamDiskSpaceCheck();
}
double getNativeTransportQueueMaxItemAgeThreshold();
void setNativeTransportQueueMaxItemAgeThreshold(double threshold);
long getNativeTransportMinBackoffOnQueueOverloadInMillis();
long getNativeTransportMaxBackoffOnQueueOverloadInMillis();
void setNativeTransportBackoffOnQueueOverloadInMillis(long min, long max);
boolean getNativeTransportThrowOnOverload();
void setNativeTransportThrowOnOverload(boolean throwOnOverload);
long getNativeTransportTimeoutMillis();
void setNativeTransportTimeoutMillis(long deadlineMillis);
boolean getEnforceNativeDeadlineForHints();
void setEnforceNativeDeadlineForHints(boolean value);
}

View File

@ -27,6 +27,7 @@ import org.slf4j.LoggerFactory;
import org.apache.cassandra.net.Message;
import org.apache.cassandra.db.WriteType;
import org.apache.cassandra.transport.Dispatcher;
/**
* Handles blocking writes for ONE, ANY, TWO, THREE, QUORUM, and ALL consistency levels.
@ -43,15 +44,15 @@ public class WriteResponseHandler<T> extends AbstractWriteResponseHandler<T>
Runnable callback,
WriteType writeType,
Supplier<Mutation> hintOnFailure,
long queryStartNanoTime)
Dispatcher.RequestTime requestTime)
{
super(replicaPlan, callback, writeType, hintOnFailure, queryStartNanoTime);
super(replicaPlan, callback, writeType, hintOnFailure, requestTime);
responses = blockFor();
}
public WriteResponseHandler(ReplicaPlan.ForWrite replicaPlan, WriteType writeType, Supplier<Mutation> hintOnFailure, long queryStartNanoTime)
public WriteResponseHandler(ReplicaPlan.ForWrite replicaPlan, WriteType writeType, Supplier<Mutation> hintOnFailure, Dispatcher.RequestTime requestTime)
{
this(replicaPlan, null, writeType, hintOnFailure, queryStartNanoTime);
this(replicaPlan, null, writeType, hintOnFailure, requestTime);
}
public void onResponse(Message<T> m)

View File

@ -24,6 +24,7 @@ import org.apache.cassandra.db.filter.DataLimits;
import org.apache.cassandra.db.transform.Transformation;
import org.apache.cassandra.schema.TableMetadata;
import org.apache.cassandra.service.ClientState;
import org.apache.cassandra.transport.Dispatcher;
import org.apache.cassandra.transport.ProtocolVersion;
abstract class AbstractQueryPager<T extends ReadQuery> implements QueryPager
@ -59,7 +60,7 @@ abstract class AbstractQueryPager<T extends ReadQuery> implements QueryPager
return query.executionController();
}
public PartitionIterator fetchPage(int pageSize, ConsistencyLevel consistency, ClientState clientState, long queryStartNanoTime)
public PartitionIterator fetchPage(int pageSize, ConsistencyLevel consistency, ClientState clientState, Dispatcher.RequestTime requestTime)
{
if (isExhausted())
return EmptyIterators.partition();
@ -72,7 +73,7 @@ abstract class AbstractQueryPager<T extends ReadQuery> implements QueryPager
exhausted = true;
return EmptyIterators.partition();
}
return Transformation.apply(readQuery.execute(consistency, clientState, queryStartNanoTime), pager);
return Transformation.apply(readQuery.execute(consistency, clientState, requestTime), pager);
}
public PartitionIterator fetchPageInternal(int pageSize, ReadExecutionController executionController)

View File

@ -28,8 +28,7 @@ import org.apache.cassandra.db.partitions.PartitionIterator;
import org.apache.cassandra.db.rows.Row;
import org.apache.cassandra.db.rows.RowIterator;
import org.apache.cassandra.service.ClientState;
import static org.apache.cassandra.utils.Clock.Global.nanoTime;
import org.apache.cassandra.transport.Dispatcher;
/**
* {@code QueryPager} that takes care of fetching the pages for aggregation queries.
@ -55,12 +54,12 @@ public final class AggregationQueryPager implements QueryPager
public PartitionIterator fetchPage(int pageSize,
ConsistencyLevel consistency,
ClientState clientState,
long queryStartNanoTime)
Dispatcher.RequestTime requestTime)
{
if (limits.isGroupByLimit())
return new GroupByPartitionIterator(pageSize, consistency, clientState, queryStartNanoTime);
return new GroupByPartitionIterator(pageSize, consistency, clientState, requestTime);
return new AggregationPartitionIterator(pageSize, consistency, clientState, queryStartNanoTime);
return new AggregationPartitionIterator(pageSize, consistency, clientState, requestTime);
}
@Override
@ -73,9 +72,9 @@ public final class AggregationQueryPager implements QueryPager
public PartitionIterator fetchPageInternal(int pageSize, ReadExecutionController executionController)
{
if (limits.isGroupByLimit())
return new GroupByPartitionIterator(pageSize, executionController, nanoTime());
return new GroupByPartitionIterator(pageSize, executionController, Dispatcher.RequestTime.forImmediateExecution());
return new AggregationPartitionIterator(pageSize, executionController, nanoTime());
return new AggregationPartitionIterator(pageSize, executionController, Dispatcher.RequestTime.forImmediateExecution());
}
@Override
@ -161,34 +160,34 @@ public final class AggregationQueryPager implements QueryPager
*/
private int initialMaxRemaining;
private long queryStartNanoTime;
private Dispatcher.RequestTime requestTime;
public GroupByPartitionIterator(int pageSize,
ConsistencyLevel consistency,
ClientState clientState,
long queryStartNanoTime)
ConsistencyLevel consistency,
ClientState clientState,
Dispatcher.RequestTime requestTime)
{
this(pageSize, consistency, clientState, null, queryStartNanoTime);
this(pageSize, consistency, clientState, null, requestTime);
}
public GroupByPartitionIterator(int pageSize,
ReadExecutionController executionController,
long queryStartNanoTime)
Dispatcher.RequestTime requestTime)
{
this(pageSize, null, null, executionController, queryStartNanoTime);
this(pageSize, null, null, executionController, requestTime);
}
private GroupByPartitionIterator(int pageSize,
ConsistencyLevel consistency,
ClientState clientState,
ReadExecutionController executionController,
long queryStartNanoTime)
Dispatcher.RequestTime requestTime)
{
this.pageSize = handlePagingOff(pageSize);
this.consistency = consistency;
this.clientState = clientState;
this.executionController = executionController;
this.queryStartNanoTime = queryStartNanoTime;
this.requestTime = requestTime;
}
private int handlePagingOff(int pageSize)
@ -295,7 +294,7 @@ public final class AggregationQueryPager implements QueryPager
*/
private final PartitionIterator fetchSubPage(int subPageSize)
{
return consistency != null ? subPager.fetchPage(subPageSize, consistency, clientState, queryStartNanoTime)
return consistency != null ? subPager.fetchPage(subPageSize, consistency, clientState, requestTime)
: subPager.fetchPageInternal(subPageSize, executionController);
}
@ -409,16 +408,16 @@ public final class AggregationQueryPager implements QueryPager
public AggregationPartitionIterator(int pageSize,
ConsistencyLevel consistency,
ClientState clientState,
long queryStartNanoTime)
Dispatcher.RequestTime requestTime)
{
super(pageSize, consistency, clientState, queryStartNanoTime);
super(pageSize, consistency, clientState, requestTime);
}
public AggregationPartitionIterator(int pageSize,
ReadExecutionController executionController,
long queryStartNanoTime)
Dispatcher.RequestTime requestTime)
{
super(pageSize, executionController, queryStartNanoTime);
super(pageSize, executionController, requestTime);
}
@Override

View File

@ -17,6 +17,7 @@
*/
package org.apache.cassandra.service.pager;
import org.apache.cassandra.transport.Dispatcher;
import org.apache.cassandra.transport.ProtocolVersion;
import org.apache.cassandra.utils.AbstractIterator;
@ -30,8 +31,6 @@ import org.apache.cassandra.exceptions.RequestValidationException;
import org.apache.cassandra.exceptions.RequestExecutionException;
import org.apache.cassandra.service.ClientState;
import static org.apache.cassandra.utils.Clock.Global.nanoTime;
/**
* Pager over a list of SinglePartitionReadQuery.
*
@ -149,16 +148,18 @@ public class MultiPartitionPager<T extends SinglePartitionReadQuery> implements
throw new AssertionError("Shouldn't be called on an exhausted pager");
}
public PartitionIterator fetchPage(int pageSize, ConsistencyLevel consistency, ClientState clientState, long queryStartNanoTime) throws RequestValidationException, RequestExecutionException
@SuppressWarnings("resource") // iter closed via countingIter
@Override
public PartitionIterator fetchPage(int pageSize, ConsistencyLevel consistency, ClientState clientState, Dispatcher.RequestTime requestTime) throws RequestValidationException, RequestExecutionException
{
int toQuery = Math.min(remaining, pageSize);
return new PagersIterator(toQuery, consistency, clientState, null, queryStartNanoTime);
return new PagersIterator(toQuery, consistency, clientState, null, requestTime);
}
public PartitionIterator fetchPageInternal(int pageSize, ReadExecutionController executionController) throws RequestValidationException, RequestExecutionException
{
int toQuery = Math.min(remaining, pageSize);
return new PagersIterator(toQuery, null, null, executionController, nanoTime());
return new PagersIterator(toQuery, null, null, executionController, Dispatcher.RequestTime.forImmediateExecution());
}
private class PagersIterator extends AbstractIterator<RowIterator> implements PartitionIterator
@ -166,7 +167,7 @@ public class MultiPartitionPager<T extends SinglePartitionReadQuery> implements
private final int pageSize;
private PartitionIterator result;
private boolean closed;
private final long queryStartNanoTime;
private final Dispatcher.RequestTime requestTime;
// For "normal" queries
private final ConsistencyLevel consistency;
@ -178,13 +179,13 @@ public class MultiPartitionPager<T extends SinglePartitionReadQuery> implements
private int pagerMaxRemaining;
private int counted;
public PagersIterator(int pageSize, ConsistencyLevel consistency, ClientState clientState, ReadExecutionController executionController, long queryStartNanoTime)
public PagersIterator(int pageSize, ConsistencyLevel consistency, ClientState clientState, ReadExecutionController executionController, Dispatcher.RequestTime requestTime)
{
this.pageSize = pageSize;
this.consistency = consistency;
this.clientState = clientState;
this.executionController = executionController;
this.queryStartNanoTime = queryStartNanoTime;
this.requestTime = requestTime;
}
protected RowIterator computeNext()
@ -213,7 +214,7 @@ public class MultiPartitionPager<T extends SinglePartitionReadQuery> implements
int toQuery = pageSize - counted;
result = consistency == null
? pagers[current].fetchPageInternal(toQuery, executionController)
: pagers[current].fetchPage(toQuery, consistency, clientState, queryStartNanoTime);
: pagers[current].fetchPage(toQuery, consistency, clientState, requestTime);
}
return result.next();
}

View File

@ -25,6 +25,7 @@ import org.apache.cassandra.db.partitions.PartitionIterator;
import org.apache.cassandra.exceptions.RequestExecutionException;
import org.apache.cassandra.exceptions.RequestValidationException;
import org.apache.cassandra.service.ClientState;
import org.apache.cassandra.transport.Dispatcher;
/**
* Perform a query, paging it by page of a given size.
@ -54,7 +55,7 @@ public interface QueryPager
return ReadExecutionController.empty();
}
public PartitionIterator fetchPage(int pageSize, ConsistencyLevel consistency, ClientState clientState, long queryStartNanoTime) throws RequestValidationException, RequestExecutionException
public PartitionIterator fetchPage(int pageSize, ConsistencyLevel consistency, ClientState clientState, Dispatcher.RequestTime requestTime) throws RequestValidationException, RequestExecutionException
{
return EmptyIterators.partition();
}
@ -94,7 +95,7 @@ public interface QueryPager
* {@code consistency} is a serial consistency.
* @return the page of result.
*/
public PartitionIterator fetchPage(int pageSize, ConsistencyLevel consistency, ClientState clientState, long queryStartNanoTime) throws RequestValidationException, RequestExecutionException;
public PartitionIterator fetchPage(int pageSize, ConsistencyLevel consistency, ClientState clientState, Dispatcher.RequestTime requestTime) throws RequestValidationException, RequestExecutionException;
/**
* Starts a new read operation.

View File

@ -90,6 +90,7 @@ import org.apache.cassandra.service.paxos.cleanup.PaxosRepairState;
import org.apache.cassandra.service.reads.DataResolver;
import org.apache.cassandra.service.reads.repair.NoopReadRepair;
import org.apache.cassandra.tracing.Tracing;
import org.apache.cassandra.transport.Dispatcher;
import org.apache.cassandra.triggers.TriggerExecutor;
import org.apache.cassandra.utils.CassandraVersion;
import org.apache.cassandra.utils.CollectionSerializer;
@ -820,21 +821,20 @@ public class Paxos
return read.rowIterator();
}
public static PartitionIterator read(SinglePartitionReadCommand.Group group, ConsistencyLevel consistencyForConsensus)
public static PartitionIterator read(SinglePartitionReadCommand.Group group, ConsistencyLevel consistencyForConsensus, Dispatcher.RequestTime requestTime)
throws InvalidRequestException, UnavailableException, ReadFailureException, ReadTimeoutException
{
long start = nanoTime();
long deadline = start + DatabaseDescriptor.getReadRpcTimeout(NANOSECONDS);
return read(group, consistencyForConsensus, start, deadline);
long deadline = requestTime.computeDeadline(DatabaseDescriptor.getReadRpcTimeout(NANOSECONDS));
return read(group, consistencyForConsensus, requestTime, deadline);
}
public static PartitionIterator read(SinglePartitionReadCommand.Group group, ConsistencyLevel consistencyForConsensus, long deadline)
throws InvalidRequestException, UnavailableException, ReadFailureException, ReadTimeoutException
{
return read(group, consistencyForConsensus, nanoTime(), deadline);
return read(group, consistencyForConsensus, Dispatcher.RequestTime.forImmediateExecution(), deadline);
}
private static PartitionIterator read(SinglePartitionReadCommand.Group group, ConsistencyLevel consistencyForConsensus, long start, long deadline)
private static PartitionIterator read(SinglePartitionReadCommand.Group group, ConsistencyLevel consistencyForConsensus, Dispatcher.RequestTime requestTime, long deadline)
throws InvalidRequestException, UnavailableException, ReadFailureException, ReadTimeoutException
{
if (group.queries.size() > 1)
@ -901,7 +901,9 @@ public class Paxos
}
finally
{
long latency = nanoTime() - start;
// We track latency based on request processing time, since the amount of time that request spends in the queue
// is not a representative metric of replica performance.
long latency = nanoTime() - requestTime.startedAtNanos();
readMetrics.addNano(latency);
casReadMetrics.addNano(latency);
readMetricsMap.get(consistencyForConsensus).addNano(latency);
@ -1039,7 +1041,7 @@ public class Paxos
// round's proposal (if any).
PaxosPrepare.Success success = prepare.success();
DataResolver<?, ?> resolver = new DataResolver(query, success.participants, NoopReadRepair.instance, query.creationTimeNanos());
DataResolver<?, ?> resolver = new DataResolver(query, success.participants, NoopReadRepair.instance, new Dispatcher.RequestTime(query.creationTimeNanos()));
for (int i = 0 ; i < success.responses.size() ; ++i)
resolver.preprocess(success.responses.get(i));

View File

@ -17,17 +17,17 @@
*/
package org.apache.cassandra.service.paxos.v1;
import org.apache.cassandra.utils.concurrent.CountDownLatch;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.db.ConsistencyLevel;
import org.apache.cassandra.db.WriteType;
import org.apache.cassandra.exceptions.WriteTimeoutException;
import org.apache.cassandra.net.RequestCallback;
import org.apache.cassandra.transport.Dispatcher;
import org.apache.cassandra.utils.concurrent.UncheckedInterruptedException;
import org.apache.cassandra.utils.Clock;
import org.apache.cassandra.utils.concurrent.CountDownLatch;
import static java.util.concurrent.TimeUnit.NANOSECONDS;
import static org.apache.cassandra.utils.Clock.Global.nanoTime;
import static org.apache.cassandra.utils.concurrent.CountDownLatch.newCountDownLatch;
public abstract class AbstractPaxosCallback<T> implements RequestCallback<T>
@ -35,14 +35,14 @@ public abstract class AbstractPaxosCallback<T> implements RequestCallback<T>
protected final CountDownLatch latch;
protected final int targets;
private final ConsistencyLevel consistency;
private final long queryStartNanoTime;
private final Dispatcher.RequestTime requestTime;
public AbstractPaxosCallback(int targets, ConsistencyLevel consistency, long queryStartNanoTime)
public AbstractPaxosCallback(int targets, ConsistencyLevel consistency, Dispatcher.RequestTime requestTime)
{
this.targets = targets;
this.consistency = consistency;
latch = newCountDownLatch(targets);
this.queryStartNanoTime = queryStartNanoTime;
this.requestTime = requestTime;
}
public int getResponseCount()
@ -54,7 +54,9 @@ public abstract class AbstractPaxosCallback<T> implements RequestCallback<T>
{
try
{
long timeout = DatabaseDescriptor.getWriteRpcTimeout(NANOSECONDS) - (nanoTime() - queryStartNanoTime);
long now = Clock.Global.nanoTime();
long timeout = requestTime.computeTimeout(now, DatabaseDescriptor.getWriteRpcTimeout(NANOSECONDS));
if (!latch.await(timeout, NANOSECONDS))
throw new WriteTimeoutException(WriteType.CAS, consistency, getResponseCount(), targets);
}

View File

@ -34,6 +34,7 @@ import org.slf4j.LoggerFactory;
import org.apache.cassandra.net.Message;
import org.apache.cassandra.service.paxos.Commit;
import org.apache.cassandra.service.paxos.PrepareResponse;
import org.apache.cassandra.transport.Dispatcher;
public class PrepareCallback extends AbstractPaxosCallback<PrepareResponse>
{
@ -45,9 +46,9 @@ public class PrepareCallback extends AbstractPaxosCallback<PrepareResponse>
private final Map<InetAddressAndPort, Commit> commitsByReplica = new ConcurrentHashMap<>();
public PrepareCallback(DecoratedKey key, TableMetadata metadata, int targets, ConsistencyLevel consistency, long queryStartNanoTime)
public PrepareCallback(DecoratedKey key, TableMetadata metadata, int targets, ConsistencyLevel consistency, Dispatcher.RequestTime requestTime)
{
super(targets, consistency, queryStartNanoTime);
super(targets, consistency, requestTime);
// need to inject the right key in the empty commit so comparing with empty commits in the response works as expected
mostRecentCommit = Commit.emptyCommit(key, metadata);
mostRecentInProgressCommit = Commit.emptyCommit(key, metadata);

View File

@ -26,6 +26,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.cassandra.net.Message;
import org.apache.cassandra.transport.Dispatcher;
import org.apache.cassandra.utils.Nemesis;
/**
@ -49,9 +50,9 @@ public class ProposeCallback extends AbstractPaxosCallback<Boolean>
private final int requiredAccepts;
private final boolean failFast;
public ProposeCallback(int totalTargets, int requiredTargets, boolean failFast, ConsistencyLevel consistency, long queryStartNanoTime)
public ProposeCallback(int totalTargets, int requiredTargets, boolean failFast, ConsistencyLevel consistency, Dispatcher.RequestTime requestTime)
{
super(totalTargets, consistency, queryStartNanoTime);
super(totalTargets, consistency, requestTime);
this.requiredAccepts = requiredTargets;
this.failFast = failFast;
}

View File

@ -45,10 +45,13 @@ import org.apache.cassandra.service.StorageProxy.LocalReadRunnable;
import org.apache.cassandra.service.reads.repair.ReadRepair;
import org.apache.cassandra.tracing.TraceState;
import org.apache.cassandra.tracing.Tracing;
import org.apache.cassandra.transport.Dispatcher;
import org.apache.cassandra.utils.FBUtilities;
import static com.google.common.collect.Iterables.all;
import static java.util.concurrent.TimeUnit.MICROSECONDS;
import static java.util.concurrent.TimeUnit.NANOSECONDS;
import static org.apache.cassandra.utils.MonotonicClock.Global.preciseTime;
/**
* Sends a read request to the replicas needed to satisfy a given ConsistencyLevel.
@ -69,22 +72,23 @@ public abstract class AbstractReadExecutor
protected final ReadCallback<EndpointsForToken, ReplicaPlan.ForTokenRead> handler;
protected final TraceState traceState;
protected final ColumnFamilyStore cfs;
protected final long queryStartNanoTime;
protected final Dispatcher.RequestTime requestTime;
private final int initialDataRequestCount;
protected volatile PartitionIterator result = null;
AbstractReadExecutor(ColumnFamilyStore cfs, ReadCommand command, ReplicaPlan.ForTokenRead replicaPlan, int initialDataRequestCount, long queryStartNanoTime)
AbstractReadExecutor(ColumnFamilyStore cfs, ReadCommand command, ReplicaPlan.ForTokenRead replicaPlan, int initialDataRequestCount, Dispatcher.RequestTime requestTime)
{
this.command = command;
this.replicaPlan = ReplicaPlan.shared(replicaPlan);
this.initialDataRequestCount = initialDataRequestCount;
// the ReadRepair and DigestResolver both need to see our updated
this.readRepair = ReadRepair.create(command, this.replicaPlan, queryStartNanoTime);
this.digestResolver = new DigestResolver<>(command, this.replicaPlan, queryStartNanoTime);
this.handler = new ReadCallback<>(digestResolver, command, this.replicaPlan, queryStartNanoTime);
this.readRepair = ReadRepair.create(command, this.replicaPlan, requestTime);
this.digestResolver = new DigestResolver<>(command, this.replicaPlan, requestTime);
this.handler = new ReadCallback<>(digestResolver, command, this.replicaPlan, requestTime);
this.cfs = cfs;
this.traceState = Tracing.instance.get();
this.queryStartNanoTime = queryStartNanoTime;
this.requestTime = requestTime;
// Set the digest version (if we request some digests). This is the smallest version amongst all our target replicas since new nodes
@ -147,7 +151,9 @@ public abstract class AbstractReadExecutor
traceState.trace("reading {} from {}", readCommand.isDigestQuery() ? "digest" : "data", endpoint);
if (null == message)
message = readCommand.createMessage(false);
{
message = readCommand.createMessage(false, requestTime);
}
MessagingService.instance().sendWithCallback(message, endpoint, handler);
}
@ -156,7 +162,7 @@ public abstract class AbstractReadExecutor
if (hasLocalEndpoint)
{
logger.trace("reading {} locally", readCommand.isDigestQuery() ? "digest" : "data");
Stage.READ.maybeExecuteImmediately(new LocalReadRunnable(readCommand, handler));
Stage.READ.maybeExecuteImmediately(new LocalReadRunnable(readCommand, handler, requestTime));
}
}
@ -181,9 +187,7 @@ public abstract class AbstractReadExecutor
/**
* @return an executor appropriate for the configured speculative read policy
*/
public static AbstractReadExecutor getReadExecutor(SinglePartitionReadCommand command,
ConsistencyLevel consistencyLevel,
long queryStartNanoTime) throws UnavailableException
public static AbstractReadExecutor getReadExecutor(SinglePartitionReadCommand command, ConsistencyLevel consistencyLevel, Dispatcher.RequestTime requestTime) throws UnavailableException
{
Keyspace keyspace = Keyspace.open(command.metadata().keyspace);
ColumnFamilyStore cfs = keyspace.getColumnFamilyStore(command.metadata().id);
@ -198,20 +202,20 @@ public abstract class AbstractReadExecutor
// Speculative retry is disabled *OR*
// 11980: Disable speculative retry if using EACH_QUORUM in order to prevent miscounting DC responses
if (retry.equals(NeverSpeculativeRetryPolicy.INSTANCE) || consistencyLevel == ConsistencyLevel.EACH_QUORUM)
return new NeverSpeculatingReadExecutor(cfs, command, replicaPlan, queryStartNanoTime, false);
return new NeverSpeculatingReadExecutor(cfs, command, replicaPlan, requestTime, false);
// There are simply no extra replicas to speculate.
// Handle this separately so it can record failed attempts to speculate due to lack of replicas
if (replicaPlan.contacts().size() == replicaPlan.readCandidates().size())
{
boolean recordFailedSpeculation = consistencyLevel != ConsistencyLevel.ALL;
return new NeverSpeculatingReadExecutor(cfs, command, replicaPlan, queryStartNanoTime, recordFailedSpeculation);
return new NeverSpeculatingReadExecutor(cfs, command, replicaPlan, requestTime, recordFailedSpeculation);
}
if (retry.equals(AlwaysSpeculativeRetryPolicy.INSTANCE))
return new AlwaysSpeculatingReadExecutor(cfs, command, replicaPlan, queryStartNanoTime);
return new AlwaysSpeculatingReadExecutor(cfs, command, replicaPlan, requestTime);
else // PERCENTILE or CUSTOM.
return new SpeculatingReadExecutor(cfs, command, replicaPlan, queryStartNanoTime);
return new SpeculatingReadExecutor(cfs, command, replicaPlan, requestTime);
}
public boolean hasLocalRead()
@ -225,17 +229,27 @@ public abstract class AbstractReadExecutor
*/
boolean shouldSpeculateAndMaybeWait()
{
long now = preciseTime.now();
long sampleLatencyNanos = MICROSECONDS.toNanos(cfs.sampleReadLatencyMicros);
// no latency information, or we're overloaded
if (cfs.sampleReadLatencyMicros > command.getTimeout(MICROSECONDS))
if (sampleLatencyNanos > command.getTimeout(NANOSECONDS))
{
if (logger.isTraceEnabled())
logger.trace("Decided not to speculate as {} > {}", cfs.sampleReadLatencyMicros, command.getTimeout(MICROSECONDS));
logger.trace("Decided not to speculate as {}ns > {}ns", sampleLatencyNanos, command.getTimeout(NANOSECONDS));
return false;
}
if (now + sampleLatencyNanos > requestTime.clientDeadline())
{
logger.trace("Decided not to speculate as native transport timeout will be reached before speculating");
return false;
}
// We track latency based on request processing time, since the amount of time that request spends in the queue
// is not a representative metric of replica performance.
if (logger.isTraceEnabled())
logger.trace("Awaiting {} microseconds before speculating", cfs.sampleReadLatencyMicros);
return !handler.await(cfs.sampleReadLatencyMicros, MICROSECONDS);
logger.trace("Awaiting {}ns before speculating", sampleLatencyNanos);
return !handler.awaitUntil(requestTime.startedAtNanos() + sampleLatencyNanos);
}
ReplicaPlan.ForTokenRead replicaPlan()
@ -254,9 +268,13 @@ public abstract class AbstractReadExecutor
*/
private final boolean logFailedSpeculation;
public NeverSpeculatingReadExecutor(ColumnFamilyStore cfs, ReadCommand command, ReplicaPlan.ForTokenRead replicaPlan, long queryStartNanoTime, boolean logFailedSpeculation)
public NeverSpeculatingReadExecutor(ColumnFamilyStore cfs,
ReadCommand command,
ReplicaPlan.ForTokenRead replicaPlan,
Dispatcher.RequestTime requestTime,
boolean logFailedSpeculation)
{
super(cfs, command, replicaPlan, 1, queryStartNanoTime);
super(cfs, command, replicaPlan, 1, requestTime);
this.logFailedSpeculation = logFailedSpeculation;
}
@ -276,12 +294,12 @@ public abstract class AbstractReadExecutor
public SpeculatingReadExecutor(ColumnFamilyStore cfs,
ReadCommand command,
ReplicaPlan.ForTokenRead replicaPlan,
long queryStartNanoTime)
Dispatcher.RequestTime requestTime)
{
// We're hitting additional targets for read repair (??). Since our "extra" replica is the least-
// preferred by the snitch, we do an extra data read to start with against a replica more
// likely to respond; better to let RR fail than the entire query.
super(cfs, command, replicaPlan, replicaPlan.readQuorum() < replicaPlan.contacts().size() ? 2 : 1, queryStartNanoTime);
super(cfs, command, replicaPlan, replicaPlan.readQuorum() < replicaPlan.contacts().size() ? 2 : 1, requestTime);
}
public void maybeTryAdditionalReplicas()
@ -327,7 +345,8 @@ public abstract class AbstractReadExecutor
if (traceState != null)
traceState.trace("speculating read retry on {}", extraReplica);
logger.trace("speculating read retry on {}", extraReplica);
MessagingService.instance().sendWithCallback(retryCommand.createMessage(false), extraReplica.endpoint(), handler);
MessagingService.instance().sendWithCallback(retryCommand.createMessage(false, requestTime), extraReplica.endpoint(), handler);
}
}
@ -346,11 +365,11 @@ public abstract class AbstractReadExecutor
public AlwaysSpeculatingReadExecutor(ColumnFamilyStore cfs,
ReadCommand command,
ReplicaPlan.ForTokenRead replicaPlan,
long queryStartNanoTime)
Dispatcher.RequestTime requestTime)
{
// presumably, we speculate an extra data request here in case it is our data request that fails to respond,
// and there are no more nodes to consult
super(cfs, command, replicaPlan, replicaPlan.contacts().size() > 1 ? 2 : 1, queryStartNanoTime);
super(cfs, command, replicaPlan, replicaPlan.contacts().size() > 1 ? 2 : 1, requestTime);
}
public void maybeTryAdditionalReplicas()

View File

@ -55,6 +55,7 @@ import org.apache.cassandra.service.reads.repair.NoopReadRepair;
import org.apache.cassandra.service.reads.repair.ReadRepair;
import org.apache.cassandra.service.reads.repair.RepairedDataTracker;
import org.apache.cassandra.service.reads.repair.RepairedDataVerifier;
import org.apache.cassandra.transport.Dispatcher;
import static com.google.common.collect.Iterables.*;
@ -64,14 +65,14 @@ public class DataResolver<E extends Endpoints<E>, P extends ReplicaPlan.ForRead<
private final ReadRepair<E, P> readRepair;
private final boolean trackRepairedStatus;
public DataResolver(ReadCommand command, Supplier<? extends P> replicaPlan, ReadRepair<E, P> readRepair, long queryStartNanoTime)
public DataResolver(ReadCommand command, Supplier<? extends P> replicaPlan, ReadRepair<E, P> readRepair, Dispatcher.RequestTime requestTime)
{
this(command, replicaPlan, readRepair, queryStartNanoTime, false);
this(command, replicaPlan, readRepair, requestTime, false);
}
public DataResolver(ReadCommand command, Supplier<? extends P> replicaPlan, ReadRepair<E, P> readRepair, long queryStartNanoTime, boolean trackRepairedStatus)
public DataResolver(ReadCommand command, Supplier<? extends P> replicaPlan, ReadRepair<E, P> readRepair, Dispatcher.RequestTime requestTime, boolean trackRepairedStatus)
{
super(command, replicaPlan, queryStartNanoTime);
super(command, replicaPlan, requestTime);
this.enforceStrictLiveness = command.metadata().enforceStrictLiveness();
this.readRepair = readRepair;
this.trackRepairedStatus = trackRepairedStatus;
@ -208,7 +209,7 @@ public class DataResolver<E extends Endpoints<E>, P extends ReplicaPlan.ForRead<
originalResponse,
command,
context.mergedResultCounter,
queryStartNanoTime,
requestTime,
enforceStrictLiveness)
: originalResponse;
}
@ -251,7 +252,7 @@ public class DataResolver<E extends Endpoints<E>, P extends ReplicaPlan.ForRead<
ReplicaFilteringProtection<E> rfp = new ReplicaFilteringProtection<>(replicaPlan().keyspace(),
command,
replicaPlan().consistencyLevel(),
queryStartNanoTime,
requestTime,
replicas,
DatabaseDescriptor.getCachedReplicaRowsWarnThreshold(),
DatabaseDescriptor.getCachedReplicaRowsFailThreshold());

View File

@ -35,6 +35,7 @@ import org.apache.cassandra.locator.InetAddressAndPort;
import org.apache.cassandra.locator.ReplicaPlan;
import org.apache.cassandra.net.Message;
import org.apache.cassandra.service.reads.repair.NoopReadRepair;
import org.apache.cassandra.transport.Dispatcher;
import org.apache.cassandra.utils.ByteBufferUtil;
import static com.google.common.collect.Iterables.any;
@ -44,9 +45,9 @@ public class DigestResolver<E extends Endpoints<E>, P extends ReplicaPlan.ForRea
{
private volatile Message<ReadResponse> dataResponse;
public DigestResolver(ReadCommand command, ReplicaPlan.Shared<E, P> replicaPlan, long queryStartNanoTime)
public DigestResolver(ReadCommand command, ReplicaPlan.Shared<E, P> replicaPlan, Dispatcher.RequestTime requestTime)
{
super(command, replicaPlan, queryStartNanoTime);
super(command, replicaPlan, requestTime);
Preconditions.checkArgument(command instanceof SinglePartitionReadCommand,
"DigestResolver can only be used with SinglePartitionReadCommand commands");
}
@ -86,7 +87,7 @@ public class DigestResolver<E extends Endpoints<E>, P extends ReplicaPlan.ForRea
// This path can be triggered only if we've got responses from full replicas and they match, but
// transient replica response still contains data, which needs to be reconciled.
DataResolver<E, P> dataResolver
= new DataResolver<>(command, replicaPlan, NoopReadRepair.instance, queryStartNanoTime);
= new DataResolver<>(command, replicaPlan, NoopReadRepair.instance, requestTime);
dataResolver.preprocess(dataResponse);
// Reconcile with transient replicas

View File

@ -24,13 +24,11 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.db.MessageParams;
import org.apache.cassandra.locator.ReplicaPlan;
import org.apache.cassandra.utils.concurrent.Condition;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.db.MessageParams;
import org.apache.cassandra.db.PartitionRangeReadCommand;
import org.apache.cassandra.db.ReadCommand;
import org.apache.cassandra.db.ReadResponse;
@ -39,6 +37,7 @@ import org.apache.cassandra.exceptions.ReadTimeoutException;
import org.apache.cassandra.exceptions.RequestFailureReason;
import org.apache.cassandra.locator.Endpoints;
import org.apache.cassandra.locator.InetAddressAndPort;
import org.apache.cassandra.locator.ReplicaPlan;
import org.apache.cassandra.net.Message;
import org.apache.cassandra.net.ParamType;
import org.apache.cassandra.net.RequestCallback;
@ -47,13 +46,13 @@ import org.apache.cassandra.service.reads.thresholds.CoordinatorWarnings;
import org.apache.cassandra.service.reads.thresholds.WarningContext;
import org.apache.cassandra.service.reads.thresholds.WarningsSnapshot;
import org.apache.cassandra.tracing.Tracing;
import org.apache.cassandra.transport.Dispatcher;
import org.apache.cassandra.utils.concurrent.Condition;
import org.apache.cassandra.utils.concurrent.UncheckedInterruptedException;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.NANOSECONDS;
import static java.util.concurrent.atomic.AtomicIntegerFieldUpdater.newUpdater;
import static org.apache.cassandra.tracing.Tracing.isTracing;
import static org.apache.cassandra.utils.Clock.Global.nanoTime;
import static org.apache.cassandra.utils.concurrent.Condition.newOneTimeCondition;
public class ReadCallback<E extends Endpoints<E>, P extends ReplicaPlan.ForRead<E, P>> implements RequestCallback<ReadResponse>
@ -62,7 +61,7 @@ public class ReadCallback<E extends Endpoints<E>, P extends ReplicaPlan.ForRead<
public final ResponseResolver<E, P> resolver;
final Condition condition = newOneTimeCondition();
private final long queryStartNanoTime;
private final Dispatcher.RequestTime requestTime;
final int blockFor; // TODO: move to replica plan as well?
// this uses a plain reference, but is initialised before handoff to any other threads; the later updates
// may not be visible to the threads immediately, but ReplicaPlan only contains final fields, so they will never see an uninitialised object
@ -76,11 +75,11 @@ public class ReadCallback<E extends Endpoints<E>, P extends ReplicaPlan.ForRead<
private static final AtomicReferenceFieldUpdater<ReadCallback, WarningContext> warningsUpdater
= AtomicReferenceFieldUpdater.newUpdater(ReadCallback.class, WarningContext.class, "warningContext");
public ReadCallback(ResponseResolver<E, P> resolver, ReadCommand command, ReplicaPlan.Shared<E, P> replicaPlan, long queryStartNanoTime)
public ReadCallback(ResponseResolver<E, P> resolver, ReadCommand command, ReplicaPlan.Shared<E, P> replicaPlan, Dispatcher.RequestTime requestTime)
{
this.command = command;
this.resolver = resolver;
this.queryStartNanoTime = queryStartNanoTime;
this.requestTime = requestTime;
this.replicaPlan = replicaPlan;
this.blockFor = replicaPlan.get().readQuorum();
this.failureReasonByEndpoint = new ConcurrentHashMap<>();
@ -96,12 +95,29 @@ public class ReadCallback<E extends Endpoints<E>, P extends ReplicaPlan.ForRead<
return replicaPlan.get();
}
public boolean await(long timePastStart, TimeUnit unit)
public boolean await(long commandTimeout, TimeUnit unit)
{
return awaitUntil(requestTime.computeDeadline(unit.toNanos(commandTimeout)));
}
/**
* In case of speculation, we want to time out the request immediately if we have _also_ hit a deadline.
*
* For example, we have a read timeout of 10s, 99% latency of 5 seconds, native_transport_timeout of 12s,
* and time base is QUEUE:
* * Request has spent 3 seconds in the queue. Here, we will wait for 2 seconds and try to speculate
* * Request has spent 10 seconds in the queue. Here, we will wait for 0 seconds and try to speculate
*
* If the time base is REQUEST:
* * Request has spent 10 seconds in the queue. Here, we will only wait 2 seconds and then try to speculate
*
* We should _not_ speculate in all these cases, since by that time we are already past request deadline.
*/
public boolean awaitUntil(long deadline)
{
long time = unit.toNanos(timePastStart) - (nanoTime() - queryStartNanoTime);
try
{
return condition.await(time, NANOSECONDS);
return condition.awaitUntil(deadline);
}
catch (InterruptedException e)
{

View File

@ -72,6 +72,7 @@ import org.apache.cassandra.service.ClientWarn;
import org.apache.cassandra.service.StorageProxy;
import org.apache.cassandra.service.reads.repair.NoopReadRepair;
import org.apache.cassandra.tracing.Tracing;
import org.apache.cassandra.transport.Dispatcher;
import org.apache.cassandra.utils.NoSpamLogger;
import org.apache.cassandra.utils.btree.BTreeSet;
@ -100,7 +101,7 @@ public class ReplicaFilteringProtection<E extends Endpoints<E>>
private final Keyspace keyspace;
private final ReadCommand command;
private final ConsistencyLevel consistency;
private final long queryStartNanoTime;
private final Dispatcher.RequestTime requestTime;
private final E sources;
private final TableMetrics tableMetrics;
@ -121,7 +122,7 @@ public class ReplicaFilteringProtection<E extends Endpoints<E>>
ReplicaFilteringProtection(Keyspace keyspace,
ReadCommand command,
ConsistencyLevel consistency,
long queryStartNanoTime,
Dispatcher.RequestTime requestTime,
E sources,
int cachedRowsWarnThreshold,
int cachedRowsFailThreshold)
@ -129,7 +130,7 @@ public class ReplicaFilteringProtection<E extends Endpoints<E>>
this.keyspace = keyspace;
this.command = command;
this.consistency = consistency;
this.queryStartNanoTime = queryStartNanoTime;
this.requestTime = requestTime;
this.sources = sources;
this.originalPartitions = new ArrayList<>(sources.size());
@ -148,19 +149,19 @@ public class ReplicaFilteringProtection<E extends Endpoints<E>>
{
@SuppressWarnings("unchecked")
DataResolver<EndpointsForToken, ReplicaPlan.ForTokenRead> resolver =
new DataResolver<>(cmd, replicaPlan, (NoopReadRepair<EndpointsForToken, ReplicaPlan.ForTokenRead>) NoopReadRepair.instance, queryStartNanoTime);
new DataResolver<>(cmd, replicaPlan, (NoopReadRepair<EndpointsForToken, ReplicaPlan.ForTokenRead>) NoopReadRepair.instance, requestTime);
ReadCallback<EndpointsForToken, ReplicaPlan.ForTokenRead> handler = new ReadCallback<>(resolver, cmd, replicaPlan, queryStartNanoTime);
ReadCallback<EndpointsForToken, ReplicaPlan.ForTokenRead> handler = new ReadCallback<>(resolver, cmd, replicaPlan, requestTime);
if (source.isSelf())
{
Stage.READ.maybeExecuteImmediately(new StorageProxy.LocalReadRunnable(cmd, handler));
Stage.READ.maybeExecuteImmediately(new StorageProxy.LocalReadRunnable(cmd, handler, requestTime));
}
else
{
if (source.isTransient())
cmd = cmd.copyAsTransientQuery(source);
MessagingService.instance().sendWithCallback(cmd.createMessage(false), source.endpoint(), handler);
MessagingService.instance().sendWithCallback(cmd.createMessage(false, requestTime), source.endpoint(), handler);
}
// We don't call handler.get() because we want to preserve tombstones since we're still in the middle of merging node results.

View File

@ -27,6 +27,7 @@ import org.apache.cassandra.db.ReadResponse;
import org.apache.cassandra.locator.Endpoints;
import org.apache.cassandra.locator.ReplicaPlan;
import org.apache.cassandra.net.Message;
import org.apache.cassandra.transport.Dispatcher;
import org.apache.cassandra.utils.concurrent.Accumulator;
public abstract class ResponseResolver<E extends Endpoints<E>, P extends ReplicaPlan.ForRead<E, P>>
@ -39,14 +40,14 @@ public abstract class ResponseResolver<E extends Endpoints<E>, P extends Replica
// Accumulator gives us non-blocking thread-safety with optimal algorithmic constraints
protected final Accumulator<Message<ReadResponse>> responses;
protected final long queryStartNanoTime;
protected final Dispatcher.RequestTime requestTime;
public ResponseResolver(ReadCommand command, Supplier<? extends P> replicaPlan, long queryStartNanoTime)
public ResponseResolver(ReadCommand command, Supplier<? extends P> replicaPlan, Dispatcher.RequestTime requestTime)
{
this.command = command;
this.replicaPlan = replicaPlan;
this.responses = new Accumulator<>(replicaPlan.get().readCandidates().size());
this.queryStartNanoTime = queryStartNanoTime;
this.requestTime = requestTime;
}
protected P replicaPlan()

View File

@ -46,6 +46,7 @@ import org.apache.cassandra.net.MessagingService;
import org.apache.cassandra.service.reads.repair.NoopReadRepair;
import org.apache.cassandra.service.StorageProxy;
import org.apache.cassandra.tracing.Tracing;
import org.apache.cassandra.transport.Dispatcher;
public class ShortReadPartitionsProtection extends Transformation<UnfilteredRowIterator> implements MorePartitions<UnfilteredPartitionIterator>
{
@ -62,21 +63,21 @@ public class ShortReadPartitionsProtection extends Transformation<UnfilteredRowI
private boolean partitionsFetched; // whether we've seen any new partitions since iteration start or last moreContents() call
private final long queryStartNanoTime;
private final Dispatcher.RequestTime requestTime;
public ShortReadPartitionsProtection(ReadCommand command,
Replica source,
Runnable preFetchCallback,
DataLimits.Counter singleResultCounter,
DataLimits.Counter mergedResultCounter,
long queryStartNanoTime)
Dispatcher.RequestTime requestTime)
{
this.command = command;
this.source = source;
this.preFetchCallback = preFetchCallback;
this.singleResultCounter = singleResultCounter;
this.mergedResultCounter = mergedResultCounter;
this.queryStartNanoTime = queryStartNanoTime;
this.requestTime = requestTime;
}
@Override
@ -177,18 +178,18 @@ public class ShortReadPartitionsProtection extends Transformation<UnfilteredRowI
private <E extends Endpoints<E>, P extends ReplicaPlan.ForRead<E, P>>
UnfilteredPartitionIterator executeReadCommand(ReadCommand cmd, ReplicaPlan.Shared<E, P> replicaPlan)
{
DataResolver<E, P> resolver = new DataResolver<>(cmd, replicaPlan, (NoopReadRepair<E, P>)NoopReadRepair.instance, queryStartNanoTime);
ReadCallback<E, P> handler = new ReadCallback<>(resolver, cmd, replicaPlan, queryStartNanoTime);
DataResolver<E, P> resolver = new DataResolver<>(cmd, replicaPlan, (NoopReadRepair<E, P>)NoopReadRepair.instance, requestTime);
ReadCallback<E, P> handler = new ReadCallback<>(resolver, cmd, replicaPlan, requestTime);
if (source.isSelf())
{
Stage.READ.maybeExecuteImmediately(new StorageProxy.LocalReadRunnable(cmd, handler));
Stage.READ.maybeExecuteImmediately(new StorageProxy.LocalReadRunnable(cmd, handler, requestTime));
}
else
{
if (source.isTransient())
cmd = cmd.copyAsTransientQuery(source);
MessagingService.instance().sendWithCallback(cmd.createMessage(false), source.endpoint(), handler);
MessagingService.instance().sendWithCallback(cmd.createMessage(false, requestTime), source.endpoint(), handler);
}
// We don't call handler.get() because we want to preserve tombstones since we're still in the middle of merging node results.

View File

@ -25,6 +25,7 @@ import org.apache.cassandra.db.partitions.UnfilteredPartitionIterator;
import org.apache.cassandra.db.transform.MorePartitions;
import org.apache.cassandra.db.transform.Transformation;
import org.apache.cassandra.locator.Replica;
import org.apache.cassandra.transport.Dispatcher;
/**
* We have a potential short read if the result from a given node contains the requested number of rows
@ -43,7 +44,7 @@ public class ShortReadProtection
UnfilteredPartitionIterator partitions,
ReadCommand command,
DataLimits.Counter mergedResultCounter,
long queryStartNanoTime,
Dispatcher.RequestTime requestTime,
boolean enforceStrictLiveness)
{
DataLimits.Counter singleResultCounter = command.limits().newCounter(command.nowInSec(),
@ -56,7 +57,7 @@ public class ShortReadProtection
preFetchCallback,
singleResultCounter,
mergedResultCounter,
queryStartNanoTime);
requestTime);
/*
* The order of extention and transformations is important here. Extending with more partitions has to happen

View File

@ -50,6 +50,7 @@ import org.apache.cassandra.service.reads.DataResolver;
import org.apache.cassandra.service.reads.ReadCallback;
import org.apache.cassandra.service.reads.repair.ReadRepair;
import org.apache.cassandra.tracing.Tracing;
import org.apache.cassandra.transport.Dispatcher;
import org.apache.cassandra.utils.AbstractIterator;
import org.apache.cassandra.utils.CloseableIterator;
@ -66,11 +67,12 @@ public class RangeCommandIterator extends AbstractIterator<RowIterator> implemen
final int totalRangeCount;
final PartitionRangeReadCommand command;
final boolean enforceStrictLiveness;
final long queryStartNanoTime;
final Dispatcher.RequestTime requestTime;
int rangesQueried;
int batchesRequested = 0;
private final long startTime;
private DataLimits.Counter counter;
private PartitionIterator sentQueryIterator;
@ -85,16 +87,14 @@ public class RangeCommandIterator extends AbstractIterator<RowIterator> implemen
int concurrencyFactor,
int maxConcurrencyFactor,
int totalRangeCount,
long queryStartNanoTime)
Dispatcher.RequestTime requestTime)
{
this.replicaPlans = replicaPlans;
this.command = command;
this.concurrencyFactor = concurrencyFactor;
this.maxConcurrencyFactor = maxConcurrencyFactor;
this.totalRangeCount = totalRangeCount;
this.queryStartNanoTime = queryStartNanoTime;
startTime = nanoTime();
this.requestTime = requestTime;
enforceStrictLiveness = command.metadata().enforceStrictLiveness();
}
@ -196,15 +196,15 @@ public class RangeCommandIterator extends AbstractIterator<RowIterator> implemen
ReplicaPlan.SharedForRangeRead sharedReplicaPlan = ReplicaPlan.shared(replicaPlan);
ReadRepair<EndpointsForRange, ReplicaPlan.ForRangeRead> readRepair =
ReadRepair.create(command, sharedReplicaPlan, queryStartNanoTime);
ReadRepair.create(command, sharedReplicaPlan, requestTime);
DataResolver<EndpointsForRange, ReplicaPlan.ForRangeRead> resolver =
new DataResolver<>(rangeCommand, sharedReplicaPlan, readRepair, queryStartNanoTime, trackRepairedStatus);
new DataResolver<>(rangeCommand, sharedReplicaPlan, readRepair, requestTime, trackRepairedStatus);
ReadCallback<EndpointsForRange, ReplicaPlan.ForRangeRead> handler =
new ReadCallback<>(resolver, rangeCommand, sharedReplicaPlan, queryStartNanoTime);
new ReadCallback<>(resolver, rangeCommand, sharedReplicaPlan, requestTime);
if (replicaPlan.contacts().size() == 1 && replicaPlan.contacts().get(0).isSelf())
{
Stage.READ.execute(new StorageProxy.LocalReadRunnable(rangeCommand, handler, trackRepairedStatus));
Stage.READ.execute(new StorageProxy.LocalReadRunnable(rangeCommand, handler, requestTime, trackRepairedStatus));
}
else
{
@ -212,7 +212,7 @@ public class RangeCommandIterator extends AbstractIterator<RowIterator> implemen
{
Tracing.trace("Enqueuing request to {}", replica);
ReadCommand command = replica.isFull() ? rangeCommand : rangeCommand.copyAsTransientQuery(replica);
Message<ReadCommand> message = command.createMessage(trackRepairedStatus && replica.isFull());
Message<ReadCommand> message = command.createMessage(trackRepairedStatus && replica.isFull(), requestTime);
MessagingService.instance().sendWithCallback(message, replica.endpoint(), handler);
}
}
@ -267,7 +267,9 @@ public class RangeCommandIterator extends AbstractIterator<RowIterator> implemen
}
finally
{
long latency = nanoTime() - startTime;
// We track latency based on request processing time, since the amount of time that request spends in the queue
// is not a representative metric of replica performance.
long latency = nanoTime() - requestTime.startedAtNanos();
rangeMetrics.addNano(latency);
rangeMetrics.roundTrips.update(batchesRequested);
Keyspace.openAndGetStore(command.metadata()).metric.coordinatorScanLatency.update(latency, TimeUnit.NANOSECONDS);

View File

@ -35,6 +35,7 @@ import org.apache.cassandra.index.Index;
import org.apache.cassandra.locator.ReplicaPlans;
import org.apache.cassandra.schema.TableMetadata;
import org.apache.cassandra.tracing.Tracing;
import org.apache.cassandra.transport.Dispatcher;
import org.apache.cassandra.utils.FBUtilities;
public class RangeCommands
@ -54,10 +55,10 @@ public class RangeCommands
public static PartitionIterator partitions(PartitionRangeReadCommand command,
ConsistencyLevel consistencyLevel,
long queryStartNanoTime)
Dispatcher.RequestTime requestTime)
{
// Note that in general, a RangeCommandIterator will honor the command limit for each range, but will not enforce it globally.
RangeCommandIterator rangeCommands = rangeCommandIterator(command, consistencyLevel, queryStartNanoTime);
RangeCommandIterator rangeCommands = rangeCommandIterator(command, consistencyLevel, requestTime);
return command.limits().filter(command.postReconciliationProcessing(rangeCommands),
command.nowInSec(),
command.selectsFullPartition(),
@ -67,7 +68,7 @@ public class RangeCommands
@VisibleForTesting
static RangeCommandIterator rangeCommandIterator(PartitionRangeReadCommand command,
ConsistencyLevel consistencyLevel,
long queryStartNanoTime)
Dispatcher.RequestTime requestTime)
{
Tracing.trace("Computing ranges to query");
@ -78,7 +79,7 @@ public class RangeCommands
consistencyLevel);
if (command.isTopK())
return new ScanAllRangesCommandIterator(keyspace, replicaPlans, command, replicaPlans.size(), queryStartNanoTime);
return new ScanAllRangesCommandIterator(keyspace, replicaPlans, command, replicaPlans.size(), requestTime);
int maxConcurrencyFactor = Math.min(replicaPlans.size(), MAX_CONCURRENT_RANGE_REQUESTS);
int concurrencyFactor = maxConcurrencyFactor;
@ -111,7 +112,7 @@ public class RangeCommands
concurrencyFactor,
maxConcurrencyFactor,
replicaPlans.size(),
queryStartNanoTime);
requestTime);
}
/**

View File

@ -40,6 +40,7 @@ import org.apache.cassandra.service.reads.DataResolver;
import org.apache.cassandra.service.reads.ReadCallback;
import org.apache.cassandra.service.reads.repair.NoopReadRepair;
import org.apache.cassandra.tracing.Tracing;
import org.apache.cassandra.transport.Dispatcher;
import org.apache.cassandra.utils.CloseableIterator;
/**
@ -62,9 +63,9 @@ public class ScanAllRangesCommandIterator extends RangeCommandIterator
ScanAllRangesCommandIterator(Keyspace keyspace, CloseableIterator<ReplicaPlan.ForRangeRead> replicaPlans,
PartitionRangeReadCommand command,
int totalRangeCount,
long queryStartNanoTime)
Dispatcher.RequestTime requestTime)
{
super(replicaPlans, command, totalRangeCount, totalRangeCount, totalRangeCount, queryStartNanoTime);
super(replicaPlans, command, totalRangeCount, totalRangeCount, totalRangeCount, requestTime);
Preconditions.checkState(command.isTopK());
this.keyspace = keyspace;
@ -91,14 +92,14 @@ public class ScanAllRangesCommandIterator extends RangeCommandIterator
ReplicaPlan.ForRangeRead plan = ReplicaPlans.forFullRangeRead(keyspace, consistencyLevel, command.dataRange().keyRange(), replicasToQuery, totalRangeCount);
ReplicaPlan.SharedForRangeRead sharedReplicaPlan = ReplicaPlan.shared(plan);
DataResolver<EndpointsForRange, ReplicaPlan.ForRangeRead> resolver = new DataResolver<>(command, sharedReplicaPlan, NoopReadRepair.instance, queryStartNanoTime, false);
ReadCallback<EndpointsForRange, ReplicaPlan.ForRangeRead> handler = new ReadCallback<>(resolver, command, sharedReplicaPlan, queryStartNanoTime);
DataResolver<EndpointsForRange, ReplicaPlan.ForRangeRead> resolver = new DataResolver<>(command, sharedReplicaPlan, NoopReadRepair.instance, requestTime, false);
ReadCallback<EndpointsForRange, ReplicaPlan.ForRangeRead> handler = new ReadCallback<>(resolver, command, sharedReplicaPlan, requestTime);
int nodes = 0;
for (InetAddressAndPort endpoint : replicasToQuery)
{
Tracing.trace("Enqueuing request to {}", endpoint);
Message<ReadCommand> message = command.createMessage(false);
Message<ReadCommand> message = command.createMessage(false, requestTime);
MessagingService.instance().sendWithCallback(message, endpoint, handler);
nodes++;
}

View File

@ -20,12 +20,11 @@ package org.apache.cassandra.service.reads.repair;
import java.util.function.Consumer;
import com.codahale.metrics.Meter;
import com.google.common.base.Preconditions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.codahale.metrics.Meter;
import org.apache.cassandra.concurrent.Stage;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.db.ColumnFamilyStore;
@ -46,6 +45,7 @@ import org.apache.cassandra.service.reads.DataResolver;
import org.apache.cassandra.service.reads.DigestResolver;
import org.apache.cassandra.service.reads.ReadCallback;
import org.apache.cassandra.tracing.Tracing;
import org.apache.cassandra.transport.Dispatcher;
import static java.util.concurrent.TimeUnit.MICROSECONDS;
@ -55,7 +55,7 @@ public abstract class AbstractReadRepair<E extends Endpoints<E>, P extends Repli
protected static final Logger logger = LoggerFactory.getLogger(AbstractReadRepair.class);
protected final ReadCommand command;
protected final long queryStartNanoTime;
protected final Dispatcher.RequestTime requestTime;
protected final ReplicaPlan.Shared<E, P> replicaPlan;
protected final ColumnFamilyStore cfs;
@ -77,10 +77,10 @@ public abstract class AbstractReadRepair<E extends Endpoints<E>, P extends Repli
public AbstractReadRepair(ReadCommand command,
ReplicaPlan.Shared<E, P> replicaPlan,
long queryStartNanoTime)
Dispatcher.RequestTime requestTime)
{
this.command = command;
this.queryStartNanoTime = queryStartNanoTime;
this.requestTime = requestTime;
this.replicaPlan = replicaPlan;
this.cfs = Keyspace.openAndGetStore(command.metadata());
}
@ -96,7 +96,7 @@ public abstract class AbstractReadRepair<E extends Endpoints<E>, P extends Repli
if (to.isSelf())
{
Stage.READ.maybeExecuteImmediately(new StorageProxy.LocalReadRunnable(command, readCallback, trackRepairedStatus));
Stage.READ.maybeExecuteImmediately(new StorageProxy.LocalReadRunnable(command, readCallback, requestTime, trackRepairedStatus));
return;
}
@ -116,7 +116,7 @@ public abstract class AbstractReadRepair<E extends Endpoints<E>, P extends Repli
Tracing.trace("Enqueuing {} data read to {}", type, to);
}
Message<ReadCommand> message = command.createMessage(trackRepairedStatus && to.isFull());
Message<ReadCommand> message = command.createMessage(trackRepairedStatus && to.isFull(), requestTime);
MessagingService.instance().sendWithCallback(message, to.endpoint(), readCallback);
}
@ -139,8 +139,8 @@ public abstract class AbstractReadRepair<E extends Endpoints<E>, P extends Repli
boolean trackRepairedStatus = DatabaseDescriptor.getRepairedDataTrackingForPartitionReadsEnabled();
// Do a full data read to resolve the correct response (and repair node that need be)
DataResolver<E, P> resolver = new DataResolver<>(command, replicaPlan, this, queryStartNanoTime, trackRepairedStatus);
ReadCallback<E, P> readCallback = new ReadCallback<>(resolver, command, replicaPlan, queryStartNanoTime);
DataResolver<E, P> resolver = new DataResolver<>(command, replicaPlan, this, requestTime, trackRepairedStatus);
ReadCallback<E, P> readCallback = new ReadCallback<>(resolver, command, replicaPlan, requestTime);
digestRepair = new DigestRepair<>(resolver, readCallback, resultConsumer);
@ -190,7 +190,7 @@ public abstract class AbstractReadRepair<E extends Endpoints<E>, P extends Repli
if (repair == null)
return;
if (shouldSpeculate() && !repair.readCallback.await(cfs.sampleReadLatencyMicros, MICROSECONDS))
if (shouldSpeculate() && !repair.readCallback.awaitUntil(requestTime.startedAtNanos() + MICROSECONDS.toNanos(cfs.sampleReadLatencyMicros)))
{
Replica uncontacted = replicaPlan().firstUncontactedCandidate(replica -> true);
if (uncontacted == null)

View File

@ -37,6 +37,7 @@ import org.apache.cassandra.locator.Replica;
import org.apache.cassandra.locator.ReplicaPlan;
import org.apache.cassandra.metrics.ReadRepairMetrics;
import org.apache.cassandra.tracing.Tracing;
import org.apache.cassandra.transport.Dispatcher;
import static java.util.concurrent.TimeUnit.MICROSECONDS;
import static java.util.concurrent.TimeUnit.NANOSECONDS;
@ -53,9 +54,9 @@ public class BlockingReadRepair<E extends Endpoints<E>, P extends ReplicaPlan.Fo
protected final Queue<BlockingPartitionRepair> repairs = new ConcurrentLinkedQueue<>();
BlockingReadRepair(ReadCommand command, ReplicaPlan.Shared<E, P> replicaPlan, long queryStartNanoTime)
BlockingReadRepair(ReadCommand command, ReplicaPlan.Shared<E, P> replicaPlan, Dispatcher.RequestTime requestTime)
{
super(command, replicaPlan, queryStartNanoTime);
super(command, replicaPlan, requestTime);
}
public UnfilteredPartitionIterators.MergeListener getMergeListener(P replicaPlan)
@ -84,7 +85,9 @@ public class BlockingReadRepair<E extends Endpoints<E>, P extends ReplicaPlan.Fo
BlockingPartitionRepair timedOut = null;
for (BlockingPartitionRepair repair : repairs)
{
if (!repair.awaitRepairsUntil(DatabaseDescriptor.getReadRpcTimeout(NANOSECONDS) + queryStartNanoTime, NANOSECONDS))
long deadline = requestTime.computeDeadline(DatabaseDescriptor.getReadRpcTimeout(NANOSECONDS));
if (!repair.awaitRepairsUntil(deadline, NANOSECONDS))
{
timedOut = repair;
break;

View File

@ -29,6 +29,7 @@ import org.apache.cassandra.locator.Endpoints;
import org.apache.cassandra.locator.Replica;
import org.apache.cassandra.locator.ReplicaPlan;
import org.apache.cassandra.metrics.ReadRepairMetrics;
import org.apache.cassandra.transport.Dispatcher;
/**
* Only performs the collection of data responses and reconciliation of them, doesn't send repair mutations
@ -37,9 +38,9 @@ import org.apache.cassandra.metrics.ReadRepairMetrics;
public class ReadOnlyReadRepair<E extends Endpoints<E>, P extends ReplicaPlan.ForRead<E, P>>
extends AbstractReadRepair<E, P>
{
ReadOnlyReadRepair(ReadCommand command, ReplicaPlan.Shared<E, P> replicaPlan, long queryStartNanoTime)
ReadOnlyReadRepair(ReadCommand command, ReplicaPlan.Shared<E, P> replicaPlan, Dispatcher.RequestTime requestTime)
{
super(command, replicaPlan, queryStartNanoTime);
super(command, replicaPlan, requestTime);
}
@Override

View File

@ -31,19 +31,20 @@ import org.apache.cassandra.exceptions.ReadTimeoutException;
import org.apache.cassandra.locator.Replica;
import org.apache.cassandra.locator.ReplicaPlan;
import org.apache.cassandra.service.reads.DigestResolver;
import org.apache.cassandra.transport.Dispatcher;
public interface ReadRepair<E extends Endpoints<E>, P extends ReplicaPlan.ForRead<E, P>>
{
public interface Factory
{
<E extends Endpoints<E>, P extends ReplicaPlan.ForRead<E, P>>
ReadRepair<E, P> create(ReadCommand command, ReplicaPlan.Shared<E, P> replicaPlan, long queryStartNanoTime);
ReadRepair<E, P> create(ReadCommand command, ReplicaPlan.Shared<E, P> replicaPlan, Dispatcher.RequestTime requestTime);
}
static <E extends Endpoints<E>, P extends ReplicaPlan.ForRead<E, P>>
ReadRepair<E, P> create(ReadCommand command, ReplicaPlan.Shared<E, P> replicaPlan, long queryStartNanoTime)
ReadRepair<E, P> create(ReadCommand command, ReplicaPlan.Shared<E, P> replicaPlan, Dispatcher.RequestTime requestTime)
{
return command.metadata().params.readRepair.create(command, replicaPlan, queryStartNanoTime);
return command.metadata().params.readRepair.create(command, replicaPlan, requestTime);
}
/**

View File

@ -21,24 +21,25 @@ package org.apache.cassandra.service.reads.repair;
import org.apache.cassandra.db.ReadCommand;
import org.apache.cassandra.locator.Endpoints;
import org.apache.cassandra.locator.ReplicaPlan;
import org.apache.cassandra.transport.Dispatcher;
public enum ReadRepairStrategy implements ReadRepair.Factory
{
NONE
{
public <E extends Endpoints<E>, P extends ReplicaPlan.ForRead<E, P>>
ReadRepair<E, P> create(ReadCommand command, ReplicaPlan.Shared<E, P> replicaPlan, long queryStartNanoTime)
ReadRepair<E, P> create(ReadCommand command, ReplicaPlan.Shared<E, P> replicaPlan, Dispatcher.RequestTime requestTime)
{
return new ReadOnlyReadRepair<>(command, replicaPlan, queryStartNanoTime);
return new ReadOnlyReadRepair<>(command, replicaPlan, requestTime);
}
},
BLOCKING
{
public <E extends Endpoints<E>, P extends ReplicaPlan.ForRead<E, P>>
ReadRepair<E, P> create(ReadCommand command, ReplicaPlan.Shared<E, P> replicaPlan, long queryStartNanoTime)
ReadRepair<E, P> create(ReadCommand command, ReplicaPlan.Shared<E, P> replicaPlan, Dispatcher.RequestTime requestTime)
{
return new BlockingReadRepair<>(command, replicaPlan, queryStartNanoTime);
return new BlockingReadRepair<>(command, replicaPlan, requestTime);
}
};

View File

@ -1337,9 +1337,9 @@ public class NodeProbe implements AutoCloseable
}
}
public void stopNativeTransport()
public void stopNativeTransport(boolean force)
{
ssProxy.stopNativeTransport();
ssProxy.stopNativeTransport(force);
}
public void startNativeTransport()

View File

@ -18,16 +18,19 @@
package org.apache.cassandra.tools.nodetool;
import io.airlift.airline.Command;
import io.airlift.airline.Option;
import org.apache.cassandra.tools.NodeProbe;
import org.apache.cassandra.tools.NodeTool.NodeToolCmd;
@Command(name = "disablebinary", description = "Disable native transport (binary protocol)")
public class DisableBinary extends NodeToolCmd
{
@Option(title = "force", name = { "-f", "--force"}, description = "Use -f to interrupt client requests that have already started")
private boolean force = false;
@Override
public void execute(NodeProbe probe)
{
probe.stopNativeTransport();
probe.stopNativeTransport(force);
}
}

View File

@ -33,6 +33,7 @@ import org.apache.cassandra.db.Mutation;
import org.apache.cassandra.exceptions.OverloadedException;
import org.apache.cassandra.locator.InetAddressAndPort;
import org.apache.cassandra.service.StorageProxy;
import org.apache.cassandra.transport.Dispatcher;
import org.apache.cassandra.utils.JVMStabilityInspector;
import org.apache.cassandra.utils.TimeUUID;
import org.apache.cassandra.utils.concurrent.Future;
@ -40,7 +41,6 @@ import org.apache.cassandra.utils.concurrent.FutureCombiner;
import static java.util.Collections.singletonList;
import static org.apache.cassandra.db.ConsistencyLevel.ANY;
import static org.apache.cassandra.utils.Clock.Global.nanoTime;
/**
* ThreadLocal state for a tracing session. The presence of an instance of this class as a ThreadLocal denotes that an
@ -113,7 +113,7 @@ public class TraceStateImpl extends TraceState
{
try
{
StorageProxy.mutate(singletonList(mutation), ANY, nanoTime());
StorageProxy.mutate(singletonList(mutation), ANY, Dispatcher.RequestTime.forImmediateExecution());
}
catch (OverloadedException e)
{

View File

@ -21,10 +21,9 @@ package org.apache.cassandra.transport;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
import com.google.common.primitives.Ints;
import org.apache.cassandra.transport.ClientResourceLimits.Overload;
import org.apache.cassandra.utils.concurrent.NonBlockingRateLimiter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -33,8 +32,8 @@ import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.exceptions.OverloadedException;
import org.apache.cassandra.metrics.ClientMetrics;
import org.apache.cassandra.metrics.ClientMessageSizeMetrics;
import org.apache.cassandra.metrics.ClientMetrics;
import org.apache.cassandra.net.AbstractMessageHandler;
import org.apache.cassandra.net.FrameDecoder;
import org.apache.cassandra.net.FrameDecoder.IntactFrame;
@ -42,9 +41,11 @@ import org.apache.cassandra.net.FrameEncoder;
import org.apache.cassandra.net.ResourceLimits;
import org.apache.cassandra.net.ResourceLimits.Limit;
import org.apache.cassandra.net.ShareableBytes;
import org.apache.cassandra.transport.ClientResourceLimits.Overload;
import org.apache.cassandra.transport.Flusher.FlushItem.Framed;
import org.apache.cassandra.transport.messages.ErrorMessage;
import org.apache.cassandra.utils.NoSpamLogger;
import org.apache.cassandra.utils.concurrent.NonBlockingRateLimiter;
import static org.apache.cassandra.utils.MonotonicClock.Global.approxTime;
@ -81,6 +82,7 @@ public class CQLMessageHandler<M extends Message> extends AbstractMessageHandler
public static final int LARGE_MESSAGE_THRESHOLD = FrameEncoder.Payload.MAX_SIZE - 1;
public static final TimeUnit RATE_LIMITER_DELAY_UNIT = TimeUnit.NANOSECONDS;
private final QueueBackpressure queueBackpressure;
private final Envelope.Decoder envelopeDecoder;
private final Message.Decoder<M> messageDecoder;
private final FrameEncoder.PayloadAllocator payloadAllocator;
@ -95,7 +97,8 @@ public class CQLMessageHandler<M extends Message> extends AbstractMessageHandler
interface MessageConsumer<M extends Message>
{
void accept(Channel channel, M message, Dispatcher.FlushItemConverter toFlushItem, Overload backpressure);
void dispatch(Channel channel, M message, Dispatcher.FlushItemConverter toFlushItem, Overload backpressure);
boolean hasQueueCapacity();
}
interface ErrorHandler
@ -111,6 +114,7 @@ public class CQLMessageHandler<M extends Message> extends AbstractMessageHandler
MessageConsumer<M> dispatcher,
FrameEncoder.PayloadAllocator payloadAllocator,
int queueCapacity,
QueueBackpressure queueBackpressure,
ClientResourceLimits.ResourceProvider resources,
OnHandlerClosed onClosed,
ErrorHandler errorHandler,
@ -129,6 +133,7 @@ public class CQLMessageHandler<M extends Message> extends AbstractMessageHandler
this.messageDecoder = messageDecoder;
this.payloadAllocator = payloadAllocator;
this.dispatcher = dispatcher;
this.queueBackpressure = queueBackpressure;
this.errorHandler = errorHandler;
this.throwOnOverload = throwOnOverload;
this.version = version;
@ -160,6 +165,7 @@ public class CQLMessageHandler<M extends Message> extends AbstractMessageHandler
* @throws ErrorMessage.WrappedException with an {@link OverloadedException} if overload occurs and the
* connection is configured to throw on overload
*/
@Override
protected boolean processOneContainedMessage(ShareableBytes bytes, Limit endpointReserve, Limit globalReserve)
{
ByteBuffer buf = bytes.get();
@ -178,7 +184,8 @@ public class CQLMessageHandler<M extends Message> extends AbstractMessageHandler
// max CQL message size defaults to 256mb, so should be safe to downcast
int messageSize = Ints.checkedCast(header.bodySizeInBytes);
Overload backpressure = Overload.NONE;
if (throwOnOverload)
{
if (!acquireCapacity(header, endpointReserve, globalReserve))
@ -188,51 +195,61 @@ public class CQLMessageHandler<M extends Message> extends AbstractMessageHandler
}
if (DatabaseDescriptor.getNativeTransportRateLimitingEnabled() && !requestRateLimiter.tryReserve())
backpressure = Overload.REQUESTS;
else if (!dispatcher.hasQueueCapacity())
backpressure = Overload.QUEUE_TIME;
if (backpressure != Overload.NONE)
{
// We've already allocated against the bytes-in-flight limits, so release those resources.
release(header);
discardAndThrow(endpointReserve, globalReserve, buf, header, messageSize, Overload.REQUESTS);
discardAndThrow(endpointReserve, globalReserve, buf, header, messageSize, backpressure);
return true;
}
}
else
{
Overload backpressure = Overload.NONE;
long delay = -1;
if (!acquireCapacityAndQueueOnFailure(header, endpointReserve, globalReserve))
{
if (processRequestAndUpdateMetrics(bytes, header, messageSize, Overload.BYTES_IN_FLIGHT))
{
if (decoder.isActive())
ClientMetrics.instance.pauseConnection();
}
backpressure = Overload.BYTES_IN_FLIGHT;
}
// Apply rate limiting, if enabled
if (DatabaseDescriptor.getNativeTransportRateLimitingEnabled())
{
// Reserve a permit even if we've already triggered backpressure on bytes in flight.
long delay = requestRateLimiter.reserveAndGetDelay(RATE_LIMITER_DELAY_UNIT);
if (backpressure == Overload.NONE && delay > 0)
{
if (processRequestAndUpdateMetrics(bytes, header, messageSize, Overload.REQUESTS))
{
if (decoder.isActive())
ClientMetrics.instance.pauseConnection();
delay = requestRateLimiter.reserveAndGetDelay(RATE_LIMITER_DELAY_UNIT);
// Schedule a wakup here if we process successfully. The connection should be closing otherwise.
if (backpressure == Overload.NONE && delay > 0)
backpressure = Overload.REQUESTS;
}
// Check queue time, if enabled
if (backpressure == Overload.NONE && !dispatcher.hasQueueCapacity())
{
delay = queueBackpressure.markAndGetDelay(RATE_LIMITER_DELAY_UNIT);
if (delay > 0)
backpressure = Overload.QUEUE_TIME;
}
if (backpressure != Overload.NONE)
{
if (processRequestAndUpdateMetrics(bytes, header, messageSize, backpressure))
{
if (decoder.isActive())
ClientMetrics.instance.pauseConnection();
if (delay > 0)
{
// Schedule a wakeup here if we process successfully. The connection should be closing otherwise.
scheduleConnectionWakeupTask(delay, RATE_LIMITER_DELAY_UNIT);
}
backpressure = Overload.REQUESTS;
}
}
// If we triggered backpressure, make sure the caller stops processing frames after the request completes.
if (backpressure != Overload.NONE)
// If we triggered backpressure, make sure the caller stops processing frames after the request completes.
return false;
}
}
return processRequestAndUpdateMetrics(bytes, header, messageSize, Overload.NONE);
@ -252,7 +269,7 @@ public class CQLMessageHandler<M extends Message> extends AbstractMessageHandler
ClientMetrics.instance.markRequestDiscarded();
logOverload(endpointReserve, globalReserve, header, messageSize);
OverloadedException exception = buildOverloadedException(endpointReserve, globalReserve, overload);
OverloadedException exception = buildOverloadedException(endpointReserve, globalReserve, requestRateLimiter, overload);
handleError(exception, header);
// Don't stop processing incoming messages, as we rely on the client to apply
@ -262,14 +279,33 @@ public class CQLMessageHandler<M extends Message> extends AbstractMessageHandler
buf.position(buf.position() + Envelope.Header.LENGTH + messageSize);
}
private OverloadedException buildOverloadedException(Limit endpointReserve, Limit globalReserve, Overload overload) {
return overload == Overload.REQUESTS
? new OverloadedException(String.format("Request breached global limit of %d requests/second. Server is " +
"currently in an overloaded state and cannot accept more requests.",
requestRateLimiter.getRate()))
: new OverloadedException(String.format("Request breached limit on bytes in flight. (Endpoint: %d/%d bytes, Global: %d/%d bytes.) " +
"Server is currently in an overloaded state and cannot accept more requests.",
endpointReserve.using(), endpointReserve.limit(), globalReserve.using(), globalReserve.limit()));
public static OverloadedException buildOverloadedException(Limit endpointReserve, Limit globalReserve, NonBlockingRateLimiter requestRateLimiter, Overload overload)
{
return buildOverloadedException(() -> String.format("Endpoint: %d/%d bytes, Global: %d/%d bytes.", endpointReserve.using(), endpointReserve.limit(),
globalReserve.using(), globalReserve.limit()),
requestRateLimiter,
overload);
}
public static OverloadedException buildOverloadedException(Supplier<String> endpointLimits, NonBlockingRateLimiter requestRateLimiter, Overload overload)
{
switch (overload)
{
case REQUESTS:
return new OverloadedException(String.format("Request breached global limit of %d requests/second. Server is " +
"currently in an overloaded state and cannot accept more requests.",
requestRateLimiter.getRate()));
case BYTES_IN_FLIGHT:
return new OverloadedException(String.format("Request breached limit on bytes in flight. (%s)" +
"Server is currently in an overloaded state and cannot accept more requests.",
endpointLimits.get()));
case QUEUE_TIME:
return new OverloadedException(String.format("Request has spent over %s time of the maximum timeout %dms in the queue",
DatabaseDescriptor.getNativeTransportQueueMaxItemAgeThreshold(),
DatabaseDescriptor.getNativeTransportTimeout(TimeUnit.MILLISECONDS)));
default:
throw new IllegalArgumentException("Overload exception should not have been thrown with " + overload);
}
}
private void logOverload(Limit endpointReserve, Limit globalReserve, Envelope.Header header, int messageSize)
@ -348,7 +384,7 @@ public class CQLMessageHandler<M extends Message> extends AbstractMessageHandler
try
{
message = messageDecoder.decode(channel, request);
dispatcher.accept(channel, message, this::toFlushItem, backpressure);
dispatcher.dispatch(channel, message, this::toFlushItem, backpressure);
// sucessfully delivered a CQL message to the execution
// stage, so reset the counter of consecutive errors
@ -494,55 +530,79 @@ public class CQLMessageHandler<M extends Message> extends AbstractMessageHandler
LargeMessage largeMessage = new LargeMessage(header);
if (!acquireCapacity(header, endpointReserve, globalReserve))
if (throwOnOverload)
{
// In the case of large messages, never stop processing incoming frames
// as this will halt the client meaning no further frames will be sent,
// leading to starvation.
// If the throwOnOverload option is set, don't process the message once
// read, return an error response to notify the client that resource
// limits have been exceeded. If the option isn't set, the only thing we
// can do is to consume the subsequent frames and process the message.
// Large and small messages are never interleaved for a single client, so
// we know that this client will finish sending the large message before
// anything else. Other clients sending small messages concurrently will
// be backpressured by the global resource limits. The server is still
// vulnerable to overload by multiple clients sending large messages
// concurrently.
if (throwOnOverload)
if (!acquireCapacity(header, endpointReserve, globalReserve))
{
// In the case of large messages, never stop processing incoming frames
// as this will halt the client meaning no further frames will be sent,
// leading to starvation.
// If the throwOnOverload option is set, don't process the message once
// read, return an error response to notify the client that resource
// limits have been exceeded. If the option isn't set, the only thing we
// can do is to consume the subsequent frames and process the message.
// Large and small messages are never interleaved for a single client, so
// we know that this client will finish sending the large message before
// anything else. Other clients sending small messages concurrently will
// be backpressured by the global resource limits. The server is still
// vulnerable to overload by multiple clients sending large messages
// concurrently.
// Mark as overloaded so that discard the message after consuming any subsequent frames.
ClientMetrics.instance.markRequestDiscarded();
logOverload(endpointReserve, globalReserve, header, messageSize);
largeMessage.markOverloaded(Overload.BYTES_IN_FLIGHT);
}
}
else if (DatabaseDescriptor.getNativeTransportRateLimitingEnabled())
{
if (throwOnOverload)
else
{
if (!requestRateLimiter.tryReserve())
Overload backpressure = Overload.NONE;
if (DatabaseDescriptor.getNativeTransportRateLimitingEnabled() && !requestRateLimiter.tryReserve())
backpressure = Overload.REQUESTS;
else if (!dispatcher.hasQueueCapacity())
backpressure = Overload.QUEUE_TIME;
if (backpressure != Overload.NONE)
{
ClientMetrics.instance.markRequestDiscarded();
logOverload(endpointReserve, globalReserve, header, messageSize);
// Mark as overloaded so that we discard the message after consuming any subsequent frames.
// (i.e. Request resources we may already have acquired above will be released.)
largeMessage.markOverloaded(Overload.REQUESTS);
largeMessage.markOverloaded(backpressure);
this.largeMessage = largeMessage;
largeMessage.supply(frame);
return true;
}
}
else
}
else
{
if (acquireCapacity(header, endpointReserve, globalReserve))
{
long delay = requestRateLimiter.reserveAndGetDelay(RATE_LIMITER_DELAY_UNIT);
long delay = -1;
Overload backpressure = Overload.NONE;
if (DatabaseDescriptor.getNativeTransportRateLimitingEnabled())
{
delay = requestRateLimiter.reserveAndGetDelay(RATE_LIMITER_DELAY_UNIT);
if (delay > 0)
backpressure = Overload.REQUESTS;
}
if (backpressure == Overload.NONE && !dispatcher.hasQueueCapacity())
{
delay = queueBackpressure.markAndGetDelay(RATE_LIMITER_DELAY_UNIT);
if (delay > 0)
backpressure = Overload.QUEUE_TIME;
}
if (delay > 0)
{
this.largeMessage = largeMessage;
largeMessage.markBackpressure(Overload.REQUESTS);
largeMessage.markBackpressure(backpressure);
largeMessage.supply(frame);
if (decoder.isActive())
@ -553,7 +613,7 @@ public class CQLMessageHandler<M extends Message> extends AbstractMessageHandler
}
}
}
this.largeMessage = largeMessage;
largeMessage.supply(frame);
return true;
@ -695,7 +755,7 @@ public class CQLMessageHandler<M extends Message> extends AbstractMessageHandler
protected void onComplete()
{
if (overload != Overload.NONE)
handleErrorAndRelease(buildOverloadedException(endpointReserveCapacity, globalReserveCapacity, overload), header);
handleErrorAndRelease(buildOverloadedException(endpointReserveCapacity, globalReserveCapacity, requestRateLimiter, overload), header);
else if (!isCorrupt)
processRequest(assembleFrame(), backpressure);
}

View File

@ -45,7 +45,7 @@ public class ClientResourceLimits
public static final NonBlockingRateLimiter GLOBAL_REQUEST_LIMITER = new NonBlockingRateLimiter(getNativeTransportMaxRequestsPerSecond());
public enum Overload { NONE, REQUESTS, BYTES_IN_FLIGHT }
public enum Overload { NONE, REQUESTS, BYTES_IN_FLIGHT, QUEUE_TIME }
public static Allocator getAllocatorForEndpoint(InetAddress endpoint)
{

View File

@ -83,5 +83,6 @@ public class Connection
public interface Tracker
{
void addConnection(Channel ch, Connection connection);
boolean isRunning();
}
}

View File

@ -31,9 +31,10 @@ import org.slf4j.LoggerFactory;
import io.netty.channel.Channel;
import io.netty.channel.EventLoop;
import io.netty.util.AttributeKey;
import org.apache.cassandra.concurrent.DebuggableTask.RunnableDebuggableTask;
import org.apache.cassandra.concurrent.DebuggableTask;
import org.apache.cassandra.concurrent.LocalAwareExecutorPlus;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.exceptions.OverloadedException;
import org.apache.cassandra.metrics.ClientMetrics;
import org.apache.cassandra.net.FrameEncoder;
import org.apache.cassandra.service.ClientWarn;
@ -44,12 +45,12 @@ import org.apache.cassandra.transport.Flusher.FlushItem;
import org.apache.cassandra.transport.messages.ErrorMessage;
import org.apache.cassandra.transport.messages.EventMessage;
import org.apache.cassandra.utils.JVMStabilityInspector;
import org.apache.cassandra.utils.MonotonicClock;
import org.apache.cassandra.utils.NoSpamLogger;
import static org.apache.cassandra.concurrent.SharedExecutorPool.SHARED;
import static org.apache.cassandra.utils.Clock.Global.nanoTime;
public class Dispatcher
public class Dispatcher implements CQLMessageHandler.MessageConsumer<Message.Request>
{
private static final Logger logger = LoggerFactory.getLogger(Dispatcher.class);
@ -99,8 +100,23 @@ public class Dispatcher
this.useLegacyFlusher = useLegacyFlusher;
}
@Override
public void dispatch(Channel channel, Message.Request request, FlushItemConverter forFlusher, Overload backpressure)
{
if (!request.connection().getTracker().isRunning())
{
// We can not respond with a custom, transport, or server exceptions since, given current implementation of clients,
// they will defunct the connection. Without a protocol version bump that introduces an "I am going away message",
// we have to stick to an existing error code.
Message.Response response = ErrorMessage.fromException(new OverloadedException("Server is shutting down"));
response.setStreamId(request.getStreamId());
response.setWarnings(ClientWarn.instance.getWarnings());
response.attach(request.connection);
FlushItem<?> toFlush = forFlusher.toFlushItem(channel, request, response);
flush(toFlush);
return;
}
// if native_transport_max_auth_threads is < 1, don't delegate to new pool on auth messages
boolean isAuthQuery = DatabaseDescriptor.getNativeTransportMaxAuthThreads() > 0 &&
(request.type == Message.Type.AUTH_RESPONSE || request.type == Message.Type.CREDENTIALS);
@ -112,16 +128,170 @@ public class Dispatcher
ClientMetrics.instance.markRequestDispatched();
}
public class RequestProcessor implements RunnableDebuggableTask
public static class RequestTime
{
private final long enqueuedAtNanos;
private final long startedAtNanos;
public RequestTime(long createdAtNanos)
{
this(createdAtNanos, createdAtNanos);
}
public RequestTime(long enqueuedAtNanos, long startedAtNanos)
{
this.enqueuedAtNanos = enqueuedAtNanos;
this.startedAtNanos = startedAtNanos;
}
public static RequestTime forImmediateExecution()
{
return new RequestTime(MonotonicClock.Global.preciseTime.now());
}
public long startedAtNanos()
{
return startedAtNanos;
}
public long enqueuedAtNanos()
{
return enqueuedAtNanos;
}
/**
* Base time is used by timeouts, and can be set to either when the request was added to the queue,
* or when the processing has started, which is controlled by {@link DatabaseDescriptor#getCQLStartTime()}
*
* Since client read/write timeouts are usually aligned with server-side timeouts, it is desireable to use
* enqueue time as a base. However, since client removes the handler `readTimeoutMillis` (which is 12 seconds
* by default), the upper bound for any execution on the coordinator is 12 seconds (thanks to CASSANDRA-7392,
* any replica-side query is capped by the verb timeout), if REQUEST option is used. But even simply allowing
* such long timeouts also implicitly allows queues to grow large, since our queues are currently unbounded.
*
* Latency, however, is _always_ based on request processing time, since the amount of time that request spends
* in the queue is not a representative metric of replica performance.
*/
public long baseTimeNanos()
{
switch (DatabaseDescriptor.getCQLStartTime())
{
case REQUEST:
return startedAtNanos();
case QUEUE:
return enqueuedAtNanos();
default:
throw new IllegalArgumentException("Unknown start time: " + DatabaseDescriptor.getCQLStartTime());
}
}
/**
* Given the current time and a base timeout for the verb return a request's expiration deadline,
* the time at which it becomes eligible for load shedding.
* The two factors to consider are the per-verb and client timeouts. Both are calculated by subtracting the
* time already elapsed during the lifetime of the request from some base value.
*
* When deriving verb timeout, two alternative semantics are available. This timeout may represent either:
* * the total time available for a coordinator to process a client request and return its response
* * a time bound for a coordinator to send internode requests and gather responses from replicas
*
* The point from which elapsed time is measured here is configurable to accommodate these two different
* options. For the former, the clock starts when a client request is received and enqueued by the coordinator.
* For the latter, it starts when the request is dequeued by the coordinator and processing is started.
* See {@link #baseTimeNanos()} for details.
*
* The client timeout represents how long the sender of a request is prepared to wait for a response. By
* implication, after this window has passed any further work done on the server side is wasted effort. Ideally,
* the base for this timeout would be set on a per-request basis but as this not currently supported in the
* protocol, it is configured uniformly for all requests. See {@link DatabaseDescriptor#getNativeTransportTimeout}.
* For this calculation, elapsed time is always measured from the point when a request is received and enqueued.
*
* Where verb timeout is based on queue admission, deadline computation is straightforward. The expiration
* deadline is simply the current time plus the smaller of the verb and client timeouts.
*
* However, if verb timeout is based on when the request is dequeued, the implications are more nuanced.
* In this scenario, while there may still be "headroom" available within the verb timeout, using it could
* exceed the client timeout (which is always based on admission time).
*
* For example:
*
* * Client timeout base is 10 (cb), verb timeout base is 5 (vb)
* * Request is enqueued at t1 (e)
* * Request is dequeued at t8 (d)
* * computeDeadline is called at t9 (n)
*
* If verb timeout is based on dequeuing, there would still some time remaining before a verb-based deadline.
* elapsed = (n - d) ; 1
* timeout = (vb - elapsed) ; 4
* deadline = (n + timeout) ; t13
* ostensibly, the coordinator has until t13 to complete processing
*
* But as client timeout is measured from admission time, the request may exceeded the maximum wait period for
* the client sooner.
* elapsed = (n - e) ; 8
* timeout = (cb - elapsed) ; 2
* deadline = (n + timeout) ; t11
* So the coordinator actually only has until t11 to complete processing, beyond then the client will not accept
* any response.
*
* @param verbExpiresAfterNanos the base timeout value for the verb being executed
* @return the point in time after which no further processing should occur
*/
public long computeDeadline(long verbExpiresAfterNanos)
{
long clientDeadline = clientDeadline();
long verbDeadline = baseTimeNanos() + verbExpiresAfterNanos;
// Whichever one is closer
return Math.min(verbDeadline, clientDeadline);
}
public long computeTimeout(long now, long verbExpiresAfterNanos)
{
return computeDeadline(verbExpiresAfterNanos) - now;
}
/**
* No request should survive native request deadline, but in order to err on the side of caution, we have this
* swtich that allows hints to be submitted to mutation stage when cluster is potentially overloaded. Allowing
* hints to be not bound by deadline can exacerbate overload, but since there are also correctness implications,
* this seemed like a reasonable configuration option.
*/
public boolean shouldSendHints()
{
if (!DatabaseDescriptor.getEnforceNativeDeadlineForHints())
return true;
long now = MonotonicClock.Global.preciseTime.now();
long clientDeadline = clientDeadline();
return now < clientDeadline;
}
public long clientDeadline()
{
return enqueuedAtNanos() + DatabaseDescriptor.getNativeTransportTimeout(TimeUnit.NANOSECONDS);
}
public long timeSpentInQueueNanos()
{
return startedAtNanos - enqueuedAtNanos;
}
}
/**
* It is important to keep this class an instance of {@link DebuggableTask}, either runnable or callable since this
* is the only way we can keep it not wrapped into a callable on SEPExecutor submission path. And we need this
* functionality for tracking time purposes.
*/
public class RequestProcessor implements DebuggableTask.RunnableDebuggableTask
{
private final Channel channel;
private final Message.Request request;
private final FlushItemConverter forFlusher;
private final Overload backpressure;
private final long creationTimeNanos = nanoTime();
private volatile long startTimeNanos;
public RequestProcessor(Channel channel, Message.Request request, FlushItemConverter forFlusher, Overload backpressure)
{
this.channel = channel;
@ -133,14 +303,14 @@ public class Dispatcher
@Override
public void run()
{
startTimeNanos = nanoTime();
processRequest(channel, request, forFlusher, backpressure, startTimeNanos);
startTimeNanos = MonotonicClock.Global.preciseTime.now();
processRequest(channel, request, forFlusher, backpressure, new RequestTime(request.createdAtNanos, startTimeNanos));
}
@Override
public long creationTimeNanos()
{
return creationTimeNanos;
return request.createdAtNanos;
}
@Override
@ -154,14 +324,49 @@ public class Dispatcher
{
return request.toString();
}
@Override
public String toString()
{
return "RequestProcessor{" +
"request=" + request +
", approxStartTimeNanos=" + startTimeNanos +
'}';
}
}
/**
* Checks if the item in the head of the queue has spent more than allowed time in the queue.
*/
@Override
public boolean hasQueueCapacity()
{
double threshold = DatabaseDescriptor.getNativeTransportQueueMaxItemAgeThreshold();
if (threshold <= 0)
return true;
return requestExecutor.oldestTaskQueueTime() < (DatabaseDescriptor.getNativeTransportTimeout(TimeUnit.NANOSECONDS) * threshold);
}
/**
* Note: this method may be executed on the netty event loop, during initial protocol negotiation; the caller is
* responsible for cleaning up any global or thread-local state. (ex. tracing, client warnings, etc.).
*/
private static Message.Response processRequest(ServerConnection connection, Message.Request request, Overload backpressure, long startTimeNanos)
private static Message.Response processRequest(ServerConnection connection, Message.Request request, Overload backpressure, RequestTime requestTime)
{
long queueTime = requestTime.timeSpentInQueueNanos();
// If we have already crossed the max timeout for all possible RPCs, we time out the query immediately.
// We do not differentiate between query types here, since if we got into a situation when, say, we have a PREPARE
// query that is stuck behind the EXECUTE query, we would rather time it out and catch up with a backlog, expecting
// that the bursts are going to be short-lived.
ClientMetrics.instance.queueTime(queueTime, TimeUnit.NANOSECONDS);
if (queueTime > DatabaseDescriptor.getNativeTransportTimeout(TimeUnit.NANOSECONDS))
{
ClientMetrics.instance.markTimedOutBeforeProcessing();
return ErrorMessage.fromException(new OverloadedException("Query timed out before it could start"));
}
if (connection.getVersion().isGreaterOrEqualTo(ProtocolVersion.V4))
ClientWarn.instance.captureWarnings();
@ -170,28 +375,45 @@ public class Dispatcher
if (request.isTrackable())
CoordinatorWarnings.init();
if (backpressure == Overload.REQUESTS)
switch (backpressure)
{
String message = String.format("Request breached global limit of %d requests/second and triggered backpressure.",
ClientResourceLimits.getNativeTransportMaxRequestsPerSecond());
case NONE:
break;
case REQUESTS:
{
String message = String.format("Request breached global limit of %d requests/second and triggered backpressure.",
ClientResourceLimits.getNativeTransportMaxRequestsPerSecond());
NoSpamLogger.log(logger, NoSpamLogger.Level.INFO, 1, TimeUnit.MINUTES, message);
ClientWarn.instance.warn(message);
}
else if (backpressure == Overload.BYTES_IN_FLIGHT)
{
String message = String.format("Request breached limit(s) on bytes in flight (Endpoint: %d, Global: %d) and triggered backpressure.",
ClientResourceLimits.getEndpointLimit(), ClientResourceLimits.getGlobalLimit());
NoSpamLogger.log(logger, NoSpamLogger.Level.INFO, 1, TimeUnit.MINUTES, message);
ClientWarn.instance.warn(message);
break;
}
case BYTES_IN_FLIGHT:
{
String message = String.format("Request breached limit(s) on bytes in flight (Endpoint: %d, Global: %d) and triggered backpressure.",
ClientResourceLimits.getEndpointLimit(), ClientResourceLimits.getGlobalLimit());
NoSpamLogger.log(logger, NoSpamLogger.Level.INFO, 1, TimeUnit.MINUTES, message);
ClientWarn.instance.warn(message);
NoSpamLogger.log(logger, NoSpamLogger.Level.INFO, 1, TimeUnit.MINUTES, message);
ClientWarn.instance.warn(message);
break;
}
case QUEUE_TIME:
{
String message = String.format("Request has spent over %s time of the maximum timeout %dms in the queue",
DatabaseDescriptor.getNativeTransportQueueMaxItemAgeThreshold(),
DatabaseDescriptor.getNativeTransportTimeout(TimeUnit.MILLISECONDS));
NoSpamLogger.log(logger, NoSpamLogger.Level.INFO, 1, TimeUnit.MINUTES, message);
ClientWarn.instance.warn(message);
break;
}
}
QueryState qstate = connection.validateNewMessage(request.type, connection.getVersion());
Message.logger.trace("Received: {}, v={}", request, connection.getVersion());
connection.requests.inc();
Message.Response response = request.execute(qstate, startTimeNanos);
Message.Response response = request.execute(qstate, requestTime);
if (request.isTrackable())
CoordinatorWarnings.done();
@ -206,11 +428,11 @@ public class Dispatcher
/**
* Note: this method may be executed on the netty event loop.
*/
static Message.Response processRequest(Channel channel, Message.Request request, Overload backpressure, long startTimeNanos)
static Message.Response processRequest(Channel channel, Message.Request request, Overload backpressure, RequestTime requestTime)
{
try
{
return processRequest((ServerConnection) request.connection(), request, backpressure, startTimeNanos);
return processRequest((ServerConnection) request.connection(), request, backpressure, requestTime);
}
catch (Throwable t)
{
@ -235,9 +457,9 @@ public class Dispatcher
/**
* Note: this method is not expected to execute on the netty event loop.
*/
void processRequest(Channel channel, Message.Request request, FlushItemConverter forFlusher, Overload backpressure, long startTimeNanos)
void processRequest(Channel channel, Message.Request request, FlushItemConverter forFlusher, Overload backpressure, RequestTime requestTime)
{
Message.Response response = processRequest(channel, request, backpressure, startTimeNanos);
Message.Response response = processRequest(channel, request, backpressure, requestTime);
FlushItem<?> toFlush = forFlusher.toFlushItem(channel, request, response);
Message.logger.trace("Responding: {}, v={}", response, request.connection().getVersion());
flush(toFlush);
@ -259,6 +481,11 @@ public class Dispatcher
flusher.start();
}
public boolean isDone()
{
return requestExecutor.getPendingTaskCount() == 0 && requestExecutor.getActiveTaskCount() == 0;
}
public static void shutdown()
{
requestExecutor.shutdown();
@ -270,7 +497,7 @@ public class Dispatcher
* for delivering events to registered clients is dependent on protocol version and the configuration
* of the pipeline. For v5 and newer connections, the event message is encoded into an Envelope,
* wrapped in a FlushItem and then delivered via the pipeline's flusher, in a similar way to
* a Response returned from {@link #processRequest(Channel, Message.Request, FlushItemConverter, Overload, long)}.
* a Response returned from {@link #processRequest(Channel, Message.Request, FlushItemConverter, Overload, RequestTime)}.
* It's worth noting that events are not generally fired as a direct response to a client request,
* so this flush item has a null request attribute. The dispatcher itself is created when the
* pipeline is first configured during protocol negotiation and is attached to the channel for

View File

@ -26,7 +26,6 @@ import java.util.List;
import java.util.Map;
import org.apache.cassandra.transport.ClientResourceLimits.Overload;
import org.apache.cassandra.utils.MonotonicClock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -149,8 +148,7 @@ public class InitialConnectionHandler extends ByteToMessageDecoder
promise = new VoidChannelPromise(ctx.channel(), false);
}
long approxStartTimeNanos = MonotonicClock.Global.approxTime.now();
final Message.Response response = Dispatcher.processRequest(ctx.channel(), startup, Overload.NONE, approxStartTimeNanos);
final Message.Response response = Dispatcher.processRequest(ctx.channel(), startup, Overload.NONE, Dispatcher.RequestTime.forImmediateExecution());
outbound = response.encode(inbound.header.version);
ctx.writeAndFlush(outbound, promise);

View File

@ -36,6 +36,7 @@ import org.apache.cassandra.service.StorageService;
import org.apache.cassandra.tracing.Tracing;
import org.apache.cassandra.transport.messages.*;
import org.apache.cassandra.service.QueryState;
import org.apache.cassandra.utils.MonotonicClock;
import org.apache.cassandra.utils.ReflectionUtils;
import org.apache.cassandra.utils.TimeUUID;
@ -203,11 +204,13 @@ public abstract class Message
public static abstract class Request extends Message
{
private boolean tracingRequested;
public final long createdAtNanos;
protected Request(Type type)
{
super(type);
createdAtNanos = MonotonicClock.Global.preciseTime.now();
if (type.direction != Direction.REQUEST)
throw new IllegalArgumentException();
}
@ -228,9 +231,9 @@ public abstract class Message
return false;
}
protected abstract Response execute(QueryState queryState, long queryStartNanoTime, boolean traceRequest);
protected abstract Response execute(QueryState queryState, Dispatcher.RequestTime requestTime, boolean traceRequest);
public final Response execute(QueryState queryState, long queryStartNanoTime)
public final Response execute(QueryState queryState, Dispatcher.RequestTime requestTime)
{
boolean shouldTrace = false;
TimeUUID tracingSessionId = null;
@ -253,7 +256,7 @@ public abstract class Message
Response response;
try
{
response = execute(queryState, queryStartNanoTime, shouldTrace);
response = execute(queryState, requestTime, shouldTrace);
}
finally
{
@ -276,6 +279,15 @@ public abstract class Message
{
return tracingRequested;
}
@Override
public String toString()
{
return "Request{" +
"tracingRequested=" + tracingRequested +
", createdAtNanos=" + createdAtNanos +
'}';
}
}
public static abstract class Response extends Message

View File

@ -23,6 +23,7 @@ import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Strings;
import org.slf4j.Logger;
@ -97,16 +98,32 @@ public class PipelineConfigurator
private final boolean keepAlive;
private final EncryptionOptions.TlsEncryptionPolicy tlsEncryptionPolicy;
private final Dispatcher dispatcher;
// Shared between pre-v5 and CQLMessage handlers
private final QueueBackpressure queueBackpressure;
public PipelineConfigurator(boolean epoll,
boolean keepAlive,
boolean legacyFlusher,
EncryptionOptions.TlsEncryptionPolicy encryptionPolicy,
Dispatcher dispatcher)
{
this.epoll = epoll;
this.keepAlive = keepAlive;
this.tlsEncryptionPolicy = encryptionPolicy;
this.dispatcher = dispatcher;
this.queueBackpressure = QueueBackpressure.DEFAULT;
}
@VisibleForTesting
public PipelineConfigurator(boolean epoll,
boolean keepAlive,
boolean useLegacyFlusher,
EncryptionOptions.TlsEncryptionPolicy encryptionPolicy)
{
this.epoll = epoll;
this.keepAlive = keepAlive;
this.tlsEncryptionPolicy = encryptionPolicy;
this.dispatcher = dispatcher(legacyFlusher);
this.dispatcher = new Dispatcher(useLegacyFlusher);
this.queueBackpressure = QueueBackpressure.DEFAULT;
}
public ChannelFuture initializeChannel(final EventLoopGroup workerGroup,
@ -288,7 +305,12 @@ public class PipelineConfigurator
int queueCapacity = DatabaseDescriptor.getNativeTransportReceiveQueueCapacityInBytes();
ClientResourceLimits.ResourceProvider resourceProvider = resourceProvider(resourceAllocator);
AbstractMessageHandler.OnHandlerClosed onClosed = handler -> resourceProvider.release();
boolean throwOnOverload = "1".equals(options.get(StartupMessage.THROW_ON_OVERLOAD));
String fromOptions = options.get(StartupMessage.THROW_ON_OVERLOAD);
boolean throwOnOverload;
if (fromOptions == null)
throwOnOverload = DatabaseDescriptor.getNativeTransportThrowOnOverload();
else
throwOnOverload = "1".equals(fromOptions);
CQLMessageHandler.MessageConsumer<Message.Request> messageConsumer = messageConsumer();
CQLMessageHandler<Message.Request> processor =
@ -300,6 +322,7 @@ public class PipelineConfigurator
messageConsumer,
payloadAllocator,
queueCapacity,
queueBackpressure,
resourceProvider,
onClosed,
errorHandler,
@ -334,7 +357,7 @@ public class PipelineConfigurator
protected CQLMessageHandler.MessageConsumer<Message.Request> messageConsumer()
{
return dispatcher::dispatch;
return dispatcher;
}
protected Message.Decoder<Message.Request> messageDecoder()
@ -368,7 +391,7 @@ public class PipelineConfigurator
pipeline.addBefore(INITIAL_HANDLER, MESSAGE_COMPRESSOR, Envelope.Compressor.instance);
pipeline.addBefore(INITIAL_HANDLER, MESSAGE_DECODER, PreV5Handlers.ProtocolDecoder.instance);
pipeline.addBefore(INITIAL_HANDLER, MESSAGE_ENCODER, PreV5Handlers.ProtocolEncoder.instance);
pipeline.addBefore(INITIAL_HANDLER, LEGACY_MESSAGE_PROCESSOR, new PreV5Handlers.LegacyDispatchHandler(dispatcher, limits));
pipeline.addBefore(INITIAL_HANDLER, LEGACY_MESSAGE_PROCESSOR, new PreV5Handlers.LegacyDispatchHandler(dispatcher, queueBackpressure, limits));
pipeline.remove(INITIAL_HANDLER);
onNegotiationComplete(pipeline);
}

View File

@ -21,9 +21,6 @@ package org.apache.cassandra.transport;
import java.util.List;
import com.google.common.base.Predicate;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.transport.ClientResourceLimits.Overload;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -37,9 +34,11 @@ import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.MessageToMessageDecoder;
import io.netty.handler.codec.MessageToMessageEncoder;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.exceptions.OverloadedException;
import org.apache.cassandra.metrics.ClientMetrics;
import org.apache.cassandra.net.ResourceLimits;
import org.apache.cassandra.transport.ClientResourceLimits.Overload;
import org.apache.cassandra.transport.messages.ErrorMessage;
import org.apache.cassandra.utils.JVMStabilityInspector;
@ -59,6 +58,8 @@ public class PreV5Handlers
private final Dispatcher dispatcher;
private final ClientResourceLimits.Allocator endpointPayloadTracker;
private final QueueBackpressure queueBackpressure;
/**
* Current count of *request* bytes that are live on the channel.
* <p>
@ -69,9 +70,10 @@ public class PreV5Handlers
/** The cause of the current connection pause, or {@link Overload#NONE} if it is unpaused. */
private Overload backpressure = Overload.NONE;
LegacyDispatchHandler(Dispatcher dispatcher, ClientResourceLimits.Allocator endpointPayloadTracker)
LegacyDispatchHandler(Dispatcher dispatcher, QueueBackpressure queueBackpressure, ClientResourceLimits.Allocator endpointPayloadTracker)
{
this.dispatcher = dispatcher;
this.queueBackpressure = queueBackpressure;
this.endpointPayloadTracker = endpointPayloadTracker;
}
@ -152,12 +154,19 @@ public class PreV5Handlers
discardAndThrow(request, requestSize, Overload.BYTES_IN_FLIGHT);
}
Overload backpressure = Overload.NONE;
if (DatabaseDescriptor.getNativeTransportRateLimitingEnabled() && !GLOBAL_REQUEST_LIMITER.tryReserve())
backpressure = Overload.REQUESTS;
else if (!dispatcher.hasQueueCapacity())
backpressure = Overload.QUEUE_TIME;
if (backpressure != Overload.NONE)
{
// We've already allocated against the payload tracker here, so release those resources.
endpointPayloadTracker.release(requestSize);
discardAndThrow(request, requestSize, Overload.REQUESTS);
discardAndThrow(request, requestSize, backpressure);
}
}
else
{
@ -172,20 +181,33 @@ public class PreV5Handlers
backpressure = Overload.BYTES_IN_FLIGHT;
}
long delay = -1;
if (DatabaseDescriptor.getNativeTransportRateLimitingEnabled())
{
// Reserve a permit even if we've already triggered backpressure on bytes in flight.
long delay = GLOBAL_REQUEST_LIMITER.reserveAndGetDelay(RATE_LIMITER_DELAY_UNIT);
delay = GLOBAL_REQUEST_LIMITER.reserveAndGetDelay(RATE_LIMITER_DELAY_UNIT);
// If we've already triggered backpressure on bytes in flight, no further action is necessary.
if (backpressure == Overload.NONE && delay > 0)
{
pauseConnection(ctx);
// A permit isn't immediately available, so schedule an unpause for when it is.
ctx.channel().eventLoop().schedule(() -> unpauseConnection(ctx.channel().config()), delay, RATE_LIMITER_DELAY_UNIT);
backpressure = Overload.REQUESTS;
}
}
if (backpressure == Overload.NONE && !dispatcher.hasQueueCapacity())
{
delay = queueBackpressure.markAndGetDelay(RATE_LIMITER_DELAY_UNIT);
if (delay > 0)
backpressure = Overload.QUEUE_TIME;
}
if (delay > 0)
{
assert backpressure == Overload.REQUESTS || backpressure == Overload.QUEUE_TIME : backpressure;
pauseConnection(ctx);
// A permit isn't immediately available, so schedule an unpause for when it is.
ctx.channel().eventLoop().schedule(() -> unpauseConnection(ctx.channel().config()), delay, RATE_LIMITER_DELAY_UNIT);
}
}
}
@ -219,15 +241,9 @@ public class PreV5Handlers
requestSize, channelPayloadBytesInFlight, endpointPayloadTracker,
GLOBAL_REQUEST_LIMITER, request);
OverloadedException exception = overload == Overload.REQUESTS
? new OverloadedException(String.format("Request breached global limit of %d requests/second. Server is " +
"currently in an overloaded state and cannot accept more requests.",
GLOBAL_REQUEST_LIMITER.getRate()))
: new OverloadedException(String.format("Request breached limit on bytes in flight. (%s)) " +
"Server is currently in an overloaded state and cannot accept more requests.",
endpointPayloadTracker));
OverloadedException exception = CQLMessageHandler.buildOverloadedException(endpointPayloadTracker::toString,
GLOBAL_REQUEST_LIMITER,
overload);
throw ErrorMessage.wrap(exception, request.getSource().header.streamId);
}

View File

@ -0,0 +1,146 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.cassandra.transport;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.LongSupplier;
import com.google.common.annotations.VisibleForTesting;
import org.apache.cassandra.config.DatabaseDescriptor;
import static org.apache.cassandra.utils.MonotonicClock.Global.preciseTime;
/**
* Native Queue Backpressure mechanism. If the queue fills up above {@link DatabaseDescriptor#getNativeTransportQueueMaxItemAgeThreshold()}.
* In other words, request has been sitting more than a % of {@link DatabaseDescriptor#getNativeTransportQueueMaxItemAgeThreshold()} in the queue,
* we start an incident.
* <p>
* Incident starts by marking the incident time and raising severity level to 1. Each time we observe an old item in the head of
* the queue, we first bump then number of times we have applied the backpressure. After bumping it 10 times, we increase severity level by 1.
* <p>
* Backpressure delay applied to the client socket is computed by multiplying the severity level by the minimum delay.
* <p>
* If we have not seen old requests in the head of the queue for 1 second, we close the incident.
* <p>
* If the queue remains saturated for a prolonged period (meaning {@link Dispatcher#hasQueueCapacity} returns false), the amount of delay
* will increase in proportion to the request rate as appliedTimes & severityLevel are incremented. If no new requests are considered
* overloaded in this way for a second, the incident will be reset and so the delay will drop back down to {@link Incident#minDelayNanos()}.
*/
public interface QueueBackpressure
{
QueueBackpressure NO_OP = timeUnit -> 0;
QueueBackpressure DEFAULT = new QueueBackpressure()
{
private final AtomicReference<Incident> state = new AtomicReference<>(noBackpressure(() -> DatabaseDescriptor.getNativeTransportMinBackoffOnQueueOverload(TimeUnit.NANOSECONDS),
() -> DatabaseDescriptor.getNativeTransportMaxBackoffOnQueueOverload(TimeUnit.NANOSECONDS)));
public long markAndGetDelay(TimeUnit timeUnit)
{
return state.updateAndGet(Incident::mark).delay(timeUnit);
}
};
long markAndGetDelay(TimeUnit timeUnit);
static Incident noBackpressure(LongSupplier minDelayNanos, LongSupplier maxDelayNanos)
{
return new Incident(minDelayNanos, maxDelayNanos,
-1, 0, 0);
}
@VisibleForTesting
class Incident
{
private final long appliedAt;
private final int severityLevel;
private final int appliedTimes;
private final LongSupplier minDelayNanos;
private final LongSupplier maxDelayNanos;
@VisibleForTesting
public Incident(LongSupplier minDelayNanos, LongSupplier maxDelayNanos, long appliedAt, int severityLevel, int appliedTimes)
{
this.minDelayNanos = minDelayNanos;
this.maxDelayNanos = maxDelayNanos;
this.appliedAt = appliedAt;
this.severityLevel = severityLevel;
this.appliedTimes = appliedTimes;
}
public Incident mark()
{
return mark(preciseTime.now());
}
@VisibleForTesting
public Incident mark(long now)
{
// Last time we have applied backpressure was over a second ago, consider this a new incident
if (appliedAt > 0 && now - appliedAt >= TimeUnit.SECONDS.toNanos(1))
{
return new Incident(minDelayNanos, maxDelayNanos, now, 1, 1);
}
// Continuing incident: apply backpressure but do not bump severity level yet
else if (appliedTimes < 10)
{
return new Incident(minDelayNanos, maxDelayNanos, now, severityLevel == 0 ? 1 : severityLevel, appliedTimes + 1);
}
//
else
{
return new Incident(minDelayNanos, maxDelayNanos, now, severityLevel + 1, 1);
}
}
public long appliedAt()
{
return appliedAt;
}
public long delay(TimeUnit timeUnit)
{
return timeUnit.convert(Math.min(maxDelayNanos(), severityLevel * minDelayNanos()), TimeUnit.NANOSECONDS);
}
public long minDelayNanos()
{
return minDelayNanos.getAsLong();
}
public long maxDelayNanos()
{
return maxDelayNanos.getAsLong();
}
public String toString()
{
return "QueueBackpressure{" +
"appliedAgo=" + (appliedAt == -1 ? "never" : TimeUnit.NANOSECONDS.toMillis(preciseTime.now() - appliedAt)) +
", severityLevel=" + severityLevel +
", appliedTimes=" + appliedTimes +
", currentDelay=" + delay(TimeUnit.MILLISECONDS) + "ms" +
'}';
}
}
}

View File

@ -22,7 +22,10 @@ import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.LockSupport;
import java.util.function.BooleanSupplier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -64,7 +67,7 @@ public class Server implements CassandraDaemon.Server
private static final Logger logger = LoggerFactory.getLogger(Server.class);
private static final boolean useEpoll = NativeTransportService.useEpoll();
private final ConnectionTracker connectionTracker = new ConnectionTracker();
private final ConnectionTracker connectionTracker;
private final Connection.Factory connectionFactory = new Connection.Factory()
{
@ -79,7 +82,7 @@ public class Server implements CassandraDaemon.Server
private final AtomicBoolean isRunning = new AtomicBoolean(false);
private final PipelineConfigurator pipelineConfigurator;
private final EventLoopGroup workerGroup;
private final Dispatcher dispatcher;
private Server (Builder builder)
{
this.socket = builder.getSocket();
@ -96,14 +99,16 @@ public class Server implements CassandraDaemon.Server
workerGroup = new NioEventLoopGroup();
}
dispatcher = new Dispatcher(DatabaseDescriptor.useNativeTransportLegacyFlusher());
pipelineConfigurator = builder.pipelineConfigurator != null
? builder.pipelineConfigurator
: new PipelineConfigurator(useEpoll,
DatabaseDescriptor.getRpcKeepAlive(),
DatabaseDescriptor.useNativeTransportLegacyFlusher(),
builder.tlsEncryptionPolicy);
builder.tlsEncryptionPolicy,
dispatcher);
EventNotifier notifier = builder.eventNotifier != null ? builder.eventNotifier : new EventNotifier();
connectionTracker = new ConnectionTracker(isRunning::get);
notifier.registerConnectionTracker(connectionTracker);
StorageService.instance.register(notifier);
Schema.instance.registerListener(notifier);
@ -111,8 +116,13 @@ public class Server implements CassandraDaemon.Server
public void stop()
{
if (isRunning.compareAndSet(true, false))
close();
stop(false);
}
public void stop(boolean force)
{
if (isRunning.compareAndSet(true, false))
close(force);
}
public boolean isRunning()
@ -168,8 +178,15 @@ public class Server implements CassandraDaemon.Server
connectionTracker.protocolVersionTracker.clear();
}
private void close()
private void close(boolean force)
{
if (!force)
{
while (!dispatcher.isDone())
LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(100));
}
// Close opened connections
connectionTracker.closeAll();
@ -257,11 +274,13 @@ public class Server implements CassandraDaemon.Server
public final ChannelGroup allChannels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
private final EnumMap<Event.Type, ChannelGroup> groups = new EnumMap<>(Event.Type.class);
private final ProtocolVersionTracker protocolVersionTracker = new ProtocolVersionTracker();
private final BooleanSupplier isRunning;
public ConnectionTracker()
private ConnectionTracker(BooleanSupplier isRunning)
{
for (Event.Type type : Event.Type.values())
groups.put(type, new DefaultChannelGroup(type.toString(), GlobalEventExecutor.INSTANCE));
this.isRunning = isRunning;
}
public void addConnection(Channel ch, Connection connection)
@ -272,6 +291,11 @@ public class Server implements CassandraDaemon.Server
protocolVersionTracker.addConnection(((InetSocketAddress) ch.remoteAddress()).getAddress(), connection.getVersion());
}
public boolean isRunning()
{
return isRunning.getAsBoolean();
}
public void register(Event.Type type, Channel ch)
{
groups.get(type).add(ch);
@ -293,7 +317,7 @@ public class Server implements CassandraDaemon.Server
void closeAll()
{
allChannels.close().awaitUninterruptibly();
allChannels.flush().close().awaitUninterruptibly();
}
int countConnectedClients()

View File

@ -367,6 +367,12 @@ public class SimpleClient implements Closeable
private static class ConnectionTracker implements Connection.Tracker
{
public void addConnection(Channel ch, Connection connection) {}
@Override
public boolean isRunning()
{
return true;
}
}
private static class HandlerNames
@ -447,8 +453,17 @@ public class SimpleClient implements Closeable
FrameEncoder frameEncoder = frameEncoder(ctx);
FrameEncoder.PayloadAllocator payloadAllocator = frameEncoder.allocator();
CQLMessageHandler.MessageConsumer<Message.Response> responseConsumer = (c, message, converter, backpressured) -> {
responseHandler.handleResponse(c, message);
CQLMessageHandler.MessageConsumer<Message.Response> responseConsumer = new CQLMessageHandler.MessageConsumer<Message.Response>()
{
public void dispatch(Channel channel, Message.Response message, Dispatcher.FlushItemConverter toFlushItem, Overload backpressure)
{
responseHandler.handleResponse(channel, message);
}
public boolean hasQueueCapacity()
{
return true;
}
};
CQLMessageHandler.ErrorHandler errorHandler = (error) -> {
@ -503,6 +518,7 @@ public class SimpleClient implements Closeable
responseConsumer,
payloadAllocator,
queueCapacity,
QueueBackpressure.NO_OP,
resources,
handler -> {},
errorHandler,
@ -541,7 +557,7 @@ public class SimpleClient implements Closeable
pipeline.remove(this);
Message.Response message = messageDecoder.decode(ctx.channel(), response);
responseConsumer.accept(channel, message, (ch, req, resp) -> null, Overload.NONE);
responseConsumer.dispatch(channel, message, (ch, req, resp) -> null, Overload.NONE);
}
private FrameDecoder frameDecoder(ChannelHandlerContext ctx, BufferPoolAllocator allocator)

View File

@ -69,7 +69,7 @@ public class AuthResponse extends Message.Request
}
@Override
protected Response execute(QueryState queryState, long queryStartNanoTime, boolean traceRequest)
protected Response execute(QueryState queryState, Dispatcher.RequestTime requestTime, boolean traceRequest)
{
try
{

View File

@ -40,6 +40,7 @@ import org.apache.cassandra.service.ClientState;
import org.apache.cassandra.service.QueryState;
import org.apache.cassandra.tracing.Tracing;
import org.apache.cassandra.transport.CBUtil;
import org.apache.cassandra.transport.Dispatcher;
import org.apache.cassandra.transport.Message;
import org.apache.cassandra.transport.ProtocolException;
import org.apache.cassandra.transport.ProtocolVersion;
@ -169,7 +170,7 @@ public class BatchMessage extends Message.Request
}
@Override
protected Message.Response execute(QueryState state, long queryStartNanoTime, boolean traceRequest)
protected Message.Response execute(QueryState state, Dispatcher.RequestTime requestTime, boolean traceRequest)
{
List<QueryHandler.Prepared> prepared = null;
try
@ -226,7 +227,7 @@ public class BatchMessage extends Message.Request
BatchStatement batch = new BatchStatement(batchType, VariableSpecifications.empty(), statements, Attributes.none());
long queryTime = currentTimeMillis();
Message.Response response = handler.processBatch(batch, state, batchOptions, getCustomPayload(), queryStartNanoTime);
Message.Response response = handler.processBatch(batch, state, batchOptions, getCustomPayload(), requestTime);
if (queries != null)
QueryEvents.instance.notifyBatchSuccess(batchType, statements, queries, values, options, state, queryTime, response);
return response;

View File

@ -35,6 +35,7 @@ import org.apache.cassandra.service.ClientState;
import org.apache.cassandra.service.QueryState;
import org.apache.cassandra.tracing.Tracing;
import org.apache.cassandra.transport.CBUtil;
import org.apache.cassandra.transport.Dispatcher;
import org.apache.cassandra.transport.Message;
import org.apache.cassandra.transport.ProtocolException;
import org.apache.cassandra.transport.ProtocolVersion;
@ -125,7 +126,7 @@ public class ExecuteMessage extends Message.Request
}
@Override
protected Message.Response execute(QueryState state, long queryStartNanoTime, boolean traceRequest)
protected Message.Response execute(QueryState state, Dispatcher.RequestTime requestTime, boolean traceRequest)
{
QueryHandler.Prepared prepared = null;
try
@ -148,7 +149,6 @@ public class ExecuteMessage extends Message.Request
nospam.error(msg);
}
CQLStatement statement = prepared.statement;
options.prepare(statement.getBindVariables());
@ -164,7 +164,7 @@ public class ExecuteMessage extends Message.Request
long requestStartTime = currentTimeMillis();
Message.Response response = handler.processPrepared(statement, state, queryOptions, getCustomPayload(), queryStartNanoTime);
Message.Response response = handler.processPrepared(statement, state, queryOptions, getCustomPayload(), requestTime);
QueryEvents.instance.notifyExecuteSuccess(prepared.statement, prepared.rawCQLStatement, options, state, requestStartTime, response);

View File

@ -27,6 +27,7 @@ import io.netty.buffer.ByteBuf;
import org.apache.cassandra.cql3.QueryProcessor;
import org.apache.cassandra.service.QueryState;
import org.apache.cassandra.transport.Compressor;
import org.apache.cassandra.transport.Dispatcher;
import org.apache.cassandra.transport.Message;
import org.apache.cassandra.transport.ProtocolVersion;
@ -58,7 +59,7 @@ public class OptionsMessage extends Message.Request
}
@Override
protected Message.Response execute(QueryState state, long queryStartNanoTime, boolean traceRequest)
protected Message.Response execute(QueryState state, Dispatcher.RequestTime requestTime, boolean traceRequest)
{
List<String> cqlVersions = new ArrayList<String>();
cqlVersions.add(QueryProcessor.CQL_VERSION.toString());

View File

@ -31,6 +31,7 @@ import org.apache.cassandra.service.ClientState;
import org.apache.cassandra.service.QueryState;
import org.apache.cassandra.tracing.Tracing;
import org.apache.cassandra.transport.CBUtil;
import org.apache.cassandra.transport.Dispatcher;
import org.apache.cassandra.transport.Message;
import org.apache.cassandra.transport.ProtocolVersion;
import org.apache.cassandra.utils.JVMStabilityInspector;
@ -114,7 +115,7 @@ public class PrepareMessage extends Message.Request
}
@Override
protected Message.Response execute(QueryState state, long queryStartNanoTime, boolean traceRequest)
protected Message.Response execute(QueryState state, Dispatcher.RequestTime requestTime, boolean traceRequest)
{
try
{

View File

@ -30,6 +30,7 @@ import org.apache.cassandra.service.ClientState;
import org.apache.cassandra.service.QueryState;
import org.apache.cassandra.tracing.Tracing;
import org.apache.cassandra.transport.CBUtil;
import org.apache.cassandra.transport.Dispatcher;
import org.apache.cassandra.transport.Message;
import org.apache.cassandra.transport.ProtocolException;
import org.apache.cassandra.transport.ProtocolVersion;
@ -98,7 +99,7 @@ public class QueryMessage extends Message.Request
}
@Override
protected Message.Response execute(QueryState state, long queryStartNanoTime, boolean traceRequest)
protected Message.Response execute(QueryState state, Dispatcher.RequestTime requestTime, boolean traceRequest)
{
CQLStatement statement = null;
try
@ -113,7 +114,7 @@ public class QueryMessage extends Message.Request
QueryHandler queryHandler = ClientState.getCQLQueryHandler();
statement = queryHandler.parse(query, state, options);
Message.Response response = queryHandler.process(statement, state, options, getCustomPayload(), queryStartNanoTime);
Message.Response response = queryHandler.process(statement, state, options, getCustomPayload(), requestTime);
QueryEvents.instance.notifyQuerySuccess(statement, query, options, state, queryStartTime, response);
if (options.skipMetadata() && response instanceof ResultMessage.Rows)

View File

@ -63,7 +63,7 @@ public class RegisterMessage extends Message.Request
}
@Override
protected Response execute(QueryState state, long queryStartNanoTime, boolean traceRequest)
protected Response execute(QueryState state, Dispatcher.RequestTime requestTime, boolean traceRequest)
{
assert connection instanceof ServerConnection;
Connection.Tracker tracker = connection.getTracker();

View File

@ -68,7 +68,7 @@ public class StartupMessage extends Message.Request
}
@Override
protected Message.Response execute(QueryState state, long queryStartNanoTime, boolean traceRequest)
protected Message.Response execute(QueryState state, Dispatcher.RequestTime requestTime, boolean traceRequest)
{
String cqlVersion = options.get(CQL_VERSION);
if (cqlVersion == null)

View File

@ -78,7 +78,7 @@ public class DriverBurnTest extends CQLTester
{
QueryMessage queryMessage = QueryMessage.codec.decode(body, version);
return new QueryMessage(queryMessage.query, queryMessage.options) {
protected Message.Response execute(QueryState state, long queryStartNanoTime, boolean traceRequest)
protected Message.Response execute(QueryState state, Dispatcher.RequestTime requestTime, boolean traceRequest)
{
try
{
@ -89,7 +89,7 @@ public class DriverBurnTest extends CQLTester
catch (NumberFormatException e)
{
// for the requests driver issues under the hood
return super.execute(state, queryStartNanoTime, traceRequest);
return super.execute(state, requestTime, traceRequest);
}
}
};
@ -338,7 +338,7 @@ public class DriverBurnTest extends CQLTester
{
QueryMessage queryMessage = QueryMessage.codec.decode(body, version);
return new QueryMessage(queryMessage.query, queryMessage.options) {
protected Message.Response execute(QueryState state, long queryStartNanoTime, boolean traceRequest)
protected Message.Response execute(QueryState state, Dispatcher.RequestTime requestTime, boolean traceRequest)
{
try
{
@ -348,7 +348,7 @@ public class DriverBurnTest extends CQLTester
catch (NumberFormatException e)
{
// for the requests driver issues under the hood
return super.execute(state, queryStartNanoTime, traceRequest);
return super.execute(state, requestTime, traceRequest);
}
}
};

Some files were not shown because too many files have changed in this diff Show More