Skip to content

Commit 61014f2

Browse files
nikhilkumawat03dcapwell
authored andcommitted
Leveled Compaction doesn't validate maxBytesForLevel when the table is altered/created
patch by Nikhil Kumawat, Nikhil; reviewed by David Capwell, guo Maxwell for CASSANDRA-20570
1 parent 2d7e99f commit 61014f2

File tree

4 files changed

+60
-4
lines changed

4 files changed

+60
-4
lines changed

CHANGES.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
4.0.20
2+
* Leveled Compaction doesn't validate maxBytesForLevel when the table is altered/created (CASSANDRA-20570)
23
* Updated dtest-api to 0.0.18 and removed JMX-related classes that now live in the dtest-api (CASSANDRA-20884)
34

45
4.0.19

src/java/org/apache/cassandra/db/compaction/LeveledCompactionStrategy.java

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
package org.apache.cassandra.db.compaction;
1919

2020
import java.util.*;
21+
import java.math.BigInteger;
2122

2223

2324
import com.google.common.annotations.VisibleForTesting;
@@ -43,6 +44,8 @@
4344
import org.apache.cassandra.io.sstable.ISSTableScanner;
4445
import org.apache.cassandra.io.sstable.format.SSTableReader;
4546

47+
import static org.apache.cassandra.db.compaction.LeveledGenerations.MAX_LEVEL_COUNT;
48+
4649
public class LeveledCompactionStrategy extends AbstractCompactionStrategy
4750
{
4851
private static final Logger logger = LoggerFactory.getLogger(LeveledCompactionStrategy.class);
@@ -560,10 +563,14 @@ public static Map<String, String> validateOptions(Map<String, String> options) t
560563
{
561564
Map<String, String> uncheckedOptions = AbstractCompactionStrategy.validateOptions(options);
562565

566+
int ssSize;
567+
int fanoutSize;
568+
569+
// Validate the sstable_size option
563570
String size = options.containsKey(SSTABLE_SIZE_OPTION) ? options.get(SSTABLE_SIZE_OPTION) : "1";
564571
try
565572
{
566-
int ssSize = Integer.parseInt(size);
573+
ssSize = Integer.parseInt(size);
567574
if (ssSize < 1)
568575
{
569576
throw new ConfigurationException(String.format("%s must be larger than 0, but was %s", SSTABLE_SIZE_OPTION, ssSize));
@@ -580,15 +587,31 @@ public static Map<String, String> validateOptions(Map<String, String> options) t
580587
String levelFanoutSize = options.containsKey(LEVEL_FANOUT_SIZE_OPTION) ? options.get(LEVEL_FANOUT_SIZE_OPTION) : String.valueOf(DEFAULT_LEVEL_FANOUT_SIZE);
581588
try
582589
{
583-
int fanoutSize = Integer.parseInt(levelFanoutSize);
590+
fanoutSize = Integer.parseInt(levelFanoutSize);
584591
if (fanoutSize < 1)
585592
{
586593
throw new ConfigurationException(String.format("%s must be larger than 0, but was %s", LEVEL_FANOUT_SIZE_OPTION, fanoutSize));
587594
}
588595
}
589596
catch (NumberFormatException ex)
590597
{
591-
throw new ConfigurationException(String.format("%s is not a parsable int (base10) for %s", size, LEVEL_FANOUT_SIZE_OPTION), ex);
598+
throw new ConfigurationException(String.format("%s is not a parsable int (base10) for %s", levelFanoutSize, LEVEL_FANOUT_SIZE_OPTION), ex);
599+
}
600+
601+
// Validate max Bytes for a level
602+
try
603+
{
604+
long maxSSTableSizeInBytes = Math.multiplyExact(ssSize, 1024L * 1024L); // Convert MB to Bytes
605+
BigInteger fanoutPower = BigInteger.valueOf(fanoutSize).pow(MAX_LEVEL_COUNT - 1);
606+
BigInteger maxBytes = fanoutPower.multiply(BigInteger.valueOf(maxSSTableSizeInBytes));
607+
BigInteger longMaxValue = BigInteger.valueOf(Long.MAX_VALUE);
608+
if (maxBytes.compareTo(longMaxValue) > 0)
609+
throw new ConfigurationException(String.format("At most %s bytes may be in a compaction level; " +
610+
"your maxSSTableSize must be absurdly high to compute %s", Long.MAX_VALUE, maxBytes));
611+
}
612+
catch (ArithmeticException ex)
613+
{
614+
throw new ConfigurationException(String.format("sstable_size_in_mb=%d is too large; resulting bytes exceed Long.MAX_VALUE (%d)", ssSize, Long.MAX_VALUE), ex);
592615
}
593616

594617
uncheckedOptions.remove(LEVEL_FANOUT_SIZE_OPTION);

test/unit/org/apache/cassandra/db/compaction/LeveledCompactionStrategyTest.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -906,6 +906,24 @@ public void testReduceScopeL0L1() throws IOException
906906
}
907907
}
908908

909+
@Test()
910+
public void testInvalidFanoutAndSSTableSize()
911+
{
912+
try
913+
{
914+
Map<String, String> options = new HashMap<>();
915+
options.put("class", "LeveledCompactionStrategy");
916+
options.put("fanout_size", "90");
917+
options.put("sstable_size_in_mb", "1089");
918+
LeveledCompactionStrategy.validateOptions(options);
919+
Assert.fail("fanout_sizeed and sstable_size_in_mb are invalid, but did not throw ConfigurationException");
920+
}
921+
catch (ConfigurationException e)
922+
{
923+
assertTrue(e.getMessage().contains("your maxSSTableSize must be absurdly high to compute"));
924+
}
925+
}
926+
909927
@Test
910928
public void testReduceScopeL0()
911929
{

test/unit/org/apache/cassandra/schema/CreateTableValidationTest.java

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import org.apache.cassandra.cql3.QueryOptions;
2929
import org.apache.cassandra.exceptions.ConfigurationException;
3030
import org.apache.cassandra.exceptions.InvalidRequestException;
31+
import org.apache.cassandra.exceptions.RequestValidationException;
3132
import org.apache.cassandra.transport.Message;
3233
import org.apache.cassandra.transport.ProtocolVersion;
3334
import org.apache.cassandra.transport.SimpleClient;
@@ -202,9 +203,22 @@ public void testNonAlphanummericTableName() throws Throwable
202203
String.format("CREATE TABLE %s.\" \" (key int PRIMARY KEY, val int)", KEYSPACE));
203204
}
204205

205-
private void expectedFailure(String statement, String errorMsg)
206+
@Test
207+
public void testInvalidCompactionOptions()
208+
{
209+
expectedFailure(ConfigurationException.class, "CREATE TABLE %s (k int PRIMARY KEY, v int) WITH compaction = {'class': 'LeveledCompactionStrategy', 'fanout_size': '90', 'sstable_size_in_mb': '1089'}",
210+
"your maxSSTableSize must be absurdly high to compute");
211+
}
212+
213+
private void expectedFailure(final Class<? extends RequestValidationException> exceptionType, String statement, String errorMsg)
206214
{
207215

216+
assertThatExceptionOfType(exceptionType)
217+
.isThrownBy(() -> createTableMayThrow(statement)) .withMessageContaining(errorMsg);
218+
}
219+
220+
private void expectedFailure(String statement, String errorMsg)
221+
{
208222
assertThatExceptionOfType(InvalidRequestException.class)
209223
.isThrownBy(() -> createTableMayThrow(statement)) .withMessageContaining(errorMsg);
210224
}

0 commit comments

Comments
 (0)