You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cassandra.apache.org by sm...@apache.org on 2022/11/19 10:09:41 UTC

[cassandra] branch trunk updated: Make incremental backup configurable per table

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

smiklosovic pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/cassandra.git


The following commit(s) were added to refs/heads/trunk by this push:
     new ee8b66da8c Make incremental backup configurable per table
ee8b66da8c is described below

commit ee8b66da8ce3bdac0378f89159d8bd7e45a91363
Author: maxwellguo <cc...@gmail.com>
AuthorDate: Thu Oct 27 22:05:49 2022 +0800

    Make incremental backup configurable per table
    
    patch by Maxwell Guo; reviewed by Stefan Miklosovic and Benjamin Lerer for CASSANDRA-15402
---
 CHANGES.txt                                        |   1 +
 NEWS.txt                                           |   6 +-
 conf/cassandra.yaml                                |   8 +-
 pylib/cqlshlib/cql3handling.py                     |   3 +
 pylib/cqlshlib/test/test_cqlsh_completion.py       |   3 +
 pylib/cqlshlib/test/test_cqlsh_output.py           |   1 +
 .../cql3/statements/schema/TableAttributes.java    |   3 +
 .../org/apache/cassandra/db/ColumnFamilyStore.java |   5 +
 .../org/apache/cassandra/db/lifecycle/Tracker.java |   2 +-
 .../apache/cassandra/schema/SchemaKeyspace.java    |  11 ++
 .../org/apache/cassandra/schema/TableMetadata.java |   5 +
 .../org/apache/cassandra/schema/TableParams.java   |  18 ++-
 .../test/TableLevelIncrementalBackupsTest.java     | 169 +++++++++++++++++++++
 .../cql3/statements/DescribeStatementTest.java     |   2 +
 .../apache/cassandra/db/SchemaCQLHelperTest.java   |   1 +
 15 files changed, 232 insertions(+), 6 deletions(-)

diff --git a/CHANGES.txt b/CHANGES.txt
index ba7c5a1be4..39363ac4c4 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
 4.2
+ * Make incremental backup configurable per table (CASSANDRA-15402)
  * Change shebangs of Python scripts to resolve Python 3 from env command (CASSANDRA-17832)
  * Add reasons to guardrail messages and consider guardrails in the error message for needed ALLOW FILTERING (CASSANDRA-17967)
  * Add support for CQL functions on collections, tuples and UDTs (CASSANDRA-17811)
diff --git a/NEWS.txt b/NEWS.txt
index 3425d75593..bc94c640c0 100644
--- a/NEWS.txt
+++ b/NEWS.txt
@@ -102,6 +102,10 @@ New features
       --older-than-timestamp 2022-12-03T10:15:30Z.
     - Cassandra logs can be viewed in the virtual table system_views.system_logs.
       Please uncomment the respective appender in logback.xml file to make logs flow into this table. This feature is turned off by default.
+    - Added new CQL table property 'incremental_backups' which is by default true. When 'incremental_backups' property in cassandra.yaml
+      is set to true and table property is set to false, incremental backups for that specific table will not be done.
+      When 'incremental_backups' in casandra.yaml is set to false, the newly added table property does not have any effect.
+      Both properties have to be set to true (cassandra.yaml and table property) in order to make incremental backups.
 
 Upgrading
 ---------
@@ -109,7 +113,7 @@ Upgrading
       there is a dedicated flag in snapshot manifest instead. On upgrade of a node to version 4.2, on node's start, in case there
       are such ephemeral snapshots on disk, they will be deleted (same behaviour as before) and any new ephemeral snapshots
       will stop to create ephemeral marker files as flag in a snapshot manifest was introduced instead.
-    - There was new table property introduced called 'allow_auto_snapshot' (see section 'New features'). Hence, upgraded
+    - There were new table properties introduced called 'allow_auto_snapshot' and 'incremental_backups' (see section 'New features'). Hence, upgraded
       node will be on a new schema version. Please do a rolling upgrade of nodes of a cluster to converge to one schema version.
 
 Deprecation
diff --git a/conf/cassandra.yaml b/conf/cassandra.yaml
index ab15c87ebc..cdbbc252bc 100644
--- a/conf/cassandra.yaml
+++ b/conf/cassandra.yaml
@@ -923,9 +923,11 @@ rpc_keepalive: true
 # internode_socket_receive_buffer_size:
 
 # Set to true to have Cassandra create a hard link to each sstable
-# flushed or streamed locally in a backups/ subdirectory of the
-# keyspace data.  Removing these links is the operator's
-# responsibility.
+# flushed or streamed locally in a backups/ subdirectory of all the
+# keyspace data in this node.  Removing these links is the operator's
+# responsibility. The operator can also turn off incremental backups 
+# for specified table by setting table parameter incremental_backups to 
+# false, which is set to true by default. See CASSANDRA-15402
 incremental_backups: false
 
 # Whether or not to take a snapshot before each compaction.  Be
diff --git a/pylib/cqlshlib/cql3handling.py b/pylib/cqlshlib/cql3handling.py
index c23e2d397d..ddf8723863 100644
--- a/pylib/cqlshlib/cql3handling.py
+++ b/pylib/cqlshlib/cql3handling.py
@@ -47,6 +47,7 @@ class Cql3ParsingRuleSet(CqlParsingRuleSet):
         ('bloom_filter_fp_chance', None),
         ('comment', None),
         ('gc_grace_seconds', None),
+        ('incremental_backups', None),
         ('min_index_interval', None),
         ('max_index_interval', None),
         ('default_time_to_live', None),
@@ -523,6 +524,8 @@ def cf_prop_val_completer(ctxt, cass):
         return [Hint('<\'none\'|\'blocking\'>')]
     if this_opt == 'allow_auto_snapshot':
         return [Hint('<boolean>')]
+    if this_opt == 'incremental_backups':
+        return [Hint('<boolean>')]
     return [Hint('<option_value>')]
 
 
diff --git a/pylib/cqlshlib/test/test_cqlsh_completion.py b/pylib/cqlshlib/test/test_cqlsh_completion.py
index c52624b1ed..6126566368 100644
--- a/pylib/cqlshlib/test/test_cqlsh_completion.py
+++ b/pylib/cqlshlib/test/test_cqlsh_completion.py
@@ -639,6 +639,7 @@ class TestCqlshCompletion(CqlshCompletionCase):
                                      'bloom_filter_fp_chance', 'compaction',
                                      'compression',
                                      'default_time_to_live', 'gc_grace_seconds',
+                                     'incremental_backups',
                                      'max_index_interval',
                                      'memtable',
                                      'memtable_flush_period_in_ms',
@@ -650,6 +651,7 @@ class TestCqlshCompletion(CqlshCompletionCase):
                                      'bloom_filter_fp_chance', 'compaction',
                                      'compression',
                                      'default_time_to_live', 'gc_grace_seconds',
+                                     'incremental_backups',
                                      'max_index_interval',
                                      'memtable',
                                      'memtable_flush_period_in_ms',
@@ -699,6 +701,7 @@ class TestCqlshCompletion(CqlshCompletionCase):
                             choices=['allow_auto_snapshot', 'bloom_filter_fp_chance', 'compaction',
                                      'compression',
                                      'default_time_to_live', 'gc_grace_seconds',
+                                     'incremental_backups',
                                      'max_index_interval',
                                      'memtable',
                                      'memtable_flush_period_in_ms',
diff --git a/pylib/cqlshlib/test/test_cqlsh_output.py b/pylib/cqlshlib/test/test_cqlsh_output.py
index e54bf24234..e8961f6321 100644
--- a/pylib/cqlshlib/test/test_cqlsh_output.py
+++ b/pylib/cqlshlib/test/test_cqlsh_output.py
@@ -664,6 +664,7 @@ class TestCqlshOutput(BaseTestCase):
                 AND default_time_to_live = 0
                 AND extensions = {}
                 AND gc_grace_seconds = 864000
+                AND incremental_backups = true
                 AND max_index_interval = 2048
                 AND memtable_flush_period_in_ms = 0
                 AND min_index_interval = 128
diff --git a/src/java/org/apache/cassandra/cql3/statements/schema/TableAttributes.java b/src/java/org/apache/cassandra/cql3/statements/schema/TableAttributes.java
index 2f1eaf025c..c55d79e452 100644
--- a/src/java/org/apache/cassandra/cql3/statements/schema/TableAttributes.java
+++ b/src/java/org/apache/cassandra/cql3/statements/schema/TableAttributes.java
@@ -133,6 +133,9 @@ public final class TableAttributes extends PropertyDefinitions
 
         if (hasOption(GC_GRACE_SECONDS))
             builder.gcGraceSeconds(getInt(GC_GRACE_SECONDS));
+        
+        if (hasOption(INCREMENTAL_BACKUPS))
+            builder.incrementalBackups(getBoolean(INCREMENTAL_BACKUPS.toString(), true));
 
         if (hasOption(MAX_INDEX_INTERVAL))
             builder.maxIndexInterval(getInt(MAX_INDEX_INTERVAL));
diff --git a/src/java/org/apache/cassandra/db/ColumnFamilyStore.java b/src/java/org/apache/cassandra/db/ColumnFamilyStore.java
index 9fc0f775e1..e7bad897ba 100644
--- a/src/java/org/apache/cassandra/db/ColumnFamilyStore.java
+++ b/src/java/org/apache/cassandra/db/ColumnFamilyStore.java
@@ -3134,6 +3134,11 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean, Memtable.Owner
         return metadata().params.allowAutoSnapshot && DatabaseDescriptor.isAutoSnapshot();
     }
 
+    public boolean isTableIncrementalBackupsEnabled()
+    {
+        return DatabaseDescriptor.isIncrementalBackupsEnabled() && metadata().params.incrementalBackups;
+    }
+
     /**
      * Discard all SSTables that were created before given timestamp.
      *
diff --git a/src/java/org/apache/cassandra/db/lifecycle/Tracker.java b/src/java/org/apache/cassandra/db/lifecycle/Tracker.java
index 66ecf1c8d8..12c76af8c9 100644
--- a/src/java/org/apache/cassandra/db/lifecycle/Tracker.java
+++ b/src/java/org/apache/cassandra/db/lifecycle/Tracker.java
@@ -418,7 +418,7 @@ public class Tracker
 
     public void maybeIncrementallyBackup(final Iterable<SSTableReader> sstables)
     {
-        if (!DatabaseDescriptor.isIncrementalBackupsEnabled())
+        if (!cfstore.isTableIncrementalBackupsEnabled())
             return;
 
         for (SSTableReader sstable : sstables)
diff --git a/src/java/org/apache/cassandra/schema/SchemaKeyspace.java b/src/java/org/apache/cassandra/schema/SchemaKeyspace.java
index 23544cc0b9..8059e51b76 100644
--- a/src/java/org/apache/cassandra/schema/SchemaKeyspace.java
+++ b/src/java/org/apache/cassandra/schema/SchemaKeyspace.java
@@ -113,6 +113,7 @@ public final class SchemaKeyspace
               + "extensions frozen<map<text, blob>>,"
               + "flags frozen<set<text>>," // SUPER, COUNTER, DENSE, COMPOUND
               + "gc_grace_seconds int,"
+              + "incremental_backups boolean,"        
               + "id uuid,"
               + "max_index_interval int,"
               + "memtable_flush_period_in_ms int,"
@@ -181,6 +182,7 @@ public final class SchemaKeyspace
               + "default_time_to_live int,"
               + "extensions frozen<map<text, blob>>,"
               + "gc_grace_seconds int,"
+              + "incremental_backups boolean,"        
               + "id uuid,"
               + "include_all_columns boolean,"
               + "max_index_interval int,"
@@ -570,6 +572,11 @@ public final class SchemaKeyspace
         // auto-snapshotting is enabled, to avoid RTE in pre-4.2 versioned node during upgrades
         if (!params.allowAutoSnapshot)
             builder.add("allow_auto_snapshot", false);
+
+        // As above, only add the incremental_backups column if the value is not default (true) and
+        // incremental_backups is enabled, to avoid RTE in pre-4.2 versioned node during upgrades
+        if (!params.incrementalBackups)
+            builder.add("incremental_backups", false);
     }
 
     private static void addAlterTableToSchemaMutation(TableMetadata oldTable, TableMetadata newTable, Mutation.SimpleBuilder builder)
@@ -988,6 +995,10 @@ public final class SchemaKeyspace
         if (row.has("allow_auto_snapshot"))
             builder.allowAutoSnapshot(row.getBoolean("allow_auto_snapshot"));
 
+        // incremental_backups column was introduced in 4.2
+        if (row.has("incremental_backups"))
+            builder.incrementalBackups(row.getBoolean("incremental_backups"));
+
         return builder.build();
     }
 
diff --git a/src/java/org/apache/cassandra/schema/TableMetadata.java b/src/java/org/apache/cassandra/schema/TableMetadata.java
index 98fb0540c7..5e51876670 100644
--- a/src/java/org/apache/cassandra/schema/TableMetadata.java
+++ b/src/java/org/apache/cassandra/schema/TableMetadata.java
@@ -263,6 +263,11 @@ public class TableMetadata implements SchemaElement
     {
         return false;
     }
+    
+    public boolean isIncrementalBackupsEnabled()
+    {
+        return params.incrementalBackups;
+    }
 
     public boolean isStaticCompactTable()
     {
diff --git a/src/java/org/apache/cassandra/schema/TableParams.java b/src/java/org/apache/cassandra/schema/TableParams.java
index 7cb0e9bdb5..8f883f8f47 100644
--- a/src/java/org/apache/cassandra/schema/TableParams.java
+++ b/src/java/org/apache/cassandra/schema/TableParams.java
@@ -52,6 +52,7 @@ public final class TableParams
         DEFAULT_TIME_TO_LIVE,
         EXTENSIONS,
         GC_GRACE_SECONDS,
+        INCREMENTAL_BACKUPS,
         MAX_INDEX_INTERVAL,
         MEMTABLE_FLUSH_PERIOD_IN_MS,
         MIN_INDEX_INTERVAL,
@@ -73,6 +74,7 @@ public final class TableParams
     public final double bloomFilterFpChance;
     public final double crcCheckChance;
     public final int gcGraceSeconds;
+    public final boolean incrementalBackups;
     public final int defaultTimeToLive;
     public final int memtableFlushPeriodInMs;
     public final int minIndexInterval;
@@ -96,6 +98,7 @@ public final class TableParams
                             : builder.bloomFilterFpChance;
         crcCheckChance = builder.crcCheckChance;
         gcGraceSeconds = builder.gcGraceSeconds;
+        incrementalBackups = builder.incrementalBackups;
         defaultTimeToLive = builder.defaultTimeToLive;
         memtableFlushPeriodInMs = builder.memtableFlushPeriodInMs;
         minIndexInterval = builder.minIndexInterval;
@@ -128,6 +131,7 @@ public final class TableParams
                             .crcCheckChance(params.crcCheckChance)
                             .defaultTimeToLive(params.defaultTimeToLive)
                             .gcGraceSeconds(params.gcGraceSeconds)
+                            .incrementalBackups(params.incrementalBackups)
                             .maxIndexInterval(params.maxIndexInterval)
                             .memtableFlushPeriodInMs(params.memtableFlushPeriodInMs)
                             .minIndexInterval(params.minIndexInterval)
@@ -213,7 +217,8 @@ public final class TableParams
             && allowAutoSnapshot == p.allowAutoSnapshot
             && bloomFilterFpChance == p.bloomFilterFpChance
             && crcCheckChance == p.crcCheckChance
-            && gcGraceSeconds == p.gcGraceSeconds
+            && gcGraceSeconds == p.gcGraceSeconds 
+            && incrementalBackups == p.incrementalBackups
             && defaultTimeToLive == p.defaultTimeToLive
             && memtableFlushPeriodInMs == p.memtableFlushPeriodInMs
             && minIndexInterval == p.minIndexInterval
@@ -237,6 +242,7 @@ public final class TableParams
                                 bloomFilterFpChance,
                                 crcCheckChance,
                                 gcGraceSeconds,
+                                incrementalBackups,
                                 defaultTimeToLive,
                                 memtableFlushPeriodInMs,
                                 minIndexInterval,
@@ -262,6 +268,7 @@ public final class TableParams
                           .add(CRC_CHECK_CHANCE.toString(), crcCheckChance)
                           .add(GC_GRACE_SECONDS.toString(), gcGraceSeconds)
                           .add(DEFAULT_TIME_TO_LIVE.toString(), defaultTimeToLive)
+                          .add(INCREMENTAL_BACKUPS.toString(), incrementalBackups)
                           .add(MEMTABLE_FLUSH_PERIOD_IN_MS.toString(), memtableFlushPeriodInMs)
                           .add(MIN_INDEX_INTERVAL.toString(), minIndexInterval)
                           .add(MAX_INDEX_INTERVAL.toString(), maxIndexInterval)
@@ -314,6 +321,8 @@ public final class TableParams
                .newLine()
                .append("AND gc_grace_seconds = ").append(gcGraceSeconds)
                .newLine()
+               .append("AND incremental_backups = ").append(incrementalBackups)
+               .newLine()
                .append("AND max_index_interval = ").append(maxIndexInterval)
                .newLine()
                .append("AND memtable_flush_period_in_ms = ").append(memtableFlushPeriodInMs)
@@ -332,6 +341,7 @@ public final class TableParams
         private double bloomFilterFpChance = -1;
         private double crcCheckChance = 1.0;
         private int gcGraceSeconds = 864000; // 10 days
+        private boolean incrementalBackups = true;
         private int defaultTimeToLive = 0;
         private int memtableFlushPeriodInMs = 0;
         private int minIndexInterval = 128;
@@ -385,6 +395,12 @@ public final class TableParams
             return this;
         }
 
+        public Builder incrementalBackups(boolean val)
+        {
+            incrementalBackups = val;
+            return this;
+        }
+
         public Builder defaultTimeToLive(int val)
         {
             defaultTimeToLive = val;
diff --git a/test/distributed/org/apache/cassandra/distributed/test/TableLevelIncrementalBackupsTest.java b/test/distributed/org/apache/cassandra/distributed/test/TableLevelIncrementalBackupsTest.java
new file mode 100644
index 0000000000..a096dbdff6
--- /dev/null
+++ b/test/distributed/org/apache/cassandra/distributed/test/TableLevelIncrementalBackupsTest.java
@@ -0,0 +1,169 @@
+/*
+ * 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;
+
+import org.apache.cassandra.db.ColumnFamilyStore;
+import org.apache.cassandra.distributed.Cluster;
+import org.apache.cassandra.distributed.api.Feature;
+import org.apache.cassandra.io.sstable.Descriptor;
+import org.apache.cassandra.io.sstable.SequenceBasedSSTableId;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import static org.apache.cassandra.Util.getBackups;
+import static org.apache.cassandra.distributed.Cluster.build;
+import static org.apache.cassandra.distributed.api.ConsistencyLevel.ALL;
+import static org.apache.cassandra.distributed.test.ExecUtil.rethrow;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+public class TableLevelIncrementalBackupsTest extends TestBaseImpl  
+{
+    @Test
+    public void testIncrementalBackupEnabledCreateTable() throws Exception
+    {
+        try (Cluster c = getCluster(true))
+        {
+            c.schemaChange(withKeyspace("CREATE TABLE %s.test_table (a text primary key, b int) WITH incremental_backups = true"));
+            disableCompaction(c, KEYSPACE, "test_table");
+            c.coordinator(1).execute(withKeyspace("INSERT INTO %s.test_table (a, b) VALUES ('a', 1)"), ALL);
+            flush(c, KEYSPACE);
+            assertBackupSSTablesCount(c, 1, true, KEYSPACE, "test_table");
+
+            c.coordinator(1).execute(withKeyspace("INSERT INTO %s.test_table (a, b) VALUES ('a', 1)"), ALL);
+            flush(c, KEYSPACE);
+            assertBackupSSTablesCount(c, 2, true, KEYSPACE, "test_table");
+            c.schemaChange(withKeyspace("DROP TABLE %s.test_table"));
+
+            
+            c.schemaChange(withKeyspace("CREATE TABLE %s.test_table2 (a text primary key, b int) WITH incremental_backups = false"));
+            disableCompaction(c, KEYSPACE, "test_table2");
+            c.coordinator(1).execute(withKeyspace("INSERT INTO %s.test_table2 (a, b) VALUES ('a', 1)"), ALL);
+            flush(c, KEYSPACE);
+            assertBackupSSTablesCount(c, 0, false, KEYSPACE, "test_table2");
+            c.schemaChange(withKeyspace("DROP TABLE %s.test_table2"));
+        }
+    }
+
+    @Test
+    public void testIncrementalBackupEnabledAlterTable() throws Exception
+    {
+        try (Cluster c = getCluster(true))
+        {
+            c.schemaChange(withKeyspace("CREATE TABLE %s.test_table (a text primary key, b int) WITH incremental_backups = false"));
+            disableCompaction(c, KEYSPACE, "test_table");
+            c.coordinator(1).execute(withKeyspace("INSERT INTO %s.test_table (a, b) VALUES ('a', 1)"), ALL);
+            flush(c, KEYSPACE);
+            assertBackupSSTablesCount(c, 0, false, KEYSPACE, "test_table");
+
+            c.schemaChange(withKeyspace("ALTER TABLE %s.test_table  WITH incremental_backups = true"));
+            c.coordinator(1).execute(withKeyspace("INSERT INTO %s.test_table (a, b) VALUES ('a', 1)"), ALL);
+            flush(c, KEYSPACE);
+            assertBackupSSTablesCount(c, 1, true, KEYSPACE, "test_table");
+            c.schemaChange(withKeyspace("DROP TABLE %s.test_table"));
+
+
+            c.schemaChange(withKeyspace("CREATE TABLE %s.test_table2 (a text primary key, b int) WITH incremental_backups = true"));
+            disableCompaction(c, KEYSPACE, "test_table2");
+            c.coordinator(1).execute(withKeyspace("INSERT INTO %s.test_table2 (a, b) VALUES ('a', 1)"), ALL);
+            flush(c, KEYSPACE);
+            assertBackupSSTablesCount(c, 1, true, KEYSPACE, "test_table2");
+
+            c.schemaChange(withKeyspace("ALTER TABLE %s.test_table2  WITH incremental_backups = false"));
+            c.coordinator(1).execute(withKeyspace("INSERT INTO %s.test_table2 (a, b) VALUES ('a', 1)"), ALL);
+            flush(c, KEYSPACE);
+            assertBackupSSTablesCount(c, 1, false, KEYSPACE, "test_table2");
+            c.schemaChange(withKeyspace("DROP TABLE %s.test_table2"));
+        }
+    }
+
+    @Test
+    public void testIncrementalBackupWhenCreateTableByDefault() throws Exception
+    {
+        try (Cluster c = getCluster(true))
+        {
+            //incremental_backups is set to true by default
+            c.schemaChange(withKeyspace("CREATE TABLE %s.test_table (a text primary key, b int)"));
+            disableCompaction(c, KEYSPACE, "test_table");
+            c.coordinator(1).execute(withKeyspace("INSERT INTO %s.test_table (a, b) VALUES ('a', 1)"), ALL);
+            flush(c, KEYSPACE);
+            assertBackupSSTablesCount(c, 1, true, KEYSPACE, "test_table");
+
+            c.schemaChange(withKeyspace("ALTER TABLE %s.test_table  WITH incremental_backups = false"));
+            c.coordinator(1).execute(withKeyspace("INSERT INTO %s.test_table (a, b) VALUES ('a', 1)"), ALL);
+            flush(c, KEYSPACE);
+            assertBackupSSTablesCount(c, 1, false, KEYSPACE, "test_table");
+
+            c.schemaChange(withKeyspace("ALTER TABLE %s.test_table  WITH incremental_backups = true"));
+            c.coordinator(1).execute(withKeyspace("INSERT INTO %s.test_table (a, b) VALUES ('a', 1)"), ALL);
+            flush(c, KEYSPACE);
+            assertBackupSSTablesCount(c, 2, true, KEYSPACE, "test_table");
+            
+            c.schemaChange(withKeyspace("DROP TABLE %s.test_table"));
+        }
+    }
+
+    private Cluster getCluster(boolean incrementalBackups) throws IOException
+    {
+        return init(build(2).withDataDirCount(1).withConfig(c -> c.with(Feature.GOSSIP)
+                .set("incremental_backups", incrementalBackups)).start());
+    }
+
+    private void flush(Cluster cluster, String keyspace) 
+    {
+        for (int i = 1; i < cluster.size() + 1; i++)
+            cluster.get(i).flush(keyspace);
+    }
+
+    private void disableCompaction(Cluster cluster, String keyspace, String table)
+    {
+        for (int i = 1; i < cluster.size() + 1; i++)
+            cluster.get(i).nodetool("disableautocompaction", keyspace, table);
+    }
+
+    private static  void assertBackupSSTablesCount(Cluster cluster, int expectedSeqGenIds, boolean enable, String ks, String... tableNames)
+    {
+        for (int i = 1; i < cluster.size() + 1; i++)
+        {
+            cluster.get(i).runOnInstance(rethrow(() -> Arrays.stream(tableNames).forEach(tableName ->  assertTableMetaIncrementalBackupEnable(ks, tableName, enable))));
+            cluster.get(i).runOnInstance(rethrow(() -> Arrays.stream(tableNames).forEach(tableName -> assertSSTablesCount(getBackups(ks, tableName), tableName, expectedSeqGenIds))));
+        }
+    }
+
+    private static void assertSSTablesCount(Set<Descriptor> descs, String tableName, int expectedSeqGenIds)
+    {
+        List<String> seqSSTables = descs.stream().filter(desc -> desc.id instanceof SequenceBasedSSTableId).map(Descriptor::baseFilename).sorted().collect(Collectors.toList());
+        assertThat(seqSSTables).describedAs("SSTables of %s with sequence based id", tableName).hasSize(expectedSeqGenIds);
+    }
+
+    private static void assertTableMetaIncrementalBackupEnable(String ks, String tableName, boolean enable)
+    {
+        ColumnFamilyStore columnFamilyStore = ColumnFamilyStore.getIfExists(ks, tableName);
+        if (enable)
+            assertTrue(columnFamilyStore.isTableIncrementalBackupsEnabled());
+        else
+            assertFalse(columnFamilyStore.isTableIncrementalBackupsEnabled());
+    }
+}
diff --git a/test/unit/org/apache/cassandra/cql3/statements/DescribeStatementTest.java b/test/unit/org/apache/cassandra/cql3/statements/DescribeStatementTest.java
index 83b320c5c3..857920fabc 100644
--- a/test/unit/org/apache/cassandra/cql3/statements/DescribeStatementTest.java
+++ b/test/unit/org/apache/cassandra/cql3/statements/DescribeStatementTest.java
@@ -851,6 +851,7 @@ public class DescribeStatementTest extends CQLTester
                "    AND default_time_to_live = 0\n" +
                "    AND extensions = {}\n" +
                "    AND gc_grace_seconds = 864000\n" +
+               "    AND incremental_backups = true\n" + 
                "    AND max_index_interval = 2048\n" +
                "    AND memtable_flush_period_in_ms = 0\n" +
                "    AND min_index_interval = 128\n" +
@@ -872,6 +873,7 @@ public class DescribeStatementTest extends CQLTester
                "    AND crc_check_chance = 1.0\n" +
                "    AND extensions = {}\n" +
                "    AND gc_grace_seconds = 864000\n" +
+               "    AND incremental_backups = true\n" +
                "    AND max_index_interval = 2048\n" +
                "    AND memtable_flush_period_in_ms = 0\n" +
                "    AND min_index_interval = 128\n" +
diff --git a/test/unit/org/apache/cassandra/db/SchemaCQLHelperTest.java b/test/unit/org/apache/cassandra/db/SchemaCQLHelperTest.java
index ef62acc160..ed08f371cb 100644
--- a/test/unit/org/apache/cassandra/db/SchemaCQLHelperTest.java
+++ b/test/unit/org/apache/cassandra/db/SchemaCQLHelperTest.java
@@ -309,6 +309,7 @@ public class SchemaCQLHelperTest extends CQLTester
                             "    AND default_time_to_live = 4\n" +
                             "    AND extensions = {'ext1': 0x76616c31}\n" +
                             "    AND gc_grace_seconds = 5\n" +
+                            "    AND incremental_backups = true\n" +
                             "    AND max_index_interval = 7\n" +
                             "    AND memtable_flush_period_in_ms = 8\n" +
                             "    AND min_index_interval = 6\n" +


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@cassandra.apache.org
For additional commands, e-mail: commits-help@cassandra.apache.org