diff --git a/CHANGES.txt b/CHANGES.txt index 09d1733837..02233dd17e 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -349,6 +349,7 @@ 3.11.5 + * Add flag to disable SASI indexes, and warnings on creation (CASSANDRA-14866) Merged from 3.0: * Improve `nodetool status -r` speed (CASSANDRA-14847) * Improve merkle tree size and time on heap (CASSANDRA-14096) @@ -390,7 +391,7 @@ Merged from 3.0: * Fix static column order for SELECT * wildcard queries (CASSANDRA-14638) * sstableloader should use discovered broadcast address to connect intra-cluster (CASSANDRA-14522) * Fix reading columns with non-UTF names from schema (CASSANDRA-14468) - Merged from 2.2: +Merged from 2.2: * CircleCI docker image should bake in more dependencies (CASSANDRA-14985) * MigrationManager attempts to pull schema from different major version nodes (CASSANDRA-14928) * Returns null instead of NaN or Infinity in JSON strings (CASSANDRA-14377) diff --git a/NEWS.txt b/NEWS.txt index d9950e3997..ead28f0ac3 100644 --- a/NEWS.txt +++ b/NEWS.txt @@ -210,7 +210,11 @@ Upgrading See CASSANDRA-14358 for details. - repair_session_space_in_mb setting has been added to cassandra.yaml to allow operators to reduce merkle tree size if repair is creating too much heap pressure. The repair_session_max_tree_depth - setting added in 3.0.19 and 3.11.5 is deprecated in favor of this setting. See CASSANDRA-14096 + setting added in 3.0.19 and 3.11.5 is deprecated in favor of this setting. See CASSANDRA-14096 + - The flags 'enable_materialized_views' and 'enable_sasi_indexes' in cassandra.yaml + have been set as false by default. Operators should modify them to allow the + creation of new views and SASI indexes, the existing ones will continue working. + See CASSANDRA-14866 for details. Materialized Views ------------------- @@ -223,6 +227,17 @@ Materialized Views to be NOT NULL, and no base primary key columns get automatically included in view definition. You have to specify them explicitly now. +3.11.5 +====== + +Experimental features +--------------------- + - An 'enable_sasi_indexes' flag, true by default, has been added to cassandra.yaml to allow operators to prevent + the creation of new SASI indexes, which are considered experimental and are not recommended for production use. + (See https://www.mail-archive.com/dev@cassandra.apache.org/msg13582.html) + - The flags 'enable_sasi_indexes' and 'enable_materialized_views' have been grouped under an experimental features + section in cassandra.yaml. + 3.11.4 ====== diff --git a/conf/cassandra.yaml b/conf/cassandra.yaml index 78fb16290e..ca854ca65e 100644 --- a/conf/cassandra.yaml +++ b/conf/cassandra.yaml @@ -1075,14 +1075,6 @@ enable_user_defined_functions: false # This option has no effect, if enable_user_defined_functions is false. enable_scripted_user_defined_functions: false -# Enables materialized view creation on this node. -# Materialized views are considered experimental and are not recommended for production use. -enable_materialized_views: true - -# Enables creation of transiently replicated keyspaces on this node. -# Transient replication is experimental and is not recommended for production use. -#enable_transient_replication: true - # The default Windows kernel timer and scheduling resolution is 15.6ms for power conservation. # Lowering this value on Windows can provide much tighter latency and better throughput, however # some virtualized environments may see a negative performance impact from changing this setting @@ -1299,3 +1291,18 @@ repaired_data_tracking_for_partition_reads_enabled: false # mismatches are less actionable than confirmed ones. report_unconfirmed_repaired_data_mismatches: false +######################### +# EXPERIMENTAL FEATURES # +######################### + +# Enables materialized view creation on this node. +# Materialized views are considered experimental and are not recommended for production use. +enable_materialized_views: false + +# Enables SASI index creation on this node. +# SASI indexes are considered experimental and are not recommended for production use. +enable_sasi_indexes: false + +# Enables creation of transiently replicated keyspaces on this node. +# Transient replication is experimental and is not recommended for production use. +enable_transient_replication: false \ No newline at end of file diff --git a/src/java/org/apache/cassandra/config/Config.java b/src/java/org/apache/cassandra/config/Config.java index a95db233d0..04ac6083f5 100644 --- a/src/java/org/apache/cassandra/config/Config.java +++ b/src/java/org/apache/cassandra/config/Config.java @@ -354,10 +354,12 @@ public class Config public boolean enable_user_defined_functions = false; public boolean enable_scripted_user_defined_functions = false; - public boolean enable_materialized_views = true; + public boolean enable_materialized_views = false; public boolean enable_transient_replication = false; + public boolean enable_sasi_indexes = false; + /** * Optionally disable asynchronous UDF execution. * Disabling asynchronous UDF execution also implicitly disables the security-manager! diff --git a/src/java/org/apache/cassandra/config/DatabaseDescriptor.java b/src/java/org/apache/cassandra/config/DatabaseDescriptor.java index 3f80d7111d..e5fe77242e 100644 --- a/src/java/org/apache/cassandra/config/DatabaseDescriptor.java +++ b/src/java/org/apache/cassandra/config/DatabaseDescriptor.java @@ -2547,11 +2547,26 @@ public class DatabaseDescriptor conf.user_defined_function_warn_timeout = userDefinedFunctionWarnTimeout; } - public static boolean enableMaterializedViews() + public static boolean getEnableMaterializedViews() { return conf.enable_materialized_views; } + public static void setEnableMaterializedViews(boolean enableMaterializedViews) + { + conf.enable_materialized_views = enableMaterializedViews; + } + + public static boolean getEnableSASIIndexes() + { + return conf.enable_sasi_indexes; + } + + public static void setEnableSASIIndexes(boolean enableSASIIndexes) + { + conf.enable_sasi_indexes = enableSASIIndexes; + } + public static boolean isTransientReplicationEnabled() { return conf.enable_transient_replication; diff --git a/src/java/org/apache/cassandra/cql3/statements/schema/CreateIndexStatement.java b/src/java/org/apache/cassandra/cql3/statements/schema/CreateIndexStatement.java index dbca160e3d..18c6511f0c 100644 --- a/src/java/org/apache/cassandra/cql3/statements/schema/CreateIndexStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/schema/CreateIndexStatement.java @@ -19,11 +19,13 @@ package org.apache.cassandra.cql3.statements.schema; import java.util.*; +import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; import org.apache.cassandra.audit.AuditLogContext; import org.apache.cassandra.audit.AuditLogEntryType; import org.apache.cassandra.auth.Permission; +import org.apache.cassandra.config.DatabaseDescriptor; import org.apache.cassandra.cql3.CQLStatement; import org.apache.cassandra.cql3.ColumnIdentifier; import org.apache.cassandra.cql3.QualifiedName; @@ -31,6 +33,7 @@ import org.apache.cassandra.cql3.statements.schema.IndexTarget.Type; import org.apache.cassandra.db.Keyspace; import org.apache.cassandra.db.marshal.MapType; import org.apache.cassandra.exceptions.InvalidRequestException; +import org.apache.cassandra.index.sasi.SASIIndex; import org.apache.cassandra.schema.*; import org.apache.cassandra.schema.Keyspaces.KeyspacesDiff; import org.apache.cassandra.service.ClientState; @@ -68,6 +71,9 @@ public final class CreateIndexStatement extends AlterSchemaStatement { attrs.validate(); + if (attrs.isCustom && attrs.customClass.equals(SASIIndex.class.getName()) && !DatabaseDescriptor.getEnableSASIIndexes()) + throw new InvalidRequestException("SASI indexes are disabled. Enable in cassandra.yaml to use."); + KeyspaceMetadata keyspace = schema.getNullable(keyspaceName); if (null == keyspace) throw ire("Keyspace '%s' doesn't exist", keyspaceName); @@ -135,6 +141,15 @@ public final class CreateIndexStatement extends AlterSchemaStatement return schema.withAddedOrUpdated(keyspace.withSwapped(keyspace.tables.withSwapped(newTable))); } + @Override + Set clientWarnings(KeyspacesDiff diff) + { + if (attrs.isCustom && attrs.customClass.equals(SASIIndex.class.getName())) + return ImmutableSet.of(SASIIndex.USAGE_WARNING); + + return ImmutableSet.of(); + } + private void validateIndexTarget(TableMetadata table, IndexTarget target) { ColumnMetadata column = table.getColumn(target.column); diff --git a/src/java/org/apache/cassandra/cql3/statements/schema/CreateViewStatement.java b/src/java/org/apache/cassandra/cql3/statements/schema/CreateViewStatement.java index bf6bcff53b..7e51eb2ad8 100644 --- a/src/java/org/apache/cassandra/cql3/statements/schema/CreateViewStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/schema/CreateViewStatement.java @@ -31,9 +31,9 @@ import org.apache.cassandra.cql3.restrictions.StatementRestrictions; import org.apache.cassandra.cql3.selection.RawSelector; import org.apache.cassandra.cql3.selection.Selectable; import org.apache.cassandra.cql3.statements.StatementType; -import org.apache.cassandra.db.Keyspace; import org.apache.cassandra.db.marshal.AbstractType; import org.apache.cassandra.db.marshal.ReversedType; +import org.apache.cassandra.db.view.View; import org.apache.cassandra.exceptions.AlreadyExistsException; import org.apache.cassandra.exceptions.InvalidRequestException; import org.apache.cassandra.schema.*; @@ -98,7 +98,7 @@ public final class CreateViewStatement extends AlterSchemaStatement public Keyspaces apply(Keyspaces schema) { - if (!DatabaseDescriptor.enableMaterializedViews()) + if (!DatabaseDescriptor.getEnableMaterializedViews()) throw ire("Materialized views are disabled. Enable in cassandra.yaml to use."); /* @@ -337,7 +337,7 @@ public final class CreateViewStatement extends AlterSchemaStatement @Override Set clientWarnings(KeyspacesDiff diff) { - return ImmutableSet.of("Materialized views are experimental and are not recommended for production use."); + return ImmutableSet.of(View.USAGE_WARNING); } @Override diff --git a/src/java/org/apache/cassandra/db/view/View.java b/src/java/org/apache/cassandra/db/view/View.java index 40f1b84859..af470ffa86 100644 --- a/src/java/org/apache/cassandra/db/view/View.java +++ b/src/java/org/apache/cassandra/db/view/View.java @@ -45,6 +45,8 @@ import org.slf4j.LoggerFactory; */ public class View { + public final static String USAGE_WARNING = "Materialized views are experimental and are not recommended for production use."; + private static final Logger logger = LoggerFactory.getLogger(View.class); public final String name; diff --git a/src/java/org/apache/cassandra/index/sasi/SASIIndex.java b/src/java/org/apache/cassandra/index/sasi/SASIIndex.java index 3ffcb6ebf7..19c09cc516 100644 --- a/src/java/org/apache/cassandra/index/sasi/SASIIndex.java +++ b/src/java/org/apache/cassandra/index/sasi/SASIIndex.java @@ -62,6 +62,8 @@ import org.apache.cassandra.utils.concurrent.OpOrder; public class SASIIndex implements Index, INotificationConsumer { + public final static String USAGE_WARNING = "SASI indexes are experimental and are not recommended for production use."; + private static class SASIIndexBuildingSupport implements IndexBuildingSupport { public SecondaryIndexBuilder getIndexBuildTask(ColumnFamilyStore cfs, diff --git a/test/conf/cassandra-murmur.yaml b/test/conf/cassandra-murmur.yaml index a8288d0688..e933837ccd 100644 --- a/test/conf/cassandra-murmur.yaml +++ b/test/conf/cassandra-murmur.yaml @@ -40,3 +40,5 @@ row_cache_class_name: org.apache.cassandra.cache.OHCProvider row_cache_size_in_mb: 16 enable_user_defined_functions: true enable_scripted_user_defined_functions: true +enable_sasi_indexes: true +enable_materialized_views: true diff --git a/test/conf/cassandra.yaml b/test/conf/cassandra.yaml index 3c096372ee..d94c478b9a 100644 --- a/test/conf/cassandra.yaml +++ b/test/conf/cassandra.yaml @@ -48,3 +48,5 @@ prepared_statements_cache_size_mb: 1 corrupted_tombstone_strategy: exception stream_entire_sstables: true stream_throughput_outbound_megabits_per_sec: 200000000 +enable_sasi_indexes: true +enable_materialized_views: true diff --git a/test/unit/org/apache/cassandra/cql3/ViewTest.java b/test/unit/org/apache/cassandra/cql3/ViewTest.java index 4f99d0340f..02fa19effa 100644 --- a/test/unit/org/apache/cassandra/cql3/ViewTest.java +++ b/test/unit/org/apache/cassandra/cql3/ViewTest.java @@ -39,12 +39,15 @@ import com.datastax.driver.core.exceptions.InvalidQueryException; import org.apache.cassandra.concurrent.SEPExecutor; import org.apache.cassandra.concurrent.Stage; import org.apache.cassandra.concurrent.StageManager; +import org.apache.cassandra.config.DatabaseDescriptor; +import org.apache.cassandra.db.view.View; import org.apache.cassandra.schema.TableMetadata; import org.apache.cassandra.schema.ColumnMetadata; import org.apache.cassandra.db.ColumnFamilyStore; import org.apache.cassandra.db.Keyspace; import org.apache.cassandra.db.SystemKeyspace; import org.apache.cassandra.db.compaction.CompactionManager; +import org.apache.cassandra.service.ClientWarn; import org.apache.cassandra.transport.ProtocolVersion; import org.apache.cassandra.utils.FBUtilities; @@ -1392,4 +1395,52 @@ public class ViewTest extends CQLTester testViewBuilderResume(i); } } + + /** + * Tests that a client warning is issued on materialized view creation. + */ + @Test + public void testClientWarningOnCreate() throws Throwable + { + createTable("CREATE TABLE %s (k int PRIMARY KEY, v int)"); + + ClientWarn.instance.captureWarnings(); + String viewName = keyspace() + ".warning_view"; + execute("CREATE MATERIALIZED VIEW " + viewName + + " AS SELECT * FROM %s WHERE k IS NOT NULL AND v IS NOT NULL PRIMARY KEY (v, k)"); + views.add(viewName); + List warnings = ClientWarn.instance.getWarnings(); + + Assert.assertNotNull(warnings); + Assert.assertEquals(1, warnings.size()); + Assert.assertEquals(View.USAGE_WARNING, warnings.get(0)); + } + + /** + * Tests the configuration flag to disable materialized views. + */ + @Test + public void testDisableMaterializedViews() throws Throwable + { + createTable("CREATE TABLE %s (k int PRIMARY KEY, v int)"); + + executeNet(protocolVersion, "USE " + keyspace()); + + boolean enableMaterializedViews = DatabaseDescriptor.getEnableMaterializedViews(); + try + { + DatabaseDescriptor.setEnableMaterializedViews(false); + createView("view1", "CREATE MATERIALIZED VIEW %s AS SELECT v FROM %%s WHERE k IS NOT NULL AND v IS NOT NULL PRIMARY KEY (v, k)"); + Assert.fail("Should not be able to create a materialized view if they are disabled"); + } + catch (Throwable e) + { + Assert.assertTrue(e instanceof InvalidQueryException); + Assert.assertTrue(e.getMessage().contains("Materialized views are disabled")); + } + finally + { + DatabaseDescriptor.setEnableMaterializedViews(enableMaterializedViews); + } + } } \ No newline at end of file diff --git a/test/unit/org/apache/cassandra/index/sasi/SASICQLTest.java b/test/unit/org/apache/cassandra/index/sasi/SASICQLTest.java index f46e0b80a1..f78d35e058 100644 --- a/test/unit/org/apache/cassandra/index/sasi/SASICQLTest.java +++ b/test/unit/org/apache/cassandra/index/sasi/SASICQLTest.java @@ -28,7 +28,11 @@ import com.datastax.driver.core.Row; import com.datastax.driver.core.Session; import com.datastax.driver.core.SimpleStatement; import org.junit.Assert; + +import org.apache.cassandra.config.DatabaseDescriptor; import org.apache.cassandra.cql3.CQLTester; +import org.apache.cassandra.exceptions.InvalidRequestException; +import org.apache.cassandra.service.ClientWarn; public class SASICQLTest extends CQLTester { @@ -78,4 +82,49 @@ public class SASICQLTest extends CQLTester Assert.assertEquals(20, rs.size()); } } + + /** + * Tests that a client warning is issued on SASI index creation. + */ + @Test + public void testClientWarningOnCreate() + { + createTable("CREATE TABLE %s (k int PRIMARY KEY, v int)"); + + ClientWarn.instance.captureWarnings(); + createIndex("CREATE CUSTOM INDEX ON %s (v) USING 'org.apache.cassandra.index.sasi.SASIIndex'"); + List warnings = ClientWarn.instance.getWarnings(); + + Assert.assertNotNull(warnings); + Assert.assertEquals(1, warnings.size()); + Assert.assertEquals(SASIIndex.USAGE_WARNING, warnings.get(0)); + } + + /** + * Tests the configuration flag to disable SASI indexes. + */ + @Test + public void testDisableSASIIndexes() + { + createTable("CREATE TABLE %s (k int PRIMARY KEY, v int)"); + + boolean enableSASIIndexes = DatabaseDescriptor.getEnableSASIIndexes(); + try + { + DatabaseDescriptor.setEnableSASIIndexes(false); + createIndex("CREATE CUSTOM INDEX ON %s (v) USING 'org.apache.cassandra.index.sasi.SASIIndex'"); + Assert.fail("Should not be able to create a SASI index if they are disabled"); + } + catch (RuntimeException e) + { + Throwable cause = e.getCause(); + Assert.assertNotNull(cause); + Assert.assertTrue(cause instanceof InvalidRequestException); + Assert.assertTrue(cause.getMessage().contains("SASI indexes are disabled")); + } + finally + { + DatabaseDescriptor.setEnableSASIIndexes(enableSASIIndexes); + } + } }