{"record":{"id":"1fecce6fa2402ffe","repo":"apache/cassandra","slug":"tombstoneabortexception","errorCode":null,"errorMessage":"TombstoneAbortException","messagePattern":"TombstoneAbortException","errorType":"exception","errorClass":"TombstoneAbortException","httpStatus":null,"severity":"error","filePath":"src/java/org/apache/cassandra/service/reads/thresholds/WarningsSnapshot.java","lineNumber":110,"sourceCode":"    {\n        return this != EMPTY;\n    }\n\n    @VisibleForTesting\n    WarningsSnapshot merge(WarningsSnapshot other)\n    {\n        if (other == null || other == EMPTY)\n            return this;\n        return WarningsSnapshot.create(tombstones.merge(other.tombstones),\n                                       localReadSize.merge(other.localReadSize),\n                                       rowIndexReadSize.merge(other.rowIndexReadSize),\n                                       indexReadSSTablesCount.merge(other.indexReadSSTablesCount));\n    }\n\n    public void maybeAbort(ReadCommand command, ConsistencyLevel cl, int received, int blockFor, boolean isDataPresent, Map<InetAddressAndPort, RequestFailureReason> failureReasonByEndpoint)\n    {\n        if (!tombstones.aborts.instances.isEmpty())\n            throw new TombstoneAbortException(tombstoneAbortMessage(tombstones.aborts.instances.size(), tombstones.aborts.maxValue, command.toCQLString()), tombstones.aborts.instances.size(), tombstones.aborts.maxValue, isDataPresent,\n                                              cl, received, blockFor, failureReasonByEndpoint);\n\n        if (!localReadSize.aborts.instances.isEmpty())\n            throw new ReadSizeAbortException(localReadSizeAbortMessage(localReadSize.aborts.instances.size(), localReadSize.aborts.maxValue, command.toCQLString()),\n                                             cl, received, blockFor, isDataPresent, failureReasonByEndpoint);\n\n        if (!rowIndexReadSize.aborts.instances.isEmpty())\n            throw new ReadSizeAbortException(rowIndexReadSizeAbortMessage(rowIndexReadSize.aborts.instances.size(), rowIndexReadSize.aborts.maxValue, command.toCQLString()),\n                                             cl, received, blockFor, isDataPresent, failureReasonByEndpoint);\n\n        if (!indexReadSSTablesCount.aborts.instances.isEmpty())\n            throw new QueryReferencesTooManyIndexesAbortException(tooManyIndexesReadAbortMessage(indexReadSSTablesCount.aborts.instances.size(), indexReadSSTablesCount.aborts.maxValue, command.toCQLString()),\n                                                                  indexReadSSTablesCount.aborts.instances.size(),\n                                                                  indexReadSSTablesCount.aborts.maxValue,\n                                                                  isDataPresent,\n                                                                  cl, received, blockFor, failureReasonByEndpoint);\n    }\n","sourceCodeStart":92,"sourceCodeEnd":128,"githubUrl":"https://github.com/apache/cassandra/blob/88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1/src/java/org/apache/cassandra/service/reads/thresholds/WarningsSnapshot.java#L92-L128","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Run nodetool compact (or major compaction / repair-assisted) on affected tables to purge tombstones","Reduce TTL churn or increase compaction throughput so tombstones get purged promptly","Raise tombstone_failure_threshold / tombstone_warn_threshold in cassandra.yaml only if justified","Rewrite queries to touch narrower partitions and avoid scanning tombstone-dense ranges"],"exampleFix":"// before\n$ nodetool compact ks tbl   # tombstones still accumulating\n// after\nALTER TABLE ks.tbl WITH compaction = {'class':'SizeTieredCompactionStrategy','tombstone_threshold':'0.2'};","handlingStrategy":"try-catch","validationCode":"// Pre-flight: check tombstone density before scanning\n// shell: nodetool tablehistograms ks tbl  — if 'Tombstones per slice' p99 approaches the threshold, compact first","typeGuard":null,"tryCatchPattern":"try {\n  return session.execute(scanQuery);\n} catch (e) {\n  if (e instanceof TombstoneAbortException) {\n    logger.warn('tombstone abort on query; compacting table and retrying later', e);\n    scheduleCompaction(keyspace, table);\n    throw e; // do not blindly retry a doomed scan\n  }\n  throw e;\n}","preventionTips":["Monitor tombstone_warn_threshold hits in logs — fix before they become aborts","Ensure compaction keeps up with delete/TTL churn (tombstone_threshold/tombstone_compaction_interval)","Avoid unbounded partition scans on churn-heavy tables","Raise tombstone_failure_threshold only after fixing compaction, not instead of it"],"tags":["tombstones","read-path","aborted-query","compaction"],"backgroundTag":"tombstone-threshold-exceeded","analyzedSha":"88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1","analyzedAt":"2026-09-10T07:29:22.284Z","contentChangedAt":"2026-09-10T07:29:22.284Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}