Skip to content

Commit 68133fe

Browse files
committed
Merge branch 'cassandra-6.0' into trunk
2 parents c588b67 + e21461e commit 68133fe

3 files changed

Lines changed: 48 additions & 17 deletions

File tree

CHANGES.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
* Allow nodetool garbagecollect to take a user defined list of SSTables (CASSANDRA-16767)
44
* Add a guardrail for misprepared statements (CASSANDRA-21139)
55
Merged from 6.0:
6+
* Move exception handling of SPI startup checks when iterating over them (CASSANDRA-21409)
67
* Avoid type lookup in SerializationHeader#getType if schema and SSTable are aligned (CASSANDRA-21402)
78
* Replace LongAdder in metric-like logic with ThreadLocalCounter (CASSANDRA-21400)
89
* BTreeRow#hasLiveData: Avoid Cell iteration if there are no tombstones in a row (CASSANDRA-21363)

src/java/org/apache/cassandra/service/StartupChecks.java

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -156,29 +156,24 @@ public StartupChecks withDefaultTests()
156156

157157
public StartupChecks withServiceLoaderTests()
158158
{
159-
ServiceLoader<StartupCheck> loader;
159+
Set<StartupCheck> customChecks = new HashSet<>();
160+
Set<String> uniqueNames = new HashSet<>();
161+
Set<String> duplicitNames = new HashSet<>();
160162

161163
try
162164
{
163-
loader = ServiceLoader.load(StartupCheck.class);
165+
for (StartupCheck check : ServiceLoader.load(StartupCheck.class))
166+
{
167+
if (!uniqueNames.add(check.name()))
168+
duplicitNames.add(check.name());
169+
else
170+
customChecks.add(check);
171+
}
164172
}
165173
catch (ServiceConfigurationError t)
166174
{
167-
logger.warn("Unable to get startup checks via ServiceLoader. " +
168-
"Custom checks will not be triggered. Reason: " + t.getMessage());
169-
return this;
170-
}
171-
172-
Set<StartupCheck> customChecks = new HashSet<>();
173-
Set<String> uniqueNames = new HashSet<>();
174-
Set<String> duplicitNames = new HashSet<>();
175-
176-
for (StartupCheck check : loader)
177-
{
178-
if (!uniqueNames.add(check.name()))
179-
duplicitNames.add(check.name());
180-
else
181-
customChecks.add(check);
175+
throw new ConfigurationException("Unable to get startup checks via ServiceLoader. " +
176+
"Custom checks will not be triggered.", t);
182177
}
183178

184179
if (!duplicitNames.isEmpty())

test/unit/org/apache/cassandra/service/StartupChecksTest.java

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,10 @@
2828
import java.nio.file.spi.FileSystemProvider;
2929
import java.time.Instant;
3030
import java.util.HashMap;
31+
import java.util.Iterator;
3132
import java.util.List;
3233
import java.util.Map;
34+
import java.util.ServiceConfigurationError;
3335
import java.util.ServiceLoader;
3436
import java.util.Set;
3537
import java.util.UUID;
@@ -55,6 +57,7 @@
5557
import org.apache.cassandra.db.ColumnFamilyStore;
5658
import org.apache.cassandra.db.Directories;
5759
import org.apache.cassandra.db.Keyspace;
60+
import org.apache.cassandra.exceptions.ConfigurationException;
5861
import org.apache.cassandra.exceptions.StartupException;
5962
import org.apache.cassandra.io.filesystem.ForwardingFileSystem;
6063
import org.apache.cassandra.io.filesystem.ForwardingFileSystemProvider;
@@ -308,6 +311,38 @@ public void testKernelBug1057843Check() throws Exception
308311
testKernelBug1057843Check("ext4", DiskAccessMode.mmap, new Semver("6.1.64.1-generic"), false);
309312
}
310313

314+
@SuppressWarnings("unchecked")
315+
@Test
316+
public void testErrorneousCustomCheckFailsStartup()
317+
{
318+
// ServiceLoader instantiates providers lazily: a custom StartupCheck whose no-arg
319+
// constructor throws does NOT fail ServiceLoader.load(), it fails later, during
320+
// iteration, surfacing as a ServiceConfigurationError from the iterator. We model that
321+
// here by having the iterator throw on next(). withServiceLoaderTests() must catch the
322+
// error around the iteration loop and rethrow it as a ConfigurationException, rather than
323+
// letting it escape uncaught (which would be wrapped by applyStartupChecks() into a
324+
// misleading "Invalid configuration of startup_checks" failure).
325+
ServiceConfigurationError error = new ServiceConfigurationError("org.example.BadCheck could not be instantiated",
326+
new RuntimeException("Failure to instantiate"));
327+
328+
Iterator<StartupCheck> failingIterator = mock(Iterator.class);
329+
when(failingIterator.hasNext()).thenReturn(true);
330+
when(failingIterator.next()).thenThrow(error);
331+
332+
ServiceLoader<StartupCheck> loader = mock(ServiceLoader.class);
333+
doReturn(failingIterator).when(loader).iterator();
334+
335+
try (MockedStatic<ServiceLoader> serviceLoader = Mockito.mockStatic(ServiceLoader.class))
336+
{
337+
serviceLoader.when(() -> ServiceLoader.load(StartupCheck.class)).thenReturn(loader);
338+
339+
assertThatExceptionOfType(ConfigurationException.class)
340+
.isThrownBy(() -> new StartupChecks().withDefaultTests().withServiceLoaderTests())
341+
.withMessageContaining("Unable to get startup checks via ServiceLoader")
342+
.withCause(error);
343+
}
344+
}
345+
311346
@SuppressWarnings("unchecked")
312347
@Test
313348
public void testExternalCheckIsLoaded() throws StartupException

0 commit comments

Comments
 (0)