Query tracing

To help troubleshooting performance, Cassandra offers the ability to trace a query, in other words capture detailed information about the the internal operations performed by all nodes in the cluster in order to build the response.

The driver provides a way to enable tracing on a particular statement, and an API to examine the results.

Enabling tracing

Set the tracing flag on the Statement instance. There are various ways depending on how you build it (see statements for more details):

// Setter-based:
Statement statement =
  SimpleStatement.newInstance("SELECT * FROM users WHERE id = 1234").setTracing(true);

// Builder-based:
Statement statement =
  SimpleStatement.builder("SELECT * FROM users WHERE id = 1234").setTracing().build();

Tracing is supposed to be run on a small percentage of requests only. Do not enable it on every request, you would risk overwhelming your cluster.

Retrieving tracing data

Once you’ve executed a statement with tracing enabled, tracing data is available through the ExecutionInfo:

ResultSet rs = session.execute(statement);
ExecutionInfo executionInfo = rs.getExecutionInfo();

Tracing id

Cassandra assigns a unique identifier to each query trace. It is returned with the query results, and therefore available immediately:

UUID tracingId = executionInfo.getTracingId();

This is the primary key in the system_traces.sessions and system_traces.events tables where Cassandra stores tracing data (you don’t need to query those tables manually, see the next section).

If you call getTracingId() for a statement that didn’t have tracing enabled, the resulting id will be null.

Tracing information

To get to the details of the trace, retrieve the QueryTrace instance:

QueryTrace trace = executionInfo.getQueryTrace();

// Or asynchronous equivalent:
CompletionStage<QueryTrace> traceFuture = executionInfo.getQueryTraceAsync();

This triggers background queries to fetch the information from the system_traces.sessions and system_traces.events tables. Because Cassandra writes that information asynchronously, it might not be immediately available, therefore the driver will retry a few times if necessary. You can control this behavior through the configuration:

# These options can be changed at runtime, the new values will be used for requests issued after
# the change. They can be overridden in a profile.
datastax-java-driver.advanced.request.trace {
  # How many times the driver will attempt to fetch the query if it is not ready yet.
  attempts = 5

  # The interval between each attempt.
  interval = 3 milliseconds

  # The consistency level to use for trace queries.
  # Note that the default replication strategy for the system_traces keyspace is SimpleStrategy
  # with RF=2, therefore LOCAL_ONE might not work if the local DC has no replicas for a given
  # trace id.
  consistency = ONE

Once you have the QueryTrace object, access its properties for relevant information, for example:

    "'%s' to %s took %dμs%n",
    trace.getRequestType(), trace.getCoordinator(), trace.getDurationMicros());
for (TraceEvent event : trace.getEvents()) {
      "  %d - %s - %s%n",
      event.getSourceElapsedMicros(), event.getSource(), event.getActivity());

If you call getQueryTrace() for a statement that didn’t have tracing enabled, an exception is thrown.