-
Notifications
You must be signed in to change notification settings - Fork 21
CDNB-15253: make UCS shard progression smoother when num_shards and min sstable size are configured #2031
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Checklist before you submit for review
|
static final boolean USE_FACTORIZATION_SHARD_COUNT_GROWTH = Boolean.parseBoolean(getSystemProperty("use_factorization_shard_count_growth", "true")); | ||
|
||
/** Cache for factorized shard sequences to avoid repeated calculations for the same target values */ | ||
private static final Map<Integer, List<Integer>> shardSequenceCache = new ConcurrentHashMap<>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of making this a static cache, we can calculate it for the given baseShardCount
during initialization (the count is final, so we can make the calculation final as well).
The cost of calculating the factors is not negligible (i.e. we could not do it on every getNumShards
call), but is not as high as to prohibit it being done on every Controller
construction, and it will save the map lookup on every getNumShards
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good idea, updated to private final Optional<int[]> factorizedShardSequence
@VisibleForTesting | ||
boolean useFactorizationShardCountGrowth() | ||
{ | ||
return USE_FACTORIZATION_SHARD_COUNT_GROWTH && sstableGrowthModifier == 1.0 && baseShardCount > 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should always use this calculation whenever baseShardCount
is not a power of two, regardless of sstableGrowthModifier
.
The only reason to not use it for powers of two is that the existing formula provides the same result quicker.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
changed to USE_FACTORIZATION_SHARD_COUNT_GROWTH && baseShardCount > 0 && Integer.bitCount(baseShardCount) != 1
} | ||
else | ||
{ | ||
// Keep original power-of-two logic for dynamic growth mode |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In dynamic growth mode, we also respect baseShardCount
and it makes no sense to not apply the smoother logic there as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
removed this comment..
int result = 1; | ||
for (int shards : shardSequence) | ||
{ | ||
if (shards <= currentCountBasedOnDensity) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not a binary search?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
updated to binary search. Initially I thought iterating array would be as fast because of cpu cache
|
||
// Check odd factors up to sqrt(num) | ||
int sqrtNum = (int) Math.sqrt(num); | ||
for (int factor = 3; factor <= sqrtNum; factor += 2) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can do
for (int factor = 3; factor*factor <= num; factor += 2)
to avoid the square root and also finish quicker by taking into account the reduction of num
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
updated to for (int factor = 3; factor <= num / factor; factor += 2)
. this is safer..
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are right, the multiplication will overflow when the limit is e.g. Integer.MAX_VALUE
.
I'd personally still use 1L * factor * factor <= num
which does not involve division, but it's a moot point since we are performing the modulus for the same division on the next line anyway, and a compiler could optimize one of them away.
List<Integer> primes = primeFactors(target); | ||
|
||
// 2) Sort factors in descending order | ||
primes.sort(Comparator.reverseOrder()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can use primes.reversed()
or just do the loop by decreasing index.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
updated to loop by decreasing index
primes.sort(Comparator.reverseOrder()); | ||
|
||
// 3) Cumulative product to form the chain. | ||
List<Integer> divisors = new ArrayList<>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would use int[]
(we know the size at this point).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
updated to int[]
0e0a45e
to
8fff3f4
Compare
|
||
// insertion point | ||
int insertionPoint = -idx - 1; | ||
int candidateIndex = Math.max(0, insertionPoint - 1); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: max
is not needed here (-(-1) -1
is 0). Generally it could result in sequence.length
, but that will happen only if the count is larger than the base shard count, which won't end up in this function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, -idx - 1
is always positive. It's searching for the value before insertion point via insertionPoint - 1
, so it uses max
when insertionPoint
is 0.
// Test 3200 = 5^2 * 2^7 | ||
int[] expected3200 = new int[]{ 1, 5, 25, 50, 100, 200, 400, 800, 1600, 3200 }; | ||
assertArrayEquals(expected3200, Controller.factorizedSmoothShardSequence(3200)); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's add a test with Integer.MAX_VALUE
as well.
|
||
// Check odd factors up to sqrt(num) | ||
int sqrtNum = (int) Math.sqrt(num); | ||
for (int factor = 3; factor <= sqrtNum; factor += 2) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are right, the multiplication will overflow when the limit is e.g. Integer.MAX_VALUE
.
I'd personally still use 1L * factor * factor <= num
which does not involve division, but it's a moot point since we are performing the modulus for the same division on the next line anyway, and a compiler could optimize one of them away.
* use prime factorization to create smooth sequences like 1→5→25→125→250→500→1000 for num_shards=1000. | ||
* This prevents the large jumps that can cause data loss issues (e.g., HCD-130). | ||
* <p> | ||
* Only applies when num_shards is explicitly configured |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This line is no longer correct, and maybe the first line of the doc should also be changed to base_shard_count
not being a power of two.
src/java/org/apache/cassandra/db/compaction/unified/Controller.java
Outdated
Show resolved
Hide resolved
…nt growth when num_shards and min sstable size are configured * Replace power-of-two shard progression with factorization-based smooth growth when num_shards is explicitly configured. This prevents problematic large jumps that can cause data loss (e.g., HCD-130). For example, with num_shards 1000 which is 5^3* 2^3: Before: 1 → 2 → 8 → 1000 (125x jump causes data loss) After: 1 → 5 → 25 → 125 → 250 → 500 → 1000 (max 5x jump) * new behavior can be disabled via `-Dunified_compaction.use_factorization_shard_count_growth`
d66ce6f
to
49cca51
Compare
|
❌ Build ds-cassandra-pr-gate/PR-2031 rejected by Butler2 regressions found Found 2 new test failures
Found 1 known test failures |
…shards and min sstable size are configured (#2031) riptano/cndb#15253 large shard count jump was involved in HCD-130 Replace power-of-two shard progression with factorization-based smooth growth when base shard count is not power of 2. This prevents problematic large jumps. For example, with num_shards 1000 which is 5^3* 2^3: - Before: 1 → 2 → 8 → 1000 (125x jump causes data loss due to hcd-130) - After: 1 → 5 → 25 → 125 → 250 → 500 → 1000 (max 5x jump) * new behavior can be disabled via `-Dunified_compaction.use_factorization_shard_count_growth`
…shards and min sstable size are configured (#2031) riptano/cndb#15253 large shard count jump was involved in HCD-130 Replace power-of-two shard progression with factorization-based smooth growth when base shard count is not power of 2. This prevents problematic large jumps. For example, with num_shards 1000 which is 5^3* 2^3: - Before: 1 → 2 → 8 → 1000 (125x jump causes data loss due to hcd-130) - After: 1 → 5 → 25 → 125 → 250 → 500 → 1000 (max 5x jump) * new behavior can be disabled via `-Dunified_compaction.use_factorization_shard_count_growth`
What is the issue
https://github.com/riptano/cndb/issues/15253 large shard count jump caused HCD-130
What does this PR fix and why was it fixed
Replace power-of-two shard progression with factorization-based smooth growth when num_shards is explicitly configured. This prevents problematic large jumps that can cause data loss (e.g., HCD-130). For example, with num_shards 1000 which is 5^3* 2^3:
-Dunified_compaction.use_factorization_shard_count_growth