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]