You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cassandra.apache.org by ma...@apache.org on 2025/06/27 16:43:10 UTC
(cassandra) branch cassandra-5.0 updated: Avoid availability gap between UP and queryability marking for already built SAI indexes on bounce
This is an automated email from the ASF dual-hosted git repository.
maedhroz pushed a commit to branch cassandra-5.0
in repository https://gitbox.apache.org/repos/asf/cassandra.git
The following commit(s) were added to refs/heads/cassandra-5.0 by this push:
new f553acea3e Avoid availability gap between UP and queryability marking for already built SAI indexes on bounce
f553acea3e is described below
commit f553acea3e867320dcf97a19510bef1502b3e5a5
Author: Caleb Rackliffe <ca...@gmail.com>
AuthorDate: Thu Jun 26 11:58:33 2025 -0500
Avoid availability gap between UP and queryability marking for already built SAI indexes on bounce
patch by Caleb Rackliffe; reviewed by David Capwell and Dmitry Konstantinov for CASSANDRA-20732
---
CHANGES.txt | 1 +
.../cassandra/index/sai/StorageAttachedIndex.java | 33 +++++++++++++++-----
.../apache/cassandra/service/StorageService.java | 6 ++++
.../index/sai/cql/EmptyStringLifecycleTest.java | 8 +++++
.../index/sai/cql/StorageAttachedIndexDDLTest.java | 35 ++++++++++++++++++++++
.../index/sai/functional/FailureTest.java | 8 +++++
.../index/sai/virtual/IndexesSystemViewTest.java | 4 +--
7 files changed, 85 insertions(+), 10 deletions(-)
diff --git a/CHANGES.txt b/CHANGES.txt
index 947891e3f2..5b4586a178 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
5.0.5
+ * Avoid availability gap between UP and queryability marking for already built SAI indexes on bounce (CASSANDRA-20732)
* Make Commitlog flush data safely in Direct IO mode (CASSANDRA-20692)
* Get SAI MemtableIndex refs before SSTableIndex refs at query time (CASSANDRA-20709)
* Fix MAX_SEGMENT_SIZE < chunkSize in MmappedRegions::updateState (CASSANDRA-20636)
diff --git a/src/java/org/apache/cassandra/index/sai/StorageAttachedIndex.java b/src/java/org/apache/cassandra/index/sai/StorageAttachedIndex.java
index b42a165f11..17c5cd1ffc 100644
--- a/src/java/org/apache/cassandra/index/sai/StorageAttachedIndex.java
+++ b/src/java/org/apache/cassandra/index/sai/StorageAttachedIndex.java
@@ -331,9 +331,30 @@ public class StorageAttachedIndex implements Index
public Callable<?> getInitializationTask()
{
// New storage-attached indexes will be available for queries after on disk index data are built.
- // Memtable data will be indexed via flushing triggered by schema change
- // We only want to validate the index files if we are starting up
- IndexValidation validation = StorageService.instance.isStarting() ? IndexValidation.HEADER_FOOTER : IndexValidation.NONE;
+ // Memtable data will be indexed via flushing triggered by schema change.
+ // We only want to validate the index files if we are starting up.
+ boolean isStarting = StorageService.instance.isStarting();
+ IndexValidation validation = isStarting ? IndexValidation.HEADER_FOOTER : IndexValidation.NONE;
+
+ // Only attempt to make the index queryable if we are starting up. Otherwise, if we create a new index on top
+ // of nothing but existing Memtable data (i.e. no SSTables), that data will temporarily be lost until flush.
+ if (isStarting)
+ {
+ StorageAttachedIndexGroup indexGroup = StorageAttachedIndexGroup.getIndexGroup(baseCfs);
+ assert indexGroup != null : "Index group does not exist for table " + baseCfs.keyspace + '.' + baseCfs.name;
+
+ Collection<SSTableReader> nonIndexed = findNonIndexedSSTables(baseCfs, indexGroup, validation);
+
+ if (nonIndexed.isEmpty())
+ {
+ // If the index is complete, mark it queryable and avoid an initial build:
+ baseCfs.indexManager.makeIndexQueryable(this, Status.BUILD_SUCCEEDED);
+ logger.debug(indexIdentifier.logMessage("Skipping initial build, as index is already queryable..."));
+ initBuildStarted = true;
+ return () -> ImmediateFuture.success(null);
+ }
+ }
+
return () -> startInitialBuild(baseCfs, validation).get();
}
@@ -843,15 +864,12 @@ public class StorageAttachedIndex implements Index
// Force another flush to make sure on disk index is generated for memtable data before marking it queryable.
// In the case of offline scrub, there are no live memtables.
if (!baseCfs.getTracker().getView().liveMemtables.isEmpty())
- {
baseCfs.forceBlockingFlush(ColumnFamilyStore.FlushReason.INDEX_BUILD_STARTED);
- }
// It is now safe to flush indexes directly from flushing Memtables.
initBuildStarted = true;
StorageAttachedIndexGroup indexGroup = StorageAttachedIndexGroup.getIndexGroup(baseCfs);
-
assert indexGroup != null : "Index group does not exist for table " + baseCfs.keyspace + '.' + baseCfs.name;
List<SSTableReader> nonIndexed = findNonIndexedSSTables(baseCfs, indexGroup, validation);
@@ -888,8 +906,7 @@ public class StorageAttachedIndex implements Index
}
StorageAttachedIndexGroup indexGroup = StorageAttachedIndexGroup.getIndexGroup(baseCfs);
-
- assert indexGroup != null : "Index group does not exist for table";
+ assert indexGroup != null : "Index group does not exist for table " + baseCfs.keyspace + '.' + baseCfs.name;
Collection<SSTableReader> nonIndexed = findNonIndexedSSTables(baseCfs, indexGroup, IndexValidation.HEADER_FOOTER);
diff --git a/src/java/org/apache/cassandra/service/StorageService.java b/src/java/org/apache/cassandra/service/StorageService.java
index 0ad51da3e8..ee38ab2a78 100644
--- a/src/java/org/apache/cassandra/service/StorageService.java
+++ b/src/java/org/apache/cassandra/service/StorageService.java
@@ -2029,6 +2029,12 @@ public class StorageService extends NotificationBroadcasterSupport implements IE
setMode(Mode.NORMAL, true);
}
+ @VisibleForTesting
+ public void setStartingModeUnsafe()
+ {
+ setMode(Mode.STARTING, true);
+ }
+
private void setMode(Mode m, boolean log)
{
setMode(m, null, log);
diff --git a/test/unit/org/apache/cassandra/index/sai/cql/EmptyStringLifecycleTest.java b/test/unit/org/apache/cassandra/index/sai/cql/EmptyStringLifecycleTest.java
index 7ace636e4f..e5620da869 100644
--- a/test/unit/org/apache/cassandra/index/sai/cql/EmptyStringLifecycleTest.java
+++ b/test/unit/org/apache/cassandra/index/sai/cql/EmptyStringLifecycleTest.java
@@ -18,6 +18,7 @@
package org.apache.cassandra.index.sai.cql;
+import org.junit.BeforeClass;
import org.junit.Test;
import org.apache.cassandra.cql3.UntypedResultSet;
@@ -25,6 +26,13 @@ import org.apache.cassandra.index.sai.SAITester;
public class EmptyStringLifecycleTest extends SAITester
{
+ @BeforeClass
+ public static void setup()
+ {
+ setUpClass();
+ requireNetwork(); // Ensure the node has advanced out of STARTING mode
+ }
+
@Test
public void testBeforeAndAfterFlush()
{
diff --git a/test/unit/org/apache/cassandra/index/sai/cql/StorageAttachedIndexDDLTest.java b/test/unit/org/apache/cassandra/index/sai/cql/StorageAttachedIndexDDLTest.java
index cc2e2f4b13..4fd1dde952 100644
--- a/test/unit/org/apache/cassandra/index/sai/cql/StorageAttachedIndexDDLTest.java
+++ b/test/unit/org/apache/cassandra/index/sai/cql/StorageAttachedIndexDDLTest.java
@@ -76,6 +76,7 @@ import org.apache.cassandra.inject.InvokePointBuilder;
import org.apache.cassandra.io.sstable.format.SSTableReader;
import org.apache.cassandra.schema.SchemaConstants;
import org.apache.cassandra.schema.TableMetadata;
+import org.apache.cassandra.service.StorageService;
import org.apache.cassandra.utils.Throwables;
import org.assertj.core.api.Assertions;
import org.mockito.Mockito;
@@ -1311,6 +1312,40 @@ public class StorageAttachedIndexDDLTest extends SAITester
assertTrue(verifyChecksum(numericIndexTermType, numericIndexIdentifier));
}
+ @Test
+ public void shouldMarkQueryableInInitializationTask() throws Throwable
+ {
+ createTable(CREATE_TABLE_TEMPLATE);
+ disableCompaction(KEYSPACE);
+ IndexIdentifier idxIdentifier = createIndexIdentifier(createIndexAsync(String.format(CREATE_INDEX_TEMPLATE, "v1")));
+
+ // create 10 SSTables
+ for (int i = 0; i < 10; i++)
+ {
+ execute("INSERT INTO %s (id1, v1, v2) VALUES (?, ?, ?)", String.valueOf(i), i, String.valueOf(i));
+ flush();
+ }
+
+ ResultSet rows = executeNet("SELECT id1 FROM %s WHERE v1 >= 5");
+ assertEquals(5, rows.all().size());
+
+ // Make the index artificially non-queryable:
+ ColumnFamilyStore cfs = getCurrentColumnFamilyStore();
+ Index index = cfs.indexManager.getIndexByName(idxIdentifier.indexName);
+ cfs.indexManager.makeIndexNonQueryable(index, Index.Status.BUILD_FAILED);
+
+ // Query should fail with the index in an artificially non-queryable state:
+ assertThatThrownBy(() -> executeNet("SELECT id1 FROM %s WHERE v1 >= 5")).isInstanceOf(ReadFailureException.class);
+
+ // Node must be in STARTING mode for it to be necessary for the initialization task to pre-emptively validate:
+ StorageService.instance.setStartingModeUnsafe();
+ // Simply getting the initialization task (and not running it) will validate and mark the index queryable again:
+ cfs.indexManager.buildIndex(index);
+ StorageService.instance.setNormalModeUnsafe();
+ rows = executeNet("SELECT id1 FROM %s WHERE v1 >= 5");
+ assertEquals(5, rows.all().size());
+ }
+
@Test
public void shouldRejectQueriesWithCustomExpressions()
{
diff --git a/test/unit/org/apache/cassandra/index/sai/functional/FailureTest.java b/test/unit/org/apache/cassandra/index/sai/functional/FailureTest.java
index 6907006e08..81c01b93ef 100644
--- a/test/unit/org/apache/cassandra/index/sai/functional/FailureTest.java
+++ b/test/unit/org/apache/cassandra/index/sai/functional/FailureTest.java
@@ -20,6 +20,7 @@
*/
package org.apache.cassandra.index.sai.functional;
+import org.junit.BeforeClass;
import org.junit.Test;
import org.apache.cassandra.db.marshal.Int32Type;
@@ -36,6 +37,13 @@ import static org.junit.Assert.assertEquals;
public class FailureTest extends SAITester
{
+ @BeforeClass
+ public static void setup()
+ {
+ setUpClass();
+ requireNetwork(); // Ensure the node has advanced out of STARTING mode
+ }
+
@Test
public void shouldMakeIndexNonQueryableOnSSTableContextFailureDuringFlush() throws Throwable
{
diff --git a/test/unit/org/apache/cassandra/index/sai/virtual/IndexesSystemViewTest.java b/test/unit/org/apache/cassandra/index/sai/virtual/IndexesSystemViewTest.java
index d52d1d5395..a52bb5c3b2 100644
--- a/test/unit/org/apache/cassandra/index/sai/virtual/IndexesSystemViewTest.java
+++ b/test/unit/org/apache/cassandra/index/sai/virtual/IndexesSystemViewTest.java
@@ -21,7 +21,6 @@ import com.google.common.collect.ImmutableList;
import org.junit.BeforeClass;
import org.junit.Test;
-import org.apache.cassandra.cql3.CQLTester;
import org.apache.cassandra.db.ColumnFamilyStore;
import org.apache.cassandra.db.virtual.VirtualKeyspace;
import org.apache.cassandra.db.virtual.VirtualKeyspaceRegistry;
@@ -60,7 +59,8 @@ public class IndexesSystemViewTest extends SAITester
{
VirtualKeyspaceRegistry.instance.register(new VirtualKeyspace(SchemaConstants.VIRTUAL_VIEWS, ImmutableList.of(new ColumnIndexesSystemView(SchemaConstants.VIRTUAL_VIEWS))));
- CQLTester.setUpClass();
+ setUpClass();
+ requireNetwork(); // Ensure the node has advanced out of STARTING mode
}
@Test
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@cassandra.apache.org
For additional commands, e-mail: commits-help@cassandra.apache.org