apache/cassandra · error · TombstoneAbortException

TombstoneAbortException

Error message

TombstoneAbortException

What it means

WarningsSnapshot.maybeAbort throws TombstoneAbortException when the coordinator's collected warnings show the query hit the tombstone abort threshold (tombstone_failure_threshold) — too many tombstones encountered while reading. Unlike the warning threshold, the abort threshold fails the read outright to protect nodes from tombstone-heavy scans.

Source

Thrown at src/java/org/apache/cassandra/service/reads/thresholds/WarningsSnapshot.java:110

    {
        return this != EMPTY;
    }

    @VisibleForTesting
    WarningsSnapshot merge(WarningsSnapshot other)
    {
        if (other == null || other == EMPTY)
            return this;
        return WarningsSnapshot.create(tombstones.merge(other.tombstones),
                                       localReadSize.merge(other.localReadSize),
                                       rowIndexReadSize.merge(other.rowIndexReadSize),
                                       indexReadSSTablesCount.merge(other.indexReadSSTablesCount));
    }

    public void maybeAbort(ReadCommand command, ConsistencyLevel cl, int received, int blockFor, boolean isDataPresent, Map<InetAddressAndPort, RequestFailureReason> failureReasonByEndpoint)
    {
        if (!tombstones.aborts.instances.isEmpty())
            throw new TombstoneAbortException(tombstoneAbortMessage(tombstones.aborts.instances.size(), tombstones.aborts.maxValue, command.toCQLString()), tombstones.aborts.instances.size(), tombstones.aborts.maxValue, isDataPresent,
                                              cl, received, blockFor, failureReasonByEndpoint);

        if (!localReadSize.aborts.instances.isEmpty())
            throw new ReadSizeAbortException(localReadSizeAbortMessage(localReadSize.aborts.instances.size(), localReadSize.aborts.maxValue, command.toCQLString()),
                                             cl, received, blockFor, isDataPresent, failureReasonByEndpoint);

        if (!rowIndexReadSize.aborts.instances.isEmpty())
            throw new ReadSizeAbortException(rowIndexReadSizeAbortMessage(rowIndexReadSize.aborts.instances.size(), rowIndexReadSize.aborts.maxValue, command.toCQLString()),
                                             cl, received, blockFor, isDataPresent, failureReasonByEndpoint);

        if (!indexReadSSTablesCount.aborts.instances.isEmpty())
            throw new QueryReferencesTooManyIndexesAbortException(tooManyIndexesReadAbortMessage(indexReadSSTablesCount.aborts.instances.size(), indexReadSSTablesCount.aborts.maxValue, command.toCQLString()),
                                                                  indexReadSSTablesCount.aborts.instances.size(),
                                                                  indexReadSSTablesCount.aborts.maxValue,
                                                                  isDataPresent,
                                                                  cl, received, blockFor, failureReasonByEndpoint);
    }

View on GitHub (pinned to 88fd0f6a0e)

Solutions

  1. Run nodetool compact (or major compaction / repair-assisted) on affected tables to purge tombstones
  2. Reduce TTL churn or increase compaction throughput so tombstones get purged promptly
  3. Raise tombstone_failure_threshold / tombstone_warn_threshold in cassandra.yaml only if justified
  4. Rewrite queries to touch narrower partitions and avoid scanning tombstone-dense ranges

Example fix

// before
$ nodetool compact ks tbl   # tombstones still accumulating
// after
ALTER TABLE ks.tbl WITH compaction = {'class':'SizeTieredCompactionStrategy','tombstone_threshold':'0.2'};
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-flight: check tombstone density before scanning
// shell: nodetool tablehistograms ks tbl  — if 'Tombstones per slice' p99 approaches the threshold, compact first

Try / catch

try {
  return session.execute(scanQuery);
} catch (e) {
  if (e instanceof TombstoneAbortException) {
    logger.warn('tombstone abort on query; compacting table and retrying later', e);
    scheduleCompaction(keyspace, table);
    throw e; // do not blindly retry a doomed scan
  }
  throw e;
}

Prevention

When it happens

Trigger: A read command (e.g. big partition scan or filtering query) visits more tombstones than tombstone_failure_threshold (default 100000) across replicas; awaitResults calls maybeAbort and the snapshot contains abort instances.

Common situations: Tables with heavy delete/overwrite churn never compacted; TTL-expired data piling up; queries scanning expired cells with gc_grace not yet elapsed; thresholds too low for legitimately tombstone-heavy workloads.

Related errors


AI-assisted analysis of apache/cassandra@88fd0f6a0e (2026-09-10). Data as JSON: /api/errors/1fecce6fa2402ffe. Report an issue: GitHub.