This is an automated email from the ASF dual-hosted git repository.

bbotella pushed a commit to branch cassandra-6.0
in repository https://gitbox.apache.org/repos/asf/cassandra.git


The following commit(s) were added to refs/heads/cassandra-6.0 by this push:
     new 8b6f6cfebb Fix mixed state
8b6f6cfebb is described below

commit 8b6f6cfebb72e62207ddcc672fcb06d0e42066a0
Author: Bernardo Botella <[email protected]>
AuthorDate: Fri Sep 18 14:53:54 2026 +0200

    Fix mixed state
---
 CHANGES.txt                                        |   1 +
 .../apache/cassandra/schema/SchemaKeyspace.java    |  33 +------
 .../cdc/MixedCdcEnabledSchemaDisagreementTest.java | 102 +++++++++++++++++++++
 3 files changed, 108 insertions(+), 28 deletions(-)

diff --git a/CHANGES.txt b/CHANGES.txt
index 875fccd0c7..4fb8825eec 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
 6.0-alpha3
+ * Add node to mixed cdc_enabled cluster can cause schema disagreement 
(CASSANDRA-21618)
  * Preserve the original exception when an in-memory local response fails to 
close (CASSANDRA-21710)
  * Generate autocompletion scripts for the nodetool command during the build 
(CASSANDRA-20794)
  * Implement Zstd compression level guardrail (CASSANDRA-21021)
diff --git a/src/java/org/apache/cassandra/schema/SchemaKeyspace.java 
b/src/java/org/apache/cassandra/schema/SchemaKeyspace.java
index d466f09ed5..e06981da26 100644
--- a/src/java/org/apache/cassandra/schema/SchemaKeyspace.java
+++ b/src/java/org/apache/cassandra/schema/SchemaKeyspace.java
@@ -35,7 +35,6 @@ import javax.annotation.concurrent.NotThreadSafe;
 
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.MapDifference;
 import com.google.common.collect.Maps;
 
@@ -133,12 +132,6 @@ public final class SchemaKeyspace
     private static final boolean FLUSH_SCHEMA_TABLES = 
TEST_FLUSH_LOCAL_SCHEMA_CHANGES.getBoolean();
     private static final boolean IGNORE_CORRUPTED_SCHEMA_TABLES_PROPERTY_VALUE 
= IGNORE_CORRUPTED_SCHEMA_TABLES.getBoolean();
 
-    /**
-     * The tables to which we added the cdc column. This is used in {@link 
#makeUpdateForSchema} below to make sure we skip that
-     * column is cdc is disabled as the columns breaks pre-cdc to post-cdc 
upgrades (typically, 3.0 -> 3.X).
-     */
-    private static final Set<String> TABLES_WITH_CDC_ADDED = 
ImmutableSet.of(SchemaKeyspaceTables.TABLES, SchemaKeyspaceTables.VIEWS);
-
     private static final TableMetadata Keyspaces =
         parse(KEYSPACES,
               "keyspace definitions",
@@ -497,27 +490,10 @@ public final class SchemaKeyspace
 
     /**
      * Creates a PartitionUpdate from a partition containing some schema table 
content.
-     * This is mainly calling {@code PartitionUpdate.fromIterator} except for 
the fact that it deals with
-     * the problem described in #12236.
      */
     private static PartitionUpdate makeUpdateForSchema(UnfilteredRowIterator 
partition, ColumnFilter filter)
     {
-        // This method is used during schema migration tasks, and if cdc is 
disabled, we want to force excluding the
-        // 'cdc' column from the TABLES/VIEWS schema table because it is 
problematic if received by older nodes (see #12236
-        // and #12697). Otherwise though, we just simply "buffer" the content 
of the partition into a PartitionUpdate.
-        if (DatabaseDescriptor.isCDCEnabled() || 
!TABLES_WITH_CDC_ADDED.contains(partition.metadata().name))
-            return PartitionUpdate.fromIterator(partition, filter);
-
-        // We want to skip the 'cdc' column. A simple solution for that is 
based on the fact that
-        // 'PartitionUpdate.fromIterator()' will ignore any columns that are 
marked as 'fetched' but not 'queried'.
-        ColumnFilter.Builder builder = 
ColumnFilter.allRegularColumnsBuilder(partition.metadata(), false);
-        for (ColumnMetadata column : filter.fetchedColumns())
-        {
-            if (!column.name.toString().equals("cdc"))
-                builder.add(column);
-        }
-
-        return PartitionUpdate.fromIterator(partition, builder.build());
+        return PartitionUpdate.fromIterator(partition, filter);
     }
 
     private static boolean isSystemKeyspaceSchemaPartition(DecoratedKey 
partitionKey)
@@ -643,9 +619,10 @@ public final class SchemaKeyspace
                .add("read_repair", params.readRepair.toString())
                .add("extensions", params.extensions);
 
-        // Only add CDC-enabled flag to schema if it's enabled on the node. 
This is to work around RTE's post-8099 if a 3.8+
-        // node sends table schema to a < 3.8 versioned node with an unknown 
column.
-        if (DatabaseDescriptor.isCDCEnabled())
+        // Only add the cdc column if it's set, to avoid RTE in mixed 
operation.
+        // cdc_enabled is a per-node setting and must not gate
+        // whether this table's actual cdc value gets persisted - only the 
value itself matters here.
+        if (params.cdc)
             builder.add("cdc", params.cdc);
 
         // As above, only add the memtable column if the table uses a 
non-default memtable configuration to avoid RTE
diff --git 
a/test/distributed/org/apache/cassandra/distributed/test/cdc/MixedCdcEnabledSchemaDisagreementTest.java
 
b/test/distributed/org/apache/cassandra/distributed/test/cdc/MixedCdcEnabledSchemaDisagreementTest.java
new file mode 100644
index 0000000000..e71a2b9f79
--- /dev/null
+++ 
b/test/distributed/org/apache/cassandra/distributed/test/cdc/MixedCdcEnabledSchemaDisagreementTest.java
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.cassandra.distributed.test.cdc;
+
+import org.junit.Test;
+
+import org.apache.cassandra.distributed.Cluster;
+import org.apache.cassandra.distributed.api.IInstanceConfig;
+import org.apache.cassandra.distributed.api.IInvokableInstance;
+import org.apache.cassandra.distributed.api.TokenSupplier;
+import org.apache.cassandra.distributed.shared.NetworkTopology;
+import org.apache.cassandra.distributed.test.TestBaseImpl;
+
+import static org.apache.cassandra.distributed.api.Feature.GOSSIP;
+import static org.apache.cassandra.distributed.api.Feature.NATIVE_PROTOCOL;
+import static org.apache.cassandra.distributed.api.Feature.NETWORK;
+import static org.apache.cassandra.distributed.shared.AssertUtils.assertRows;
+import static org.apache.cassandra.distributed.shared.AssertUtils.row;
+
+/**
+ * Tests covering clusters with mixed CDC configurations
+ */
+public class MixedCdcEnabledSchemaDisagreementTest extends TestBaseImpl
+{
+    /**
+     * ALTER TABLE ... WITH cdc=true, issued from a node that does have
+     * cdc_enabled=true, must be visible as cdc=true in system_schema.tables 
on every node -
+     * including ones with cdc_enabled=false.
+     */
+    @Test
+    public void testAlterTableCdcMixedClusterCausesSchemaDisagreement() throws 
Exception
+    {
+        try (Cluster cluster = init(Cluster.build(3)
+                                           .withConfig(config -> {
+                                               if (config.num() == 3)
+                                                   config.set("cdc_enabled", 
true);
+                                               config.with(NETWORK, GOSSIP);
+                                           })
+                                           .start()))
+        {
+            cluster.schemaChange(withKeyspace("CREATE TABLE %s.tbl (k INT 
PRIMARY KEY, v INT)"));
+
+            // node 3 is the only node with cdc_enabled=true; it's the one 
flipping cdc on.
+            cluster.get(3).schemaChangeInternal(withKeyspace("ALTER TABLE 
%s.tbl WITH cdc = true"));
+
+            for (int i = 1; i <= 3; i++)
+            {
+                Object[][] rows = cluster.get(i).executeInternal(
+                    "SELECT cdc FROM system_schema.tables WHERE keyspace_name 
= ? AND table_name = ?",
+                    KEYSPACE, "tbl");
+                assertRows(rows, row(true));
+            }
+        }
+    }
+
+    /**
+     * bootstrapping a brand-new node with cdc_enabled=false into a cluster 
that
+     * already has cdc=true tables must not leave the new node's local 
system_schema.tables
+     * disagreeing with the rest of the cluster.
+     */
+    @Test
+    public void testBootstrapNodeWithCdcDisabledCausesSchemaDisagreement() 
throws Exception
+    {
+        try (Cluster cluster = init(builder().withNodes(1)
+                                             
.withTokenSupplier(TokenSupplier.evenlyDistributedTokens(2))
+                                             
.withNodeIdTopology(NetworkTopology.singleDcNetworkTopology(2, "dc0", "rack0"))
+                                             .withConfig(config -> 
config.set("cdc_enabled", true)
+                                                                         
.with(NETWORK, GOSSIP, NATIVE_PROTOCOL))
+                                             .start()))
+        {
+            cluster.schemaChange(withKeyspace("CREATE TABLE %s.tbl (k INT 
PRIMARY KEY, v INT) WITH cdc=true"));
+
+            IInstanceConfig newNodeConfig = cluster.newInstanceConfig();
+            newNodeConfig.set("auto_bootstrap", true);
+            newNodeConfig.set("cdc_enabled", false);
+
+            IInvokableInstance newNode = cluster.bootstrap(newNodeConfig);
+            newNode.startup();
+
+            Object[][] rows = newNode.executeInternal(
+                "SELECT cdc FROM system_schema.tables WHERE keyspace_name = ? 
AND table_name = ?",
+                KEYSPACE, "tbl");
+            assertRows(rows, row(true));
+        }
+    }
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to