Skip to content

CASSANDRA-21605: Improve algorithmic complexity for finding intersecting SSTables in LCS - #5135

Open
cheeeee wants to merge 1 commit into
apache:trunkfrom
cheeeee:CASSANDRA-21605-trunk
Open

CASSANDRA-21605: Improve algorithmic complexity for finding intersecting SSTables in LCS#5135
cheeeee wants to merge 1 commit into
apache:trunkfrom
cheeeee:CASSANDRA-21605-trunk

Conversation

@cheeeee

@cheeeee cheeeee commented Sep 9, 2026

Copy link
Copy Markdown

Jira Issue

Motivation

In LeveledCompactionStrategy, SSTables in levels L1 through L8 are stored in a TreeSet and are strictly disjoint and sorted by first key. However, LeveledManifest.getCandidatesFor(level) previously performed an $O(N)$ linear scan over all SSTables in the next level, constructing an intermediate HashMap of bounds for the entire level (genBounds) on every candidate iteration. On large tables with thousands of SSTables per level, this resulted in $O(N \times M)$ comparisons and heavy garbage generation.

Changes

  1. $O(\log N + K)$ Binary Search (LeveledGenerations): Added getOverlapping(level, sstable). For levels $\ge 1$, queries the level's TreeSet using sortedLevel.floor(sstable) to locate the sole predecessor that could potentially overlap, then iterates forward via tailSet, breaking immediately once candidate.first.token > sstable.last.token.
  2. Zero-Garbage Selection (LeveledManifest): Eliminated the intermediate genBounds HashMap creation and invoked generations.getOverlapping(level + 1, sstable).
  3. Complexity Reduction: Candidate intersection checks drop from $O(N \times M)$ to $O(N \log M + N \times K)$, where $K \ll M$ is the number of overlapping tables.

Testing

  • LeveledGenerationsTest: 5/5 tests pass (added testGetOverlapping covering exact boundaries, multi-table spans, gaps between tables, floor-only, successor-only, and empty levels).
  • LeveledCompactionStrategyTest: full test suite passes cleanly (18/18 tests).

…CS (CASSANDRA-21605)

In LeveledCompactionStrategy, SSTables in levels L1 through L8 are stored
in a TreeSet and are strictly disjoint and sorted by first key.
However, LeveledManifest.getCandidatesFor(level) previously performed an
O(N) linear scan over all sstables in the next level (generating an intermediate
HashMap of bounds for the entire level via genBounds on every candidate iteration).

This patch replaces the O(N) scan with an O(log N + K) binary search:
1. LeveledGenerations: add getOverlapping(level, sstable) which queries
   the level's TreeSet using sortedLevel.floor(sstable), checking if the
   predecessor extends into the target bounds, and then scanning forward
   with an early break as soon as candidate.first.token > sstable.last.token.
2. LeveledManifest: eliminate intermediate genBounds HashMap creation and
   invoke generations.getOverlapping(level + 1, sstable).
3. Unit tests: comprehensive testGetOverlapping in LeveledGenerationsTest
   covering exact matches, multi-table spans, gaps, floor-only, successor-only,
   empty levels, and Level 0.

Fixes: CASSANDRA-21605
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant