Skip to content

Commit 1d6ebd5

Browse files
jacek-lewandowskidriftx
authored andcommitted
CNDB-12972: Enhance controller config file handling for long names (#1862)
This pull request introduces improvements to the handling of controller configuration files in the compaction subsystem of Apache Cassandra. The changes focus on consolidating constants, enhancing file naming logic to support very long table/keyspace names. In particular - controller configuration files consists of keyspace and table names, separated by dot and a fixed suffix. This scheme is retained as long as the while filename does not exceed 255 chars. If it does, keyspace and table names are abbreviated and a third component is added - table id. In a situation when the keyspace and table names needs to be resolved from the file name, the code examines the filename whether it has 2 or 3 components (delimited by dots). If 2, follow the legacy pattern, if 3, ignore keyspace and table names, and read just the table id. Table id is then used to resolve metadata from schema. The change is forward compatible because: - for normal keyspace and table names lengths, the 2 components scheme is still readable by the new version - for long keyspace and tables names lengths, the file was not created at all The change is backward compatible because: - for normal keyspace and table names lengths, the 2 components scheme is retained, thus the old version will still able to resolve it - for long keyspace and table names lengths, the 3 components schema will be wrongly resolved and lead to deletion of the controller file as wrongly resolved keyspace and table names would not be found. This is compatible with the current behaviour of the old version because in such a case, the controller config file would not be created at all.
1 parent 1f3490c commit 1d6ebd5

File tree

10 files changed

+122
-82
lines changed

10 files changed

+122
-82
lines changed

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

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
import com.google.common.util.concurrent.Uninterruptibles;
6060
import org.slf4j.Logger;
6161
import org.slf4j.LoggerFactory;
62+
6263
import com.codahale.metrics.Meter;
6364
import io.netty.util.concurrent.FastThreadLocal;
6465
import org.apache.cassandra.cache.AutoSavingCache;
@@ -109,6 +110,7 @@
109110
import org.apache.cassandra.schema.CompactionParams.TombstoneOption;
110111
import org.apache.cassandra.schema.Schema;
111112
import org.apache.cassandra.schema.SchemaConstants;
113+
import org.apache.cassandra.schema.TableId;
112114
import org.apache.cassandra.schema.TableMetadata;
113115
import org.apache.cassandra.service.ActiveRepairService;
114116
import org.apache.cassandra.service.StorageService;
@@ -165,6 +167,7 @@ protected Boolean initialValue()
165167
}
166168
};
167169
private static final int ACQUIRE_GRANULARITY = COMPACTION_RATE_LIMIT_GRANULARITY_IN_KB.getInt(128) * 1024;
170+
private static final String CONTROLLER_CONFIG_JSON_SUFFIX = "-controller-config.JSON";
168171

169172
static
170173
{
@@ -247,26 +250,40 @@ public static void storeControllerConfig()
247250
@VisibleForTesting
248251
public static void cleanupControllerConfig()
249252
{
250-
Pattern fileNamePattern = Pattern.compile("-controller-config.JSON", Pattern.LITERAL);
253+
Pattern fileNamePattern = Pattern.compile(CONTROLLER_CONFIG_JSON_SUFFIX, Pattern.LITERAL);
251254
Pattern keyspaceNameSeparator = Pattern.compile("\\.");
252255
File dir = DatabaseDescriptor.getMetadataDirectory();
253256
if (dir != null)
254257
{
255258
for (File file : dir.tryList())
256259
{
257-
if (file.name().contains("-controller-config.JSON"))
260+
if (file.name().contains(CONTROLLER_CONFIG_JSON_SUFFIX))
258261
{
259262
String[] names = keyspaceNameSeparator.split(fileNamePattern.matcher(file.name()).replaceAll(""));
260-
try
263+
if (names.length == 2)
261264
{
262-
//table exists so keep the file
263-
Schema.instance.getKeyspaceInstance(names[0]).getColumnFamilyStore(names[1]);
265+
try
266+
{
267+
//table exists so keep the file
268+
Schema.instance.getKeyspaceInstance(names[0]).getColumnFamilyStore(names[1]);
269+
}
270+
catch (NullPointerException e)
271+
{
272+
//table does not exist so delete the file
273+
logger.debug("Removing " + file + " because it does not correspond to an existing table");
274+
file.delete();
275+
}
264276
}
265-
catch(NullPointerException e)
277+
else if (names.length == 3) // if keyspace/table names are long, we include table id as a 3rd component while the keyspace and table names are abbreviated
266278
{
267-
//table does not exist so delete the file
268-
logger.debug("Removing " + file + " because it does not correspond to an existing table");
269-
file.delete();
279+
TableId tableId = TableId.fromHexString(names[2]);
280+
//table exists so keep the file
281+
if (Schema.instance.getTableMetadata(tableId) == null)
282+
{
283+
//table does not exist so delete the file
284+
logger.debug("Removing " + file + " because it does not correspond to an existing table");
285+
file.delete();
286+
}
270287
}
271288
}
272289
}

src/java/org/apache/cassandra/db/compaction/unified/AdaptiveController.java

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -23,18 +23,18 @@
2323
import javax.annotation.Nullable;
2424

2525
import com.google.common.annotations.VisibleForTesting;
26-
27-
import org.apache.cassandra.io.FSError;
28-
import org.apache.cassandra.utils.JVMStabilityInspector;
2926
import org.slf4j.Logger;
3027
import org.slf4j.LoggerFactory;
3128

3229
import org.apache.cassandra.db.compaction.CompactionPick;
3330
import org.apache.cassandra.db.compaction.UnifiedCompactionStrategy;
3431
import org.apache.cassandra.exceptions.ConfigurationException;
32+
import org.apache.cassandra.io.FSError;
3533
import org.apache.cassandra.io.util.File;
3634
import org.apache.cassandra.io.util.FileReader;
35+
import org.apache.cassandra.schema.TableMetadata;
3736
import org.apache.cassandra.utils.FBUtilities;
37+
import org.apache.cassandra.utils.JVMStabilityInspector;
3838
import org.apache.cassandra.utils.MonotonicClock;
3939
import org.apache.cassandra.utils.Overlaps;
4040
import org.json.simple.JSONArray;
@@ -132,8 +132,7 @@ public AdaptiveController(MonotonicClock clock,
132132
double threshold,
133133
int minCost,
134134
int maxAdaptiveCompactions,
135-
String keyspaceName,
136-
String tableName)
135+
TableMetadata metadata)
137136
{
138137
super(clock,
139138
env,
@@ -154,7 +153,8 @@ public AdaptiveController(MonotonicClock clock,
154153
reservationsType,
155154
overlapInclusionMethod,
156155
parallelizeOutputShards,
157-
hasVectorType);
156+
hasVectorType,
157+
metadata);
158158

159159
this.scalingParameters = scalingParameters;
160160
this.previousScalingParameters = previousScalingParameters;
@@ -164,8 +164,6 @@ public AdaptiveController(MonotonicClock clock,
164164
this.threshold = threshold;
165165
this.minCost = minCost;
166166
this.maxAdaptiveCompactions = maxAdaptiveCompactions;
167-
this.keyspaceName = keyspaceName;
168-
this.tableName = tableName;
169167
}
170168

171169
static Controller fromOptions(Environment env,
@@ -186,14 +184,13 @@ static Controller fromOptions(Environment env,
186184
Overlaps.InclusionMethod overlapInclusionMethod,
187185
boolean parallelizeOutputShards,
188186
boolean hasVectorType,
189-
String keyspaceName,
190-
String tableName,
187+
TableMetadata metadata,
191188
Map<String, String> options)
192189
{
193190
int[] scalingParameters = null;
194191
long currentFlushSize = flushSizeOverride;
195192

196-
File f = getControllerConfigPath(keyspaceName, tableName);
193+
File f = getControllerConfigPath(metadata);
197194
try
198195
{
199196
JSONParser jsonParser = new JSONParser();
@@ -301,8 +298,7 @@ else if (staticScalingFactors != null)
301298
threshold,
302299
minCost,
303300
maxAdaptiveCompactions,
304-
keyspaceName,
305-
tableName);
301+
metadata);
306302
}
307303

308304
private static int[] readStoredScalingParameters(JSONArray storedScalingParameters)
@@ -602,7 +598,7 @@ else if (i == scalingParameters.length-1)
602598
@Override
603599
public void storeControllerConfig()
604600
{
605-
storeOptions(keyspaceName, tableName, scalingParameters, getFlushSizeBytes());
601+
storeOptions(metadata, scalingParameters, getFlushSizeBytes());
606602
}
607603

608604
@Override

src/java/org/apache/cassandra/db/compaction/unified/Controller.java

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -330,8 +330,7 @@ public abstract class Controller
330330
protected final long expiredSSTableCheckFrequency;
331331
protected final boolean ignoreOverlapsInExpirationCheck;
332332
protected final boolean parallelizeOutputShards;
333-
protected String keyspaceName;
334-
protected String tableName;
333+
protected final TableMetadata metadata;
335334

336335
protected final int baseShardCount;
337336
private final boolean isReplicaAware;
@@ -369,7 +368,8 @@ public abstract class Controller
369368
Reservations.Type reservationsType,
370369
Overlaps.InclusionMethod overlapInclusionMethod,
371370
boolean parallelizeOutputShards,
372-
boolean hasVectorType)
371+
boolean hasVectorType,
372+
TableMetadata metadata)
373373
{
374374
this.clock = clock;
375375
this.env = env;
@@ -390,6 +390,7 @@ public abstract class Controller
390390
this.l0ShardsEnabled = UCS_L0_SHARDS_ENABLED.getBooleanWithLegacyFallback(false); // FIXME VECTOR-23
391391
this.parallelizeOutputShards = parallelizeOutputShards;
392392
this.hasVectorType = hasVectorType;
393+
this.metadata = metadata;
393394

394395
if (maxSSTablesToCompact <= 0) // use half the maximum permitted compaction size as upper bound by default
395396
maxSSTablesToCompact = (int) (dataSetSize * this.maxSpaceOverhead * 0.5 / getMinSstableSizeBytes());
@@ -404,17 +405,26 @@ public abstract class Controller
404405
this.ignoreOverlapsInExpirationCheck = ALLOW_UNSAFE_AGGRESSIVE_SSTABLE_EXPIRATION && ignoreOverlapsInExpirationCheck;
405406
}
406407

407-
public static File getControllerConfigPath(String keyspaceName, String tableName)
408+
public static File getControllerConfigPath(TableMetadata metadata)
408409
{
409-
String fileName = keyspaceName + '.' + tableName + '-' + "controller-config.JSON";
410+
String suffix = "-controller-config.JSON";
411+
String fileName = metadata.keyspace + '.' + metadata.name + suffix;
412+
if (fileName.length() > 255)
413+
{
414+
int spaceLeft = 255 - suffix.length() - 36 - 2; // 36 is the length of a UUID, 2 - for two separators
415+
String keyspaceAbbrev = metadata.keyspace.substring(0, Math.min(metadata.keyspace.length(), spaceLeft / 2));
416+
spaceLeft -= keyspaceAbbrev.length();
417+
String tableAbbrev = metadata.name.substring(0, Math.min(metadata.name.length(), spaceLeft));
418+
fileName = String.format("%s.%s.%s%s", keyspaceAbbrev, tableAbbrev, metadata.id.toHexString(), suffix);
419+
}
410420
return new File(DatabaseDescriptor.getMetadataDirectory(), fileName);
411421
}
412422

413-
public static void storeOptions(String keyspaceName, String tableName, int[] scalingParameters, long flushSizeBytes)
423+
public static void storeOptions(TableMetadata metadata, int[] scalingParameters, long flushSizeBytes)
414424
{
415-
if (SchemaConstants.isSystemKeyspace(keyspaceName))
425+
if (SchemaConstants.isSystemKeyspace(metadata.keyspace))
416426
return;
417-
File f = getControllerConfigPath(keyspaceName, tableName);
427+
File f = getControllerConfigPath(metadata);
418428
try(FileWriter fileWriter = new FileWriter(f, File.WriteMode.OVERWRITE);)
419429
{
420430
JSONArray jsonArray = new JSONArray();
@@ -1058,8 +1068,7 @@ public static Controller fromOptions(CompactionRealm realm, Map<String, String>
10581068
overlapInclusionMethod,
10591069
parallelizeOutputShards,
10601070
hasVectorType,
1061-
realm.getKeyspaceName(),
1062-
realm.getTableName(),
1071+
realm.metadata(),
10631072
options)
10641073
: StaticController.fromOptions(env,
10651074
survivalFactors,
@@ -1079,8 +1088,7 @@ public static Controller fromOptions(CompactionRealm realm, Map<String, String>
10791088
overlapInclusionMethod,
10801089
parallelizeOutputShards,
10811090
hasVectorType,
1082-
realm.getKeyspaceName(),
1083-
realm.getTableName(),
1091+
realm.metadata(),
10841092
options,
10851093
useVectorOptions);
10861094
}

src/java/org/apache/cassandra/db/compaction/unified/StaticController.java

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import org.apache.cassandra.io.FSError;
2929
import org.apache.cassandra.io.util.File;
3030
import org.apache.cassandra.io.util.FileReader;
31+
import org.apache.cassandra.schema.TableMetadata;
3132
import org.apache.cassandra.utils.JVMStabilityInspector;
3233
import org.apache.cassandra.utils.MonotonicClock;
3334
import org.apache.cassandra.utils.Overlaps;
@@ -75,8 +76,7 @@ public StaticController(Environment env,
7576
Overlaps.InclusionMethod overlapInclusionMethod,
7677
boolean parallelizeOutputShards,
7778
boolean hasVectorType,
78-
String keyspaceName,
79-
String tableName)
79+
TableMetadata metadata)
8080
{
8181
super(MonotonicClock.Global.preciseTime,
8282
env,
@@ -97,10 +97,9 @@ public StaticController(Environment env,
9797
reservationsType,
9898
overlapInclusionMethod,
9999
parallelizeOutputShards,
100-
hasVectorType);
100+
hasVectorType,
101+
metadata);
101102
this.scalingParameters = scalingParameters;
102-
this.keyspaceName = keyspaceName;
103-
this.tableName = tableName;
104103
}
105104

106105
static Controller fromOptions(Environment env,
@@ -121,8 +120,7 @@ static Controller fromOptions(Environment env,
121120
Overlaps.InclusionMethod overlapInclusionMethod,
122121
boolean parallelizeOutputShards,
123122
boolean hasVectorType,
124-
String keyspaceName,
125-
String tableName,
123+
TableMetadata metadata,
126124
Map<String, String> options,
127125
boolean useVectorOptions)
128126
{
@@ -136,7 +134,7 @@ static Controller fromOptions(Environment env,
136134

137135
long currentFlushSize = flushSizeOverride;
138136

139-
File f = getControllerConfigPath(keyspaceName, tableName);
137+
File f = getControllerConfigPath(metadata);
140138
try
141139
{
142140
JSONParser jsonParser = new JSONParser();
@@ -184,8 +182,7 @@ static Controller fromOptions(Environment env,
184182
overlapInclusionMethod,
185183
parallelizeOutputShards,
186184
hasVectorType,
187-
keyspaceName,
188-
tableName);
185+
metadata);
189186
}
190187

191188
public static Map<String, String> validateOptions(Map<String, String> options) throws ConfigurationException
@@ -232,7 +229,7 @@ public int getMaxRecentAdaptiveCompactions()
232229
@Override
233230
public void storeControllerConfig()
234231
{
235-
storeOptions(keyspaceName, tableName, scalingParameters, getFlushSizeBytes());
232+
storeOptions(metadata, scalingParameters, getFlushSizeBytes());
236233
}
237234

238235
@Override

src/java/org/apache/cassandra/schema/TableId.java

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,14 @@ public static TableId fromString(String idString)
6666
return new TableId(UUID.fromString(idString));
6767
}
6868

69+
public static TableId fromHexString(String nonDashUUID)
70+
{
71+
ByteBuffer bytes = ByteBufferUtil.hexToBytes(nonDashUUID);
72+
long msb = bytes.getLong(0);
73+
long lsb = bytes.getLong(8);
74+
return fromUUID(new UUID(msb, lsb));
75+
}
76+
6977
@Nullable
7078
public static Pair<String, TableId> tableNameAndIdFromFilename(String filename)
7179
{
@@ -79,14 +87,6 @@ public static Pair<String, TableId> tableNameAndIdFromFilename(String filename)
7987
return Pair.create(tableName, id);
8088
}
8189

82-
private static TableId fromHexString(String nonDashUUID)
83-
{
84-
ByteBuffer bytes = ByteBufferUtil.hexToBytes(nonDashUUID);
85-
long msb = bytes.getLong(0);
86-
long lsb = bytes.getLong(8);
87-
return fromUUID(new UUID(msb, lsb));
88-
}
89-
9090
/**
9191
* Creates the UUID of a system table.
9292
*

0 commit comments

Comments
 (0)