You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cassandra.apache.org by sa...@apache.org on 2023/03/14 16:45:18 UTC
[cassandra] 02/25: [CEP-21] Move schema management to TCM
This is an automated email from the ASF dual-hosted git repository.
samt pushed a commit to branch cep-21-tcm
in repository https://gitbox.apache.org/repos/asf/cassandra.git
commit 728a9d9a072e8972b8af70389b6bff2aa424ac5a
Author: Sam Tunnicliffe <sa...@apache.org>
AuthorDate: Tue Feb 28 17:49:58 2023 +0000
[CEP-21] Move schema management to TCM
Schema itself is a component of ClusterMetadata and all DDL updates are
applied by the CMS inserting a log entry containing a schema
transformation. As the log entries are disseminated around the cluster,
each peer applies the transformation to its local ClusterMetadata,
enacting the schema change. This entails some changes to the way the
database objects represented in schema are intialised (db objects refers
to classes like Keyspace, ColumnFamilyStore, etc).
Co-authored-by: Marcus Eriksson <ma...@apache.org>
Co-authored-by: Alex Petrov <ol...@gmail.com>
Co-authored-by: Sam Tunnicliffe <sa...@apache.org>
---
.../apache/cassandra/cache/AutoSavingCache.java | 10 +-
src/java/org/apache/cassandra/config/Config.java | 2 +-
.../org/apache/cassandra/cql3/QueryProcessor.java | 88 ++-
.../cassandra/cql3/functions/UDAggregate.java | 75 ++
.../cassandra/cql3/functions/UDFunction.java | 72 ++
.../cql3/statements/DescribeStatement.java | 5 +-
.../statements/schema/AlterKeyspaceStatement.java | 32 +-
.../statements/schema/AlterSchemaStatement.java | 35 +-
.../statements/schema/AlterTableStatement.java | 38 +-
.../cql3/statements/schema/AlterTypeStatement.java | 4 +-
.../cql3/statements/schema/AlterViewStatement.java | 4 +-
.../schema/CreateAggregateStatement.java | 4 +-
.../statements/schema/CreateFunctionStatement.java | 3 +-
.../statements/schema/CreateIndexStatement.java | 4 +-
.../statements/schema/CreateKeyspaceStatement.java | 10 +-
.../statements/schema/CreateTableStatement.java | 7 +-
.../statements/schema/CreateTriggerStatement.java | 4 +-
.../statements/schema/CreateTypeStatement.java | 3 +-
.../statements/schema/CreateViewStatement.java | 6 +-
.../statements/schema/DropAggregateStatement.java | 3 +-
.../statements/schema/DropFunctionStatement.java | 4 +-
.../cql3/statements/schema/DropIndexStatement.java | 4 +-
.../statements/schema/DropKeyspaceStatement.java | 4 +-
.../cql3/statements/schema/DropTableStatement.java | 3 +-
.../statements/schema/DropTriggerStatement.java | 4 +-
.../cql3/statements/schema/DropTypeStatement.java | 4 +-
.../cql3/statements/schema/DropViewStatement.java | 4 +-
.../org/apache/cassandra/db/ColumnFamilyStore.java | 63 +-
src/java/org/apache/cassandra/db/Keyspace.java | 125 ++--
.../cassandra/db/compaction/CompactionManager.java | 11 +-
.../org/apache/cassandra/db/lifecycle/Tracker.java | 10 +-
.../org/apache/cassandra/db/view/TableViews.java | 47 +-
.../apache/cassandra/db/view/ViewBuilderTask.java | 2 +-
.../org/apache/cassandra/db/view/ViewManager.java | 19 +-
.../org/apache/cassandra/dht/BootStrapper.java | 31 +-
.../cassandra/hadoop/cql3/CqlBulkRecordWriter.java | 3 +-
.../cassandra/hints/HintsDispatchTrigger.java | 2 +-
.../cassandra/index/SecondaryIndexManager.java | 5 +-
.../cassandra/index/internal/CassandraIndex.java | 13 +-
.../cassandra/io/sstable/CQLSSTableWriter.java | 18 +-
.../cassandra/io/sstable/format/SSTableReader.java | 5 +-
.../format/SSTableReaderLoadingBuilder.java | 2 +-
.../io/sstable/format/big/BigTableWriter.java | 1 -
.../locator/AbstractReplicationStrategy.java | 34 +-
.../cassandra/locator/NetworkTopologyStrategy.java | 2 +-
.../org/apache/cassandra/metrics/TableMetrics.java | 2 +-
.../apache/cassandra/schema/ColumnMetadata.java | 46 ++
.../schema/DefaultSchemaUpdateHandler.java | 356 ----------
.../schema/DefaultSchemaUpdateHandlerFactory.java | 36 -
.../schema/DistributedMetadataLogKeyspace.java | 1 +
.../apache/cassandra/schema/DistributedSchema.java | 262 ++++++-
.../org/apache/cassandra/schema/DroppedColumn.java | 34 +
.../org/apache/cassandra/schema/IndexMetadata.java | 41 ++
src/java/org/apache/cassandra/schema/Indexes.java | 36 +
.../apache/cassandra/schema/KeyspaceMetadata.java | 76 ++-
.../apache/cassandra/schema/KeyspaceParams.java | 30 +
.../org/apache/cassandra/schema/Keyspaces.java | 142 ++--
.../cassandra/schema/MigrationCoordinator.java | 760 ---------------------
.../schema/OfflineSchemaUpdateHandler.java | 96 ---
src/java/org/apache/cassandra/schema/Schema.java | 739 +++++---------------
.../apache/cassandra/schema/SchemaDiagnostics.java | 44 +-
.../org/apache/cassandra/schema/SchemaEvent.java | 10 +-
.../apache/cassandra/schema/SchemaKeyspace.java | 20 +-
.../apache/cassandra/schema/SchemaProvider.java | 190 +++++-
.../cassandra/schema/SchemaTransformation.java | 62 +-
.../cassandra/schema/SchemaTransformations.java | 24 +-
.../cassandra/schema/SchemaUpdateHandler.java | 78 ---
.../schema/SchemaUpdateHandlerFactory.java | 35 -
.../schema/SchemaUpdateHandlerFactoryProvider.java | 60 --
src/java/org/apache/cassandra/schema/TableId.java | 11 +-
.../org/apache/cassandra/schema/TableMetadata.java | 147 +++-
.../apache/cassandra/schema/TableMetadataRef.java | 132 +++-
.../cassandra/schema/TableMetadataRefCache.java | 152 -----
.../org/apache/cassandra/schema/TableParams.java | 140 ++++
src/java/org/apache/cassandra/schema/Tables.java | 38 ++
.../apache/cassandra/schema/TriggerMetadata.java | 32 +
src/java/org/apache/cassandra/schema/Triggers.java | 39 ++
src/java/org/apache/cassandra/schema/Types.java | 66 ++
.../org/apache/cassandra/schema/UserFunctions.java | 64 ++
.../org/apache/cassandra/schema/ViewMetadata.java | 52 ++
src/java/org/apache/cassandra/schema/Views.java | 38 ++
.../apache/cassandra/service/CassandraDaemon.java | 40 +-
.../apache/cassandra/service/StorageService.java | 70 +-
.../org/apache/cassandra/tcm/ClusterMetadata.java | 38 +-
.../cassandra/tcm/ClusterMetadataService.java | 21 +
.../org/apache/cassandra/tcm/MetadataKeys.java | 5 +-
.../org/apache/cassandra/tcm/Transformation.java | 3 +-
.../cassandra/tcm/compatibility/GossipHelper.java | 52 ++
.../cassandra/tcm/listeners/SchemaListener.java | 48 ++
.../org/apache/cassandra/tcm/log/LocalLog.java | 2 +
.../cassandra/tcm/transformations/AlterSchema.java | 107 +++
.../cassandra/tools/SSTableExpiredBlockers.java | 4 +-
.../org/apache/cassandra/tools/SSTableExport.java | 1 +
.../cassandra/tools/SSTableLevelResetter.java | 8 +-
.../cassandra/tools/SSTableOfflineRelevel.java | 6 +-
.../cassandra/tools/StandaloneSSTableUtil.java | 7 +-
.../apache/cassandra/tools/StandaloneScrubber.java | 5 +-
.../apache/cassandra/tools/StandaloneSplitter.java | 6 +-
.../apache/cassandra/tools/StandaloneUpgrader.java | 8 +-
.../apache/cassandra/tools/StandaloneVerifier.java | 8 +-
src/java/org/apache/cassandra/tools/Util.java | 3 +-
.../org/apache/cassandra/utils/FBUtilities.java | 39 +-
.../cassandra/utils/btree/AbstractBTreeMap.java | 157 +++++
.../org/apache/cassandra/utils/btree/BTreeMap.java | 232 +++++++
.../cassandra/distributed/action/GossipHelper.java | 25 +-
.../cassandra/distributed/impl/Instance.java | 10 +-
.../cassandra/distributed/test/SchemaTest.java | 200 ------
.../distributed/test/ring/NodeNotInRingTest.java | 4 +-
.../distributed/test/ring/PendingWritesTest.java | 109 ---
.../cluster/OnInstanceSyncSchemaForBootstrap.java | 6 +-
.../org/apache/cassandra/cql3/KeyCacheCqlTest.java | 2 +-
test/unit/org/apache/cassandra/db/CleanupTest.java | 54 +-
.../cassandra/db/DiskBoundaryManagerTest.java | 2 +-
test/unit/org/apache/cassandra/db/ImportTest.java | 2 +-
.../unit/org/apache/cassandra/db/KeyspaceTest.java | 2 +-
.../compaction/CompactionStrategyManagerTest.java | 4 +-
.../cassandra/db/view/ViewBuilderTaskTest.java | 2 +-
.../index/internal/CustomCassandraIndex.java | 37 +-
.../locator/AssureSufficientLiveNodesTest.java | 13 +-
.../cassandra/schema/MigrationCoordinatorTest.java | 443 ------------
.../cassandra/schema/MigrationManagerTest.java | 6 +-
.../org/apache/cassandra/schema/MockSchema.java | 140 +++-
.../schema/RemoveWithoutDroppingTest.java | 25 +-
.../cassandra/schema/SchemaKeyspaceTest.java | 20 +-
.../org/apache/cassandra/schema/SchemaTest.java | 9 +-
.../apache/cassandra/schema/SchemaTestUtil.java | 43 +-
.../cassandra/streaming/StreamSessionTest.java | 2 +-
.../io/sstable/StressCQLSSTableWriter.java | 10 +-
128 files changed, 3374 insertions(+), 3586 deletions(-)
diff --git a/src/java/org/apache/cassandra/cache/AutoSavingCache.java b/src/java/org/apache/cassandra/cache/AutoSavingCache.java
index 243c070a32..59587713aa 100644
--- a/src/java/org/apache/cassandra/cache/AutoSavingCache.java
+++ b/src/java/org/apache/cassandra/cache/AutoSavingCache.java
@@ -56,6 +56,7 @@ import org.apache.cassandra.schema.SchemaConstants;
import org.apache.cassandra.schema.TableId;
import org.apache.cassandra.schema.TableMetadata;
import org.apache.cassandra.service.CacheService;
+import org.apache.cassandra.tcm.ClusterMetadata;
import org.apache.cassandra.utils.JVMStabilityInspector;
import org.apache.cassandra.utils.Pair;
import org.apache.cassandra.utils.concurrent.Future;
@@ -205,12 +206,13 @@ public class AutoSavingCache<K extends CacheKey, V> extends InstrumentingCache<K
in = streamFactory.getInputStream(dataPath, crcPath);
//Check the schema has not changed since CFs are looked up by name which is ambiguous
- UUID schemaVersion = new UUID(in.readLong(), in.readLong());
- if (!schemaVersion.equals(Schema.instance.getVersion()))
+ UUID expected = new UUID(in.readLong(), in.readLong());
+ UUID actual = ClusterMetadata.current().schema.getVersion();
+ if (!expected.equals(actual))
throw new RuntimeException("Cache schema version "
- + schemaVersion
+ + expected
+ " does not match current schema version "
- + Schema.instance.getVersion());
+ + actual);
ArrayDeque<Future<Pair<K, V>>> futures = new ArrayDeque<>();
long loadByNanos = start + TimeUnit.SECONDS.toNanos(DatabaseDescriptor.getCacheLoadTimeout());
diff --git a/src/java/org/apache/cassandra/config/Config.java b/src/java/org/apache/cassandra/config/Config.java
index c1944f145d..5a37eb1600 100644
--- a/src/java/org/apache/cassandra/config/Config.java
+++ b/src/java/org/apache/cassandra/config/Config.java
@@ -118,7 +118,7 @@ public class Config
public DiskFailurePolicy disk_failure_policy = DiskFailurePolicy.ignore;
public CommitFailurePolicy commit_failure_policy = CommitFailurePolicy.stop;
- public volatile boolean use_deterministic_table_id = false;
+ public volatile boolean use_deterministic_table_id = true;
/* initial token in the ring */
public String initial_token;
diff --git a/src/java/org/apache/cassandra/cql3/QueryProcessor.java b/src/java/org/apache/cassandra/cql3/QueryProcessor.java
index ca3eb9a77f..b52eccbd7f 100644
--- a/src/java/org/apache/cassandra/cql3/QueryProcessor.java
+++ b/src/java/org/apache/cassandra/cql3/QueryProcessor.java
@@ -18,28 +18,64 @@
package org.apache.cassandra.cql3;
import java.nio.ByteBuffer;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
-import com.github.benmanes.caffeine.cache.Cache;
-import com.github.benmanes.caffeine.cache.Caffeine;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Predicate;
-import com.google.common.collect.*;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Iterators;
import com.google.common.primitives.Ints;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.antlr.runtime.*;
+import com.github.benmanes.caffeine.cache.Cache;
+import com.github.benmanes.caffeine.cache.Caffeine;
+import org.antlr.runtime.RecognitionException;
import org.apache.cassandra.concurrent.ImmediateExecutor;
import org.apache.cassandra.concurrent.ScheduledExecutors;
import org.apache.cassandra.config.DatabaseDescriptor;
+import org.apache.cassandra.cql3.functions.Function;
+import org.apache.cassandra.cql3.functions.FunctionName;
+import org.apache.cassandra.cql3.functions.UDAggregate;
+import org.apache.cassandra.cql3.functions.UDFunction;
+import org.apache.cassandra.cql3.selection.ResultSetBuilder;
+import org.apache.cassandra.cql3.statements.BatchStatement;
+import org.apache.cassandra.cql3.statements.ModificationStatement;
+import org.apache.cassandra.cql3.statements.QualifiedStatement;
+import org.apache.cassandra.cql3.statements.SelectStatement;
+import org.apache.cassandra.cql3.statements.schema.AlterSchemaStatement;
+import org.apache.cassandra.db.ConsistencyLevel;
+import org.apache.cassandra.db.DecoratedKey;
+import org.apache.cassandra.db.ReadCommand;
+import org.apache.cassandra.db.ReadQuery;
+import org.apache.cassandra.db.ReadResponse;
+import org.apache.cassandra.db.SinglePartitionReadQuery;
+import org.apache.cassandra.db.SystemKeyspace;
+import org.apache.cassandra.db.marshal.AbstractType;
+import org.apache.cassandra.db.partitions.PartitionIterator;
+import org.apache.cassandra.db.partitions.PartitionIterators;
import org.apache.cassandra.db.partitions.UnfilteredPartitionIterators;
+import org.apache.cassandra.db.rows.Row;
+import org.apache.cassandra.db.rows.RowIterator;
+import org.apache.cassandra.exceptions.CassandraException;
+import org.apache.cassandra.exceptions.InvalidRequestException;
+import org.apache.cassandra.exceptions.IsBootstrappingException;
+import org.apache.cassandra.exceptions.RequestExecutionException;
+import org.apache.cassandra.exceptions.RequestValidationException;
+import org.apache.cassandra.exceptions.SyntaxException;
+import org.apache.cassandra.gms.Gossiper;
import org.apache.cassandra.locator.InetAddressAndPort;
+import org.apache.cassandra.metrics.CQLMetrics;
import org.apache.cassandra.metrics.ClientRequestMetrics;
import org.apache.cassandra.metrics.ClientRequestsMetricsHolder;
import org.apache.cassandra.net.Message;
@@ -49,27 +85,19 @@ import org.apache.cassandra.schema.Schema;
import org.apache.cassandra.schema.SchemaChangeListener;
import org.apache.cassandra.schema.SchemaConstants;
import org.apache.cassandra.schema.TableMetadata;
-import org.apache.cassandra.cql3.functions.UDAggregate;
-import org.apache.cassandra.cql3.functions.UDFunction;
-import org.apache.cassandra.cql3.functions.Function;
-import org.apache.cassandra.cql3.functions.FunctionName;
-import org.apache.cassandra.cql3.selection.ResultSetBuilder;
-import org.apache.cassandra.cql3.statements.*;
-import org.apache.cassandra.db.*;
-import org.apache.cassandra.db.rows.Row;
-import org.apache.cassandra.db.rows.RowIterator;
-import org.apache.cassandra.db.partitions.PartitionIterator;
-import org.apache.cassandra.db.partitions.PartitionIterators;
-import org.apache.cassandra.db.marshal.AbstractType;
-import org.apache.cassandra.exceptions.*;
-import org.apache.cassandra.gms.Gossiper;
-import org.apache.cassandra.metrics.CQLMetrics;
-import org.apache.cassandra.service.*;
+import org.apache.cassandra.service.ClientState;
+import org.apache.cassandra.service.QueryState;
+import org.apache.cassandra.service.StorageService;
import org.apache.cassandra.service.pager.QueryPager;
import org.apache.cassandra.tracing.Tracing;
import org.apache.cassandra.transport.ProtocolVersion;
import org.apache.cassandra.transport.messages.ResultMessage;
-import org.apache.cassandra.utils.*;
+import org.apache.cassandra.utils.ByteBufferUtil;
+import org.apache.cassandra.utils.CassandraVersion;
+import org.apache.cassandra.utils.FBUtilities;
+import org.apache.cassandra.utils.JVMStabilityInspector;
+import org.apache.cassandra.utils.MD5Digest;
+import org.apache.cassandra.utils.ObjectSizes;
import org.apache.cassandra.utils.concurrent.Future;
import org.apache.cassandra.utils.concurrent.FutureCombiner;
@@ -208,7 +236,6 @@ public class QueryProcessor implements QueryHandler
private QueryProcessor()
{
- Schema.instance.registerListener(new StatementInvalidatingListener());
}
@VisibleForTesting
@@ -275,7 +302,7 @@ public class QueryProcessor implements QueryHandler
private ResultMessage processNodeLocalWrite(CQLStatement statement, QueryState queryState, QueryOptions options)
{
- ClientRequestMetrics levelMetrics = ClientRequestsMetricsHolder.writeMetricsForLevel(ConsistencyLevel.NODE_LOCAL);
+ ClientRequestMetrics levelMetrics = ClientRequestsMetricsHolder.writeMetricsForLevel(ConsistencyLevel.NODE_LOCAL);
ClientRequestMetrics globalMetrics = ClientRequestsMetricsHolder.writeMetrics;
long startTime = nanoTime();
@@ -856,7 +883,11 @@ public class QueryProcessor implements QueryHandler
((QualifiedStatement) statement).setKeyspace(clientState);
Tracing.trace("Preparing statement");
- return statement.prepare(clientState);
+ CQLStatement prepared = statement.prepare(clientState);
+ if (prepared instanceof AlterSchemaStatement)
+ ((AlterSchemaStatement) prepared).setCql(queryStr);
+
+ return prepared;
}
public static <T extends CQLStatement.Raw> T parseStatement(String queryStr, Class<T> klass, String type) throws SyntaxException
@@ -919,6 +950,11 @@ public class QueryProcessor implements QueryHandler
preparedStatements.asMap().clear();
}
+ public static void registerStatementInvalidatingListener()
+ {
+ Schema.instance.registerListener(new StatementInvalidatingListener());
+ }
+
private static class StatementInvalidatingListener implements SchemaChangeListener
{
private static void removeInvalidPreparedStatements(String ksName, String cfName)
@@ -1079,4 +1115,4 @@ public class QueryProcessor implements QueryHandler
removeInvalidPreparedStatementsForFunction(aggregate.name().keyspace, aggregate.name().name);
}
}
-}
+}
\ No newline at end of file
diff --git a/src/java/org/apache/cassandra/cql3/functions/UDAggregate.java b/src/java/org/apache/cassandra/cql3/functions/UDAggregate.java
index 08483b3c64..9ede9cf6e5 100644
--- a/src/java/org/apache/cassandra/cql3/functions/UDAggregate.java
+++ b/src/java/org/apache/cassandra/cql3/functions/UDAggregate.java
@@ -17,6 +17,7 @@
*/
package org.apache.cassandra.cql3.functions;
+import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.*;
@@ -31,13 +32,20 @@ import org.apache.cassandra.db.marshal.AbstractType;
import org.apache.cassandra.db.marshal.UserType;
import org.apache.cassandra.exceptions.ConfigurationException;
import org.apache.cassandra.exceptions.InvalidRequestException;
+import org.apache.cassandra.io.util.DataInputPlus;
+import org.apache.cassandra.io.util.DataOutputPlus;
+import org.apache.cassandra.tcm.serialization.Version;
+import org.apache.cassandra.schema.CQLTypeParser;
import org.apache.cassandra.schema.Difference;
import org.apache.cassandra.schema.UserFunctions;
+import org.apache.cassandra.schema.Types;
import org.apache.cassandra.tracing.Tracing;
import org.apache.cassandra.transport.ProtocolVersion;
+import org.apache.cassandra.utils.ByteBufferUtil;
import static com.google.common.collect.Iterables.any;
import static com.google.common.collect.Iterables.transform;
+import static org.apache.cassandra.db.TypeSizes.sizeof;
import static org.apache.cassandra.utils.Clock.Global.nanoTime;
/**
@@ -45,6 +53,8 @@ import static org.apache.cassandra.utils.Clock.Global.nanoTime;
*/
public class UDAggregate extends UserFunction implements AggregateFunction
{
+ public static final Serializer serializer = new Serializer();
+
protected static final Logger logger = LoggerFactory.getLogger(UDAggregate.class);
private final AbstractType<?> stateType;
@@ -368,4 +378,69 @@ public class UDAggregate extends UserFunction implements AggregateFunction
return builder.append(";")
.toString();
}
+
+ // Not quite a MetadataSerializer, or even a UDTAwareMetadataSerializer, as it needs the collection of UDFs during deserialization.
+ public static class Serializer
+ {
+ public void serialize(UDAggregate t, DataOutputPlus out, Version version) throws IOException
+ {
+ out.writeUTF(t.name().keyspace);
+ out.writeUTF(t.name().name);
+ out.writeInt(t.argumentsList().size());
+ for (String arg : t.argumentsList())
+ out.writeUTF(arg);
+ out.writeUTF(t.returnType().asCQL3Type().toString());
+ out.writeUTF(t.stateFunction.name().name);
+ out.writeUTF(t.stateType.asCQL3Type().toString());
+ out.writeBoolean(t.finalFunction() != null);
+ if (t.finalFunction() != null)
+ out.writeUTF(t.finalFunction().name().name);
+ out.writeBoolean(t.initialCondition() != null);
+ if (t.initialCondition() != null)
+ ByteBufferUtil.writeWithShortLength(t.initialCondition(), out);
+ }
+
+ public UDAggregate deserialize(DataInputPlus in, Types types, Collection<UDFunction> functions, Version version) throws IOException
+ {
+ String ks = in.readUTF();
+ String name = in.readUTF();
+ FunctionName fn = new FunctionName(ks, name);
+ int argCount = in.readInt();
+ List<AbstractType<?>> argList = new ArrayList<>(argCount);
+ for (int i = 0; i < argCount; i++)
+ argList.add(CQLTypeParser.parse(ks, in.readUTF(), types).udfType());
+ AbstractType<?> returnType = CQLTypeParser.parse(ks, in.readUTF(), types).udfType();
+ FunctionName stateFunction = new FunctionName(ks, in.readUTF());
+ AbstractType<?> stateType = CQLTypeParser.parse(ks, in.readUTF(), types).udfType();
+ boolean hasFinalFunction = in.readBoolean();
+ FunctionName finalFunction = null;
+ if (hasFinalFunction)
+ finalFunction = new FunctionName(ks, in.readUTF());
+ boolean hasInitialCondition = in.readBoolean();
+ ByteBuffer initCond = null;
+ if (hasInitialCondition)
+ initCond = ByteBufferUtil.readWithShortLength(in);
+ return UDAggregate.create(functions, fn, argList, returnType, stateFunction, finalFunction, stateType, initCond);
+ }
+
+ public long serializedSize(UDAggregate t, Version version)
+ {
+ long size = sizeof(t.name().keyspace) + sizeof(t.name().name);
+
+ size += sizeof(t.argumentsList().size());
+ for (String arg : t.argumentsList())
+ size += sizeof(arg);
+
+ size += sizeof(t.returnType().asCQL3Type().toString());
+ size += sizeof(t.stateFunction.name().name);
+ size += sizeof(t.stateType.asCQL3Type().toString());
+ size += sizeof(t.finalFunction() != null);
+ if (t.finalFunction() != null)
+ size += sizeof(t.finalFunction().name().name);
+ size += sizeof(t.initialCondition() != null);
+ if (t.initialCondition() != null)
+ size += ByteBufferUtil.serializedSizeWithShortLength(t.initialCondition());
+ return size;
+ }
+ }
}
diff --git a/src/java/org/apache/cassandra/cql3/functions/UDFunction.java b/src/java/org/apache/cassandra/cql3/functions/UDFunction.java
index 58454a49ac..f6e261badd 100644
--- a/src/java/org/apache/cassandra/cql3/functions/UDFunction.java
+++ b/src/java/org/apache/cassandra/cql3/functions/UDFunction.java
@@ -17,6 +17,7 @@
*/
package org.apache.cassandra.cql3.functions;
+import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadMXBean;
import java.net.InetAddress;
@@ -35,6 +36,7 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
+import java.util.stream.Collectors;
import com.google.common.base.Objects;
import com.google.common.collect.Lists;
@@ -52,6 +54,10 @@ import org.apache.cassandra.db.marshal.AbstractType;
import org.apache.cassandra.db.marshal.UserType;
import org.apache.cassandra.exceptions.FunctionExecutionException;
import org.apache.cassandra.exceptions.InvalidRequestException;
+import org.apache.cassandra.io.util.DataInputPlus;
+import org.apache.cassandra.io.util.DataOutputPlus;
+import org.apache.cassandra.tcm.serialization.UDTAwareMetadataSerializer;
+import org.apache.cassandra.tcm.serialization.Version;
import org.apache.cassandra.schema.*;
import org.apache.cassandra.service.ClientWarn;
import org.apache.cassandra.tracing.Tracing;
@@ -61,6 +67,8 @@ import org.apache.cassandra.utils.concurrent.UncheckedInterruptedException;
import static com.google.common.collect.Iterables.any;
import static com.google.common.collect.Iterables.transform;
+import static org.apache.cassandra.db.TypeSizes.*;
+import static org.apache.cassandra.schema.SchemaKeyspace.bbToString;
import static org.apache.cassandra.utils.Clock.Global.nanoTime;
/**
@@ -68,6 +76,8 @@ import static org.apache.cassandra.utils.Clock.Global.nanoTime;
*/
public abstract class UDFunction extends UserFunction implements ScalarFunction
{
+ public static final Serializer serializer = new Serializer();
+
protected static final Logger logger = LoggerFactory.getLogger(UDFunction.class);
static final ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
@@ -790,4 +800,66 @@ public abstract class UDFunction extends UserFunction implements ScalarFunction
return super.loadClass(name);
}
}
+
+ public static class Serializer implements UDTAwareMetadataSerializer<UDFunction>
+ {
+ public void serialize(UDFunction t, DataOutputPlus out, Version version) throws IOException
+ {
+ out.writeUTF(t.name().keyspace);
+ out.writeUTF(t.name().name);
+ out.writeUTF(t.body());
+ out.writeUTF(t.language());
+ out.writeUTF(t.returnType().asCQL3Type().toString());
+ out.writeBoolean(t.isCalledOnNullInput());
+ List<String> arguments = t.argNames().stream().map(c -> bbToString(c.bytes)).collect(Collectors.toList());
+ out.writeInt(arguments.size());
+ for (String argument : arguments)
+ out.writeUTF(argument);
+
+ out.writeInt(t.argumentsList().size());
+ for (String type : t.argumentsList())
+ out.writeUTF(type);
+ }
+
+ public UDFunction deserialize(DataInputPlus in, Types types, Version version) throws IOException
+ {
+ String keyspace = in.readUTF();
+ String name = in.readUTF();
+ FunctionName fn = new FunctionName(keyspace, name);
+ String body = in.readUTF();
+ String language = in.readUTF();
+ AbstractType<?> returnType = CQLTypeParser.parse(keyspace, in.readUTF(), types).udfType();
+ boolean isCalledOnNullInput = in.readBoolean();
+ int argumentCount = in.readInt();
+ List<ColumnIdentifier> arguments = new ArrayList<>(argumentCount);
+ for (int i = 0; i < argumentCount; i++)
+ arguments.add(new ColumnIdentifier(in.readUTF(), true));
+
+ int argumentTypeCount = in.readInt();
+ List<AbstractType<?>> argTypes = new ArrayList<>(argumentTypeCount);
+ for (int i = 0; i < argumentTypeCount; i++)
+ argTypes.add(CQLTypeParser.parse(keyspace, in.readUTF(), types).udfType());
+
+ return UDFunction.create(fn, arguments, argTypes, returnType, isCalledOnNullInput, language, body);
+ }
+
+ public long serializedSize(UDFunction t, Version version)
+ {
+ long size = sizeof(t.name().keyspace);
+ size += sizeof(t.name().name);
+ size += sizeof(t.body());
+ size += sizeof(t.language());
+ size += sizeof(t.returnType().asCQL3Type().toString());
+ size += sizeof(t.isCalledOnNullInput());
+ List<String> arguments = t.argNames().stream().map(c -> bbToString(c.bytes)).collect(Collectors.toList());
+ size += sizeof(arguments.size());
+ for (String argument : arguments)
+ size += sizeof(argument);
+
+ size += sizeof(t.argumentsList().size());
+ for (String type : t.argumentsList())
+ size += sizeof(type);
+ return size;
+ }
+ }
}
diff --git a/src/java/org/apache/cassandra/cql3/statements/DescribeStatement.java b/src/java/org/apache/cassandra/cql3/statements/DescribeStatement.java
index 96a02475a4..af899101a8 100644
--- a/src/java/org/apache/cassandra/cql3/statements/DescribeStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/DescribeStatement.java
@@ -135,10 +135,7 @@ public abstract class DescribeStatement<T> extends CQLStatement.Raw implements C
Keyspaces keyspaces = Schema.instance.distributedAndLocalKeyspaces();
UUID schemaVersion = Schema.instance.getVersion();
- keyspaces = Keyspaces.builder()
- .add(keyspaces)
- .add(VirtualKeyspaceRegistry.instance.virtualKeyspacesMetadata())
- .build();
+ keyspaces = keyspaces.with(VirtualKeyspaceRegistry.instance.virtualKeyspacesMetadata());
PagingState pagingState = options.getPagingState();
diff --git a/src/java/org/apache/cassandra/cql3/statements/schema/AlterKeyspaceStatement.java b/src/java/org/apache/cassandra/cql3/statements/schema/AlterKeyspaceStatement.java
index dec0655e74..9efccd5464 100644
--- a/src/java/org/apache/cassandra/cql3/statements/schema/AlterKeyspaceStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/schema/AlterKeyspaceStatement.java
@@ -23,6 +23,9 @@ import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
import org.apache.cassandra.audit.AuditLogContext;
import org.apache.cassandra.audit.AuditLogEntryType;
import org.apache.cassandra.auth.Permission;
@@ -44,15 +47,17 @@ import org.apache.cassandra.schema.KeyspaceMetadata.KeyspaceDiff;
import org.apache.cassandra.schema.Keyspaces;
import org.apache.cassandra.schema.Keyspaces.KeyspacesDiff;
import org.apache.cassandra.service.ClientState;
+import org.apache.cassandra.tcm.ClusterMetadata;
import org.apache.cassandra.transport.Event.SchemaChange;
import org.apache.cassandra.transport.Event.SchemaChange.Change;
import org.apache.cassandra.utils.FBUtilities;
public final class AlterKeyspaceStatement extends AlterSchemaStatement
{
+ private static final Logger logger = LoggerFactory.getLogger(AlterKeyspaceStatement.class);
+
private static final boolean allow_alter_rf_during_range_movement = Boolean.getBoolean(Config.PROPERTY_PREFIX + "allow_alter_rf_during_range_movement");
private static final boolean allow_unsafe_transient_changes = Boolean.getBoolean(Config.PROPERTY_PREFIX + "allow_unsafe_transient_changes");
- private final HashSet<String> clientWarnings = new HashSet<>();
private final KeyspaceAttributes attrs;
private final boolean ifExists;
@@ -64,7 +69,7 @@ public final class AlterKeyspaceStatement extends AlterSchemaStatement
this.ifExists = ifExists;
}
- public Keyspaces apply(Keyspaces schema)
+ public Keyspaces apply(ClusterMetadata metadata, Keyspaces schema)
{
attrs.validate();
@@ -85,13 +90,21 @@ public final class AlterKeyspaceStatement extends AlterSchemaStatement
throw ire("Unable to use given strategy class: LocalStrategy is reserved for internal use.");
newKeyspace.params.validate(keyspaceName, state);
+ newKeyspace.replicationStrategy.validate();
- validateNoRangeMovements();
- validateTransientReplication(keyspace.createReplicationStrategy(), newKeyspace.createReplicationStrategy());
+ validateTransientReplication(keyspace.replicationStrategy, newKeyspace.replicationStrategy);
- Keyspaces res = schema.withAddedOrUpdated(newKeyspace);
+ // Because we used to not properly validate unrecognized options, we only log a warning if we find one.
+ try
+ {
+ newKeyspace.replicationStrategy.validateExpectedOptions();
+ }
+ catch (ConfigurationException e)
+ {
+ logger.warn("Ignoring {}", e.getMessage());
+ }
- return res;
+ return schema.withAddedOrUpdated(newKeyspace);
}
SchemaChange schemaChangeEvent(KeyspacesDiff diff)
@@ -107,13 +120,14 @@ public final class AlterKeyspaceStatement extends AlterSchemaStatement
@Override
Set<String> clientWarnings(KeyspacesDiff diff)
{
+ HashSet<String> clientWarnings = new HashSet<>();
if (diff.isEmpty())
return clientWarnings;
KeyspaceDiff keyspaceDiff = diff.altered.get(0);
- AbstractReplicationStrategy before = keyspaceDiff.before.createReplicationStrategy();
- AbstractReplicationStrategy after = keyspaceDiff.after.createReplicationStrategy();
+ AbstractReplicationStrategy before = keyspaceDiff.before.replicationStrategy;
+ AbstractReplicationStrategy after = keyspaceDiff.after.replicationStrategy;
if (before.getReplicationFactor().fullReplicas < after.getReplicationFactor().fullReplicas)
clientWarnings.add("When increasing replication factor you need to run a full (-full) repair to distribute the data.");
@@ -128,7 +142,7 @@ public final class AlterKeyspaceStatement extends AlterSchemaStatement
Stream<InetAddressAndPort> unreachableNotAdministrativelyInactive =
Gossiper.instance.getUnreachableMembers().stream().filter(endpoint -> !FBUtilities.getBroadcastAddressAndPort().equals(endpoint) &&
- !Gossiper.instance.isAdministrativelyInactiveState(endpoint));
+ !Gossiper.instance.isAdministrativelyInactiveState(endpoint));
Stream<InetAddressAndPort> endpoints = Stream.concat(Gossiper.instance.getLiveMembers().stream(),
unreachableNotAdministrativelyInactive);
List<InetAddressAndPort> notNormalEndpoints = endpoints.filter(endpoint -> !FBUtilities.getBroadcastAddressAndPort().equals(endpoint) &&
diff --git a/src/java/org/apache/cassandra/cql3/statements/schema/AlterSchemaStatement.java b/src/java/org/apache/cassandra/cql3/statements/schema/AlterSchemaStatement.java
index a539ea74e8..be6734f25a 100644
--- a/src/java/org/apache/cassandra/cql3/statements/schema/AlterSchemaStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/schema/AlterSchemaStatement.java
@@ -34,6 +34,7 @@ import org.apache.cassandra.schema.Keyspaces.KeyspacesDiff;
import org.apache.cassandra.service.ClientState;
import org.apache.cassandra.service.ClientWarn;
import org.apache.cassandra.service.QueryState;
+import org.apache.cassandra.tcm.ClusterMetadata;
import org.apache.cassandra.transport.Event.SchemaChange;
import org.apache.cassandra.transport.messages.ResultMessage;
@@ -41,12 +42,28 @@ abstract public class AlterSchemaStatement implements CQLStatement.SingleKeyspac
{
protected final String keyspaceName; // name of the keyspace affected by the statement
protected ClientState state;
+ // TODO: not sure if this is going to stay the same, or will be replaced by more efficient serialization/sanitation means
+ // or just `toString` for every statement
+ private String cql;
protected AlterSchemaStatement(String keyspaceName)
{
this.keyspaceName = keyspaceName;
}
+ public void setCql(String cql)
+ {
+ this.cql = cql;
+ }
+
+ @Override
+ public String cql()
+ {
+ assert cql != null;
+ return cql;
+ }
+
+ // TODO: validation should be performed during application
public void validate(ClientState state)
{
// validation is performed while executing the statement, in apply()
@@ -57,7 +74,7 @@ abstract public class AlterSchemaStatement implements CQLStatement.SingleKeyspac
public ResultMessage execute(QueryState state, QueryOptions options, long queryStartNanoTime)
{
- return execute(state, false);
+ return execute(state);
}
@Override
@@ -68,7 +85,7 @@ abstract public class AlterSchemaStatement implements CQLStatement.SingleKeyspac
public ResultMessage executeLocally(QueryState state, QueryOptions options)
{
- return execute(state, true);
+ return execute(state);
}
/**
@@ -100,7 +117,7 @@ abstract public class AlterSchemaStatement implements CQLStatement.SingleKeyspac
return ImmutableSet.of();
}
- public ResultMessage execute(QueryState state, boolean locally)
+ public ResultMessage execute(QueryState state)
{
if (SchemaConstants.isLocalSystemKeyspace(keyspaceName))
throw ire("System keyspace '%s' is not user-modifiable", keyspaceName);
@@ -110,12 +127,14 @@ abstract public class AlterSchemaStatement implements CQLStatement.SingleKeyspac
throw ire("Virtual keyspace '%s' is not user-modifiable", keyspaceName);
validateKeyspaceName();
+ Keyspaces before = ClusterMetadata.current().schema.getKeyspaces();
- SchemaTransformationResult result = Schema.instance.transform(this, locally);
+ ClusterMetadata result = Schema.instance.submit(this);
- clientWarnings(result.diff).forEach(ClientWarn.instance::warn);
+ KeyspacesDiff diff = Keyspaces.diff(before, result.schema.getKeyspaces());
+ clientWarnings(diff).forEach(ClientWarn.instance::warn);
- if (result.diff.isEmpty())
+ if (diff.isEmpty())
return new ResultMessage.Void();
/*
@@ -127,9 +146,9 @@ abstract public class AlterSchemaStatement implements CQLStatement.SingleKeyspac
*/
AuthenticatedUser user = state.getClientState().getUser();
if (null != user && !user.isAnonymous())
- createdResources(result.diff).forEach(r -> grantPermissionsOnResource(r, user));
+ createdResources(diff).forEach(r -> grantPermissionsOnResource(r, user));
- return new ResultMessage.SchemaChange(schemaChangeEvent(result.diff));
+ return new ResultMessage.SchemaChange(schemaChangeEvent(diff));
}
private void validateKeyspaceName()
diff --git a/src/java/org/apache/cassandra/cql3/statements/schema/AlterTableStatement.java b/src/java/org/apache/cassandra/cql3/statements/schema/AlterTableStatement.java
index ad0358c2cb..875f22aca4 100644
--- a/src/java/org/apache/cassandra/cql3/statements/schema/AlterTableStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/schema/AlterTableStatement.java
@@ -28,7 +28,6 @@ import java.util.concurrent.TimeUnit;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableSet;
-
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -43,7 +42,6 @@ import org.apache.cassandra.cql3.QualifiedName;
import org.apache.cassandra.db.Keyspace;
import org.apache.cassandra.db.guardrails.Guardrails;
import org.apache.cassandra.db.marshal.AbstractType;
-
import org.apache.cassandra.exceptions.InvalidRequestException;
import org.apache.cassandra.gms.ApplicationState;
import org.apache.cassandra.gms.Gossiper;
@@ -61,17 +59,17 @@ import org.apache.cassandra.schema.Views;
import org.apache.cassandra.service.ClientState;
import org.apache.cassandra.service.StorageService;
import org.apache.cassandra.service.reads.repair.ReadRepairStrategy;
+import org.apache.cassandra.tcm.ClusterMetadata;
+import org.apache.cassandra.tcm.Epoch;
import org.apache.cassandra.transport.Event.SchemaChange;
import org.apache.cassandra.transport.Event.SchemaChange.Change;
import org.apache.cassandra.transport.Event.SchemaChange.Target;
import org.apache.cassandra.utils.NoSpamLogger;
-import static java.lang.String.format;
-import static java.lang.String.join;
-
import static com.google.common.collect.Iterables.isEmpty;
import static com.google.common.collect.Iterables.transform;
-
+import static java.lang.String.format;
+import static java.lang.String.join;
import static org.apache.cassandra.schema.TableMetadata.Flag;
public abstract class AlterTableStatement extends AlterSchemaStatement
@@ -96,7 +94,7 @@ public abstract class AlterTableStatement extends AlterSchemaStatement
this.state = state;
}
- public Keyspaces apply(Keyspaces schema)
+ public Keyspaces apply(ClusterMetadata metadata, Keyspaces schema)
{
KeyspaceMetadata keyspace = schema.getNullable(keyspaceName);
@@ -114,7 +112,7 @@ public abstract class AlterTableStatement extends AlterSchemaStatement
if (table.isView())
throw ire("Cannot use ALTER TABLE on a materialized view; use ALTER MATERIALIZED VIEW instead");
- return schema.withAddedOrUpdated(apply(keyspace, table));
+ return schema.withAddedOrUpdated(apply(metadata.nextEpoch(), keyspace, table));
}
SchemaChange schemaChangeEvent(KeyspacesDiff diff)
@@ -138,7 +136,7 @@ public abstract class AlterTableStatement extends AlterSchemaStatement
return format("%s (%s, %s)", getClass().getSimpleName(), keyspaceName, tableName);
}
- abstract KeyspaceMetadata apply(KeyspaceMetadata keyspace, TableMetadata table);
+ abstract KeyspaceMetadata apply(Epoch epoch, KeyspaceMetadata keyspace, TableMetadata table);
/**
* ALTER TABLE [IF EXISTS] <table> ALTER <column> TYPE <newtype>;
@@ -152,7 +150,7 @@ public abstract class AlterTableStatement extends AlterSchemaStatement
super(keyspaceName, tableName, ifTableExists);
}
- public KeyspaceMetadata apply(KeyspaceMetadata keyspace, TableMetadata table)
+ public KeyspaceMetadata apply(Epoch epoch, KeyspaceMetadata keyspace, TableMetadata table)
{
throw ire("Altering column types is no longer supported");
}
@@ -194,10 +192,10 @@ public abstract class AlterTableStatement extends AlterSchemaStatement
super.validate(state);
}
- public KeyspaceMetadata apply(KeyspaceMetadata keyspace, TableMetadata table)
+ public KeyspaceMetadata apply(Epoch epoch, KeyspaceMetadata keyspace, TableMetadata table)
{
Guardrails.alterTableEnabled.ensureEnabled("ALTER TABLE changing columns", state);
- TableMetadata.Builder tableBuilder = table.unbuild();
+ TableMetadata.Builder tableBuilder = table.unbuild().epoch(epoch);
Views.Builder viewsBuilder = keyspace.views.unbuild();
newColumns.forEach(c -> addColumn(keyspace, table, c, ifColumnNotExists, tableBuilder, viewsBuilder));
@@ -297,10 +295,10 @@ public abstract class AlterTableStatement extends AlterSchemaStatement
this.timestamp = timestamp;
}
- public KeyspaceMetadata apply(KeyspaceMetadata keyspace, TableMetadata table)
+ public KeyspaceMetadata apply(Epoch epoch, KeyspaceMetadata keyspace, TableMetadata table)
{
Guardrails.alterTableEnabled.ensureEnabled("ALTER TABLE changing columns", state);
- TableMetadata.Builder builder = table.unbuild();
+ TableMetadata.Builder builder = table.unbuild().epoch(epoch);
removedColumns.forEach(c -> dropColumn(keyspace, table, c, ifColumnExists, builder));
return keyspace.withSwapped(keyspace.tables.withSwapped(builder.build()));
}
@@ -365,10 +363,10 @@ public abstract class AlterTableStatement extends AlterSchemaStatement
this.ifColumnsExists = ifColumnsExists;
}
- public KeyspaceMetadata apply(KeyspaceMetadata keyspace, TableMetadata table)
+ public KeyspaceMetadata apply(Epoch epoch, KeyspaceMetadata keyspace, TableMetadata table)
{
Guardrails.alterTableEnabled.ensureEnabled("ALTER TABLE changing columns", state);
- TableMetadata.Builder tableBuilder = table.unbuild();
+ TableMetadata.Builder tableBuilder = table.unbuild().epoch(epoch);
Views.Builder viewsBuilder = keyspace.views.unbuild();
renamedColumns.forEach((o, n) -> renameColumn(keyspace, table, o, n, ifColumnsExists, tableBuilder, viewsBuilder));
@@ -447,7 +445,7 @@ public abstract class AlterTableStatement extends AlterSchemaStatement
validateDefaultTimeToLive(attrs.asNewTableParams());
}
- public KeyspaceMetadata apply(KeyspaceMetadata keyspace, TableMetadata table)
+ public KeyspaceMetadata apply(Epoch epoch, KeyspaceMetadata keyspace, TableMetadata table)
{
attrs.validate();
@@ -465,7 +463,7 @@ public abstract class AlterTableStatement extends AlterSchemaStatement
"before being replayed.");
}
- if (keyspace.createReplicationStrategy().hasTransientReplicas()
+ if (keyspace.replicationStrategy.hasTransientReplicas()
&& params.readRepair != ReadRepairStrategy.NONE)
{
throw ire("read_repair must be set to 'NONE' for transiently replicated keyspaces");
@@ -491,7 +489,7 @@ public abstract class AlterTableStatement extends AlterSchemaStatement
super(keyspaceName, tableName, ifTableExists);
}
- public KeyspaceMetadata apply(KeyspaceMetadata keyspace, TableMetadata table)
+ public KeyspaceMetadata apply(Epoch epoch, KeyspaceMetadata keyspace, TableMetadata table)
{
if (!DatabaseDescriptor.enableDropCompactStorage())
throw new InvalidRequestException("DROP COMPACT STORAGE is disabled. Enable in cassandra.yaml to use.");
@@ -505,7 +503,7 @@ public abstract class AlterTableStatement extends AlterSchemaStatement
? ImmutableSet.of(Flag.COMPOUND, Flag.COUNTER)
: ImmutableSet.of(Flag.COMPOUND);
- return keyspace.withSwapped(keyspace.tables.withSwapped(table.withSwapped(flags)));
+ return keyspace.withSwapped(keyspace.tables.withSwapped(table.unbuild().epoch(epoch).flags(flags).build()));
}
/**
diff --git a/src/java/org/apache/cassandra/cql3/statements/schema/AlterTypeStatement.java b/src/java/org/apache/cassandra/cql3/statements/schema/AlterTypeStatement.java
index 89cb990ae2..5b36fb9b54 100644
--- a/src/java/org/apache/cassandra/cql3/statements/schema/AlterTypeStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/schema/AlterTypeStatement.java
@@ -34,6 +34,7 @@ import org.apache.cassandra.schema.KeyspaceMetadata;
import org.apache.cassandra.schema.Keyspaces;
import org.apache.cassandra.schema.TableMetadata;
import org.apache.cassandra.service.ClientState;
+import org.apache.cassandra.tcm.ClusterMetadata;
import org.apache.cassandra.transport.Event.SchemaChange;
import org.apache.cassandra.transport.Event.SchemaChange.Change;
import org.apache.cassandra.transport.Event.SchemaChange.Target;
@@ -69,7 +70,8 @@ public abstract class AlterTypeStatement extends AlterSchemaStatement
return new SchemaChange(Change.UPDATED, Target.TYPE, keyspaceName, typeName);
}
- public Keyspaces apply(Keyspaces schema)
+ @Override
+ public Keyspaces apply(ClusterMetadata metadata, Keyspaces schema)
{
KeyspaceMetadata keyspace = schema.getNullable(keyspaceName);
diff --git a/src/java/org/apache/cassandra/cql3/statements/schema/AlterViewStatement.java b/src/java/org/apache/cassandra/cql3/statements/schema/AlterViewStatement.java
index 7e707f476b..d571bbdea8 100644
--- a/src/java/org/apache/cassandra/cql3/statements/schema/AlterViewStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/schema/AlterViewStatement.java
@@ -26,6 +26,7 @@ import org.apache.cassandra.db.guardrails.Guardrails;
import org.apache.cassandra.schema.*;
import org.apache.cassandra.schema.Keyspaces.KeyspacesDiff;
import org.apache.cassandra.service.ClientState;
+import org.apache.cassandra.tcm.ClusterMetadata;
import org.apache.cassandra.transport.Event.SchemaChange;
import org.apache.cassandra.transport.Event.SchemaChange.Change;
import org.apache.cassandra.transport.Event.SchemaChange.Target;
@@ -54,7 +55,8 @@ public final class AlterViewStatement extends AlterSchemaStatement
this.state = state;
}
- public Keyspaces apply(Keyspaces schema)
+ @Override
+ public Keyspaces apply(ClusterMetadata metadata, Keyspaces schema)
{
KeyspaceMetadata keyspace = schema.getNullable(keyspaceName);
diff --git a/src/java/org/apache/cassandra/cql3/statements/schema/CreateAggregateStatement.java b/src/java/org/apache/cassandra/cql3/statements/schema/CreateAggregateStatement.java
index 010f46412d..83ddc59850 100644
--- a/src/java/org/apache/cassandra/cql3/statements/schema/CreateAggregateStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/schema/CreateAggregateStatement.java
@@ -45,6 +45,7 @@ import org.apache.cassandra.schema.Keyspaces.KeyspacesDiff;
import org.apache.cassandra.schema.Schema;
import org.apache.cassandra.serializers.MarshalException;
import org.apache.cassandra.service.ClientState;
+import org.apache.cassandra.tcm.ClusterMetadata;
import org.apache.cassandra.transport.Event.SchemaChange;
import org.apache.cassandra.transport.Event.SchemaChange.Change;
import org.apache.cassandra.transport.Event.SchemaChange.Target;
@@ -91,7 +92,8 @@ public final class CreateAggregateStatement extends AlterSchemaStatement
this.ifNotExists = ifNotExists;
}
- public Keyspaces apply(Keyspaces schema)
+ @Override
+ public Keyspaces apply(ClusterMetadata metadata, Keyspaces schema)
{
if (ifNotExists && orReplace)
throw ire("Cannot use both 'OR REPLACE' and 'IF NOT EXISTS' directives");
diff --git a/src/java/org/apache/cassandra/cql3/statements/schema/CreateFunctionStatement.java b/src/java/org/apache/cassandra/cql3/statements/schema/CreateFunctionStatement.java
index 8e3c79ca70..bb3e12ae2b 100644
--- a/src/java/org/apache/cassandra/cql3/statements/schema/CreateFunctionStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/schema/CreateFunctionStatement.java
@@ -40,6 +40,7 @@ import org.apache.cassandra.schema.Keyspaces;
import org.apache.cassandra.schema.Keyspaces.KeyspacesDiff;
import org.apache.cassandra.schema.Schema;
import org.apache.cassandra.service.ClientState;
+import org.apache.cassandra.tcm.ClusterMetadata;
import org.apache.cassandra.transport.Event.SchemaChange;
import org.apache.cassandra.transport.Event.SchemaChange.Change;
import org.apache.cassandra.transport.Event.SchemaChange.Target;
@@ -82,7 +83,7 @@ public final class CreateFunctionStatement extends AlterSchemaStatement
}
// TODO: replace affected aggregates !!
- public Keyspaces apply(Keyspaces schema)
+ public Keyspaces apply(ClusterMetadata metadata, Keyspaces schema)
{
if (ifNotExists && orReplace)
throw ire("Cannot use both 'OR REPLACE' and 'IF NOT EXISTS' directives");
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 7a3a41e618..2a979a1462 100644
--- a/src/java/org/apache/cassandra/cql3/statements/schema/CreateIndexStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/schema/CreateIndexStatement.java
@@ -39,6 +39,7 @@ 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;
+import org.apache.cassandra.tcm.ClusterMetadata;
import org.apache.cassandra.transport.Event.SchemaChange;
import org.apache.cassandra.transport.Event.SchemaChange.Change;
import org.apache.cassandra.transport.Event.SchemaChange.Target;
@@ -80,7 +81,8 @@ public final class CreateIndexStatement extends AlterSchemaStatement
this.state = state;
}
- public Keyspaces apply(Keyspaces schema)
+ @Override
+ public Keyspaces apply(ClusterMetadata metadata, Keyspaces schema)
{
attrs.validate();
diff --git a/src/java/org/apache/cassandra/cql3/statements/schema/CreateKeyspaceStatement.java b/src/java/org/apache/cassandra/cql3/statements/schema/CreateKeyspaceStatement.java
index ad6bcc472d..7fdff7b6be 100644
--- a/src/java/org/apache/cassandra/cql3/statements/schema/CreateKeyspaceStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/schema/CreateKeyspaceStatement.java
@@ -37,12 +37,11 @@ import org.apache.cassandra.db.guardrails.Guardrails;
import org.apache.cassandra.exceptions.AlreadyExistsException;
import org.apache.cassandra.locator.LocalStrategy;
import org.apache.cassandra.locator.SimpleStrategy;
-import org.apache.cassandra.schema.KeyspaceMetadata;
+import org.apache.cassandra.schema.*;
import org.apache.cassandra.schema.KeyspaceParams.Option;
-import org.apache.cassandra.schema.Keyspaces;
import org.apache.cassandra.schema.Keyspaces.KeyspacesDiff;
-import org.apache.cassandra.schema.Schema;
import org.apache.cassandra.service.ClientState;
+import org.apache.cassandra.tcm.ClusterMetadata;
import org.apache.cassandra.transport.Event.SchemaChange;
import org.apache.cassandra.transport.Event.SchemaChange.Change;
@@ -52,7 +51,6 @@ public final class CreateKeyspaceStatement extends AlterSchemaStatement
private final KeyspaceAttributes attrs;
private final boolean ifNotExists;
- private final HashSet<String> clientWarnings = new HashSet<>();
public CreateKeyspaceStatement(String keyspaceName, KeyspaceAttributes attrs, boolean ifNotExists)
{
@@ -61,7 +59,7 @@ public final class CreateKeyspaceStatement extends AlterSchemaStatement
this.ifNotExists = ifNotExists;
}
- public Keyspaces apply(Keyspaces schema)
+ public Keyspaces apply(ClusterMetadata metadata, Keyspaces schema)
{
attrs.validate();
@@ -85,6 +83,7 @@ public final class CreateKeyspaceStatement extends AlterSchemaStatement
throw ire("Unable to use given strategy class: LocalStrategy is reserved for internal use.");
keyspace.params.validate(keyspaceName, state);
+ keyspace.replicationStrategy.validateExpectedOptions();
return schema.withAddedOrUpdated(keyspace);
}
@@ -128,6 +127,7 @@ public final class CreateKeyspaceStatement extends AlterSchemaStatement
Set<String> clientWarnings(KeyspacesDiff diff)
{
// this threshold is deprecated, it will be replaced by the guardrail used in #validate(ClientState)
+ HashSet<String> clientWarnings = new HashSet<>();
int keyspaceCount = Schema.instance.getKeyspaces().size();
if (keyspaceCount > DatabaseDescriptor.keyspaceCountWarnThreshold())
{
diff --git a/src/java/org/apache/cassandra/cql3/statements/schema/CreateTableStatement.java b/src/java/org/apache/cassandra/cql3/statements/schema/CreateTableStatement.java
index 3b1ea36b4e..7a6bb2fb7d 100644
--- a/src/java/org/apache/cassandra/cql3/statements/schema/CreateTableStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/schema/CreateTableStatement.java
@@ -40,6 +40,7 @@ import org.apache.cassandra.exceptions.AlreadyExistsException;
import org.apache.cassandra.schema.*;
import org.apache.cassandra.schema.Keyspaces.KeyspacesDiff;
import org.apache.cassandra.service.ClientState;
+import org.apache.cassandra.tcm.ClusterMetadata;
import org.apache.cassandra.service.reads.repair.ReadRepairStrategy;
import org.apache.cassandra.transport.Event.SchemaChange;
import org.apache.cassandra.transport.Event.SchemaChange.Change;
@@ -94,7 +95,7 @@ public final class CreateTableStatement extends AlterSchemaStatement
this.useCompactStorage = useCompactStorage;
}
- public Keyspaces apply(Keyspaces schema)
+ public Keyspaces apply(ClusterMetadata metadata, Keyspaces schema)
{
KeyspaceMetadata keyspace = schema.getNullable(keyspaceName);
if (null == keyspace)
@@ -108,10 +109,10 @@ public final class CreateTableStatement extends AlterSchemaStatement
throw new AlreadyExistsException(keyspaceName, tableName);
}
- TableMetadata table = builder(keyspace.types).build();
+ TableMetadata table = builder(keyspace.types).epoch(metadata.nextEpoch()).build();
table.validate();
- if (keyspace.createReplicationStrategy().hasTransientReplicas()
+ if (keyspace.replicationStrategy.hasTransientReplicas()
&& table.params.readRepair != ReadRepairStrategy.NONE)
{
throw ire("read_repair must be set to 'NONE' for transiently replicated keyspaces");
diff --git a/src/java/org/apache/cassandra/cql3/statements/schema/CreateTriggerStatement.java b/src/java/org/apache/cassandra/cql3/statements/schema/CreateTriggerStatement.java
index e85ffd80ae..537027f733 100644
--- a/src/java/org/apache/cassandra/cql3/statements/schema/CreateTriggerStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/schema/CreateTriggerStatement.java
@@ -24,6 +24,7 @@ import org.apache.cassandra.cql3.QualifiedName;
import org.apache.cassandra.schema.*;
import org.apache.cassandra.schema.Keyspaces.KeyspacesDiff;
import org.apache.cassandra.service.ClientState;
+import org.apache.cassandra.tcm.ClusterMetadata;
import org.apache.cassandra.triggers.TriggerExecutor;
import org.apache.cassandra.transport.Event.SchemaChange;
import org.apache.cassandra.transport.Event.SchemaChange.Change;
@@ -45,7 +46,8 @@ public final class CreateTriggerStatement extends AlterSchemaStatement
this.ifNotExists = ifNotExists;
}
- public Keyspaces apply(Keyspaces schema)
+ @Override
+ public Keyspaces apply(ClusterMetadata metadata, Keyspaces schema)
{
KeyspaceMetadata keyspace = schema.getNullable(keyspaceName);
if (null == keyspace)
diff --git a/src/java/org/apache/cassandra/cql3/statements/schema/CreateTypeStatement.java b/src/java/org/apache/cassandra/cql3/statements/schema/CreateTypeStatement.java
index e015c34ede..e16a906100 100644
--- a/src/java/org/apache/cassandra/cql3/statements/schema/CreateTypeStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/schema/CreateTypeStatement.java
@@ -34,6 +34,7 @@ import org.apache.cassandra.schema.Keyspaces;
import org.apache.cassandra.schema.Keyspaces.KeyspacesDiff;
import org.apache.cassandra.schema.Types;
import org.apache.cassandra.service.ClientState;
+import org.apache.cassandra.tcm.ClusterMetadata;
import org.apache.cassandra.transport.Event.SchemaChange;
import org.apache.cassandra.transport.Event.SchemaChange.Change;
import org.apache.cassandra.transport.Event.SchemaChange.Target;
@@ -70,7 +71,7 @@ public final class CreateTypeStatement extends AlterSchemaStatement
Guardrails.fieldsPerUDT.guard(fieldNames.size(), typeName, false, state);
}
- public Keyspaces apply(Keyspaces schema)
+ public Keyspaces apply(ClusterMetadata metadata, Keyspaces schema)
{
KeyspaceMetadata keyspace = schema.getNullable(keyspaceName);
if (null == keyspace)
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 dce054faa4..a0648edbe8 100644
--- a/src/java/org/apache/cassandra/cql3/statements/schema/CreateViewStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/schema/CreateViewStatement.java
@@ -41,6 +41,7 @@ import org.apache.cassandra.exceptions.InvalidRequestException;
import org.apache.cassandra.schema.*;
import org.apache.cassandra.schema.Keyspaces.KeyspacesDiff;
import org.apache.cassandra.service.ClientState;
+import org.apache.cassandra.tcm.ClusterMetadata;
import org.apache.cassandra.transport.Event.SchemaChange;
import org.apache.cassandra.transport.Event.SchemaChange.Change;
import org.apache.cassandra.transport.Event.SchemaChange.Target;
@@ -109,7 +110,8 @@ public final class CreateViewStatement extends AlterSchemaStatement
this.state = state;
}
- public Keyspaces apply(Keyspaces schema)
+ @Override
+ public Keyspaces apply(ClusterMetadata metadata, Keyspaces schema)
{
if (!DatabaseDescriptor.getMaterializedViewsEnabled())
throw ire("Materialized views are disabled. Enable in cassandra.yaml to use.");
@@ -122,7 +124,7 @@ public final class CreateViewStatement extends AlterSchemaStatement
if (null == keyspace)
throw ire("Keyspace '%s' doesn't exist", keyspaceName);
- if (keyspace.createReplicationStrategy().hasTransientReplicas())
+ if (keyspace.replicationStrategy.hasTransientReplicas())
throw new InvalidRequestException("Materialized views are not supported on transiently replicated keyspaces");
TableMetadata table = keyspace.tables.getNullable(tableName);
diff --git a/src/java/org/apache/cassandra/cql3/statements/schema/DropAggregateStatement.java b/src/java/org/apache/cassandra/cql3/statements/schema/DropAggregateStatement.java
index 186891e2f1..2daa0966b0 100644
--- a/src/java/org/apache/cassandra/cql3/statements/schema/DropAggregateStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/schema/DropAggregateStatement.java
@@ -35,6 +35,7 @@ import org.apache.cassandra.db.marshal.AbstractType;
import org.apache.cassandra.schema.*;
import org.apache.cassandra.schema.Keyspaces.KeyspacesDiff;
import org.apache.cassandra.service.ClientState;
+import org.apache.cassandra.tcm.ClusterMetadata;
import org.apache.cassandra.transport.Event.SchemaChange;
import org.apache.cassandra.transport.Event.SchemaChange.Change;
@@ -64,7 +65,7 @@ public final class DropAggregateStatement extends AlterSchemaStatement
this.ifExists = ifExists;
}
- public Keyspaces apply(Keyspaces schema)
+ public Keyspaces apply(ClusterMetadata metadata, Keyspaces schema)
{
String name =
argumentsSpeficied
diff --git a/src/java/org/apache/cassandra/cql3/statements/schema/DropFunctionStatement.java b/src/java/org/apache/cassandra/cql3/statements/schema/DropFunctionStatement.java
index dd6afbd1cd..012c0b187b 100644
--- a/src/java/org/apache/cassandra/cql3/statements/schema/DropFunctionStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/schema/DropFunctionStatement.java
@@ -35,6 +35,7 @@ import org.apache.cassandra.db.marshal.AbstractType;
import org.apache.cassandra.schema.*;
import org.apache.cassandra.schema.Keyspaces.KeyspacesDiff;
import org.apache.cassandra.service.ClientState;
+import org.apache.cassandra.tcm.ClusterMetadata;
import org.apache.cassandra.transport.Event.SchemaChange;
import org.apache.cassandra.transport.Event.SchemaChange.Change;
@@ -65,7 +66,8 @@ public final class DropFunctionStatement extends AlterSchemaStatement
this.ifExists = ifExists;
}
- public Keyspaces apply(Keyspaces schema)
+ @Override
+ public Keyspaces apply(ClusterMetadata metadata, Keyspaces schema)
{
String name =
argumentsSpeficied
diff --git a/src/java/org/apache/cassandra/cql3/statements/schema/DropIndexStatement.java b/src/java/org/apache/cassandra/cql3/statements/schema/DropIndexStatement.java
index 24b372d8c3..5781b4e9da 100644
--- a/src/java/org/apache/cassandra/cql3/statements/schema/DropIndexStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/schema/DropIndexStatement.java
@@ -26,6 +26,7 @@ import org.apache.cassandra.schema.*;
import org.apache.cassandra.schema.KeyspaceMetadata.KeyspaceDiff;
import org.apache.cassandra.schema.Keyspaces.KeyspacesDiff;
import org.apache.cassandra.service.ClientState;
+import org.apache.cassandra.tcm.ClusterMetadata;
import org.apache.cassandra.transport.Event.SchemaChange;
import org.apache.cassandra.transport.Event.SchemaChange.Change;
import org.apache.cassandra.transport.Event.SchemaChange.Target;
@@ -42,7 +43,8 @@ public final class DropIndexStatement extends AlterSchemaStatement
this.ifExists = ifExists;
}
- public Keyspaces apply(Keyspaces schema)
+ @Override
+ public Keyspaces apply(ClusterMetadata metadata, Keyspaces schema)
{
KeyspaceMetadata keyspace = schema.getNullable(keyspaceName);
diff --git a/src/java/org/apache/cassandra/cql3/statements/schema/DropKeyspaceStatement.java b/src/java/org/apache/cassandra/cql3/statements/schema/DropKeyspaceStatement.java
index 47e514a527..2d925a0687 100644
--- a/src/java/org/apache/cassandra/cql3/statements/schema/DropKeyspaceStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/schema/DropKeyspaceStatement.java
@@ -25,6 +25,7 @@ import org.apache.cassandra.db.guardrails.Guardrails;
import org.apache.cassandra.schema.Keyspaces;
import org.apache.cassandra.schema.Keyspaces.KeyspacesDiff;
import org.apache.cassandra.service.ClientState;
+import org.apache.cassandra.tcm.ClusterMetadata;
import org.apache.cassandra.transport.Event.SchemaChange;
import org.apache.cassandra.transport.Event.SchemaChange.Change;
@@ -38,7 +39,8 @@ public final class DropKeyspaceStatement extends AlterSchemaStatement
this.ifExists = ifExists;
}
- public Keyspaces apply(Keyspaces schema)
+ @Override
+ public Keyspaces apply(ClusterMetadata metadata, Keyspaces schema)
{
Guardrails.dropKeyspaceEnabled.ensureEnabled(state);
diff --git a/src/java/org/apache/cassandra/cql3/statements/schema/DropTableStatement.java b/src/java/org/apache/cassandra/cql3/statements/schema/DropTableStatement.java
index 78c98be3a7..311a5c4a7c 100644
--- a/src/java/org/apache/cassandra/cql3/statements/schema/DropTableStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/schema/DropTableStatement.java
@@ -26,6 +26,7 @@ import org.apache.cassandra.db.guardrails.Guardrails;
import org.apache.cassandra.schema.*;
import org.apache.cassandra.schema.Keyspaces.KeyspacesDiff;
import org.apache.cassandra.service.ClientState;
+import org.apache.cassandra.tcm.ClusterMetadata;
import org.apache.cassandra.transport.Event.SchemaChange;
import org.apache.cassandra.transport.Event.SchemaChange.Change;
import org.apache.cassandra.transport.Event.SchemaChange.Target;
@@ -47,7 +48,7 @@ public final class DropTableStatement extends AlterSchemaStatement
this.ifExists = ifExists;
}
- public Keyspaces apply(Keyspaces schema)
+ public Keyspaces apply(ClusterMetadata metadata, Keyspaces schema)
{
Guardrails.dropTruncateTableEnabled.ensureEnabled(state);
diff --git a/src/java/org/apache/cassandra/cql3/statements/schema/DropTriggerStatement.java b/src/java/org/apache/cassandra/cql3/statements/schema/DropTriggerStatement.java
index 967e56834f..46fc9c9670 100644
--- a/src/java/org/apache/cassandra/cql3/statements/schema/DropTriggerStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/schema/DropTriggerStatement.java
@@ -24,6 +24,7 @@ import org.apache.cassandra.cql3.QualifiedName;
import org.apache.cassandra.schema.*;
import org.apache.cassandra.schema.Keyspaces.KeyspacesDiff;
import org.apache.cassandra.service.ClientState;
+import org.apache.cassandra.tcm.ClusterMetadata;
import org.apache.cassandra.transport.Event.SchemaChange;
import org.apache.cassandra.transport.Event.SchemaChange.Change;
import org.apache.cassandra.transport.Event.SchemaChange.Target;
@@ -42,7 +43,8 @@ public final class DropTriggerStatement extends AlterSchemaStatement
this.ifExists = ifExists;
}
- public Keyspaces apply(Keyspaces schema)
+ @Override
+ public Keyspaces apply(ClusterMetadata metadata, Keyspaces schema)
{
KeyspaceMetadata keyspace = schema.getNullable(keyspaceName);
diff --git a/src/java/org/apache/cassandra/cql3/statements/schema/DropTypeStatement.java b/src/java/org/apache/cassandra/cql3/statements/schema/DropTypeStatement.java
index 97830c882a..c7b24f8b53 100644
--- a/src/java/org/apache/cassandra/cql3/statements/schema/DropTypeStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/schema/DropTypeStatement.java
@@ -31,6 +31,7 @@ import org.apache.cassandra.schema.Keyspaces.KeyspacesDiff;
import org.apache.cassandra.schema.Keyspaces;
import org.apache.cassandra.schema.TableMetadata;
import org.apache.cassandra.service.ClientState;
+import org.apache.cassandra.tcm.ClusterMetadata;
import org.apache.cassandra.transport.Event.SchemaChange.Change;
import org.apache.cassandra.transport.Event.SchemaChange.Target;
import org.apache.cassandra.transport.Event.SchemaChange;
@@ -55,7 +56,8 @@ public final class DropTypeStatement extends AlterSchemaStatement
}
// TODO: expand types into tuples in all dropped columns of all tables
- public Keyspaces apply(Keyspaces schema)
+ @Override
+ public Keyspaces apply(ClusterMetadata metadata, Keyspaces schema)
{
ByteBuffer name = bytes(typeName);
diff --git a/src/java/org/apache/cassandra/cql3/statements/schema/DropViewStatement.java b/src/java/org/apache/cassandra/cql3/statements/schema/DropViewStatement.java
index 2c73717546..57e3eaea47 100644
--- a/src/java/org/apache/cassandra/cql3/statements/schema/DropViewStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/schema/DropViewStatement.java
@@ -25,6 +25,7 @@ import org.apache.cassandra.cql3.QualifiedName;
import org.apache.cassandra.schema.*;
import org.apache.cassandra.schema.Keyspaces.KeyspacesDiff;
import org.apache.cassandra.service.ClientState;
+import org.apache.cassandra.tcm.ClusterMetadata;
import org.apache.cassandra.transport.Event.SchemaChange;
import org.apache.cassandra.transport.Event.SchemaChange.Change;
import org.apache.cassandra.transport.Event.SchemaChange.Target;
@@ -41,7 +42,8 @@ public final class DropViewStatement extends AlterSchemaStatement
this.ifExists = ifExists;
}
- public Keyspaces apply(Keyspaces schema)
+ @Override
+ public Keyspaces apply(ClusterMetadata metadata, Keyspaces schema)
{
KeyspaceMetadata keyspace = schema.getNullable(keyspaceName);
diff --git a/src/java/org/apache/cassandra/db/ColumnFamilyStore.java b/src/java/org/apache/cassandra/db/ColumnFamilyStore.java
index 4c567701b3..0affba5814 100644
--- a/src/java/org/apache/cassandra/db/ColumnFamilyStore.java
+++ b/src/java/org/apache/cassandra/db/ColumnFamilyStore.java
@@ -161,6 +161,7 @@ import org.apache.cassandra.service.snapshot.SnapshotLoader;
import org.apache.cassandra.service.snapshot.SnapshotManifest;
import org.apache.cassandra.service.snapshot.TableSnapshot;
import org.apache.cassandra.streaming.TableStreamManager;
+import org.apache.cassandra.tcm.ClusterMetadata;
import org.apache.cassandra.utils.ByteBufferUtil;
import org.apache.cassandra.utils.DefaultValue;
import org.apache.cassandra.utils.ExecutorUtils;
@@ -370,23 +371,28 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean, Memtable.Owner
}
public void reload()
+ {
+ reload(metadata());
+ }
+
+ public void reload(TableMetadata tableMetadata)
{
// metadata object has been mutated directly. make all the members jibe with new settings.
// only update these runtime-modifiable settings if they have not been modified.
if (!minCompactionThreshold.isModified())
for (ColumnFamilyStore cfs : concatWithIndexes())
- cfs.minCompactionThreshold = new DefaultValue(metadata().params.compaction.minCompactionThreshold());
+ cfs.minCompactionThreshold = new DefaultValue<>(tableMetadata.params.compaction.minCompactionThreshold());
if (!maxCompactionThreshold.isModified())
for (ColumnFamilyStore cfs : concatWithIndexes())
- cfs.maxCompactionThreshold = new DefaultValue(metadata().params.compaction.maxCompactionThreshold());
+ cfs.maxCompactionThreshold = new DefaultValue<>(tableMetadata.params.compaction.maxCompactionThreshold());
if (!crcCheckChance.isModified())
for (ColumnFamilyStore cfs : concatWithIndexes())
- cfs.crcCheckChance = new DefaultValue(metadata().params.crcCheckChance);
+ cfs.crcCheckChance = new DefaultValue<>(tableMetadata.params.crcCheckChance);
- compactionStrategyManager.maybeReloadParamsFromSchema(metadata().params.compaction);
+ compactionStrategyManager.maybeReloadParamsFromSchema(tableMetadata.params.compaction);
- indexManager.reload();
+ indexManager.reload(tableMetadata);
memtableFactory = metadata().params.memtable.factory();
switchMemtableOrNotify(FlushReason.SCHEMA_CHANGE, Memtable::metadataUpdated);
@@ -448,7 +454,9 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean, Memtable.Owner
{
CompressionParams params = CompressionParams.fromMap(opts);
params.validate();
- metadata.setLocalOverrides(metadata().unbuild().compression(params).build());
+
+ TableMetadata orig = metadata();
+ metadata.setLocalOverrides(orig.unbuild().compression(params).epoch(orig.epoch).build());
}
catch (ConfigurationException e)
{
@@ -465,27 +473,27 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean, Memtable.Owner
public ColumnFamilyStore(Keyspace keyspace,
String columnFamilyName,
Supplier<? extends SSTableId> sstableIdGenerator,
- TableMetadataRef metadata,
+ TableMetadata initMetadata,
Directories directories,
boolean loadSSTables,
boolean registerBookeeping,
boolean offline)
{
assert directories != null;
- assert metadata != null : "null metadata for " + keyspace + ':' + columnFamilyName;
+ assert initMetadata != null : "null metadata for " + keyspace + ':' + columnFamilyName;
this.keyspace = keyspace;
- this.metadata = metadata;
+ this.metadata = initMetadata.ref;
this.directories = directories;
name = columnFamilyName;
- minCompactionThreshold = new DefaultValue<>(metadata.get().params.compaction.minCompactionThreshold());
- maxCompactionThreshold = new DefaultValue<>(metadata.get().params.compaction.maxCompactionThreshold());
- crcCheckChance = new DefaultValue<>(metadata.get().params.crcCheckChance);
- viewManager = keyspace.viewManager.forTable(metadata.id);
+ minCompactionThreshold = new DefaultValue<>(initMetadata.params.compaction.minCompactionThreshold());
+ maxCompactionThreshold = new DefaultValue<>(initMetadata.params.compaction.maxCompactionThreshold());
+ crcCheckChance = new DefaultValue<>(initMetadata.params.crcCheckChance);
+ viewManager = keyspace.viewManager.forTable(initMetadata);
this.sstableIdGenerator = sstableIdGenerator;
sampleReadLatencyMicros = DatabaseDescriptor.getReadRpcTimeout(TimeUnit.MICROSECONDS) / 2;
additionalWriteLatencyMicros = DatabaseDescriptor.getWriteRpcTimeout(TimeUnit.MICROSECONDS) / 2;
- memtableFactory = metadata.get().params.memtable.factory();
+ memtableFactory = initMetadata.params.memtable.factory();
logger.info("Initializing {}.{}", keyspace.getName(), name);
@@ -509,10 +517,9 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean, Memtable.Owner
{
Directories.SSTableLister sstableFiles = directories.sstableLister(Directories.OnTxnErr.IGNORE).skipTemporary(true);
sstables = SSTableReader.openAll(this, sstableFiles.list().entrySet(), metadata);
- data.addInitialSSTablesWithoutUpdatingSize(sstables);
+ data.addInitialSSTablesWithoutUpdatingSize(sstables, this);
}
- // compaction strategy should be created after the CFS has been prepared
compactionStrategyManager = new CompactionStrategyManager(this);
if (maxCompactionThreshold.value() <= 0 || minCompactionThreshold.value() <=0)
@@ -523,7 +530,7 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean, Memtable.Owner
// create the private ColumnFamilyStores for the secondary column indexes
indexManager = new SecondaryIndexManager(this);
- for (IndexMetadata info : metadata.get().indexes)
+ for (IndexMetadata info : initMetadata.indexes)
{
indexManager.addIndex(info, true);
}
@@ -538,8 +545,8 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean, Memtable.Owner
if (registerBookeeping)
{
// register the mbean
- mbeanName = getTableMBeanName(keyspace.getName(), name, isIndex());
- oldMBeanName = getColumnFamilieMBeanName(keyspace.getName(), name, isIndex());
+ mbeanName = getTableMBeanName(keyspace.getName(), name, initMetadata.isIndex());
+ oldMBeanName = getColumnFamilieMBeanName(keyspace.getName(), name, initMetadata.isIndex());
String[] objectNames = {mbeanName, oldMBeanName};
for (String objectName : objectNames)
@@ -558,7 +565,7 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean, Memtable.Owner
if (SchemaConstants.isSystemKeyspace(keyspace.getName()))
topPartitions = null;
else
- topPartitions = new TopPartitionTracker(metadata());
+ topPartitions = new TopPartitionTracker(initMetadata);
}
public static String getTableMBeanName(String ks, String name, boolean isIndex)
@@ -729,24 +736,24 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean, Memtable.Owner
}
- public static ColumnFamilyStore createColumnFamilyStore(Keyspace keyspace, TableMetadataRef metadata, boolean loadSSTables)
+ public static ColumnFamilyStore createColumnFamilyStore(Keyspace keyspace, TableMetadata metadata, boolean loadSSTables)
{
return createColumnFamilyStore(keyspace, metadata.name, metadata, loadSSTables);
}
public static ColumnFamilyStore createColumnFamilyStore(Keyspace keyspace,
- String columnFamily,
- TableMetadataRef metadata,
- boolean loadSSTables)
+ String columnFamily,
+ TableMetadata metadata,
+ boolean loadSSTables)
{
- Directories directories = new Directories(metadata.get());
+ Directories directories = new Directories(metadata);
return createColumnFamilyStore(keyspace, columnFamily, metadata, directories, loadSSTables, true, false);
}
/** This is only directly used by offline tools */
public static synchronized ColumnFamilyStore createColumnFamilyStore(Keyspace keyspace,
String columnFamily,
- TableMetadataRef metadata,
+ TableMetadata metadata,
Directories directories,
boolean loadSSTables,
boolean registerBookkeeping,
@@ -1449,6 +1456,7 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean, Memtable.Owner
return ShardBoundaries.NONE;
ShardBoundaries shardBoundaries = cachedShardBoundaries;
+ ClusterMetadata metadata = ClusterMetadata.current();
if (shardBoundaries == null ||
shardBoundaries.shardCount() != shardCount ||
@@ -1457,12 +1465,13 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean, Memtable.Owner
List<Splitter.WeightedRange> weightedRanges;
long ringVersion;
if (!SchemaConstants.isLocalSystemKeyspace(keyspace.getName())
- && getPartitioner() == StorageService.instance.getTokenMetadata().partitioner)
+ && getPartitioner() == metadata.current().partitioner)
{
DiskBoundaryManager.VersionedRangesAtEndpoint versionedLocalRanges = DiskBoundaryManager.getVersionedLocalRanges(this);
Set<Range<Token>> localRanges = versionedLocalRanges.rangesAtEndpoint.ranges();
ringVersion = versionedLocalRanges.ringVersion;
+
if (!localRanges.isEmpty())
{
weightedRanges = new ArrayList<>(localRanges.size());
diff --git a/src/java/org/apache/cassandra/db/Keyspace.java b/src/java/org/apache/cassandra/db/Keyspace.java
index 9dc4d4c189..2e163da3d0 100644
--- a/src/java/org/apache/cassandra/db/Keyspace.java
+++ b/src/java/org/apache/cassandra/db/Keyspace.java
@@ -56,13 +56,11 @@ import org.apache.cassandra.locator.AbstractReplicationStrategy;
import org.apache.cassandra.metrics.KeyspaceMetrics;
import org.apache.cassandra.repair.KeyspaceRepairManager;
import org.apache.cassandra.schema.KeyspaceMetadata;
-import org.apache.cassandra.schema.ReplicationParams;
import org.apache.cassandra.schema.Schema;
import org.apache.cassandra.schema.SchemaConstants;
import org.apache.cassandra.schema.SchemaProvider;
import org.apache.cassandra.schema.TableId;
import org.apache.cassandra.schema.TableMetadata;
-import org.apache.cassandra.schema.TableMetadataRef;
import org.apache.cassandra.service.snapshot.TableSnapshot;
import org.apache.cassandra.tracing.Tracing;
import org.apache.cassandra.utils.ByteBufferUtil;
@@ -92,6 +90,7 @@ public class Keyspace
private static int TEST_FAIL_MV_LOCKS_COUNT = Integer.getInteger("cassandra.test.fail_mv_locks_count", 0);
public final KeyspaceMetrics metric;
+ public final KeyspaceMetadataRef metadataRef;
// It is possible to call Keyspace.open without a running daemon, so it makes sense to ensure
// proper directories here as well as in CassandraDaemon.
@@ -101,8 +100,6 @@ public class Keyspace
DatabaseDescriptor.createAllDirectories();
}
- private volatile KeyspaceMetadata metadata;
-
//OpOrder is defined globally since we need to order writes across
//Keyspaces in the case of Views (batchlog of view mutations)
public static final OpOrder writeOrder = new OpOrder();
@@ -110,12 +107,11 @@ public class Keyspace
/* ColumnFamilyStore per column family */
private final ConcurrentMap<TableId, ColumnFamilyStore> columnFamilyStores = new ConcurrentHashMap<>();
- private volatile AbstractReplicationStrategy replicationStrategy;
public final ViewManager viewManager;
private final KeyspaceWriteHandler writeHandler;
- private volatile ReplicationParams replicationParams;
private final KeyspaceRepairManager repairManager;
private final SchemaProvider schema;
+ private final String name;
private static volatile boolean initialized = false;
@@ -149,23 +145,13 @@ public class Keyspace
public static Keyspace open(String keyspaceName)
{
assert initialized || SchemaConstants.isLocalSystemKeyspace(keyspaceName) : "Initialized: " + initialized;
- return open(keyspaceName, Schema.instance, true);
+ return Schema.instance.getKeyspaceInstance(keyspaceName);
}
// to only be used by org.apache.cassandra.tools.Standalone* classes
public static Keyspace openWithoutSSTables(String keyspaceName)
{
- return open(keyspaceName, Schema.instance, false);
- }
-
- public static Keyspace open(String keyspaceName, SchemaProvider schema, boolean loadSSTables)
- {
- return schema.maybeAddKeyspaceInstance(keyspaceName, () -> new Keyspace(keyspaceName, schema, loadSSTables));
- }
-
- public static ColumnFamilyStore openAndGetStore(TableMetadataRef tableRef)
- {
- return open(tableRef.keyspace).getColumnFamilyStore(tableRef.id);
+ return Schema.instance.getKeyspaceInstance(keyspaceName);
}
public static ColumnFamilyStore openAndGetStore(TableMetadata table)
@@ -189,15 +175,9 @@ public class Keyspace
}
}
- public void setMetadata(KeyspaceMetadata metadata)
- {
- this.metadata = metadata;
- createReplicationStrategy(metadata);
- }
-
public KeyspaceMetadata getMetadata()
{
- return metadata;
+ return metadataRef.get();
}
public Collection<ColumnFamilyStore> getColumnFamilyStores()
@@ -217,7 +197,7 @@ public class Keyspace
{
ColumnFamilyStore cfs = columnFamilyStores.get(id);
if (cfs == null)
- throw new IllegalArgumentException("Unknown CF " + id);
+ throw new IllegalArgumentException(String.format("Unknown CF %s %s", id, columnFamilyStores));
return cfs;
}
@@ -318,38 +298,53 @@ public class Keyspace
return getColumnFamilyStores().stream().flatMap(cfs -> cfs.listSnapshots().values().stream());
}
+ public static Keyspace forSchema(String keyspaceName, SchemaProvider schema)
+ {
+ return new Keyspace(keyspaceName, schema, true);
+ }
+
private Keyspace(String keyspaceName, SchemaProvider schema, boolean loadSSTables)
+ {
+ this(schema, schema.getKeyspaceMetadata(keyspaceName), loadSSTables);
+ }
+
+ public Keyspace(SchemaProvider schema, KeyspaceMetadata metadata, boolean loadSSTables)
{
this.schema = schema;
- metadata = schema.getKeyspaceMetadata(keyspaceName);
- assert metadata != null : "Unknown keyspace " + keyspaceName;
-
+ this.name = metadata.name;
+
+ assert metadata != null : "Unknown keyspace " + metadata.name;
+
if (metadata.isVirtual())
- throw new IllegalStateException("Cannot initialize Keyspace with virtual metadata " + keyspaceName);
- createReplicationStrategy(metadata);
+ throw new IllegalStateException("Cannot initialize Keyspace with virtual metadata " + metadata.name);
this.metric = new KeyspaceMetrics(this);
this.viewManager = new ViewManager(this);
+
+ this.metadataRef = new KeyspaceMetadataRef(metadata, schema);
for (TableMetadata cfm : metadata.tablesAndViews())
{
logger.trace("Initializing {}.{}", getName(), cfm.name);
- initCf(schema.getTableMetadataRef(cfm.id), loadSSTables);
+ initCf(cfm, loadSSTables);
}
+
this.viewManager.reload(false);
+ this.metadataRef.unsetInitial();
this.repairManager = new CassandraKeyspaceRepairManager(this);
this.writeHandler = new CassandraKeyspaceWriteHandler(this);
}
- private Keyspace(KeyspaceMetadata metadata)
+ public Keyspace(KeyspaceMetadata metadata)
{
this.schema = Schema.instance;
- this.metadata = metadata;
- createReplicationStrategy(metadata);
+ this.name = metadata.name;
+
this.metric = new KeyspaceMetrics(this);
this.viewManager = new ViewManager(this);
this.repairManager = new CassandraKeyspaceRepairManager(this);
this.writeHandler = new CassandraKeyspaceWriteHandler(this);
+ this.metadataRef = new KeyspaceMetadataRef(metadata, schema);
}
public KeyspaceRepairManager getRepairManager()
@@ -362,18 +357,6 @@ public class Keyspace
return new Keyspace(metadata);
}
- private void createReplicationStrategy(KeyspaceMetadata ksm)
- {
- logger.info("Creating replication strategy " + ksm.name + " params " + ksm.params);
- replicationStrategy = ksm.createReplicationStrategy();
- if (!ksm.params.replication.equals(replicationParams))
- {
- logger.debug("New replication settings for keyspace {} - invalidating disk boundary caches", ksm.name);
- columnFamilyStores.values().forEach(ColumnFamilyStore::invalidateLocalRanges);
- }
- replicationParams = ksm.params.replication;
- }
-
// best invoked on the compaction manager.
public void dropCf(TableId tableId, boolean dropData)
{
@@ -434,7 +417,7 @@ public class Keyspace
/**
* adds a cf to internal structures, ends up creating disk files).
*/
- public void initCf(TableMetadataRef metadata, boolean loadSSTables)
+ public void initCf(TableMetadata metadata, boolean loadSSTables)
{
ColumnFamilyStore cfs = columnFamilyStores.get(metadata.id);
@@ -443,7 +426,8 @@ public class Keyspace
// CFS being created for the first time, either on server startup or new CF being added.
// We don't worry about races here; startup is safe, and adding multiple idential CFs
// simultaneously is a "don't do that" scenario.
- ColumnFamilyStore oldCfs = columnFamilyStores.putIfAbsent(metadata.id, ColumnFamilyStore.createColumnFamilyStore(this, metadata, loadSSTables));
+ ColumnFamilyStore oldCfs = columnFamilyStores.putIfAbsent(metadata.id,
+ ColumnFamilyStore.createColumnFamilyStore(this, metadata, loadSSTables));
// CFS mbean instantiation will error out before we hit this, but in case that changes...
if (oldCfs != null)
throw new IllegalStateException("added multiple mappings for cf id " + metadata.id);
@@ -453,7 +437,7 @@ public class Keyspace
// re-initializing an existing CF. This will happen if you cleared the schema
// on this node and it's getting repopulated from the rest of the cluster.
assert cfs.name.equals(metadata.name);
- cfs.reload();
+ cfs.reload(metadata);
}
}
@@ -515,7 +499,7 @@ public class Keyspace
boolean isDeferrable,
Promise<?> future)
{
- if (TEST_FAIL_WRITES && metadata.name.equals(TEST_FAIL_WRITES_KS))
+ if (TEST_FAIL_WRITES && getMetadata().name.equals(TEST_FAIL_WRITES_KS))
throw new RuntimeException("Testing write failures");
Lock[] locks = null;
@@ -628,7 +612,7 @@ public class Keyspace
try
{
Tracing.trace("Creating materialized view mutations from base table replica");
- viewManager.forTable(upd.metadata().id).pushViewReplicaUpdates(upd, makeDurable, baseComplete);
+ viewManager.forTable(upd.metadata()).pushViewReplicaUpdates(upd, makeDurable, baseComplete);
}
catch (Throwable t)
{
@@ -666,7 +650,7 @@ public class Keyspace
public AbstractReplicationStrategy getReplicationStrategy()
{
- return replicationStrategy;
+ return getMetadata().replicationStrategy;
}
public List<Future<?>> flush(ColumnFamilyStore.FlushReason reason)
@@ -756,7 +740,7 @@ public class Keyspace
public static Iterable<Keyspace> nonSystem()
{
- return Iterables.transform(Schema.instance.getNonSystemKeyspaces().names(), Keyspace::open);
+ return Iterables.transform(Schema.instance.distributedKeyspaces().names(), Keyspace::open);
}
public static Iterable<Keyspace> nonLocalStrategy()
@@ -777,6 +761,37 @@ public class Keyspace
public String getName()
{
- return metadata.name;
+ return name;
+ }
+
+ private static class KeyspaceMetadataRef
+ {
+ // We need "initial" keyspace metadata for initCF to run, due to circular dependency
+ // between keyspace keyspace -> column family -> keyspace metadata. There are some
+ // calls within initCF that try accessing keyspace metadata, which requires the metadata
+ // of initializing keyspace to already be visible via ClusterMetadata#schema.
+ private KeyspaceMetadata initial;
+
+ private final String name;
+ private final SchemaProvider provider;
+
+ public KeyspaceMetadataRef(KeyspaceMetadata initial, SchemaProvider provider)
+ {
+ this.initial = initial;
+ this.name = initial.name;
+ this.provider = provider;
+ }
+
+ public KeyspaceMetadata get()
+ {
+ if (initial != null)
+ return initial;
+ return provider.getKeyspaceMetadata(name);
+ }
+
+ public void unsetInitial()
+ {
+ this.initial = null;
+ }
}
}
diff --git a/src/java/org/apache/cassandra/db/compaction/CompactionManager.java b/src/java/org/apache/cassandra/db/compaction/CompactionManager.java
index 895e702ee3..201aff3f4a 100644
--- a/src/java/org/apache/cassandra/db/compaction/CompactionManager.java
+++ b/src/java/org/apache/cassandra/db/compaction/CompactionManager.java
@@ -96,6 +96,7 @@ import org.apache.cassandra.io.sstable.metadata.MetadataCollector;
import org.apache.cassandra.io.sstable.metadata.StatsMetadata;
import org.apache.cassandra.io.util.File;
import org.apache.cassandra.io.util.FileUtils;
+import org.apache.cassandra.locator.MetaStrategy;
import org.apache.cassandra.locator.RangesAtEndpoint;
import org.apache.cassandra.metrics.CompactionMetrics;
import org.apache.cassandra.metrics.TableMetrics;
@@ -595,6 +596,14 @@ public class CompactionManager implements CompactionManagerMBean
assert !cfStore.isIndex();
Keyspace keyspace = cfStore.keyspace;
+ if (!StorageService.instance.isJoined())
+ {
+ logger.info("Cleanup cannot run before a node has joined the ring");
+ return AllSSTableOpStatus.ABORTED;
+ }
+ if (cfStore.keyspace.getReplicationStrategy() instanceof MetaStrategy)
+ return AllSSTableOpStatus.SUCCESSFUL; // todo - we probably want to be able to cleanup MetaStrategy keyspaces
+
// if local ranges is empty, it means no data should remain
final RangesAtEndpoint replicas = StorageService.instance.getLocalReplicas(keyspace.getName());
final Set<Range<Token>> allRanges = replicas.ranges();
@@ -1248,7 +1257,7 @@ public class CompactionManager implements CompactionManagerMBean
/* Used in tests. */
public void disableAutoCompaction()
{
- for (String ksname : Schema.instance.getNonSystemKeyspaces().names())
+ for (String ksname : Schema.instance.distributedKeyspaces().names())
{
for (ColumnFamilyStore cfs : Keyspace.open(ksname).getColumnFamilyStores())
cfs.disableAutoCompaction();
diff --git a/src/java/org/apache/cassandra/db/lifecycle/Tracker.java b/src/java/org/apache/cassandra/db/lifecycle/Tracker.java
index c54ee83bd7..d48c33a1f3 100644
--- a/src/java/org/apache/cassandra/db/lifecycle/Tracker.java
+++ b/src/java/org/apache/cassandra/db/lifecycle/Tracker.java
@@ -224,9 +224,15 @@ public class Tracker
addSSTablesInternal(sstables, true, false, true);
}
- public void addInitialSSTablesWithoutUpdatingSize(Iterable<SSTableReader> sstables)
+ public void addInitialSSTablesWithoutUpdatingSize(Iterable<SSTableReader> sstables, ColumnFamilyStore cfs)
{
- addSSTablesInternal(sstables, true, false, false);
+ if (!isDummy())
+ {
+ for (SSTableReader reader : sstables)
+ reader.setupOnline();
+ }
+ apply(updateLiveSet(emptySet(), sstables));
+ notifyAdded(sstables, true);
}
public void updateInitialSSTableSize(Iterable<SSTableReader> sstables)
diff --git a/src/java/org/apache/cassandra/db/view/TableViews.java b/src/java/org/apache/cassandra/db/view/TableViews.java
index 619e9f8566..3fae01bd6d 100644
--- a/src/java/org/apache/cassandra/db/view/TableViews.java
+++ b/src/java/org/apache/cassandra/db/view/TableViews.java
@@ -17,7 +17,14 @@
*/
package org.apache.cassandra.db.view;
-import java.util.*;
+import java.util.AbstractCollection;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.NavigableSet;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
@@ -27,14 +34,36 @@ import com.google.common.collect.Iterables;
import com.google.common.collect.Iterators;
import com.google.common.collect.PeekingIterator;
-import org.apache.cassandra.db.*;
+import org.apache.cassandra.db.Clustering;
+import org.apache.cassandra.db.ColumnFamilyStore;
+import org.apache.cassandra.db.DecoratedKey;
+import org.apache.cassandra.db.DeletionInfo;
+import org.apache.cassandra.db.DeletionTime;
+import org.apache.cassandra.db.Keyspace;
+import org.apache.cassandra.db.Mutation;
+import org.apache.cassandra.db.RangeTombstone;
+import org.apache.cassandra.db.ReadExecutionController;
+import org.apache.cassandra.db.ReadQuery;
+import org.apache.cassandra.db.SinglePartitionReadCommand;
+import org.apache.cassandra.db.Slice;
+import org.apache.cassandra.db.Slices;
+import org.apache.cassandra.db.SystemKeyspace;
import org.apache.cassandra.db.commitlog.CommitLogPosition;
-import org.apache.cassandra.db.filter.*;
-import org.apache.cassandra.db.partitions.*;
-import org.apache.cassandra.db.rows.*;
+import org.apache.cassandra.db.filter.ClusteringIndexFilter;
+import org.apache.cassandra.db.filter.ClusteringIndexNamesFilter;
+import org.apache.cassandra.db.filter.ClusteringIndexSliceFilter;
+import org.apache.cassandra.db.filter.ColumnFilter;
+import org.apache.cassandra.db.filter.DataLimits;
+import org.apache.cassandra.db.filter.RowFilter;
+import org.apache.cassandra.db.partitions.PartitionUpdate;
+import org.apache.cassandra.db.partitions.UnfilteredPartitionIterators;
+import org.apache.cassandra.db.rows.BTreeRow;
+import org.apache.cassandra.db.rows.RangeTombstoneMarker;
+import org.apache.cassandra.db.rows.Row;
+import org.apache.cassandra.db.rows.Rows;
+import org.apache.cassandra.db.rows.Unfiltered;
+import org.apache.cassandra.db.rows.UnfilteredRowIterator;
import org.apache.cassandra.dht.Token;
-import org.apache.cassandra.schema.Schema;
-import org.apache.cassandra.schema.TableId;
import org.apache.cassandra.schema.TableMetadata;
import org.apache.cassandra.schema.TableMetadataRef;
import org.apache.cassandra.service.StorageProxy;
@@ -57,9 +86,9 @@ public class TableViews extends AbstractCollection<View>
// list is the best option.
private final List<View> views = new CopyOnWriteArrayList();
- public TableViews(TableId id)
+ public TableViews(TableMetadata tableMetadata)
{
- baseTableMetadata = Schema.instance.getTableMetadataRef(id);
+ baseTableMetadata = tableMetadata.ref;
}
public boolean hasViews()
diff --git a/src/java/org/apache/cassandra/db/view/ViewBuilderTask.java b/src/java/org/apache/cassandra/db/view/ViewBuilderTask.java
index dac96f6793..b08dd56050 100644
--- a/src/java/org/apache/cassandra/db/view/ViewBuilderTask.java
+++ b/src/java/org/apache/cassandra/db/view/ViewBuilderTask.java
@@ -111,7 +111,7 @@ public class ViewBuilderTask extends CompactionInfo.Holder implements Callable<L
UnfilteredRowIterator data = UnfilteredPartitionIterators.getOnlyElement(command.executeLocally(orderGroup), command))
{
Iterator<Collection<Mutation>> mutations = baseCfs.keyspace.viewManager
- .forTable(baseCfs.metadata.id)
+ .forTable(baseCfs.metadata.get())
.generateViewUpdates(Collections.singleton(view), data, empty, nowInSec, true);
AtomicLong noBase = new AtomicLong(Long.MAX_VALUE);
diff --git a/src/java/org/apache/cassandra/db/view/ViewManager.java b/src/java/org/apache/cassandra/db/view/ViewManager.java
index 111f96aa9e..8d8b9b912d 100644
--- a/src/java/org/apache/cassandra/db/view/ViewManager.java
+++ b/src/java/org/apache/cassandra/db/view/ViewManager.java
@@ -28,12 +28,9 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.cassandra.config.DatabaseDescriptor;
-import org.apache.cassandra.schema.TableId;
-import org.apache.cassandra.schema.ViewMetadata;
+import org.apache.cassandra.schema.*;
import org.apache.cassandra.db.*;
import org.apache.cassandra.db.partitions.*;
-import org.apache.cassandra.schema.SystemDistributedKeyspace;
-import org.apache.cassandra.schema.Views;
import org.apache.cassandra.service.StorageService;
/**
@@ -82,7 +79,7 @@ public class ViewManager
if (coordinatorBatchlog && keyspace.getReplicationStrategy().getReplicationFactor().allReplicas == 1)
continue;
- if (!forTable(update.metadata().id).updatedViews(update).isEmpty())
+ if (!forTable(update.metadata()).updatedViews(update).isEmpty())
return true;
}
}
@@ -147,7 +144,7 @@ public class ViewManager
}
View view = new View(definition, keyspace.getColumnFamilyStore(definition.baseTableId));
- forTable(view.getDefinition().baseTableId).add(view);
+ forTable(view.getDefinition().baseTableMetadata()).add(view);
viewsByName.put(definition.name(), view);
}
@@ -164,7 +161,7 @@ public class ViewManager
return;
view.stopBuild();
- forTable(view.getDefinition().baseTableId).removeByName(name);
+ forTable(view.getDefinition().baseTableMetadata()).removeByName(name);
SystemKeyspace.setViewRemoved(keyspace.getName(), view.name);
SystemDistributedKeyspace.setViewRemoved(keyspace.getName(), view.name);
}
@@ -180,13 +177,13 @@ public class ViewManager
view.build();
}
- public TableViews forTable(TableId id)
+ public TableViews forTable(TableMetadata metadata)
{
- TableViews views = viewsByBaseTable.get(id);
+ TableViews views = viewsByBaseTable.get(metadata.id);
if (views == null)
{
- views = new TableViews(id);
- TableViews previous = viewsByBaseTable.putIfAbsent(id, views);
+ views = new TableViews(metadata);
+ TableViews previous = viewsByBaseTable.putIfAbsent(metadata.id, views);
if (previous != null)
views = previous;
}
diff --git a/src/java/org/apache/cassandra/dht/BootStrapper.java b/src/java/org/apache/cassandra/dht/BootStrapper.java
index 9b017c6124..5aa5f5086e 100644
--- a/src/java/org/apache/cassandra/dht/BootStrapper.java
+++ b/src/java/org/apache/cassandra/dht/BootStrapper.java
@@ -17,15 +17,17 @@
*/
package org.apache.cassandra.dht;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
-import org.apache.cassandra.utils.concurrent.Future;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.cassandra.config.DatabaseDescriptor;
-import org.apache.cassandra.schema.Schema;
import org.apache.cassandra.db.Keyspace;
import org.apache.cassandra.dht.tokenallocator.TokenAllocation;
import org.apache.cassandra.exceptions.ConfigurationException;
@@ -33,9 +35,14 @@ import org.apache.cassandra.gms.Gossiper;
import org.apache.cassandra.locator.AbstractReplicationStrategy;
import org.apache.cassandra.locator.InetAddressAndPort;
import org.apache.cassandra.locator.TokenMetadata;
-import org.apache.cassandra.service.StorageService;
-import org.apache.cassandra.streaming.*;
+import org.apache.cassandra.schema.Schema;
+import org.apache.cassandra.streaming.StreamEvent;
+import org.apache.cassandra.streaming.StreamEventHandler;
+import org.apache.cassandra.streaming.StreamOperation;
+import org.apache.cassandra.streaming.StreamResultFuture;
+import org.apache.cassandra.streaming.StreamState;
import org.apache.cassandra.utils.FBUtilities;
+import org.apache.cassandra.utils.concurrent.Future;
import org.apache.cassandra.utils.progress.ProgressEvent;
import org.apache.cassandra.utils.progress.ProgressEventNotifierSupport;
import org.apache.cassandra.utils.progress.ProgressEventType;
@@ -174,10 +181,10 @@ public class BootStrapper extends ProgressEventNotifierSupport
throw new ConfigurationException("num_tokens must be >= 1");
if (allocationKeyspace != null)
- return allocateTokens(metadata, address, allocationKeyspace, numTokens, schemaTimeoutMillis, ringTimeoutMillis);
+ return allocateTokens(metadata, address, allocationKeyspace, numTokens);
if (allocationLocalRf != null)
- return allocateTokens(metadata, address, allocationLocalRf, numTokens, schemaTimeoutMillis, ringTimeoutMillis);
+ return allocateTokens(metadata, address, allocationLocalRf, numTokens);
if (numTokens == 1)
logger.warn("Picking random token for a single vnode. You should probably add more vnodes and/or use the automatic token allocation mechanism.");
@@ -205,11 +212,8 @@ public class BootStrapper extends ProgressEventNotifierSupport
static Collection<Token> allocateTokens(final TokenMetadata metadata,
InetAddressAndPort address,
String allocationKeyspace,
- int numTokens,
- long schemaTimeoutMillis,
- long ringTimeoutMillis)
+ int numTokens)
{
- StorageService.instance.waitForSchema(schemaTimeoutMillis, ringTimeoutMillis);
if (!FBUtilities.getBroadcastAddressAndPort().equals(InetAddressAndPort.getLoopbackAddress()))
Gossiper.waitToSettle();
@@ -227,11 +231,8 @@ public class BootStrapper extends ProgressEventNotifierSupport
static Collection<Token> allocateTokens(final TokenMetadata metadata,
InetAddressAndPort address,
int rf,
- int numTokens,
- long schemaTimeoutMillis,
- long ringTimeoutMillis)
+ int numTokens)
{
- StorageService.instance.waitForSchema(schemaTimeoutMillis, ringTimeoutMillis);
if (!FBUtilities.getBroadcastAddressAndPort().equals(InetAddressAndPort.getLoopbackAddress()))
Gossiper.waitToSettle();
diff --git a/src/java/org/apache/cassandra/hadoop/cql3/CqlBulkRecordWriter.java b/src/java/org/apache/cassandra/hadoop/cql3/CqlBulkRecordWriter.java
index 9b154e9241..ccb3733d62 100644
--- a/src/java/org/apache/cassandra/hadoop/cql3/CqlBulkRecordWriter.java
+++ b/src/java/org/apache/cassandra/hadoop/cql3/CqlBulkRecordWriter.java
@@ -49,7 +49,6 @@ import org.apache.cassandra.io.sstable.SSTableLoader;
import org.apache.cassandra.io.util.File;
import org.apache.cassandra.io.util.FileUtils;
import org.apache.cassandra.locator.InetAddressAndPort;
-import org.apache.cassandra.schema.TableMetadataRef;
import org.apache.cassandra.streaming.StreamState;
import org.apache.cassandra.utils.NativeSSTableLoaderClient;
import org.apache.cassandra.utils.OutputHandler;
@@ -184,7 +183,7 @@ public class CqlBulkRecordWriter extends RecordWriter<Object, List<ByteBuffer>>
if (loader == null)
{
ExternalClient externalClient = new ExternalClient(conf);
- externalClient.setTableMetadata(TableMetadataRef.forOfflineTools(CreateTableStatement.parse(schema, keyspace).build()));
+ externalClient.setTableMetadata(CreateTableStatement.parse(schema, keyspace).build().ref);
loader = new SSTableLoader(outputDir, externalClient, new NullOutputHandler())
{
diff --git a/src/java/org/apache/cassandra/hints/HintsDispatchTrigger.java b/src/java/org/apache/cassandra/hints/HintsDispatchTrigger.java
index 0dfc6e1323..488f9abed7 100644
--- a/src/java/org/apache/cassandra/hints/HintsDispatchTrigger.java
+++ b/src/java/org/apache/cassandra/hints/HintsDispatchTrigger.java
@@ -62,7 +62,7 @@ final class HintsDispatchTrigger implements Runnable
.filter(store -> !isScheduled(store))
.filter(HintsStore::isLive)
.filter(store -> store.isWriting() || store.hasFiles())
- .filter(store -> Schema.instance.isSameVersion(Gossiper.instance.getSchemaVersion(store.address())))
+ .filter(store -> Schema.instance.getVersion().equals(Gossiper.instance.getSchemaVersion(store.address())))
.forEach(this::schedule);
}
diff --git a/src/java/org/apache/cassandra/index/SecondaryIndexManager.java b/src/java/org/apache/cassandra/index/SecondaryIndexManager.java
index 93ecd595b5..c871c9b721 100644
--- a/src/java/org/apache/cassandra/index/SecondaryIndexManager.java
+++ b/src/java/org/apache/cassandra/index/SecondaryIndexManager.java
@@ -65,6 +65,7 @@ import org.apache.cassandra.notifications.SSTableAddedNotification;
import org.apache.cassandra.schema.ColumnMetadata;
import org.apache.cassandra.schema.IndexMetadata;
import org.apache.cassandra.schema.Indexes;
+import org.apache.cassandra.schema.TableMetadata;
import org.apache.cassandra.service.pager.SinglePartitionPager;
import org.apache.cassandra.tracing.Tracing;
import org.apache.cassandra.transport.ProtocolVersion;
@@ -177,10 +178,10 @@ public class SecondaryIndexManager implements IndexRegistry, INotificationConsum
/**
* Drops and adds new indexes associated with the underlying CF
*/
- public void reload()
+ public void reload(TableMetadata baseTable)
{
// figure out what needs to be added and dropped.
- Indexes tableIndexes = baseCfs.metadata().indexes;
+ Indexes tableIndexes = baseTable.indexes;
indexes.keySet()
.stream()
.filter(indexName -> !tableIndexes.has(indexName))
diff --git a/src/java/org/apache/cassandra/index/internal/CassandraIndex.java b/src/java/org/apache/cassandra/index/internal/CassandraIndex.java
index 0aac15d994..6684fa7191 100644
--- a/src/java/org/apache/cassandra/index/internal/CassandraIndex.java
+++ b/src/java/org/apache/cassandra/index/internal/CassandraIndex.java
@@ -33,9 +33,7 @@ import com.google.common.collect.ImmutableSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.apache.cassandra.schema.TableMetadata;
-import org.apache.cassandra.schema.TableMetadataRef;
-import org.apache.cassandra.schema.ColumnMetadata;
+import org.apache.cassandra.schema.*;
import org.apache.cassandra.cql3.Operator;
import org.apache.cassandra.cql3.statements.schema.IndexTarget;
import org.apache.cassandra.db.*;
@@ -57,7 +55,6 @@ import org.apache.cassandra.index.transactions.IndexTransaction;
import org.apache.cassandra.index.transactions.UpdateTransaction;
import org.apache.cassandra.io.sstable.ReducingKeyIterator;
import org.apache.cassandra.io.sstable.format.SSTableReader;
-import org.apache.cassandra.schema.IndexMetadata;
import org.apache.cassandra.utils.FBUtilities;
import org.apache.cassandra.utils.Pair;
import org.apache.cassandra.utils.concurrent.Refs;
@@ -200,7 +197,7 @@ public abstract class CassandraIndex implements Index
public Callable<?> getMetadataReloadTask(IndexMetadata indexDef)
{
return () -> {
- indexCfs.reload();
+ indexCfs.reload(indexCfs.metadata());
return null;
};
}
@@ -223,10 +220,10 @@ public abstract class CassandraIndex implements Index
metadata = indexDef;
Pair<ColumnMetadata, IndexTarget.Type> target = TargetParser.parse(baseCfs.metadata(), indexDef);
functions = getFunctions(indexDef, target);
- TableMetadataRef tableRef = TableMetadataRef.forOfflineTools(indexCfsMetadata(baseCfs.metadata(), indexDef));
+ TableMetadata tm = indexCfsMetadata(baseCfs.metadata(), indexDef);
indexCfs = ColumnFamilyStore.createColumnFamilyStore(baseCfs.keyspace,
- tableRef.name,
- tableRef,
+ tm.name,
+ tm,
baseCfs.getTracker().loadsstables);
indexedColumn = target.left;
}
diff --git a/src/java/org/apache/cassandra/io/sstable/CQLSSTableWriter.java b/src/java/org/apache/cassandra/io/sstable/CQLSSTableWriter.java
index 604a4ee361..2846498cbf 100644
--- a/src/java/org/apache/cassandra/io/sstable/CQLSSTableWriter.java
+++ b/src/java/org/apache/cassandra/io/sstable/CQLSSTableWriter.java
@@ -562,12 +562,12 @@ public class CQLSSTableWriter implements Closeable
String keyspaceName = schemaStatement.keyspace();
- Schema.instance.transform(SchemaTransformations.addKeyspace(KeyspaceMetadata.create(keyspaceName,
- KeyspaceParams.simple(1),
- Tables.none(),
- Views.none(),
- Types.none(),
- UserFunctions.none()), true));
+ Schema.instance.submit(SchemaTransformations.addKeyspace(KeyspaceMetadata.create(keyspaceName,
+ KeyspaceParams.simple(1),
+ Tables.none(),
+ Views.none(),
+ Types.none(),
+ UserFunctions.none()), true));
KeyspaceMetadata ksm = Schema.instance.getKeyspaceMetadata(keyspaceName);
@@ -575,14 +575,14 @@ public class CQLSSTableWriter implements Closeable
if (tableMetadata == null)
{
Types types = createTypes(keyspaceName);
- Schema.instance.transform(SchemaTransformations.addTypes(types, true));
+ Schema.instance.submit(SchemaTransformations.addTypes(types, true));
tableMetadata = createTable(types);
- Schema.instance.transform(SchemaTransformations.addTable(tableMetadata, true));
+ Schema.instance.submit(SchemaTransformations.addTable(tableMetadata, true));
}
ModificationStatement preparedModificationStatement = prepareModificationStatement();
- TableMetadataRef ref = TableMetadataRef.forOfflineTools(tableMetadata);
+ TableMetadataRef ref = tableMetadata.ref;
AbstractSSTableSimpleWriter writer = sorted
? new SSTableSimpleWriter(directory, ref, preparedModificationStatement.updatedColumns())
: new SSTableSimpleUnsortedWriter(directory, ref, preparedModificationStatement.updatedColumns(), bufferSizeInMiB);
diff --git a/src/java/org/apache/cassandra/io/sstable/format/SSTableReader.java b/src/java/org/apache/cassandra/io/sstable/format/SSTableReader.java
index 609309c608..d6bb5f360d 100644
--- a/src/java/org/apache/cassandra/io/sstable/format/SSTableReader.java
+++ b/src/java/org/apache/cassandra/io/sstable/format/SSTableReader.java
@@ -344,7 +344,7 @@ public abstract class SSTableReader extends SSTable implements UnfilteredSource,
public static SSTableReader open(SSTable.Owner owner, Descriptor desc, TableMetadataRef metadata)
{
- return open(owner, desc, null, metadata);
+ return open(owner, desc, null, metadata);
}
public static SSTableReader open(SSTable.Owner owner, Descriptor descriptor, Set<Component> components, TableMetadataRef metadata)
@@ -392,7 +392,6 @@ public abstract class SSTableReader extends SSTable implements UnfilteredSource,
boolean isOffline)
{
SSTableReaderLoadingBuilder<?, ?> builder = descriptor.getFormat().getReaderFactory().loadingBuilder(descriptor, metadata, components);
-
return builder.build(owner, validate, !isOffline);
}
@@ -534,7 +533,7 @@ public abstract class SSTableReader extends SSTable implements UnfilteredSource,
public void setupOnline()
{
- owner().ifPresent(o -> setCrcCheckChance(o.getCrcCheckChance()));
+ owner().ifPresent(o -> setCrcCheckChance(o.getCrcCheckChance()));
}
/**
diff --git a/src/java/org/apache/cassandra/io/sstable/format/SSTableReaderLoadingBuilder.java b/src/java/org/apache/cassandra/io/sstable/format/SSTableReaderLoadingBuilder.java
index aedd860922..1a4aa0d9f5 100644
--- a/src/java/org/apache/cassandra/io/sstable/format/SSTableReaderLoadingBuilder.java
+++ b/src/java/org/apache/cassandra/io/sstable/format/SSTableReaderLoadingBuilder.java
@@ -141,7 +141,7 @@ public abstract class SSTableReaderLoadingBuilder<R extends SSTableReader, B ext
{
int i = descriptor.cfname.indexOf(SECONDARY_INDEX_NAME_SEPARATOR);
String indexName = descriptor.cfname.substring(i + 1);
- metadata = Schema.instance.getIndexTableMetadataRef(descriptor.ksname, indexName);
+ metadata = Schema.instance.getIndexMetadata(descriptor.ksname, indexName).map(m -> m.ref).orElse(null);
if (metadata == null)
throw new AssertionError("Could not find index metadata for index cf " + i);
}
diff --git a/src/java/org/apache/cassandra/io/sstable/format/big/BigTableWriter.java b/src/java/org/apache/cassandra/io/sstable/format/big/BigTableWriter.java
index 5ae52cfb6c..abc2eb8854 100644
--- a/src/java/org/apache/cassandra/io/sstable/format/big/BigTableWriter.java
+++ b/src/java/org/apache/cassandra/io/sstable/format/big/BigTableWriter.java
@@ -179,7 +179,6 @@ public class BigTableWriter extends SortedTableWriter<BigFormatPartitionWriter>
builder.setFilter(filter);
indexSummary = indexWriter.summary.build(metadata().partitioner, boundary);
builder.setIndexSummary(indexSummary);
-
long indexFileLength = descriptor.fileFor(Components.PRIMARY_INDEX).length();
int indexBufferSize = ioOptions.diskOptimizationStrategy.bufferSize(indexFileLength / builder.getIndexSummary().size());
FileHandle.Builder indexFileBuilder = indexWriter.builder;
diff --git a/src/java/org/apache/cassandra/locator/AbstractReplicationStrategy.java b/src/java/org/apache/cassandra/locator/AbstractReplicationStrategy.java
index c233fc0fbc..b3b4ae6b67 100644
--- a/src/java/org/apache/cassandra/locator/AbstractReplicationStrategy.java
+++ b/src/java/org/apache/cassandra/locator/AbstractReplicationStrategy.java
@@ -40,10 +40,12 @@ import org.apache.cassandra.dht.RingPosition;
import org.apache.cassandra.dht.Token;
import org.apache.cassandra.exceptions.ConfigurationException;
import org.apache.cassandra.locator.ReplicaCollection.Builder.Conflict;
+import org.apache.cassandra.schema.ReplicationParams;
import org.apache.cassandra.service.AbstractWriteResponseHandler;
import org.apache.cassandra.service.ClientState;
import org.apache.cassandra.service.DatacenterSyncWriteResponseHandler;
import org.apache.cassandra.service.DatacenterWriteResponseHandler;
+import org.apache.cassandra.service.StorageService;
import org.apache.cassandra.service.WriteResponseHandler;
import org.apache.cassandra.utils.FBUtilities;
import org.cliffc.high_scale_lib.NonBlockingHashMap;
@@ -336,6 +338,25 @@ public abstract class AbstractReplicationStrategy
return strategy;
}
+ public static AbstractReplicationStrategy createReplicationStrategy(String keyspaceName,
+ ReplicationParams replicationParams)
+ {
+ return createReplicationStrategy(keyspaceName, replicationParams.klass, replicationParams.options);
+ }
+
+ public static AbstractReplicationStrategy createReplicationStrategy(String keyspaceName,
+ Class<? extends AbstractReplicationStrategy> strategyClass,
+ Map<String, String> strategyOptions)
+ {
+ AbstractReplicationStrategy strategy = createInternal(keyspaceName,
+ strategyClass,
+ StorageService.instance.getTokenMetadata(),
+ DatabaseDescriptor.getEndpointSnitch(),
+ strategyOptions);
+ strategy.validateOptions();
+ return strategy;
+ }
+
public static AbstractReplicationStrategy createReplicationStrategy(String keyspaceName,
Class<? extends AbstractReplicationStrategy> strategyClass,
TokenMetadata tokenMetadata,
@@ -446,7 +467,18 @@ public abstract class AbstractReplicationStrategy
}
}
- protected void validateExpectedOptions() throws ConfigurationException
+ public void validate() throws ConfigurationException
+ {
+ validateExpectedOptions();
+ validateOptions();
+ maybeWarnOnOptions();
+ if (hasTransientReplicas() && !DatabaseDescriptor.isTransientReplicationEnabled())
+ {
+ throw new ConfigurationException("Transient replication is disabled. Enable in cassandra.yaml to use.");
+ }
+ }
+
+ public void validateExpectedOptions() throws ConfigurationException
{
Collection<String> expectedOptions = recognizedOptions();
if (expectedOptions == null)
diff --git a/src/java/org/apache/cassandra/locator/NetworkTopologyStrategy.java b/src/java/org/apache/cassandra/locator/NetworkTopologyStrategy.java
index 9d1989e41f..5e6a2f8ec9 100644
--- a/src/java/org/apache/cassandra/locator/NetworkTopologyStrategy.java
+++ b/src/java/org/apache/cassandra/locator/NetworkTopologyStrategy.java
@@ -305,7 +305,7 @@ public class NetworkTopologyStrategy extends AbstractReplicationStrategy
}
@Override
- protected void validateExpectedOptions() throws ConfigurationException
+ public void validateExpectedOptions() throws ConfigurationException
{
// Do not accept query with no data centers specified.
if (this.configOptions.isEmpty())
diff --git a/src/java/org/apache/cassandra/metrics/TableMetrics.java b/src/java/org/apache/cassandra/metrics/TableMetrics.java
index a8947aa388..4f8c4db0e2 100644
--- a/src/java/org/apache/cassandra/metrics/TableMetrics.java
+++ b/src/java/org/apache/cassandra/metrics/TableMetrics.java
@@ -284,7 +284,7 @@ public class TableMetrics
{
long total = 0;
long filtered = 0;
- for (String keyspace : Schema.instance.getNonSystemKeyspaces().names())
+ for (String keyspace : Schema.instance.distributedKeyspaces().names())
{
Keyspace k = Schema.instance.getKeyspaceInstance(keyspace);
diff --git a/src/java/org/apache/cassandra/schema/ColumnMetadata.java b/src/java/org/apache/cassandra/schema/ColumnMetadata.java
index fdbd1665c7..8be97fb485 100644
--- a/src/java/org/apache/cassandra/schema/ColumnMetadata.java
+++ b/src/java/org/apache/cassandra/schema/ColumnMetadata.java
@@ -17,6 +17,7 @@
*/
package org.apache.cassandra.schema;
+import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.*;
import java.util.function.Predicate;
@@ -32,12 +33,20 @@ import org.apache.cassandra.cql3.selection.SimpleSelector;
import org.apache.cassandra.db.rows.*;
import org.apache.cassandra.db.marshal.*;
import org.apache.cassandra.exceptions.InvalidRequestException;
+import org.apache.cassandra.io.util.DataInputPlus;
+import org.apache.cassandra.io.util.DataOutputPlus;
+import org.apache.cassandra.tcm.serialization.UDTAwareMetadataSerializer;
+import org.apache.cassandra.tcm.serialization.Version;
import org.apache.cassandra.serializers.MarshalException;
+import org.apache.cassandra.utils.ByteBufferUtil;
import org.github.jamm.Unmetered;
+import static org.apache.cassandra.db.TypeSizes.sizeof;
+
@Unmetered
public final class ColumnMetadata extends ColumnSpecification implements Selectable, Comparable<ColumnMetadata>
{
+ public static final Serializer serializer = new Serializer();
public static final Comparator<Object> asymmetricColumnDataComparator =
(a, b) -> ((ColumnData) a).column().compareTo((ColumnMetadata) b);
@@ -495,4 +504,41 @@ public final class ColumnMetadata extends ColumnSpecification implements Selecta
{
return type;
}
+
+ public static class Serializer implements UDTAwareMetadataSerializer<ColumnMetadata>
+ {
+ public void serialize(ColumnMetadata t, DataOutputPlus out, Version version) throws IOException
+ {
+ out.writeUTF(t.ksName);
+ out.writeUTF(t.cfName);
+ out.writeUTF(t.kind.name());
+ out.writeInt(t.position);
+ out.writeUTF(t.type.asCQL3Type().toString());
+ out.writeUTF(t.name.toString());
+ ByteBufferUtil.writeWithShortLength(t.name.bytes, out);
+ }
+
+ public ColumnMetadata deserialize(DataInputPlus in, Types types, Version version) throws IOException
+ {
+ String ksName = in.readUTF();
+ String tableName = in.readUTF();
+ Kind kind = Kind.valueOf(in.readUTF());
+ int position = in.readInt();
+ AbstractType<?> type = CQLTypeParser.parse(ksName, in.readUTF(), types);
+ String name = in.readUTF();
+ ByteBuffer nameBB = ByteBufferUtil.readWithShortLength(in);
+ return new ColumnMetadata(ksName, tableName, new ColumnIdentifier(nameBB, name), type, position, kind);
+ }
+
+ public long serializedSize(ColumnMetadata t, Version version)
+ {
+ return sizeof(t.ksName) +
+ sizeof(t.cfName) +
+ sizeof(t.kind.name()) +
+ sizeof(t.position) +
+ sizeof(t.type.asCQL3Type().toString()) +
+ sizeof(t.name.toString()) +
+ ByteBufferUtil.serializedSizeWithShortLength(t.name.bytes);
+ }
+ }
}
diff --git a/src/java/org/apache/cassandra/schema/DefaultSchemaUpdateHandler.java b/src/java/org/apache/cassandra/schema/DefaultSchemaUpdateHandler.java
deleted file mode 100644
index 0f0c3e9cf5..0000000000
--- a/src/java/org/apache/cassandra/schema/DefaultSchemaUpdateHandler.java
+++ /dev/null
@@ -1,356 +0,0 @@
-/*
- * 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.schema;
-
-import java.time.Duration;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Map;
-import java.util.Set;
-import java.util.UUID;
-import java.util.function.BiConsumer;
-import java.util.stream.Collectors;
-
-import com.google.common.annotations.VisibleForTesting;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import org.apache.cassandra.concurrent.ScheduledExecutors;
-import org.apache.cassandra.concurrent.Stage;
-import org.apache.cassandra.config.CassandraRelevantProperties;
-import org.apache.cassandra.config.DatabaseDescriptor;
-import org.apache.cassandra.db.Mutation;
-import org.apache.cassandra.gms.ApplicationState;
-import org.apache.cassandra.gms.EndpointState;
-import org.apache.cassandra.gms.Gossiper;
-import org.apache.cassandra.gms.IEndpointStateChangeSubscriber;
-import org.apache.cassandra.gms.VersionedValue;
-import org.apache.cassandra.locator.InetAddressAndPort;
-import org.apache.cassandra.net.MessagingService;
-import org.apache.cassandra.schema.SchemaTransformation.SchemaTransformationResult;
-import org.apache.cassandra.service.StorageService;
-import org.apache.cassandra.utils.FBUtilities;
-import org.apache.cassandra.utils.Pair;
-import org.apache.cassandra.utils.concurrent.AsyncPromise;
-import org.apache.cassandra.utils.concurrent.Awaitable;
-
-import static org.apache.cassandra.schema.MigrationCoordinator.MAX_OUTSTANDING_VERSION_REQUESTS;
-
-public class DefaultSchemaUpdateHandler implements SchemaUpdateHandler, IEndpointStateChangeSubscriber
-{
- private static final Logger logger = LoggerFactory.getLogger(DefaultSchemaUpdateHandler.class);
-
- @VisibleForTesting
- final MigrationCoordinator migrationCoordinator;
-
- private final boolean requireSchemas;
- private final BiConsumer<SchemaTransformationResult, Boolean> updateCallback;
- private volatile DistributedSchema schema = DistributedSchema.EMPTY;
-
- private volatile AsyncPromise<Void> requestedReset;
-
- private MigrationCoordinator createMigrationCoordinator(MessagingService messagingService)
- {
- return new MigrationCoordinator(messagingService,
- Stage.MIGRATION.executor(),
- ScheduledExecutors.scheduledTasks,
- MAX_OUTSTANDING_VERSION_REQUESTS,
- Gossiper.instance,
- this::getSchemaVersionForCoordinator,
- this::applyMutationsFromCoordinator);
- }
-
- public DefaultSchemaUpdateHandler(BiConsumer<SchemaTransformationResult, Boolean> updateCallback)
- {
- this(null, MessagingService.instance(), !CassandraRelevantProperties.BOOTSTRAP_SKIP_SCHEMA_CHECK.getBoolean(), updateCallback);
- }
-
- public DefaultSchemaUpdateHandler(MigrationCoordinator migrationCoordinator,
- MessagingService messagingService,
- boolean requireSchemas,
- BiConsumer<SchemaTransformationResult, Boolean> updateCallback)
- {
- this.requireSchemas = requireSchemas;
- this.updateCallback = updateCallback;
- this.migrationCoordinator = migrationCoordinator == null ? createMigrationCoordinator(messagingService) : migrationCoordinator;
- Gossiper.instance.register(this);
- SchemaPushVerbHandler.instance.register(msg -> {
- synchronized (this)
- {
- if (requestedReset == null)
- applyMutations(msg.payload);
- }
- });
- SchemaPullVerbHandler.instance.register(msg -> {
- try
- {
- messagingService.send(msg.responseWith(getSchemaMutations()), msg.from());
- }
- catch (RuntimeException ex)
- {
- logger.error("Failed to send schema mutations to " + msg.from(), ex);
- }
- });
- }
-
- public synchronized void start()
- {
- if (StorageService.instance.isReplacing())
- onRemove(DatabaseDescriptor.getReplaceAddress());
-
- SchemaKeyspace.saveSystemKeyspacesSchema();
-
- migrationCoordinator.start();
- }
-
- @Override
- public boolean waitUntilReady(Duration timeout)
- {
- logger.debug("Waiting for schema to be ready (max {})", timeout);
- boolean schemasReceived = migrationCoordinator.awaitSchemaRequests(timeout.toMillis());
-
- if (schemasReceived)
- return true;
-
- logger.warn("There are nodes in the cluster with a different schema version than us, from which we did not merge schemas: " +
- "our version: ({}), outstanding versions -> endpoints: {}. Use -D{}}=true to ignore this, " +
- "-D{}=<ep1[,epN]> to skip specific endpoints, or -D{}=<ver1[,verN]> to skip specific schema versions",
- Schema.instance.getVersion(),
- migrationCoordinator.outstandingVersions(),
- CassandraRelevantProperties.BOOTSTRAP_SKIP_SCHEMA_CHECK.getKey(),
- CassandraRelevantProperties.IGNORED_SCHEMA_CHECK_ENDPOINTS.getKey(),
- CassandraRelevantProperties.IGNORED_SCHEMA_CHECK_VERSIONS.getKey());
-
- if (requireSchemas)
- {
- logger.error("Didn't receive schemas for all known versions within the {}. Use -D{}=true to skip this check.",
- timeout, CassandraRelevantProperties.BOOTSTRAP_SKIP_SCHEMA_CHECK.getKey());
-
- return false;
- }
-
- return true;
- }
-
- @Override
- public void onRemove(InetAddressAndPort endpoint)
- {
- migrationCoordinator.removeAndIgnoreEndpoint(endpoint);
- }
-
- @Override
- public void onChange(InetAddressAndPort endpoint, ApplicationState state, VersionedValue value)
- {
- if (state == ApplicationState.SCHEMA)
- {
- EndpointState epState = Gossiper.instance.getEndpointStateForEndpoint(endpoint);
- if (epState != null && !Gossiper.instance.isDeadState(epState) && StorageService.instance.getTokenMetadata().isMember(endpoint))
- {
- migrationCoordinator.reportEndpointVersion(endpoint, UUID.fromString(value.value));
- }
- }
- }
-
- @Override
- public void onJoin(InetAddressAndPort endpoint, EndpointState epState)
- {
- // no-op
- }
-
- @Override
- public void beforeChange(InetAddressAndPort endpoint, EndpointState currentState, ApplicationState newStateKey, VersionedValue newValue)
- {
- // no-op
- }
-
- @Override
- public void onAlive(InetAddressAndPort endpoint, EndpointState state)
- {
- // no-op
- }
-
- @Override
- public void onDead(InetAddressAndPort endpoint, EndpointState state)
- {
- // no-op
- }
-
- @Override
- public void onRestart(InetAddressAndPort endpoint, EndpointState state)
- {
- // no-op
- }
-
- private synchronized SchemaTransformationResult applyMutations(Collection<Mutation> schemaMutations)
- {
- // fetch the current state of schema for the affected keyspaces only
- DistributedSchema before = schema;
-
- // apply the schema mutations
- SchemaKeyspace.applyChanges(schemaMutations);
-
- // only compare the keyspaces affected by this set of schema mutations
- Set<String> affectedKeyspaces = SchemaKeyspace.affectedKeyspaces(schemaMutations);
-
- // apply the schema mutations and fetch the new versions of the altered keyspaces
- Keyspaces updatedKeyspaces = SchemaKeyspace.fetchKeyspaces(affectedKeyspaces);
- Set<String> removedKeyspaces = affectedKeyspaces.stream().filter(ks -> !updatedKeyspaces.containsKeyspace(ks)).collect(Collectors.toSet());
- Keyspaces afterKeyspaces = before.getKeyspaces().withAddedOrReplaced(updatedKeyspaces).without(removedKeyspaces);
-
- Keyspaces.KeyspacesDiff diff = Keyspaces.diff(before.getKeyspaces(), afterKeyspaces);
- UUID version = SchemaKeyspace.calculateSchemaDigest();
- DistributedSchema after = new DistributedSchema(afterKeyspaces, version);
- SchemaTransformationResult update = new SchemaTransformationResult(before, after, diff);
-
- logger.info("Applying schema change due to received mutations: {}", update);
- updateSchema(update, false);
- return update;
- }
-
- @Override
- public synchronized SchemaTransformationResult apply(SchemaTransformation transformation, boolean local)
- {
- DistributedSchema before = schema;
- Keyspaces afterKeyspaces = transformation.apply(before.getKeyspaces());
- Keyspaces.KeyspacesDiff diff = Keyspaces.diff(before.getKeyspaces(), afterKeyspaces);
-
- if (diff.isEmpty())
- return new SchemaTransformationResult(before, before, diff);
-
- Collection<Mutation> mutations = SchemaKeyspace.convertSchemaDiffToMutations(diff, transformation.fixedTimestampMicros().orElse(FBUtilities.timestampMicros()));
- SchemaKeyspace.applyChanges(mutations);
-
- DistributedSchema after = new DistributedSchema(afterKeyspaces, SchemaKeyspace.calculateSchemaDigest());
- SchemaTransformationResult update = new SchemaTransformationResult(before, after, diff);
-
- updateSchema(update, local);
- if (!local)
- {
- migrationCoordinator.executor.submit(() -> {
- Pair<Set<InetAddressAndPort>, Set<InetAddressAndPort>> endpoints = migrationCoordinator.pushSchemaMutations(mutations);
- SchemaAnnouncementDiagnostics.schemaTransformationAnnounced(endpoints.left(), endpoints.right(), transformation);
- });
- }
-
- return update;
- }
-
- private void updateSchema(SchemaTransformationResult update, boolean local)
- {
- if (!update.diff.isEmpty())
- {
- this.schema = update.after;
- logger.debug("Schema updated: {}", update);
- updateCallback.accept(update, true);
- if (!local)
- {
- migrationCoordinator.announce(update.after.getVersion());
- }
- }
- else
- {
- logger.debug("Schema update is empty - skipping");
- }
- }
-
- private synchronized void reload()
- {
- DistributedSchema before = this.schema;
- DistributedSchema after = new DistributedSchema(SchemaKeyspace.fetchNonSystemKeyspaces(), SchemaKeyspace.calculateSchemaDigest());
- Keyspaces.KeyspacesDiff diff = Keyspaces.diff(before.getKeyspaces(), after.getKeyspaces());
- SchemaTransformationResult update = new SchemaTransformationResult(before, after, diff);
-
- updateSchema(update, false);
- }
-
- @Override
- public void reset(boolean local)
- {
- if (local)
- {
- reload();
- }
- else
- {
- migrationCoordinator.reset();
- if (!migrationCoordinator.awaitSchemaRequests(CassandraRelevantProperties.MIGRATION_DELAY.getLong()))
- {
- logger.error("Timeout exceeded when waiting for schema from other nodes");
- }
- }
- }
-
- /**
- * When clear is called the update handler will flag that the clear was requested. It means that migration
- * coordinator will think that we have empty schema version and will apply whatever it receives from other nodes.
- * When a first attempt to apply mutations from other node is called, it will first clear the schema and apply
- * the mutations on a truncated table. The flag is then reset.
- * <p>
- * This way the clear is postponed until we really fetch any schema we can use as a replacement. Otherwise, nothing
- * will happen. We will simply reset the flag after the timeout and throw exceptions to the caller.
- *
- * @return
- */
- @Override
- public Awaitable clear()
- {
- synchronized (this)
- {
- if (requestedReset == null)
- {
- requestedReset = new AsyncPromise<>();
- migrationCoordinator.reset();
- }
- return requestedReset;
- }
- }
-
- private UUID getSchemaVersionForCoordinator()
- {
- if (requestedReset != null)
- return SchemaConstants.emptyVersion;
- else
- return schema.getVersion();
- }
-
- private synchronized void applyMutationsFromCoordinator(InetAddressAndPort from, Collection<Mutation> mutations)
- {
- if (requestedReset != null && !mutations.isEmpty())
- {
- schema = DistributedSchema.EMPTY;
- SchemaKeyspace.truncate();
- requestedReset.setSuccess(null);
- requestedReset = null;
- }
- applyMutations(mutations);
- }
-
- private synchronized Collection<Mutation> getSchemaMutations()
- {
- if (requestedReset != null)
- return Collections.emptyList();
- else
- return SchemaKeyspace.convertSchemaToMutations();
- }
-
- public Map<UUID, Set<InetAddressAndPort>> getOutstandingSchemaVersions()
- {
- return migrationCoordinator.outstandingVersions();
- }
-}
diff --git a/src/java/org/apache/cassandra/schema/DefaultSchemaUpdateHandlerFactory.java b/src/java/org/apache/cassandra/schema/DefaultSchemaUpdateHandlerFactory.java
deleted file mode 100644
index 2303663492..0000000000
--- a/src/java/org/apache/cassandra/schema/DefaultSchemaUpdateHandlerFactory.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * 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.schema;
-
-import java.util.function.BiConsumer;
-
-import org.apache.cassandra.schema.SchemaTransformation.SchemaTransformationResult;
-
-public class DefaultSchemaUpdateHandlerFactory implements SchemaUpdateHandlerFactory
-{
- public static final SchemaUpdateHandlerFactory instance = new DefaultSchemaUpdateHandlerFactory();
-
- @Override
- public SchemaUpdateHandler getSchemaUpdateHandler(boolean online, BiConsumer<SchemaTransformationResult, Boolean> updateSchemaCallback)
- {
- return online
- ? new DefaultSchemaUpdateHandler(updateSchemaCallback)
- : new OfflineSchemaUpdateHandler(updateSchemaCallback);
- }
-}
diff --git a/src/java/org/apache/cassandra/schema/DistributedMetadataLogKeyspace.java b/src/java/org/apache/cassandra/schema/DistributedMetadataLogKeyspace.java
index dfc13c2265..cf69429792 100644
--- a/src/java/org/apache/cassandra/schema/DistributedMetadataLogKeyspace.java
+++ b/src/java/org/apache/cassandra/schema/DistributedMetadataLogKeyspace.java
@@ -235,6 +235,7 @@ public final class DistributedMetadataLogKeyspace
{
return CreateTableStatement.parse(String.format(cql, SchemaConstants.METADATA_KEYSPACE_NAME, table), SchemaConstants.METADATA_KEYSPACE_NAME)
.id(TableId.unsafeDeterministic(SchemaConstants.METADATA_KEYSPACE_NAME, table))
+ .epoch(FIRST)
.comment(description);
}
diff --git a/src/java/org/apache/cassandra/schema/DistributedSchema.java b/src/java/org/apache/cassandra/schema/DistributedSchema.java
index d2f30aa727..aa628d66e8 100644
--- a/src/java/org/apache/cassandra/schema/DistributedSchema.java
+++ b/src/java/org/apache/cassandra/schema/DistributedSchema.java
@@ -18,30 +18,238 @@
package org.apache.cassandra.schema;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
import java.util.Objects;
import java.util.UUID;
import com.google.common.base.Preconditions;
+import org.apache.cassandra.cql3.QueryProcessor;
+import org.apache.cassandra.db.Keyspace;
+import org.apache.cassandra.db.Mutation;
+import org.apache.cassandra.io.util.DataInputPlus;
+import org.apache.cassandra.io.util.DataOutputPlus;
+import org.apache.cassandra.tcm.Epoch;
+import org.apache.cassandra.tcm.MetadataValue;
+import org.apache.cassandra.tcm.serialization.MetadataSerializer;
+import org.apache.cassandra.tcm.serialization.Version;
+import org.apache.cassandra.utils.FBUtilities;
+
+import static org.apache.cassandra.db.TypeSizes.sizeof;
+
/**
* Immutable snapshot of the current schema along with its version.
*/
-public class DistributedSchema
+public class DistributedSchema implements MetadataValue<DistributedSchema>
{
- public static final DistributedSchema EMPTY = new DistributedSchema(Keyspaces.none(), SchemaConstants.emptyVersion);
+ public static final Serializer serializer = new Serializer();
+
+ public static final DistributedSchema empty()
+ {
+ return new DistributedSchema(Keyspaces.none(), Epoch.EMPTY);
+ }
+
+ public static DistributedSchema first()
+ {
+ return new DistributedSchema(Keyspaces.of(DistributedMetadataLogKeyspace.metadata(),
+ SystemDistributedKeyspace.metadata()), Epoch.FIRST);
+ }
private final Keyspaces keyspaces;
+ private final Epoch epoch;
private final UUID version;
+ private final Map<String, Keyspace> keyspaceInstances = new HashMap<>();
+
+ public DistributedSchema(Keyspaces keyspaces)
+ {
+ this(keyspaces, Epoch.EMPTY);
+ }
- public DistributedSchema(Keyspaces keyspaces, UUID version)
+ public DistributedSchema(Keyspaces keyspaces, Epoch epoch)
{
Objects.requireNonNull(keyspaces);
- Objects.requireNonNull(version);
this.keyspaces = keyspaces;
- this.version = version;
+ this.epoch = epoch;
+ this.version = new UUID(0, epoch.getEpoch());
validate();
}
+ @Override
+ public DistributedSchema withLastModified(Epoch epoch)
+ {
+ return new DistributedSchema(keyspaces, epoch);
+ }
+
+ @Override
+ public Epoch lastModified()
+ {
+ return epoch;
+ }
+
+ public Keyspace getKeyspace(String keyspace)
+ {
+ return keyspaceInstances.get(keyspace);
+ }
+
+ public KeyspaceMetadata getKeyspaceMetadata(String keyspace)
+ {
+ return keyspaces.get(keyspace).get();
+ }
+
+ public static DistributedSchema fromSystemTables(Keyspaces keyspaces)
+ {
+ if (!keyspaces.containsKeyspace(SchemaConstants.METADATA_KEYSPACE_NAME))
+ keyspaces = keyspaces.with(DistributedMetadataLogKeyspace.metadata());
+ return new DistributedSchema(keyspaces, Epoch.UPGRADE_GOSSIP);
+ }
+
+ public void initializeKeyspaceInstances(DistributedSchema prev)
+ {
+ initializeKeyspaceInstances(prev, true);
+ }
+
+ public void initializeKeyspaceInstances(DistributedSchema prev, boolean loadSSTables)
+ {
+ keyspaceInstances.putAll(prev.keyspaceInstances);
+
+ Keyspaces.KeyspacesDiff ksDiff = Keyspaces.diff(prev.getKeyspaces(), getKeyspaces());
+
+ SchemaChangeNotifier schemaChangeNotifier = Schema.instance.schemaChangeNotifier();
+ schemaChangeNotifier.notifyPreChanges(new SchemaTransformation.SchemaTransformationResult(prev, this, ksDiff));
+
+ ksDiff.dropped.forEach(metadata -> {
+ schemaChangeNotifier.notifyKeyspaceDropped(metadata, loadSSTables);
+ dropKeyspace(metadata, true);
+ });
+
+ ksDiff.created.forEach(metadata -> {
+ schemaChangeNotifier.notifyKeyspaceCreated(metadata);
+ keyspaceInstances.put(metadata.name, new Keyspace(Schema.instance, metadata, loadSSTables));
+ });
+
+ ksDiff.altered.forEach(delta -> {
+ boolean initialized = Keyspace.isInitialized();
+
+ Keyspace keyspace = initialized ? keyspaceInstances.get(delta.before.name) : null;
+ if (initialized)
+ {
+ assert keyspace != null : String.format("Keyspace %s is not initialized. Initialized keyspaces: %s.", delta.before.name, keyspaceInstances.keySet());
+ assert delta.before.name.equals(delta.after.name);
+
+ // drop tables and views
+ delta.views.dropped.forEach(v -> dropView(keyspace, v, true));
+ delta.tables.dropped.forEach(t -> dropTable(keyspace, t, true));
+
+ // add tables and views
+ delta.tables.created.forEach(t -> createTable(keyspace, t));
+ delta.views.created.forEach(v -> createView(keyspace, v));
+
+ // update tables and views
+ delta.tables.altered.forEach(diff -> alterTable(keyspace, diff.after));
+ delta.views.altered.forEach(diff -> alterView(keyspace, diff.after));
+
+ schemaChangeNotifier.notifyKeyspaceAltered(delta, loadSSTables);
+ // deal with all added, and altered views
+ keyspaceInstances.get(delta.after.name).viewManager.reload(true);
+ }
+
+ //schemaChangeNotifier.notifyKeyspaceAltered(delta);
+ SchemaDiagnostics.keyspaceAltered(Schema.instance, delta);
+ });
+
+ // Avoid system table side effects during initialization
+ if (epoch.isEqualOrAfter(Epoch.FIRST))
+ {
+ Collection<Mutation> mutations = SchemaKeyspace.convertSchemaDiffToMutations(ksDiff, FBUtilities.timestampMicros());
+ SchemaKeyspace.applyChanges(mutations);
+ }
+
+ QueryProcessor.clearInternalStatementsCache();
+ QueryProcessor.clearPreparedStatementsCache();
+ }
+
+ private void dropView(Keyspace keyspace, ViewMetadata metadata, boolean dropData)
+ {
+ keyspace.viewManager.dropView(metadata.name());
+ dropTable(keyspace, metadata.metadata, dropData);
+ }
+
+ // TODO: handle drops after snapshots
+ private void dropKeyspace(KeyspaceMetadata keyspaceMetadata, boolean dropData)
+ {
+ SchemaDiagnostics.keyspaceDropping(Schema.instance, keyspaceMetadata);
+
+ boolean initialized = Keyspace.isInitialized();
+ Keyspace keyspace = initialized ? Keyspace.open(keyspaceMetadata.name) : null;
+ if (initialized)
+ {
+ if (keyspace == null)
+ return;
+
+ keyspaceMetadata.views.forEach(v -> dropView(keyspace, v, dropData));
+ keyspaceMetadata.tables.forEach(t -> dropTable(keyspace, t, dropData));
+
+ // remove the keyspace from the static instances
+ Keyspace unloadedKeyspace = keyspaceInstances.remove(keyspaceMetadata.name);
+ unloadedKeyspace.unload(true);
+ SchemaDiagnostics.metadataRemoved(Schema.instance, keyspaceMetadata);
+ assert unloadedKeyspace == keyspace;
+
+ Keyspace.writeOrder.awaitNewBarrier();
+ }
+ else
+ {
+ keyspace.unload(true);
+ SchemaDiagnostics.metadataRemoved(Schema.instance, keyspaceMetadata);
+ }
+
+ SchemaDiagnostics.keyspaceDropped(Schema.instance, keyspaceMetadata);
+ }
+ /**
+ *
+ * @param keyspace
+ * @param metadata
+ */
+ private void dropTable(Keyspace keyspace, TableMetadata metadata, boolean dropData)
+ {
+ SchemaDiagnostics.tableDropping(Schema.instance, metadata);
+ keyspace.dropCf(metadata.id, dropData);
+ SchemaDiagnostics.tableDropped(Schema.instance, metadata);
+ }
+
+ private void createTable(Keyspace keyspace, TableMetadata table)
+ {
+ SchemaDiagnostics.tableCreating(Schema.instance, table);
+ keyspace.initCf(table, true);
+ SchemaDiagnostics.tableCreated(Schema.instance, table);
+ }
+
+ private void createView(Keyspace keyspace, ViewMetadata view)
+ {
+ SchemaDiagnostics.tableCreating(Schema.instance, view.metadata);
+ keyspace.initCf(view.metadata, true);
+ SchemaDiagnostics.tableCreated(Schema.instance, view.metadata);
+ }
+
+ private void alterTable(Keyspace keyspace, TableMetadata updated)
+ {
+ SchemaDiagnostics.tableAltering(Schema.instance, updated);
+ keyspace.getColumnFamilyStore(updated.name).reload(updated);
+ SchemaDiagnostics.tableAltered(Schema.instance, updated);
+ }
+
+ private void alterView(Keyspace keyspace, ViewMetadata updated)
+ {
+ SchemaDiagnostics.tableAltering(Schema.instance, updated.metadata);
+ keyspace.getColumnFamilyStore(updated.name()).reload(updated.metadata);
+ SchemaDiagnostics.tableAltered(Schema.instance, updated.metadata);
+ }
+
public Keyspaces getKeyspaces()
{
return keyspaces;
@@ -49,7 +257,7 @@ public class DistributedSchema
public boolean isEmpty()
{
- return SchemaConstants.emptyVersion.equals(version);
+ return epoch.is(Epoch.EMPTY);
}
public UUID getVersion()
@@ -94,4 +302,46 @@ public class DistributedSchema
ksm.userFunctions.forEach(f -> Preconditions.checkArgument(f.name().keyspace.equals(ksm.name), "Function %s points to keyspace %s while defined in keyspace %s", f.name().name, f.name().keyspace, ksm.name));
});
}
+
+ public static class Serializer implements MetadataSerializer<DistributedSchema>
+ {
+ public void serialize(DistributedSchema t, DataOutputPlus out, Version version) throws IOException
+ {
+ Epoch.serializer.serialize(t.epoch, out, version);
+ out.writeInt(t.keyspaces.size());
+ for (KeyspaceMetadata ksm : t.keyspaces)
+ KeyspaceMetadata.serializer.serialize(ksm, out, version);
+ }
+
+ public DistributedSchema deserialize(DataInputPlus in, Version version) throws IOException
+ {
+ Epoch basedOnEpoch = Epoch.serializer.deserialize(in, version);
+ int ksCount = in.readInt();
+ List<KeyspaceMetadata> ksms = new ArrayList<>(ksCount);
+ for (int i = 0; i < ksCount; i++)
+ ksms.add(KeyspaceMetadata.serializer.deserialize(in, version));
+
+ return new DistributedSchema(Keyspaces.of(ksms.toArray(new KeyspaceMetadata[ksCount])), basedOnEpoch);
+ }
+
+ public long serializedSize(DistributedSchema t, Version version)
+ {
+ long size = Epoch.serializer.serializedSize(t.epoch, version);
+ size += sizeof(t.keyspaces.size());
+ for (KeyspaceMetadata ksm : t.keyspaces)
+ size += KeyspaceMetadata.serializer.serializedSize(ksm, version);
+ return size;
+ }
+ }
+
+ @Override
+ public String toString()
+ {
+ return "DistributedSchema{" +
+ "keyspaces=" + keyspaces +
+ ", epoch=" + epoch +
+ ", version=" + version +
+ ", keyspaceInstances=" + keyspaceInstances +
+ '}';
+ }
}
diff --git a/src/java/org/apache/cassandra/schema/DroppedColumn.java b/src/java/org/apache/cassandra/schema/DroppedColumn.java
index 90dfe651f7..c9ed2a36b1 100644
--- a/src/java/org/apache/cassandra/schema/DroppedColumn.java
+++ b/src/java/org/apache/cassandra/schema/DroppedColumn.java
@@ -17,11 +17,23 @@
*/
package org.apache.cassandra.schema;
+import java.io.IOException;
+
import com.google.common.base.MoreObjects;
import com.google.common.base.Objects;
+import org.apache.cassandra.io.util.DataInputPlus;
+import org.apache.cassandra.io.util.DataOutputPlus;
+import org.apache.cassandra.tcm.serialization.UDTAwareMetadataSerializer;
+import org.apache.cassandra.tcm.serialization.Version;
+
+import static org.apache.cassandra.db.TypeSizes.sizeof;
+
+
public final class DroppedColumn
{
+ public static final Serializer serializer = new Serializer();
+
public final ColumnMetadata column;
public final long droppedTime; // drop timestamp, in microseconds, yet with millisecond granularity
@@ -56,4 +68,26 @@ public final class DroppedColumn
{
return MoreObjects.toStringHelper(this).add("column", column).add("droppedTime", droppedTime).toString();
}
+
+ public static class Serializer implements UDTAwareMetadataSerializer<DroppedColumn>
+ {
+ public void serialize(DroppedColumn t, DataOutputPlus out, Version version) throws IOException
+ {
+ out.writeLong(t.droppedTime);
+ ColumnMetadata.serializer.serialize(t.column, out, version);
+ }
+
+ public DroppedColumn deserialize(DataInputPlus in, Types types, Version version) throws IOException
+ {
+ long droppedTime = in.readLong();
+ ColumnMetadata column = ColumnMetadata.serializer.deserialize(in, types, version);
+ return new DroppedColumn(column, droppedTime);
+ }
+
+ public long serializedSize(DroppedColumn t, Version version)
+ {
+ return sizeof(t.droppedTime)
+ + ColumnMetadata.serializer.serializedSize(t.column, version);
+ }
+ }
}
diff --git a/src/java/org/apache/cassandra/schema/IndexMetadata.java b/src/java/org/apache/cassandra/schema/IndexMetadata.java
index 4adbce170b..40177047ba 100644
--- a/src/java/org/apache/cassandra/schema/IndexMetadata.java
+++ b/src/java/org/apache/cassandra/schema/IndexMetadata.java
@@ -39,9 +39,12 @@ import org.apache.cassandra.exceptions.UnknownIndexException;
import org.apache.cassandra.index.Index;
import org.apache.cassandra.io.util.DataInputPlus;
import org.apache.cassandra.io.util.DataOutputPlus;
+import org.apache.cassandra.tcm.serialization.Version;
import org.apache.cassandra.utils.FBUtilities;
import org.apache.cassandra.utils.UUIDSerializer;
+import static org.apache.cassandra.db.TypeSizes.sizeof;
+
/**
* An immutable representation of secondary index metadata.
*/
@@ -54,6 +57,7 @@ public final class IndexMetadata
public static final Serializer serializer = new Serializer();
+ public static final MetadataSerializer metadataSerializer = new MetadataSerializer();
public enum Kind
{
@@ -300,4 +304,41 @@ public final class IndexMetadata
return UUIDSerializer.serializer.serializedSize(metadata.id, version);
}
}
+
+ public static class MetadataSerializer implements org.apache.cassandra.tcm.serialization.MetadataSerializer<IndexMetadata>
+ {
+ public void serialize(IndexMetadata t, DataOutputPlus out, Version version) throws IOException
+ {
+ out.writeUTF(t.name);
+ out.writeUTF(t.kind.name());
+ out.writeInt(t.options.size());
+ for (Map.Entry<String, String> entry : t.options.entrySet())
+ {
+ out.writeUTF(entry.getKey());
+ out.writeUTF(entry.getValue());
+ }
+ }
+
+ public IndexMetadata deserialize(DataInputPlus in, Version version) throws IOException
+ {
+ String name = in.readUTF();
+ Kind kind = Kind.valueOf(in.readUTF());
+ int size = in.readInt();
+
+ Map<String, String> options = Maps.newHashMapWithExpectedSize(size);
+ for (int i = 0; i < size; i++)
+ options.put(in.readUTF(), in.readUTF());
+ return new IndexMetadata(name, options, kind);
+ }
+
+ public long serializedSize(IndexMetadata t, Version version)
+ {
+ int size = sizeof(t.name) + sizeof(t.kind.name()) + sizeof(t.options.size());
+
+ for (Map.Entry<String, String> entry : t.options.entrySet())
+ size = size + sizeof(entry.getKey()) + sizeof(entry.getValue());
+
+ return size;
+ }
+ }
}
diff --git a/src/java/org/apache/cassandra/schema/Indexes.java b/src/java/org/apache/cassandra/schema/Indexes.java
index 2e95779674..0282cfc666 100644
--- a/src/java/org/apache/cassandra/schema/Indexes.java
+++ b/src/java/org/apache/cassandra/schema/Indexes.java
@@ -17,14 +17,21 @@
*/
package org.apache.cassandra.schema;
+import java.io.IOException;
import java.util.*;
import java.util.stream.Stream;
import com.google.common.collect.ImmutableMap;
+import org.apache.cassandra.io.util.DataInputPlus;
+import org.apache.cassandra.io.util.DataOutputPlus;
+import org.apache.cassandra.tcm.serialization.MetadataSerializer;
+import org.apache.cassandra.tcm.serialization.Version;
+
import static java.lang.String.format;
import static com.google.common.collect.Iterables.filter;
+import static org.apache.cassandra.db.TypeSizes.sizeof;
/**
* For backwards compatibility, in the first instance an IndexMetadata must have
@@ -37,6 +44,8 @@ import static com.google.common.collect.Iterables.filter;
*/
public final class Indexes implements Iterable<IndexMetadata>
{
+ public static final Serializer serializer = new Serializer();
+
private final ImmutableMap<String, IndexMetadata> indexesByName;
private final ImmutableMap<UUID, IndexMetadata> indexesById;
@@ -215,4 +224,31 @@ public final class Indexes implements Iterable<IndexMetadata>
return this;
}
}
+
+ public static class Serializer implements MetadataSerializer<Indexes>
+ {
+ public void serialize(Indexes t, DataOutputPlus out, Version version) throws IOException
+ {
+ out.writeInt(t.size());
+ for (IndexMetadata im : t.indexesById.values())
+ IndexMetadata.metadataSerializer.serialize(im, out, version);
+ }
+
+ public Indexes deserialize(DataInputPlus in, Version version) throws IOException
+ {
+ int size = in.readInt();
+ Indexes.Builder builder = Indexes.builder();
+ for (int i = 0; i < size; i++)
+ builder.add(IndexMetadata.metadataSerializer.deserialize(in, version));
+ return builder.build();
+ }
+
+ public long serializedSize(Indexes t, Version version)
+ {
+ int size = sizeof(t.size());
+ for (IndexMetadata metadata : t.indexesById.values())
+ size += IndexMetadata.metadataSerializer.serializedSize(metadata, version);
+ return size;
+ }
+ }
}
diff --git a/src/java/org/apache/cassandra/schema/KeyspaceMetadata.java b/src/java/org/apache/cassandra/schema/KeyspaceMetadata.java
index 4adf464100..d2d63e9093 100644
--- a/src/java/org/apache/cassandra/schema/KeyspaceMetadata.java
+++ b/src/java/org/apache/cassandra/schema/KeyspaceMetadata.java
@@ -17,39 +17,44 @@
*/
package org.apache.cassandra.schema;
+import java.io.IOException;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
-
import javax.annotation.Nullable;
import com.google.common.base.MoreObjects;
import com.google.common.base.Objects;
import com.google.common.collect.Iterables;
-import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.cql3.CqlBuilder;
import org.apache.cassandra.cql3.SchemaElement;
import org.apache.cassandra.cql3.functions.UDAggregate;
import org.apache.cassandra.cql3.functions.UDFunction;
import org.apache.cassandra.db.marshal.UserType;
import org.apache.cassandra.exceptions.ConfigurationException;
+import org.apache.cassandra.io.util.DataInputPlus;
+import org.apache.cassandra.io.util.DataOutputPlus;
import org.apache.cassandra.locator.AbstractReplicationStrategy;
import org.apache.cassandra.schema.UserFunctions.FunctionsDiff;
+import org.apache.cassandra.tcm.ClusterMetadata;
+import org.apache.cassandra.tcm.serialization.MetadataSerializer;
+import org.apache.cassandra.tcm.serialization.Version;
import org.apache.cassandra.schema.Tables.TablesDiff;
import org.apache.cassandra.schema.Types.TypesDiff;
import org.apache.cassandra.schema.Views.ViewsDiff;
-import org.apache.cassandra.service.StorageService;
-
-import static java.lang.String.format;
import static com.google.common.collect.Iterables.any;
+import static java.lang.String.format;
+import static org.apache.cassandra.db.TypeSizes.sizeof;
/**
* An immutable representation of keyspace metadata (name, params, tables, types, and functions).
*/
public final class KeyspaceMetadata implements SchemaElement
{
+ public static final Serializer serializer = new Serializer();
+
public enum Kind
{
REGULAR, VIRTUAL
@@ -57,21 +62,23 @@ public final class KeyspaceMetadata implements SchemaElement
public final String name;
public final Kind kind;
+ public final AbstractReplicationStrategy replicationStrategy;
public final KeyspaceParams params;
public final Tables tables;
public final Views views;
public final Types types;
public final UserFunctions userFunctions;
- private KeyspaceMetadata(String name, Kind kind, KeyspaceParams params, Tables tables, Views views, Types types, UserFunctions functions)
+ private KeyspaceMetadata(String keyspaceName, Kind kind, KeyspaceParams params, Tables tables, Views views, Types types, UserFunctions functions)
{
- this.name = name;
+ this.name = keyspaceName;
this.kind = kind;
this.params = params;
this.tables = tables;
this.views = views;
this.types = types;
this.userFunctions = functions;
+ this.replicationStrategy = AbstractReplicationStrategy.createReplicationStrategy(keyspaceName, params.replication);
}
public static KeyspaceMetadata create(String name, KeyspaceParams params)
@@ -197,6 +204,15 @@ public final class KeyspaceMetadata implements SchemaElement
return Optional.empty();
}
+ public Optional<TableMetadata> getIndexMetadata(String indexName)
+ {
+ TableMetadata metadata = tables.indexTables().get(indexName);
+ if (metadata != null)
+ return Optional.of(metadata);
+
+ return Optional.empty();
+ }
+
@Override
public int hashCode()
{
@@ -298,7 +314,7 @@ public final class KeyspaceMetadata implements SchemaElement
return builder.toString();
}
- public void validate()
+ public void validate(ClusterMetadata metadata)
{
if (!SchemaConstants.isValidName(name))
{
@@ -309,7 +325,6 @@ public final class KeyspaceMetadata implements SchemaElement
}
params.validate(name, null);
-
tablesAndViews().forEach(TableMetadata::validate);
Set<String> indexNames = new HashSet<>();
@@ -325,15 +340,6 @@ public final class KeyspaceMetadata implements SchemaElement
}
}
- public AbstractReplicationStrategy createReplicationStrategy()
- {
- return AbstractReplicationStrategy.createReplicationStrategy(name,
- params.replication.klass,
- StorageService.instance.getTokenMetadata(),
- DatabaseDescriptor.getEndpointSnitch(),
- params.replication.options);
- }
-
static Optional<KeyspaceDiff> diff(KeyspaceMetadata before, KeyspaceMetadata after)
{
return KeyspaceDiff.diff(before, after);
@@ -411,4 +417,38 @@ public final class KeyspaceMetadata implements SchemaElement
'}';
}
}
+
+ public static class Serializer implements MetadataSerializer<KeyspaceMetadata>
+ {
+ public void serialize(KeyspaceMetadata t, DataOutputPlus out, Version version) throws IOException
+ {
+ out.writeUTF(t.name);
+ Types.serializer.serialize(t.types, out, version);
+ KeyspaceParams.serializer.serialize(t.params, out, version);
+ Tables.serializer.serialize(t.tables, out, version);
+ UserFunctions.serializer.serialize(t.userFunctions, out, version);
+ Views.serializer.serialize(t.views, out, version);
+ }
+
+ public KeyspaceMetadata deserialize(DataInputPlus in, Version version) throws IOException
+ {
+ String name = in.readUTF();
+ Types types = Types.serializer.deserialize(name, in, version);
+ KeyspaceParams params = KeyspaceParams.serializer.deserialize(in, version);
+ Tables tables = Tables.serializer.deserialize(in, types, version);
+ UserFunctions functions = UserFunctions.serializer.deserialize(in, types, version);
+ Views views = Views.serializer.deserialize(in, types, version);
+ return KeyspaceMetadata.create(name, params, tables, views, types, functions);
+ }
+
+ public long serializedSize(KeyspaceMetadata t, Version version)
+ {
+ return sizeof(t.name)
+ + Types.serializer.serializedSize(t.types, version)
+ + KeyspaceParams.serializer.serializedSize(t.params, version)
+ + Tables.serializer.serializedSize(t.tables, version)
+ + UserFunctions.serializer.serializedSize(t.userFunctions, version)
+ + Views.serializer.serializedSize(t.views, version);
+ }
+ }
}
diff --git a/src/java/org/apache/cassandra/schema/KeyspaceParams.java b/src/java/org/apache/cassandra/schema/KeyspaceParams.java
index 539993e2b3..d960a709ad 100644
--- a/src/java/org/apache/cassandra/schema/KeyspaceParams.java
+++ b/src/java/org/apache/cassandra/schema/KeyspaceParams.java
@@ -17,19 +17,27 @@
*/
package org.apache.cassandra.schema;
+import java.io.IOException;
import java.util.Map;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.MoreObjects;
import com.google.common.base.Objects;
+import org.apache.cassandra.db.TypeSizes;
+import org.apache.cassandra.io.util.DataInputPlus;
+import org.apache.cassandra.io.util.DataOutputPlus;
import org.apache.cassandra.service.ClientState;
+import org.apache.cassandra.tcm.serialization.MetadataSerializer;
+import org.apache.cassandra.tcm.serialization.Version;
/**
* An immutable class representing keyspace parameters (durability and replication).
*/
public final class KeyspaceParams
{
+ public static final Serializer serializer = new Serializer();
+
public static final boolean DEFAULT_DURABLE_WRITES = true;
/**
@@ -124,4 +132,26 @@ public final class KeyspaceParams
.add(Option.REPLICATION.toString(), replication)
.toString();
}
+
+ public static class Serializer implements MetadataSerializer<KeyspaceParams>
+ {
+ public void serialize(KeyspaceParams t, DataOutputPlus out, Version version) throws IOException
+ {
+ ReplicationParams.serializer.serialize(t.replication, out, version);
+ out.writeBoolean(t.durableWrites);
+ }
+
+ public KeyspaceParams deserialize(DataInputPlus in, Version version) throws IOException
+ {
+ ReplicationParams params = ReplicationParams.serializer.deserialize(in, version);
+ boolean durableWrites = in.readBoolean();
+ return new KeyspaceParams(durableWrites, params);
+ }
+
+ public long serializedSize(KeyspaceParams t, Version version)
+ {
+ return ReplicationParams.serializer.serializedSize(t.replication, version) +
+ TypeSizes.sizeof(t.durableWrites);
+ }
+ }
}
diff --git a/src/java/org/apache/cassandra/schema/Keyspaces.java b/src/java/org/apache/cassandra/schema/Keyspaces.java
index e9bd92c7c5..3f5431da1a 100644
--- a/src/java/org/apache/cassandra/schema/Keyspaces.java
+++ b/src/java/org/apache/cassandra/schema/Keyspaces.java
@@ -19,7 +19,9 @@ package org.apache.cassandra.schema;
import java.util.Collection;
import java.util.Iterator;
+import java.util.Map;
import java.util.Optional;
+import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Stream;
@@ -28,23 +30,21 @@ import javax.annotation.Nullable;
import com.google.common.collect.*;
import org.apache.cassandra.schema.KeyspaceMetadata.KeyspaceDiff;
+import org.apache.cassandra.tcm.ClusterMetadata;
+import org.apache.cassandra.utils.btree.BTreeMap;
public final class Keyspaces implements Iterable<KeyspaceMetadata>
{
- private static final Keyspaces NONE = builder().build();
+ public static final Keyspaces NONE = new Keyspaces(BTreeMap.empty(), BTreeMap.empty());
- private final ImmutableMap<String, KeyspaceMetadata> keyspaces;
- private final ImmutableMap<TableId, TableMetadata> tables;
+ private final BTreeMap<String, KeyspaceMetadata> keyspaces;
+ private final BTreeMap<TableId, TableMetadata> tables;
- private Keyspaces(Builder builder)
+ private Keyspaces(BTreeMap<String, KeyspaceMetadata> keyspaces,
+ BTreeMap<TableId, TableMetadata> tables)
{
- keyspaces = builder.keyspaces.build();
- tables = builder.tables.build();
- }
-
- public static Builder builder()
- {
- return new Builder();
+ this.keyspaces = keyspaces;
+ this.tables = tables;
}
public static Keyspaces none()
@@ -54,7 +54,28 @@ public final class Keyspaces implements Iterable<KeyspaceMetadata>
public static Keyspaces of(KeyspaceMetadata... keyspaces)
{
- return builder().add(keyspaces).build();
+ BTreeMap<String, KeyspaceMetadata> newKeyspaces = BTreeMap.empty();
+ BTreeMap<TableId, TableMetadata> newTables = BTreeMap.empty();
+ for (KeyspaceMetadata ks : keyspaces)
+ {
+ newKeyspaces = newKeyspaces.with(ks.name, ks);
+ newTables = withTablesViews(newTables, ks);
+ }
+ return new Keyspaces(newKeyspaces, newTables);
+ }
+
+ public Keyspaces with(KeyspaceMetadata ks)
+ {
+ assert !keyspaces.containsKey(ks.name) : "Keyspace already exists: "+ks.name;
+ return new Keyspaces(keyspaces.with(ks.name, ks), withTablesViews(tables, ks));
+ }
+
+ public Keyspaces with(Iterable<KeyspaceMetadata> kss)
+ {
+ Keyspaces k = this;
+ for (KeyspaceMetadata ks : kss)
+ k = k.with(ks);
+ return k;
}
public Iterator<KeyspaceMetadata> iterator()
@@ -67,7 +88,7 @@ public final class Keyspaces implements Iterable<KeyspaceMetadata>
return keyspaces.values().stream();
}
- public ImmutableSet<String> names()
+ public Set<String> names()
{
return keyspaces.keySet();
}
@@ -107,9 +128,19 @@ public final class Keyspaces implements Iterable<KeyspaceMetadata>
public Keyspaces filter(Predicate<KeyspaceMetadata> predicate)
{
- Builder builder = builder();
- stream().filter(predicate).forEach(builder::add);
- return builder.build();
+ BTreeMap<String, KeyspaceMetadata> kss = keyspaces;
+ BTreeMap<TableId, TableMetadata> tbls = tables;
+ // todo: bulk removals from BTreeMap
+ for (Map.Entry<String, KeyspaceMetadata> entry : keyspaces.entrySet())
+ {
+ if (!predicate.test(entry.getValue()))
+ {
+ kss = kss.without(entry.getKey());
+ tbls = withoutKsTablesViews(tbls, entry.getValue());
+ }
+ }
+
+ return new Keyspaces(kss, tbls);
}
/**
@@ -131,9 +162,29 @@ public final class Keyspaces implements Iterable<KeyspaceMetadata>
public Keyspaces withAddedOrUpdated(KeyspaceMetadata keyspace)
{
- return builder().add(Iterables.filter(this, k -> !k.name.equals(keyspace.name)))
- .add(keyspace)
- .build();
+ Keyspaces updated = keyspaces.containsKey(keyspace.name) ? without(keyspace.name) : this;
+ return updated.with(keyspace);
+ }
+
+ private static BTreeMap<TableId, TableMetadata> withoutKsTablesViews(BTreeMap<TableId, TableMetadata> tables, KeyspaceMetadata ks)
+ {
+ // todo: bulk ops
+ BTreeMap<TableId, TableMetadata> tbls = tables;
+ for (TableMetadata table : ks.tables)
+ tbls = tbls.without(table.id);
+ for (ViewMetadata view : ks.views)
+ tbls = tbls.without(view.metadata.id);
+ return tbls;
+ }
+
+ private static BTreeMap<TableId, TableMetadata> withTablesViews(BTreeMap<TableId, TableMetadata> tables, KeyspaceMetadata ks)
+ {
+ BTreeMap<TableId, TableMetadata> tbls = tables;
+ for (TableMetadata table : ks.tables)
+ tbls = tbls.with(table.id, table);
+ for (ViewMetadata view : ks.views)
+ tbls = tbls.with(view.metadata.id, view.metadata);
+ return tbls;
}
/**
@@ -149,9 +200,7 @@ public final class Keyspaces implements Iterable<KeyspaceMetadata>
*/
public Keyspaces withAddedOrReplaced(KeyspaceMetadata keyspace)
{
- return builder().add(Iterables.filter(this, k -> !k.name.equals(keyspace.name)))
- .add(keyspace)
- .build();
+ return filter(ksm -> !ksm.name.equals(keyspace.name)).with(keyspace);
}
/**
@@ -162,14 +211,17 @@ public final class Keyspaces implements Iterable<KeyspaceMetadata>
*/
public Keyspaces withAddedOrReplaced(Keyspaces keyspaces)
{
- return builder().add(Iterables.filter(this, k -> !keyspaces.containsKeyspace(k.name)))
- .add(keyspaces)
- .build();
+ Keyspaces kss = this;
+
+ for (KeyspaceMetadata ksm : keyspaces)
+ kss = kss.withAddedOrReplaced(ksm);
+ return kss;
}
public void validate()
{
- keyspaces.values().forEach(KeyspaceMetadata::validate);
+ ClusterMetadata metadata = ClusterMetadata.current();
+ keyspaces.values().forEach((ksm) -> ksm.validate(metadata));
}
@Override
@@ -195,44 +247,6 @@ public final class Keyspaces implements Iterable<KeyspaceMetadata>
return keyspaces.size();
}
- public static final class Builder
- {
- private final ImmutableMap.Builder<String, KeyspaceMetadata> keyspaces = new ImmutableMap.Builder<>();
- private final ImmutableMap.Builder<TableId, TableMetadata> tables = new ImmutableMap.Builder<>();
-
- private Builder()
- {
- }
-
- public Keyspaces build()
- {
- return new Keyspaces(this);
- }
-
- public Builder add(KeyspaceMetadata keyspace)
- {
- keyspaces.put(keyspace.name, keyspace);
-
- keyspace.tables.forEach(t -> tables.put(t.id, t));
- keyspace.views.forEach(v -> tables.put(v.metadata.id, v.metadata));
-
- return this;
- }
-
- public Builder add(KeyspaceMetadata... keyspaces)
- {
- for (KeyspaceMetadata keyspace : keyspaces)
- add(keyspace);
- return this;
- }
-
- public Builder add(Iterable<KeyspaceMetadata> keyspaces)
- {
- keyspaces.forEach(this::add);
- return this;
- }
- }
-
public static KeyspacesDiff diff(Keyspaces before, Keyspaces after)
{
return KeyspacesDiff.diff(before, after);
diff --git a/src/java/org/apache/cassandra/schema/MigrationCoordinator.java b/src/java/org/apache/cassandra/schema/MigrationCoordinator.java
deleted file mode 100644
index a1af1c0445..0000000000
--- a/src/java/org/apache/cassandra/schema/MigrationCoordinator.java
+++ /dev/null
@@ -1,760 +0,0 @@
-/*
- * 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.schema;
-
-import java.lang.management.ManagementFactory;
-import java.net.UnknownHostException;
-import java.time.Duration;
-import java.util.ArrayDeque;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Deque;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-import java.util.UUID;
-import java.util.concurrent.RejectedExecutionException;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.ScheduledFuture;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicReference;
-import java.util.function.BiConsumer;
-import java.util.function.LongSupplier;
-import java.util.function.Supplier;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Preconditions;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Sets;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import org.apache.cassandra.concurrent.ExecutorPlus;
-import org.apache.cassandra.concurrent.FutureTask;
-import org.apache.cassandra.concurrent.ScheduledExecutors;
-import org.apache.cassandra.config.CassandraRelevantProperties;
-import org.apache.cassandra.db.Mutation;
-import org.apache.cassandra.exceptions.RequestFailureReason;
-import org.apache.cassandra.gms.ApplicationState;
-import org.apache.cassandra.gms.EndpointState;
-import org.apache.cassandra.gms.Gossiper;
-import org.apache.cassandra.gms.VersionedValue;
-import org.apache.cassandra.locator.InetAddressAndPort;
-import org.apache.cassandra.net.Message;
-import org.apache.cassandra.net.MessagingService;
-import org.apache.cassandra.net.NoPayload;
-import org.apache.cassandra.net.RequestCallback;
-import org.apache.cassandra.net.Verb;
-import org.apache.cassandra.service.StorageService;
-import org.apache.cassandra.utils.FBUtilities;
-import org.apache.cassandra.utils.NoSpamLogger;
-import org.apache.cassandra.utils.Pair;
-import org.apache.cassandra.utils.Simulate;
-import org.apache.cassandra.utils.concurrent.Future;
-import org.apache.cassandra.utils.concurrent.ImmediateFuture;
-import org.apache.cassandra.utils.concurrent.WaitQueue;
-
-import static org.apache.cassandra.config.CassandraRelevantProperties.IGNORED_SCHEMA_CHECK_ENDPOINTS;
-import static org.apache.cassandra.config.CassandraRelevantProperties.IGNORED_SCHEMA_CHECK_VERSIONS;
-import static org.apache.cassandra.config.CassandraRelevantProperties.SCHEMA_PULL_INTERVAL_MS;
-import static org.apache.cassandra.net.Verb.SCHEMA_PUSH_REQ;
-import static org.apache.cassandra.utils.Clock.Global.nanoTime;
-import static org.apache.cassandra.utils.Simulate.With.MONITORS;
-import static org.apache.cassandra.utils.concurrent.WaitQueue.newWaitQueue;
-
-/**
- * Migration coordinator is responsible for tracking schema versions on various nodes and, if needed, synchronize the
- * schema. It performs periodic checks and if there is a schema version mismatch between the current node and the other
- * node, it pulls the schema and applies the changes locally through the callback.
- *
- * In particular the Migration Coordinator keeps track of all schema versions reported from each node in the cluster.
- * As long as a certain version is advertised by some node, it is being tracked. As long as a version is tracked,
- * the migration coordinator tries to fetch it by its periodic job.
- *
- * It works in close cooperation with {@link DefaultSchemaUpdateHandler} which is responsible for maintaining local
- * schema metadata stored in {@link SchemaKeyspace}.
- */
-@Simulate(with = MONITORS)
-public class MigrationCoordinator
-{
- private static final Logger logger = LoggerFactory.getLogger(MigrationCoordinator.class);
- private static final NoSpamLogger noSpamLogger = NoSpamLogger.getLogger(MigrationCoordinator.logger, 1, TimeUnit.MINUTES);
- private static final Future<Void> FINISHED_FUTURE = ImmediateFuture.success(null);
-
- private static LongSupplier getUptimeFn = () -> ManagementFactory.getRuntimeMXBean().getUptime();
-
- @VisibleForTesting
- public static void setUptimeFn(LongSupplier supplier)
- {
- getUptimeFn = supplier;
- }
-
- private static final int MIGRATION_DELAY_IN_MS = CassandraRelevantProperties.MIGRATION_DELAY.getInt();
- public static final int MAX_OUTSTANDING_VERSION_REQUESTS = 3;
-
- private static ImmutableSet<UUID> getIgnoredVersions()
- {
- String s = IGNORED_SCHEMA_CHECK_VERSIONS.getString();
- if (s == null || s.isEmpty())
- return ImmutableSet.of();
-
- ImmutableSet.Builder<UUID> versions = ImmutableSet.builder();
- for (String version : s.split(","))
- {
- versions.add(UUID.fromString(version));
- }
-
- return versions.build();
- }
-
- private static final Set<UUID> IGNORED_VERSIONS = getIgnoredVersions();
-
- private static Set<InetAddressAndPort> getIgnoredEndpoints()
- {
- Set<InetAddressAndPort> endpoints = new HashSet<>();
-
- String s = IGNORED_SCHEMA_CHECK_ENDPOINTS.getString();
- if (s == null || s.isEmpty())
- return endpoints;
-
- for (String endpoint : s.split(","))
- {
- try
- {
- endpoints.add(InetAddressAndPort.getByName(endpoint));
- }
- catch (UnknownHostException e)
- {
- throw new RuntimeException(e);
- }
- }
-
- return endpoints;
- }
-
- static class VersionInfo
- {
- final UUID version;
-
- /**
- * The set of endpoints containing this schema version
- */
- final Set<InetAddressAndPort> endpoints = Sets.newConcurrentHashSet();
- /**
- * The set of endpoints from which we are already fetching the schema
- */
- final Set<InetAddressAndPort> outstandingRequests = Sets.newConcurrentHashSet();
- /**
- * The queue of endpoints from which we are going to fetch the schema
- */
- final Deque<InetAddressAndPort> requestQueue = new ArrayDeque<>();
-
- /**
- * Threads waiting for schema synchronization are waiting until this object is signalled
- */
- private final WaitQueue waitQueue = newWaitQueue();
-
- /**
- * Whether this schema version have been received
- */
- volatile boolean receivedSchema;
-
- VersionInfo(UUID version)
- {
- this.version = version;
- }
-
- WaitQueue.Signal register()
- {
- return waitQueue.register();
- }
-
- void markReceived()
- {
- if (receivedSchema)
- return;
-
- receivedSchema = true;
- waitQueue.signalAll();
- }
-
- boolean wasReceived()
- {
- return receivedSchema;
- }
-
- @Override
- public String toString()
- {
- return "VersionInfo{" +
- "version=" + version +
- ", outstandingRequests=" + outstandingRequests +
- ", requestQueue=" + requestQueue +
- ", waitQueue.waiting=" + waitQueue.getWaiting() +
- ", receivedSchema=" + receivedSchema +
- '}';
- }
- }
-
- private final Map<UUID, VersionInfo> versionInfo = new HashMap<>();
- private final Map<InetAddressAndPort, UUID> endpointVersions = new HashMap<>();
- private final Set<InetAddressAndPort> ignoredEndpoints = getIgnoredEndpoints();
- private final ScheduledExecutorService periodicCheckExecutor;
- private final MessagingService messagingService;
- private final AtomicReference<ScheduledFuture<?>> periodicPullTask = new AtomicReference<>();
- private final int maxOutstandingVersionRequests;
- private final Gossiper gossiper;
- private final Supplier<UUID> schemaVersion;
- private final BiConsumer<InetAddressAndPort, Collection<Mutation>> schemaUpdateCallback;
-
- final ExecutorPlus executor;
-
- /**
- * Creates but does not start migration coordinator instance.
- * @param messagingService messaging service instance used to communicate with other nodes for pulling schema
- * and pushing changes
- * @param periodicCheckExecutor executor on which the periodic checks are scheduled
- */
- MigrationCoordinator(MessagingService messagingService,
- ExecutorPlus executor,
- ScheduledExecutorService periodicCheckExecutor,
- int maxOutstandingVersionRequests,
- Gossiper gossiper,
- Supplier<UUID> schemaVersionSupplier,
- BiConsumer<InetAddressAndPort, Collection<Mutation>> schemaUpdateCallback)
- {
- this.messagingService = messagingService;
- this.executor = executor;
- this.periodicCheckExecutor = periodicCheckExecutor;
- this.maxOutstandingVersionRequests = maxOutstandingVersionRequests;
- this.gossiper = gossiper;
- this.schemaVersion = schemaVersionSupplier;
- this.schemaUpdateCallback = schemaUpdateCallback;
- }
-
- void start()
- {
- logger.info("Starting migration coordinator and scheduling pulling schema versions every {}", Duration.ofMillis(SCHEMA_PULL_INTERVAL_MS.getLong()));
- announce(schemaVersion.get());
- periodicPullTask.updateAndGet(curTask -> curTask == null
- ? periodicCheckExecutor.scheduleWithFixedDelay(this::pullUnreceivedSchemaVersions, SCHEMA_PULL_INTERVAL_MS.getLong(), SCHEMA_PULL_INTERVAL_MS.getLong(), TimeUnit.MILLISECONDS)
- : curTask);
- }
-
- private synchronized void pullUnreceivedSchemaVersions()
- {
- logger.debug("Pulling unreceived schema versions...");
- for (VersionInfo info : versionInfo.values())
- {
- if (info.wasReceived() || info.outstandingRequests.size() > 0)
- {
- logger.trace("Skipping pull of schema {} because it has been already recevied, or it is being received ({})", info.version, info);
- continue;
- }
-
- maybePullSchema(info);
- }
- }
-
- private synchronized Future<Void> maybePullSchema(VersionInfo info)
- {
- if (info.endpoints.isEmpty() || info.wasReceived() || !shouldPullSchema(info.version))
- {
- logger.trace("Not pulling schema {} because it was received, there is no endpoint to provide it, or we should not pull it ({})", info.version, info);
- return FINISHED_FUTURE;
- }
-
- if (info.outstandingRequests.size() >= maxOutstandingVersionRequests)
- {
- logger.trace("Not pulling schema {} because the number of outstanding requests has been exceeded ({} >= {})", info.version, info.outstandingRequests.size(), maxOutstandingVersionRequests);
- return FINISHED_FUTURE;
- }
-
- for (int i = 0, isize = info.requestQueue.size(); i < isize; i++)
- {
- InetAddressAndPort endpoint = info.requestQueue.remove();
- if (!info.endpoints.contains(endpoint))
- {
- logger.trace("Skipping request of schema {} from {} because the endpoint does not have that schema any longer", info.version, endpoint);
- continue;
- }
-
- if (shouldPullFromEndpoint(endpoint) && info.outstandingRequests.add(endpoint))
- {
- return scheduleSchemaPull(endpoint, info);
- }
- else
- {
- // return to queue
- logger.trace("Could not pull schema {} from {} - the request will be added back to the queue", info.version, endpoint);
- info.requestQueue.offer(endpoint);
- }
- }
-
- // no suitable endpoints were found, check again in a minute, the periodic task will pick it up
- return FINISHED_FUTURE;
- }
-
- synchronized Map<UUID, Set<InetAddressAndPort>> outstandingVersions()
- {
- HashMap<UUID, Set<InetAddressAndPort>> map = new HashMap<>();
- for (VersionInfo info : versionInfo.values())
- if (!info.wasReceived())
- map.put(info.version, ImmutableSet.copyOf(info.endpoints));
- return map;
- }
-
- @VisibleForTesting
- VersionInfo getVersionInfoUnsafe(UUID version)
- {
- return versionInfo.get(version);
- }
-
- private boolean shouldPullSchema(UUID version)
- {
- UUID localSchemaVersion = schemaVersion.get();
- if (localSchemaVersion == null)
- {
- logger.debug("Not pulling schema {} because the local schama version is not known yet", version);
- return false;
- }
-
- if (localSchemaVersion.equals(version))
- {
- logger.debug("Not pulling schema {} because it is the same as the local schema", version);
- return false;
- }
-
- return true;
- }
-
- private boolean shouldPullFromEndpoint(InetAddressAndPort endpoint)
- {
- if (endpoint.equals(FBUtilities.getBroadcastAddressAndPort()))
- {
- logger.trace("Not pulling schema from local endpoint");
- return false;
- }
-
- EndpointState state = gossiper.getEndpointStateForEndpoint(endpoint);
- if (state == null)
- {
- logger.trace("Not pulling schema from endpoint {} because its state is unknown", endpoint);
- return false;
- }
-
- VersionedValue releaseVersionValue = state.getApplicationState(ApplicationState.RELEASE_VERSION);
- if (releaseVersionValue == null)
- return false;
- final String releaseVersion = releaseVersionValue.value;
- final String ourMajorVersion = FBUtilities.getReleaseVersionMajor();
-
- if (!releaseVersion.startsWith(ourMajorVersion))
- {
- logger.debug("Not pulling schema from {} because release version in Gossip is not major version {}, it is {}",
- endpoint, ourMajorVersion, releaseVersion);
- return false;
- }
-
- if (!messagingService.versions.knows(endpoint))
- {
- logger.debug("Not pulling schema from {} because their messaging version is unknown", endpoint);
- return false;
- }
-
- if (messagingService.versions.getRaw(endpoint) != MessagingService.current_version)
- {
- logger.debug("Not pulling schema from {} because their schema format is incompatible", endpoint);
- return false;
- }
-
- if (gossiper.isGossipOnlyMember(endpoint))
- {
- logger.debug("Not pulling schema from {} because it's a gossip only member", endpoint);
- return false;
- }
- return true;
- }
-
- private boolean shouldPullImmediately(InetAddressAndPort endpoint, UUID version)
- {
- UUID localSchemaVersion = schemaVersion.get();
- if (SchemaConstants.emptyVersion.equals(localSchemaVersion) || getUptimeFn.getAsLong() < MIGRATION_DELAY_IN_MS)
- {
- // If we think we may be bootstrapping or have recently started, submit MigrationTask immediately
- logger.debug("Immediately submitting migration task for {}, " +
- "schema versions: local={}, remote={}",
- endpoint,
- DistributedSchema.schemaVersionToString(localSchemaVersion),
- DistributedSchema.schemaVersionToString(version));
- return true;
- }
- return false;
- }
-
- /**
- * If a previous schema update brought our version the same as the incoming schema, don't apply it
- */
- private synchronized boolean shouldApplySchemaFor(VersionInfo info)
- {
- if (info.wasReceived())
- return false;
- return !Objects.equals(schemaVersion.get(), info.version);
- }
-
- synchronized Future<Void> reportEndpointVersion(InetAddressAndPort endpoint, UUID version)
- {
- logger.debug("Reported schema {} at endpoint {}", version, endpoint);
- if (ignoredEndpoints.contains(endpoint) || IGNORED_VERSIONS.contains(version))
- {
- endpointVersions.remove(endpoint);
- removeEndpointFromVersion(endpoint, null);
- logger.debug("Discarding endpoint {} or schema {} because either endpoint or schema version were marked as ignored", endpoint, version);
- return FINISHED_FUTURE;
- }
-
- UUID current = endpointVersions.put(endpoint, version);
- if (current != null && current.equals(version))
- {
- logger.trace("Skipping report of schema {} from {} because we already know that", version, endpoint);
- return FINISHED_FUTURE;
- }
-
- VersionInfo info = versionInfo.computeIfAbsent(version, VersionInfo::new);
- if (Objects.equals(schemaVersion.get(), version))
- {
- info.markReceived();
- logger.trace("Schema {} from {} has been marked as recevied because it is equal the local schema", version, endpoint);
- }
- else
- {
- info.requestQueue.addFirst(endpoint);
- }
- info.endpoints.add(endpoint);
- logger.trace("Added endpoint {} to schema {}: {}", endpoint, info.version, info);
-
- // disassociate this endpoint from its (now) previous schema version
- removeEndpointFromVersion(endpoint, current);
-
- return maybePullSchema(info);
- }
-
- private synchronized void removeEndpointFromVersion(InetAddressAndPort endpoint, UUID version)
- {
- if (version == null)
- return;
-
- VersionInfo info = versionInfo.get(version);
-
- if (info == null)
- return;
-
- info.endpoints.remove(endpoint);
- logger.trace("Removed endpoint {} from schema {}: {}", endpoint, version, info);
- if (info.endpoints.isEmpty())
- {
- info.waitQueue.signalAll();
- versionInfo.remove(version);
- logger.trace("Removed schema info: {}", info);
- }
- }
-
- private void clearVersionsInfo()
- {
- Iterator<Map.Entry<UUID, VersionInfo>> it = versionInfo.entrySet().iterator();
- while (it.hasNext())
- {
- Map.Entry<UUID, VersionInfo> entry = it.next();
- it.remove();
- entry.getValue().waitQueue.signal();
- }
- }
-
- private void reportCurrentSchemaVersionOnEndpoint(InetAddressAndPort endpoint)
- {
- if (FBUtilities.getBroadcastAddressAndPort().equals(endpoint))
- {
- reportEndpointVersion(endpoint, schemaVersion.get());
- }
- else
- {
- EndpointState state = gossiper.getEndpointStateForEndpoint(endpoint);
- if (state != null)
- {
- UUID v = state.getSchemaVersion();
- if (v != null)
- {
- reportEndpointVersion(endpoint, v);
- }
- }
- }
- }
-
- /**
- * Resets the migration coordinator by notifying all waiting threads and removing all the existing version info.
- * Then, it is populated with the information about schema versions on different endpoints provided by Gossiper.
- * Each version is marked as unreceived so the migration coordinator will start pulling schemas from other nodes.
- */
- synchronized void reset()
- {
- logger.info("Resetting migration coordinator...");
-
- // clear all the managed information
- this.endpointVersions.clear();
- clearVersionsInfo();
-
- // now report again the versions we are aware of
- gossiper.getLiveMembers().forEach(this::reportCurrentSchemaVersionOnEndpoint);
- }
-
- synchronized void removeAndIgnoreEndpoint(InetAddressAndPort endpoint)
- {
- logger.debug("Removing and ignoring endpoint {}", endpoint);
- Preconditions.checkArgument(endpoint != null);
- // TODO The endpoint address is now ignored but when a node with the same address is added again later,
- // there will be no way to include it in schema synchronization other than restarting each other node
- // see https://issues.apache.org/jira/browse/CASSANDRA-17883 for details
- ignoredEndpoints.add(endpoint);
- Set<UUID> versions = ImmutableSet.copyOf(versionInfo.keySet());
- for (UUID version : versions)
- {
- removeEndpointFromVersion(endpoint, version);
- }
- }
-
- private Future<Void> scheduleSchemaPull(InetAddressAndPort endpoint, VersionInfo info)
- {
- FutureTask<Void> task = new FutureTask<>(() -> pullSchema(endpoint, new Callback(endpoint, info)));
-
- if (shouldPullImmediately(endpoint, info.version))
- {
- logger.debug("Pulling {} immediately from {}", info, endpoint);
- submitToMigrationIfNotShutdown(task);
- }
- else
- {
- logger.debug("Postponing pull of {} from {} for {}ms", info, endpoint, MIGRATION_DELAY_IN_MS);
- ScheduledExecutors.nonPeriodicTasks.schedule(() -> submitToMigrationIfNotShutdown(task), MIGRATION_DELAY_IN_MS, TimeUnit.MILLISECONDS);
- }
-
- return task;
- }
-
- void announce(UUID schemaVersion)
- {
- if (gossiper.isEnabled())
- gossiper.addLocalApplicationState(ApplicationState.SCHEMA, StorageService.instance.valueFactory.schema(schemaVersion));
- SchemaDiagnostics.versionAnnounced(Schema.instance);
- }
-
- private Future<?> submitToMigrationIfNotShutdown(Runnable task)
- {
- boolean skipped = false;
- try
- {
- if (executor.isShutdown() || executor.isTerminated())
- {
- skipped = true;
- return ImmediateFuture.success(null);
- }
- return executor.submit(task);
- }
- catch (RejectedExecutionException ex)
- {
- skipped = true;
- return ImmediateFuture.success(null);
- }
- finally
- {
- if (skipped)
- {
- logger.info("Skipped scheduled pulling schema from other nodes: the MIGRATION executor service has been shutdown.");
- }
- }
- }
-
- private class Callback implements RequestCallback<Collection<Mutation>>
- {
- final InetAddressAndPort endpoint;
- final VersionInfo info;
-
- public Callback(InetAddressAndPort endpoint, VersionInfo info)
- {
- this.endpoint = endpoint;
- this.info = info;
- }
-
- public void onFailure(InetAddressAndPort from, RequestFailureReason failureReason)
- {
- fail();
- }
-
- Future<Void> fail()
- {
- return pullComplete(endpoint, info, false);
- }
-
- public void onResponse(Message<Collection<Mutation>> message)
- {
- response(message.payload);
- }
-
- Future<Void> response(Collection<Mutation> mutations)
- {
- synchronized (info)
- {
- if (shouldApplySchemaFor(info))
- {
- try
- {
- schemaUpdateCallback.accept(endpoint, mutations);
- }
- catch (Exception e)
- {
- logger.error(String.format("Unable to merge schema from %s", endpoint), e);
- return fail();
- }
- }
- return pullComplete(endpoint, info, true);
- }
- }
-
- public boolean isLatencyForSnitch()
- {
- return false;
- }
- }
-
- private void pullSchema(InetAddressAndPort endpoint, RequestCallback<Collection<Mutation>> callback)
- {
- if (!gossiper.isAlive(endpoint))
- {
- noSpamLogger.warn("Can't send schema pull request: node {} is down.", endpoint);
- callback.onFailure(endpoint, RequestFailureReason.UNKNOWN);
- return;
- }
-
- // There is a chance that quite some time could have passed between now and the MM#maybeScheduleSchemaPull(),
- // potentially enough for the endpoint node to restart - which is an issue if it does restart upgraded, with
- // a higher major.
- if (!shouldPullFromEndpoint(endpoint))
- {
- logger.info("Skipped sending a migration request: node {} has a higher major version now.", endpoint);
- callback.onFailure(endpoint, RequestFailureReason.UNKNOWN);
- return;
- }
-
- logger.debug("Requesting schema from {}", endpoint);
- sendMigrationMessage(endpoint, callback);
- }
-
- private void sendMigrationMessage(InetAddressAndPort endpoint, RequestCallback<Collection<Mutation>> callback)
- {
- Message<NoPayload> message = Message.out(Verb.SCHEMA_PULL_REQ, NoPayload.noPayload);
- logger.info("Sending schema pull request to {}", endpoint);
- messagingService.sendWithCallback(message, endpoint, callback);
- }
-
- private synchronized Future<Void> pullComplete(InetAddressAndPort endpoint, VersionInfo info, boolean wasSuccessful)
- {
- if (wasSuccessful)
- info.markReceived();
-
- info.outstandingRequests.remove(endpoint);
- info.requestQueue.add(endpoint);
- return maybePullSchema(info);
- }
-
- /**
- * Wait until we've received schema responses for all versions we're aware of
- * @param waitMillis
- * @return true if response for all schemas were received, false if we timed out waiting
- */
- boolean awaitSchemaRequests(long waitMillis)
- {
- if (!FBUtilities.getBroadcastAddressAndPort().equals(InetAddressAndPort.getLoopbackAddress()))
- Gossiper.waitToSettle();
-
- if (versionInfo.isEmpty())
- logger.debug("Nothing in versionInfo - so no schemas to wait for");
-
- List<WaitQueue.Signal> signalList = null;
- try
- {
- synchronized (this)
- {
- signalList = new ArrayList<>(versionInfo.size());
- for (VersionInfo version : versionInfo.values())
- {
- if (version.wasReceived())
- continue;
-
- signalList.add(version.register());
- }
-
- if (signalList.isEmpty())
- return true;
- }
-
- long deadline = nanoTime() + TimeUnit.MILLISECONDS.toNanos(waitMillis);
- return signalList.stream().allMatch(signal -> signal.awaitUntilUninterruptibly(deadline));
- }
- finally
- {
- if (signalList != null)
- signalList.forEach(WaitQueue.Signal::cancel);
- }
- }
-
- Pair<Set<InetAddressAndPort>, Set<InetAddressAndPort>> pushSchemaMutations(Collection<Mutation> schemaMutations)
- {
- logger.debug("Pushing schema mutations: {}", schemaMutations);
- Set<InetAddressAndPort> schemaDestinationEndpoints = new HashSet<>();
- Set<InetAddressAndPort> schemaEndpointsIgnored = new HashSet<>();
- Message<Collection<Mutation>> message = Message.out(SCHEMA_PUSH_REQ, schemaMutations);
- for (InetAddressAndPort endpoint : gossiper.getLiveMembers())
- {
- if (shouldPushSchemaTo(endpoint))
- {
- logger.debug("Pushing schema mutations to {}: {}", endpoint, schemaMutations);
- messagingService.send(message, endpoint);
- schemaDestinationEndpoints.add(endpoint);
- }
- else
- {
- schemaEndpointsIgnored.add(endpoint);
- }
- }
-
- return Pair.create(schemaDestinationEndpoints, schemaEndpointsIgnored);
- }
-
- private boolean shouldPushSchemaTo(InetAddressAndPort endpoint)
- {
- // only push schema to nodes with known and equal versions
- return !endpoint.equals(FBUtilities.getBroadcastAddressAndPort())
- && messagingService.versions.knows(endpoint)
- && messagingService.versions.getRaw(endpoint) == MessagingService.current_version;
- }
-
-}
\ No newline at end of file
diff --git a/src/java/org/apache/cassandra/schema/OfflineSchemaUpdateHandler.java b/src/java/org/apache/cassandra/schema/OfflineSchemaUpdateHandler.java
deleted file mode 100644
index 16694b1f7c..0000000000
--- a/src/java/org/apache/cassandra/schema/OfflineSchemaUpdateHandler.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * 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.schema;
-
-import java.time.Duration;
-import java.util.UUID;
-import java.util.function.BiConsumer;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import org.apache.cassandra.schema.SchemaTransformation.SchemaTransformationResult;
-import org.apache.cassandra.utils.ByteArrayUtil;
-import org.apache.cassandra.utils.concurrent.Awaitable;
-import org.apache.cassandra.utils.concurrent.ImmediateFuture;
-
-/**
- * Update handler which works only in memory. It does not load or save the schema anywhere. It is used in client mode
- * applications.
- */
-public class OfflineSchemaUpdateHandler implements SchemaUpdateHandler
-{
- private static final Logger logger = LoggerFactory.getLogger(OfflineSchemaUpdateHandler.class);
-
- private final BiConsumer<SchemaTransformationResult, Boolean> updateCallback;
-
- private volatile DistributedSchema schema = DistributedSchema.EMPTY;
-
- public OfflineSchemaUpdateHandler(BiConsumer<SchemaTransformationResult, Boolean> updateCallback)
- {
- this.updateCallback = updateCallback;
- }
-
- @Override
- public void start()
- {
- // no-op
- }
-
- @Override
- public boolean waitUntilReady(Duration timeout)
- {
- return true;
- }
-
- @Override
- public synchronized SchemaTransformationResult apply(SchemaTransformation transformation, boolean local)
- {
- DistributedSchema before = schema;
- Keyspaces afterKeyspaces = transformation.apply(before.getKeyspaces());
- Keyspaces.KeyspacesDiff diff = Keyspaces.diff(before.getKeyspaces(), afterKeyspaces);
-
- if (diff.isEmpty())
- return new SchemaTransformationResult(before, before, diff);
-
- DistributedSchema after = new DistributedSchema(afterKeyspaces, UUID.nameUUIDFromBytes(ByteArrayUtil.bytes(afterKeyspaces.hashCode())));
- SchemaTransformationResult update = new SchemaTransformationResult(before, after, diff);
- this.schema = after;
- logger.debug("Schema updated: {}", update);
- updateCallback.accept(update, true);
-
- return update;
- }
-
- @Override
- public void reset(boolean local)
- {
- if (!local)
- throw new UnsupportedOperationException();
-
- apply(ignored -> SchemaKeyspace.fetchNonSystemKeyspaces(), local);
- }
-
- @Override
- public synchronized Awaitable clear()
- {
- this.schema = DistributedSchema.EMPTY;
- return ImmediateFuture.success(true);
- }
-}
diff --git a/src/java/org/apache/cassandra/schema/Schema.java b/src/java/org/apache/cassandra/schema/Schema.java
index 3cf1814a72..e56d4d6096 100644
--- a/src/java/org/apache/cassandra/schema/Schema.java
+++ b/src/java/org/apache/cassandra/schema/Schema.java
@@ -17,54 +17,30 @@
*/
package org.apache.cassandra.schema;
-import java.time.Duration;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
+import java.util.HashMap;
import java.util.Map;
-import java.util.Objects;
import java.util.Optional;
-import java.util.Set;
-import java.util.UUID;
-import java.util.concurrent.TimeUnit;
-import java.util.function.Consumer;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.concurrent.locks.LockSupport;
import java.util.function.Supplier;
-import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.MapDifference;
import org.apache.commons.lang3.ObjectUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.apache.cassandra.config.CassandraRelevantProperties;
-import org.apache.cassandra.cql3.functions.Function;
-import org.apache.cassandra.cql3.functions.FunctionName;
-import org.apache.cassandra.cql3.functions.UserFunction;
-import org.apache.cassandra.db.ColumnFamilyStore;
import org.apache.cassandra.db.Keyspace;
-import org.apache.cassandra.db.KeyspaceNotDefinedException;
import org.apache.cassandra.db.SystemKeyspace;
-import org.apache.cassandra.db.marshal.AbstractType;
import org.apache.cassandra.db.virtual.VirtualKeyspaceRegistry;
-import org.apache.cassandra.exceptions.ConfigurationException;
import org.apache.cassandra.exceptions.InvalidRequestException;
-import org.apache.cassandra.gms.Gossiper;
import org.apache.cassandra.io.sstable.Descriptor;
-import org.apache.cassandra.locator.InetAddressAndPort;
import org.apache.cassandra.locator.LocalStrategy;
-import org.apache.cassandra.schema.KeyspaceMetadata.KeyspaceDiff;
-import org.apache.cassandra.schema.Keyspaces.KeyspacesDiff;
-import org.apache.cassandra.schema.SchemaTransformation.SchemaTransformationResult;
-import org.apache.cassandra.service.PendingRangeCalculatorService;
-import org.apache.cassandra.service.StorageService;
-import org.apache.cassandra.utils.FBUtilities;
-import org.apache.cassandra.utils.concurrent.Awaitable;
-import org.apache.cassandra.utils.concurrent.LoadingMap;
+import org.apache.cassandra.tcm.ClusterMetadata;
+import org.apache.cassandra.tcm.ClusterMetadataService;
+import org.apache.cassandra.tcm.transformations.AlterSchema;
import static com.google.common.collect.Iterables.size;
-import static java.lang.String.format;
import static org.apache.cassandra.config.DatabaseDescriptor.isDaemonInitialized;
import static org.apache.cassandra.config.DatabaseDescriptor.isToolInitialized;
@@ -75,235 +51,129 @@ import static org.apache.cassandra.config.DatabaseDescriptor.isToolInitialized;
* This class should be the only entity used to query and manage schema. Internal details should not be access in
* production code (would be great if they were not accessed in the test code as well).
* <p>
- * TL;DR: All modifications are made using the implementation of {@link SchemaUpdateHandler} obtained from the provided
+ * TL;DR: All modifications are made using the implementation of SchemaUpdateHandle obtained from the provided
* factory. After each modification, the internally managed table metadata refs and keyspaces instances are updated and
* notifications are sent to the registered listeners.
* When the schema change is applied by the update handler (regardless it is initiated locally or received from outside),
* the registered callback is executed which performs the remaining updates for tables metadata refs and keyspace
- * instances (see {@link #mergeAndUpdateVersion(SchemaTransformationResult, boolean)}).
+ * instances (see mergeAndUpdateVersion(SchemaTransformationResult).
*/
-public class Schema implements SchemaProvider
+public final class Schema implements SchemaProvider
{
private static final Logger logger = LoggerFactory.getLogger(Schema.class);
- public static final Schema instance = new Schema();
+ public static final String FORCE_LOAD_LOCAL_KEYSPACES_PROP = "cassandra.schema.force_load_local_keyspaces";
+ private static final boolean FORCE_LOAD_LOCAL_KEYSPACES = Boolean.getBoolean(FORCE_LOAD_LOCAL_KEYSPACES_PROP);
- private volatile Keyspaces distributedKeyspaces = Keyspaces.none();
- private volatile Keyspaces distributedAndLocalKeyspaces;
+ public static SchemaProvider instance = initialize();
- private final Keyspaces localKeyspaces;
-
- private volatile TableMetadataRefCache tableMetadataRefCache = TableMetadataRefCache.EMPTY;
-
- // Keyspace objects, one per keyspace. Only one instance should ever exist for any given keyspace.
- // We operate on loading map because we need to achieve atomic initialization with at-most-once semantics for
- // loadFunction. Although it seems that this is a valid case for using ConcurrentHashMap.computeIfAbsent,
- // we should not use it because we have no knowledge about the loadFunction and in fact that load function may
- // do some nested calls to maybeAddKeyspaceInstance, also using different threads, and in a blocking manner.
- // This may lead to a deadlock. The documentation of ConcurrentHashMap says that manipulating other keys inside
- // the lambda passed to the computeIfAbsent method is prohibited.
- private final LoadingMap<String, Keyspace> keyspaceInstances = new LoadingMap<>();
+ private static Schema initialize()
+ {
+ Schema schema = new Schema();
- private volatile UUID version = SchemaConstants.emptyVersion;
+ if ((FORCE_LOAD_LOCAL_KEYSPACES || isDaemonInitialized() || isToolInitialized()))
+ {
+ for (KeyspaceMetadata ks : schema.localKeyspaces)
+ {
+ schema.localKeyspaceInstances.put(ks.name, new LazyVariable<>(() -> Keyspace.forSchema(ks.name, schema)));
+ }
+ }
+ return schema;
+ }
+ private final Keyspaces localKeyspaces;
private final SchemaChangeNotifier schemaChangeNotifier = new SchemaChangeNotifier();
-
- public final SchemaUpdateHandler updateHandler;
-
- private final boolean online;
+ private final Map<String, Supplier<Keyspace>> localKeyspaceInstances = new HashMap<>();
/**
* Initialize empty schema object and load the hardcoded system tables
*/
private Schema()
{
- this.online = isDaemonInitialized();
- this.localKeyspaces = (CassandraRelevantProperties.FORCE_LOAD_LOCAL_KEYSPACES.getBoolean() || isDaemonInitialized() || isToolInitialized())
- ? Keyspaces.of(SchemaKeyspace.metadata(), SystemKeyspace.metadata())
- : Keyspaces.none();
- this.distributedAndLocalKeyspaces = this.localKeyspaces;
-
- this.localKeyspaces.forEach(this::loadNew);
- this.updateHandler = SchemaUpdateHandlerFactoryProvider.instance.get().getSchemaUpdateHandler(online, this::mergeAndUpdateVersion);
- }
-
- @VisibleForTesting
- public Schema(boolean online, Keyspaces localKeyspaces, SchemaUpdateHandler updateHandler)
- {
- this.online = online;
- this.localKeyspaces = localKeyspaces;
- this.distributedAndLocalKeyspaces = this.localKeyspaces;
- this.updateHandler = updateHandler;
- }
-
- public void startSync()
- {
- logger.debug("Starting update handler");
- updateHandler.start();
- }
-
- public boolean waitUntilReady(Duration timeout)
- {
- logger.debug("Waiting for update handler to be ready...");
- return updateHandler.waitUntilReady(timeout);
- }
-
- /**
- * Load keyspaces definitions from local storage, see {@link SchemaUpdateHandler#reset(boolean)}.
- */
- public void loadFromDisk()
- {
- SchemaDiagnostics.schemaLoading(this);
- updateHandler.reset(true);
- SchemaDiagnostics.schemaLoaded(this);
+ this.localKeyspaces = Keyspaces.of(SchemaKeyspace.metadata(), SystemKeyspace.metadata());
}
/**
- * Update (or insert) new keyspace definition
+ * Add entries to system_schema.* for the hardcoded system keyspaces
*
- * @param ksm The metadata about keyspace
+ * See CASSANDRA-16856/16996. Make sure schema pulls are synchronized to prevent concurrent schema pull/writes
*/
- private synchronized void load(KeyspaceMetadata ksm)
- {
- Preconditions.checkArgument(!SchemaConstants.isLocalSystemKeyspace(ksm.name));
- KeyspaceMetadata previous = distributedKeyspaces.getNullable(ksm.name);
-
- if (previous == null)
- loadNew(ksm);
- else
- reload(previous, ksm);
-
- distributedKeyspaces = distributedKeyspaces.withAddedOrUpdated(ksm);
- distributedAndLocalKeyspaces = distributedAndLocalKeyspaces.withAddedOrUpdated(ksm);
- }
-
- private synchronized void loadNew(KeyspaceMetadata ksm)
- {
- this.tableMetadataRefCache = tableMetadataRefCache.withNewRefs(ksm);
-
- SchemaDiagnostics.metadataInitialized(this, ksm);
- }
-
- private synchronized void reload(KeyspaceMetadata previous, KeyspaceMetadata updated)
+ public synchronized void saveSystemKeyspace()
{
- Keyspace keyspace = getKeyspaceInstance(updated.name);
- if (null != keyspace)
- keyspace.setMetadata(updated);
-
- Tables.TablesDiff tablesDiff = Tables.diff(previous.tables, updated.tables);
- Views.ViewsDiff viewsDiff = Views.diff(previous.views, updated.views);
-
- MapDifference<String, TableMetadata> indexesDiff = previous.tables.indexesDiff(updated.tables);
-
- this.tableMetadataRefCache = tableMetadataRefCache.withUpdatedRefs(previous, updated);
-
- SchemaDiagnostics.metadataReloaded(this, previous, updated, tablesDiff, viewsDiff, indexesDiff);
+ SchemaKeyspace.saveSystemKeyspacesSchema();
}
+ @Override
public void registerListener(SchemaChangeListener listener)
{
schemaChangeNotifier.registerListener(listener);
}
- @SuppressWarnings("unused")
+ @Override
public void unregisterListener(SchemaChangeListener listener)
{
schemaChangeNotifier.unregisterListener(listener);
}
+ @Override
+ public SchemaChangeNotifier schemaChangeNotifier()
+ {
+ return schemaChangeNotifier;
+ }
+
/**
* Get keyspace instance by name
*
* @param keyspaceName The name of the keyspace
- *
- * @return Keyspace object or null if keyspace was not found
+ * @return Keyspace object or null if keyspace was not found, or if the keyspace has not completed construction yet
*/
@Override
public Keyspace getKeyspaceInstance(String keyspaceName)
{
- return keyspaceInstances.getIfReady(keyspaceName);
- }
-
- /**
- * Returns {@link ColumnFamilyStore} by the table identifier. Note that though, if called for {@link TableMetadata#id},
- * when metadata points to a secondary index table, the {@link TableMetadata#id} denotes the identifier of the main
- * table, not the index table. Thus, this method will return CFS of the main table rather than, probably expected,
- * CFS for the index backing table.
- */
- public ColumnFamilyStore getColumnFamilyStoreInstance(TableId id)
- {
- TableMetadata metadata = getTableMetadata(id);
- if (metadata == null)
- return null;
-
- Keyspace instance = getKeyspaceInstance(metadata.keyspace);
- if (instance == null)
+ if (SchemaConstants.isVirtualSystemKeyspace(keyspaceName))
return null;
-
- return instance.hasColumnFamilyStore(metadata.id)
- ? instance.getColumnFamilyStore(metadata.id)
- : null;
+ else if (SchemaConstants.isLocalSystemKeyspace(keyspaceName))
+ return localKeyspaceInstances.get(keyspaceName).get();
+ else
+ return ClusterMetadata.current().schema.getKeyspace(keyspaceName);
}
- @Override
- public Keyspace maybeAddKeyspaceInstance(String keyspaceName, Supplier<Keyspace> loadFunction)
+ public Keyspaces distributedAndLocalKeyspaces()
{
- return keyspaceInstances.blockingLoadIfAbsent(keyspaceName, loadFunction);
+ // TODO reimplement perf fix from CASSANDRA-18921
+ return Keyspaces.NONE.with(localKeyspaces).with(distributedKeyspaces());
}
- private Keyspace maybeRemoveKeyspaceInstance(String keyspaceName, Consumer<Keyspace> unloadFunction)
+ @Override
+ public Keyspaces distributedKeyspaces()
{
- try
- {
- return keyspaceInstances.blockingUnloadIfPresent(keyspaceName, unloadFunction);
- }
- catch (LoadingMap.UnloadExecutionException e)
- {
- throw new AssertionError("Failed to unload the keyspace " + keyspaceName, e);
- }
- }
+ ClusterMetadata metadata = ClusterMetadata.currentNullable();
+ if (metadata == null)
+ return Keyspaces.NONE;
- public Keyspaces distributedAndLocalKeyspaces()
- {
- return distributedAndLocalKeyspaces;
+ return metadata.schema.getKeyspaces();
}
- public Keyspaces distributedKeyspaces()
+ public Keyspaces localKeyspaces()
{
- return distributedKeyspaces;
+ return localKeyspaces;
}
- /**
- * Compute the largest gc grace seconds amongst all the tables
- * @return the largest gcgs.
- */
- public int largestGcgs()
+ public int getNumberOfTables()
{
- return distributedAndLocalKeyspaces().stream()
- .flatMap(ksm -> ksm.tables.stream())
- .mapToInt(tm -> tm.params.gcGraceSeconds)
- .max()
- .orElse(Integer.MIN_VALUE);
+ return distributedAndLocalKeyspaces().stream().mapToInt(k -> size(k.tablesAndViews())).sum();
}
- /**
- * Remove keyspace definition from system
- *
- * @param ksm The keyspace definition to remove
- */
- private synchronized void unload(KeyspaceMetadata ksm)
+ public Optional<TableMetadata> getIndexMetadata(String keyspace, String index)
{
- distributedKeyspaces = distributedKeyspaces.without(ksm.name);
- distributedAndLocalKeyspaces = distributedAndLocalKeyspaces.without(ksm.name);
-
- this.tableMetadataRefCache = tableMetadataRefCache.withRemovedRefs(ksm);
+ assert keyspace != null;
+ assert index != null;
- SchemaDiagnostics.metadataRemoved(this, ksm);
- }
+ KeyspaceMetadata ksm = getKeyspaceMetadata(keyspace);
+ if (ksm == null)
+ return Optional.empty();
- public int getNumberOfTables()
- {
- return distributedAndLocalKeyspaces().stream().mapToInt(k -> size(k.tablesAndViews())).sum();
+ return ksm.getIndexMetadata(index);
}
public ViewMetadata getView(String keyspaceName, String viewName)
@@ -324,19 +194,13 @@ public class Schema implements SchemaProvider
public KeyspaceMetadata getKeyspaceMetadata(String keyspaceName)
{
assert keyspaceName != null;
- KeyspaceMetadata keyspace = distributedAndLocalKeyspaces().getNullable(keyspaceName);
- return null != keyspace ? keyspace : VirtualKeyspaceRegistry.instance.getKeyspaceMetadataNullable(keyspaceName);
- }
+ KeyspaceMetadata keyspace;
+ if (SchemaConstants.isLocalSystemKeyspace(keyspaceName))
+ keyspace = localKeyspaces.getNullable(keyspaceName);
+ else
+ keyspace = distributedKeyspaces().getNullable(keyspaceName);
- /**
- * Returns all non-local keyspaces, that is, all but {@link SchemaConstants#LOCAL_SYSTEM_KEYSPACE_NAMES}
- * or virtual keyspaces.
- * @deprecated use {@link #distributedKeyspaces()}
- */
- @Deprecated
- public Keyspaces getNonSystemKeyspaces()
- {
- return distributedKeyspaces;
+ return null != keyspace ? keyspace : VirtualKeyspaceRegistry.instance.getKeyspaceMetadataNullable(keyspaceName);
}
/**
@@ -344,7 +208,7 @@ public class Schema implements SchemaProvider
*/
public Keyspaces getNonLocalStrategyKeyspaces()
{
- return distributedKeyspaces.filter(keyspace -> keyspace.params.replication.klass != LocalStrategy.class);
+ return distributedKeyspaces().filter(keyspace -> keyspace.params.replication.klass != LocalStrategy.class);
}
/**
@@ -353,7 +217,7 @@ public class Schema implements SchemaProvider
*/
public Keyspaces getUserKeyspaces()
{
- return distributedKeyspaces.without(SchemaConstants.REPLICATED_SYSTEM_KEYSPACE_NAMES);
+ return distributedKeyspaces().without(SchemaConstants.REPLICATED_SYSTEM_KEYSPACE_NAMES);
}
/**
@@ -365,7 +229,7 @@ public class Schema implements SchemaProvider
public Iterable<TableMetadata> getTablesAndViews(String keyspaceName)
{
Preconditions.checkNotNull(keyspaceName);
- KeyspaceMetadata ksm = ObjectUtils.getFirstNonNull(() -> distributedKeyspaces.getNullable(keyspaceName),
+ KeyspaceMetadata ksm = ObjectUtils.getFirstNonNull(() -> distributedKeyspaces().getNullable(keyspaceName),
() -> localKeyspaces.getNullable(keyspaceName));
Preconditions.checkNotNull(ksm, "Keyspace %s not found", keyspaceName);
return ksm.tablesAndViews();
@@ -376,50 +240,7 @@ public class Schema implements SchemaProvider
*/
public ImmutableSet<String> getKeyspaces()
{
- return distributedAndLocalKeyspaces().names();
- }
-
- public Keyspaces getLocalKeyspaces()
- {
- return localKeyspaces;
- }
-
- /* TableMetadata/Ref query/control methods */
-
- /**
- * Given a keyspace name and table/view name, get the table metadata
- * reference. If the keyspace name or table/view name is not present
- * this method returns null.
- *
- * @return TableMetadataRef object or null if it wasn't found
- */
- @Override
- public TableMetadataRef getTableMetadataRef(String keyspace, String table)
- {
- return tableMetadataRefCache.getTableMetadataRef(keyspace, table);
- }
-
- public TableMetadataRef getIndexTableMetadataRef(String keyspace, String index)
- {
- return tableMetadataRefCache.getIndexTableMetadataRef(keyspace, index);
- }
-
- /**
- * Get Table metadata by its identifier
- *
- * @param id table or view identifier
- * @return metadata about Table or View
- */
- @Override
- public TableMetadataRef getTableMetadataRef(TableId id)
- {
- return tableMetadataRefCache.getTableMetadataRef(id);
- }
-
- @Override
- public TableMetadataRef getTableMetadataRef(Descriptor descriptor)
- {
- return getTableMetadataRef(descriptor.ksname, descriptor.cfname);
+ return ImmutableSet.copyOf(distributedAndLocalKeyspaces().names());
}
/**
@@ -445,25 +266,10 @@ public class Schema implements SchemaProvider
@Override
public TableMetadata getTableMetadata(TableId id)
{
- return ObjectUtils.getFirstNonNull(() -> distributedKeyspaces.getTableOrViewNullable(id),
- () -> localKeyspaces.getTableOrViewNullable(id),
+ return ObjectUtils.getFirstNonNull(() -> localKeyspaces.getTableOrViewNullable(id),
+ () -> distributedKeyspaces().getTableOrViewNullable(id),
() -> VirtualKeyspaceRegistry.instance.getTableMetadataNullable(id));
- }
- public TableMetadata validateTable(String keyspaceName, String tableName)
- {
- if (tableName.isEmpty())
- throw new InvalidRequestException("non-empty table is required");
-
- KeyspaceMetadata keyspace = getKeyspaceMetadata(keyspaceName);
- if (keyspace == null)
- throw new KeyspaceNotDefinedException(format("keyspace %s does not exist", keyspaceName));
-
- TableMetadata metadata = keyspace.getTableOrViewNullable(tableName);
- if (metadata == null)
- throw new InvalidRequestException(format("table %s does not exist", tableName));
-
- return metadata;
}
public TableMetadata getTableMetadata(Descriptor descriptor)
@@ -471,325 +277,108 @@ public class Schema implements SchemaProvider
return getTableMetadata(descriptor.ksname, descriptor.cfname);
}
- /* Function helpers */
-
- /**
- * Get all user-defined function overloads with the specified name.
- *
- * @param name fully qualified function name
- * @return an empty list if the keyspace or the function name are not found;
- * a non-empty collection of {@link UserFunction} otherwise
- */
- public Collection<UserFunction> getUserFunctions(FunctionName name)
- {
- if (!name.hasKeyspace())
- throw new IllegalArgumentException(String.format("Function name must be fully qualified: got %s", name));
-
- KeyspaceMetadata ksm = getKeyspaceMetadata(name.keyspace);
- return ksm == null
- ? Collections.emptyList()
- : ksm.userFunctions.get(name);
- }
-
- /**
- * Find the function with the specified name and arguments.
- *
- * @param name fully qualified function name
- * @param argTypes function argument types
- * @return an empty {@link Optional} if the keyspace or the function name are not found;
- * a non-empty optional of {@link Function} otherwise
- */
- public Optional<UserFunction> findUserFunction(FunctionName name, List<AbstractType<?>> argTypes)
- {
- if (!name.hasKeyspace())
- throw new IllegalArgumentException(String.format("Function name must be fully quallified: got %s", name));
-
- return Optional.ofNullable(getKeyspaceMetadata(name.keyspace))
- .flatMap(ksm -> ksm.userFunctions.find(name, argTypes));
- }
-
- /* Version control */
-
- /**
- * @return current schema version
- */
- public UUID getVersion()
- {
- return version;
- }
-
- /**
- * Checks whether the given schema version is the same as the current local schema.
- */
- public boolean isSameVersion(UUID schemaVersion)
- {
- return schemaVersion != null && schemaVersion.equals(version);
- }
-
- /**
- * Checks whether the current schema is empty.
- */
- public boolean isEmpty()
- {
- return SchemaConstants.emptyVersion.equals(version);
- }
-
- /**
- * Read schema from system keyspace and calculate MD5 digest of every row, resulting digest
- * will be converted into UUID which would act as content-based version of the schema.
- *
- * See CASSANDRA-16856/16996. Make sure schema pulls are synchronized to prevent concurrent schema pull/writes
- */
- private synchronized void updateVersion(UUID version)
- {
- this.version = version;
- SchemaDiagnostics.versionUpdated(this);
- }
-
- /**
- * When we receive {@link SchemaTransformationResult} in a callback invocation, the transformation result includes
- * pre-transformation and post-transformation schema metadata and versions, and a diff between them. Basically
- * we expect that the local image of the schema metadata ({@link #distributedKeyspaces}) and version ({@link #version})
- * are the same as pre-transformation. However, it might not always be true because some changes might not be
- * applied completely due to some errors. This methods is to emit warning in such case and recalculate diff so that
- * it contains the changes between the local schema image ({@link #distributedKeyspaces} and the post-transformation
- * schema. That recalculation allows the following updates in the callback to recover the schema.
- *
- * @param result the incoming transformation result
- * @return recalculated transformation result if needed, otherwise the provided incoming result
- */
- private synchronized SchemaTransformationResult localDiff(SchemaTransformationResult result)
- {
- Keyspaces localBefore = distributedKeyspaces;
- UUID localVersion = version;
- boolean needNewDiff = false;
-
- if (!Objects.equals(localBefore, result.before.getKeyspaces()))
- {
- logger.info("Schema was different to what we expected: {}", Keyspaces.diff(result.before.getKeyspaces(), localBefore));
- needNewDiff = true;
- }
-
- if (!Objects.equals(localVersion, result.before.getVersion()))
+ @Override
+ public ClusterMetadata submit(SchemaTransformation transformation)
+ {
+ logger.debug("Submitting schema transformation {}", transformation);
+
+ // result of this execution can be either a complete failure/timeout, or a success, but together with a log of
+ // operations that have to be applied before we can do anything
+ // TODO perhaps we should change the retry predicate to be a bit smarter and examine the state of metadata
+ // when retrying.
+ return ClusterMetadataService.instance().commit(new AlterSchema(transformation, this),
+ (metadata) -> true,
+ (metadata) -> metadata,
+ (metadata, reason) -> {
+ throw new InvalidRequestException(reason);
+ });
+ }
+
+ // We need to lazy-initialize schema for test purposes: since column families are initialized
+ // eagerly, if local schema initialization is attempted before commit log instance is started,
+ // cf initialization will fail to grab a current commit log position.
+ //
+ // This could be further improved by providing a special Schema instance for tests, or
+ // using supplier with precomputed value for regular code path, and lazy variable for tests.
+ //
+ // TODO: it _might_ be useful to avoid eagerly initializing distributed keyspaces, especially in
+ // when the number of tables is large.
+ private final static class LazyVariable<T> implements Supplier<T>
+ {
+ private final AtomicReference<Object> ref;
+ private final Supplier<T> run;
+
+ private LazyVariable(Supplier<T> run)
{
- logger.info("Schema version was different to what we expected: {} != {}", result.before.getVersion(), localVersion);
- needNewDiff = true;
+ this.ref = new AtomicReference<>(null);
+ this.run = run;
}
- if (needNewDiff)
- return new SchemaTransformationResult(new DistributedSchema(localBefore, localVersion),
- result.after,
- Keyspaces.diff(localBefore, result.after.getKeyspaces()));
-
- return result;
- }
-
- /*
- * Reload schema from local disk. Useful if a user made changes to schema tables by hand, or has suspicion that
- * in-memory representation got out of sync somehow with what's on disk.
- */
- public void reloadSchemaAndAnnounceVersion()
- {
- updateHandler.reset(true);
- }
-
- /**
- * Merge remote schema in form of mutations with local and mutate ks/cf metadata objects
- * (which also involves fs operations on add/drop ks/cf)
- *
- * @throws ConfigurationException If one of metadata attributes has invalid value
- */
- @VisibleForTesting
- public synchronized void mergeAndUpdateVersion(SchemaTransformationResult result, boolean dropData)
- {
- result = localDiff(result);
- schemaChangeNotifier.notifyPreChanges(result);
- merge(result.diff, dropData);
- updateVersion(result.after.getVersion());
- if (online)
- SystemKeyspace.updateSchemaVersion(result.after.getVersion());
- }
-
- public SchemaTransformationResult transform(SchemaTransformation transformation)
- {
- return transform(transformation, false);
- }
-
- public SchemaTransformationResult transform(SchemaTransformation transformation, boolean local)
- {
- return updateHandler.apply(transformation, local);
- }
-
- /**
- * Clear all locally stored schema information and fetch schema from another node.
- * Called by user (via JMX) who wants to get rid of schema disagreement.
- */
- public void resetLocalSchema()
- {
- logger.debug("Clearing local schema...");
-
- if (Gossiper.instance.getLiveMembers().stream().allMatch(ep -> FBUtilities.getBroadcastAddressAndPort().equals(ep)))
- throw new InvalidRequestException("Cannot reset local schema when there are no other live nodes");
-
- Awaitable clearCompletion = updateHandler.clear();
- try
+ public T get()
{
- if (!clearCompletion.await(StorageService.SCHEMA_DELAY_MILLIS, TimeUnit.MILLISECONDS))
+ Object v = ref.get();
+ if (v == null)
{
- throw new RuntimeException("Schema reset failed - no schema received from other nodes");
+ Sentinel sentinel = new Sentinel();
+
+ if (ref.compareAndSet(null, sentinel))
+ {
+ try
+ {
+ v = run.get();
+ }
+ catch (Throwable t)
+ {
+ ref.compareAndSet(sentinel, null);
+ throw t;
+ }
+ boolean result = ref.compareAndSet(sentinel, v);
+ assert result;
+ }
+ else
+ {
+ return get();
+ }
}
- }
- catch (InterruptedException e)
- {
- Thread.currentThread().interrupt();
- throw new RuntimeException("Failed to reset schema - the thread has been interrupted");
- }
- SchemaDiagnostics.schemaCleared(this);
- logger.info("Local schema reset completed");
- }
-
- private void merge(KeyspacesDiff diff, boolean removeData)
- {
- diff.dropped.forEach(keyspace -> dropKeyspace(keyspace, removeData));
- diff.created.forEach(this::createKeyspace);
- diff.altered.forEach(delta -> alterKeyspace(delta, removeData));
- }
-
- private void alterKeyspace(KeyspaceDiff delta, boolean dropData)
- {
- SchemaDiagnostics.keyspaceAltering(this, delta);
-
- boolean initialized = Keyspace.isInitialized();
-
- Keyspace keyspace = initialized ? getKeyspaceInstance(delta.before.name) : null;
- if (initialized)
- {
- assert keyspace != null;
- assert delta.before.name.equals(delta.after.name);
-
- // drop tables and views
- delta.views.dropped.forEach(v -> dropView(keyspace, v, dropData));
- delta.tables.dropped.forEach(t -> dropTable(keyspace, t, dropData));
- }
-
- load(delta.after);
-
- if (initialized)
- {
- // add tables and views
- delta.tables.created.forEach(t -> createTable(keyspace, t));
- delta.views.created.forEach(v -> createView(keyspace, v));
-
- // update tables and views
- delta.tables.altered.forEach(diff -> alterTable(keyspace, diff.after));
- delta.views.altered.forEach(diff -> alterView(keyspace, diff.after));
-
- // deal with all added, and altered views
- Keyspace.open(delta.after.name, this, true).viewManager.reload(true);
- }
-
- schemaChangeNotifier.notifyKeyspaceAltered(delta, dropData);
- SchemaDiagnostics.keyspaceAltered(this, delta);
- }
-
- private void createKeyspace(KeyspaceMetadata keyspace)
- {
- SchemaDiagnostics.keyspaceCreating(this, keyspace);
- load(keyspace);
- if (Keyspace.isInitialized())
- {
- Keyspace.open(keyspace.name, this, true);
- }
-
- schemaChangeNotifier.notifyKeyspaceCreated(keyspace);
- SchemaDiagnostics.keyspaceCreated(this, keyspace);
-
- // If keyspace has been added, we need to recalculate pending ranges to make sure
- // we send mutations to the correct set of bootstrapping nodes. Refer CASSANDRA-15433.
- if (keyspace.params.replication.klass != LocalStrategy.class && Keyspace.isInitialized())
- {
- PendingRangeCalculatorService.calculatePendingRanges(Keyspace.open(keyspace.name, this, true).getReplicationStrategy(), keyspace.name);
+ else
+ {
+ while (v instanceof Sentinel)
+ {
+ if (((Sentinel) v).thread == Thread.currentThread())
+ {
+ throw new RuntimeException("Looks like we have a deadlock. Check sentinel for the original call.",
+ ((Sentinel) v).throwable);
+ }
+ v = ref.get();
+ LockSupport.parkNanos(100);
+ }
+ }
+ return (T) v;
}
}
- private void dropKeyspace(KeyspaceMetadata keyspaceMetadata, boolean dropData)
+ private static final class Sentinel
{
- SchemaDiagnostics.keyspaceDropping(this, keyspaceMetadata);
+ private final Thread thread;
+ private final Throwable throwable;
- boolean initialized = Keyspace.isInitialized();
- Keyspace keyspace = initialized ? Keyspace.open(keyspaceMetadata.name, this, false) : null;
- if (initialized)
+ private Sentinel()
{
- if (keyspace == null)
- return;
-
- keyspaceMetadata.views.forEach(v -> dropView(keyspace, v, dropData));
- keyspaceMetadata.tables.forEach(t -> dropTable(keyspace, t, dropData));
-
- // remove the keyspace from the static instances
- Keyspace unloadedKeyspace = maybeRemoveKeyspaceInstance(keyspaceMetadata.name, ks -> {
- ks.unload(dropData);
- unload(keyspaceMetadata);
+ this(Thread.currentThread(), new RuntimeException("Sentinel call") {
+ private final StackTraceElement[] trace = Thread.currentThread().getStackTrace();
+
+ @Override
+ public StackTraceElement[] getStackTrace()
+ {
+ return trace;
+ }
});
- assert unloadedKeyspace == keyspace;
-
- Keyspace.writeOrder.awaitNewBarrier();
}
- else
+
+ private Sentinel(Thread thread, Throwable throwable)
{
- unload(keyspaceMetadata);
+ this.thread = thread;
+ this.throwable = throwable;
}
-
- schemaChangeNotifier.notifyKeyspaceDropped(keyspaceMetadata, dropData);
- SchemaDiagnostics.keyspaceDropped(this, keyspaceMetadata);
- }
-
- private void dropView(Keyspace keyspace, ViewMetadata metadata, boolean dropData)
- {
- keyspace.viewManager.dropView(metadata.name());
- dropTable(keyspace, metadata.metadata, dropData);
- }
-
- private void dropTable(Keyspace keyspace, TableMetadata metadata, boolean dropData)
- {
- SchemaDiagnostics.tableDropping(this, metadata);
- keyspace.dropCf(metadata.id, dropData);
- SchemaDiagnostics.tableDropped(this, metadata);
}
-
- private void createTable(Keyspace keyspace, TableMetadata table)
- {
- SchemaDiagnostics.tableCreating(this, table);
- keyspace.initCf(tableMetadataRefCache.getTableMetadataRef(table.id), true);
- SchemaDiagnostics.tableCreated(this, table);
- }
-
- private void createView(Keyspace keyspace, ViewMetadata view)
- {
- SchemaDiagnostics.tableCreating(this, view.metadata);
- keyspace.initCf(tableMetadataRefCache.getTableMetadataRef(view.metadata.id), true);
- SchemaDiagnostics.tableCreated(this, view.metadata);
- }
-
- private void alterTable(Keyspace keyspace, TableMetadata updated)
- {
- SchemaDiagnostics.tableAltering(this, updated);
- keyspace.getColumnFamilyStore(updated.name).reload();
- SchemaDiagnostics.tableAltered(this, updated);
- }
-
- private void alterView(Keyspace keyspace, ViewMetadata updated)
- {
- SchemaDiagnostics.tableAltering(this, updated.metadata);
- keyspace.getColumnFamilyStore(updated.name()).reload();
- SchemaDiagnostics.tableAltered(this, updated.metadata);
- }
-
- public Map<UUID, Set<InetAddressAndPort>> getOutstandingSchemaVersions()
- {
- return updateHandler instanceof DefaultSchemaUpdateHandler
- ? ((DefaultSchemaUpdateHandler) updateHandler).getOutstandingSchemaVersions()
- : Collections.emptyMap();
- }
-
}
\ No newline at end of file
diff --git a/src/java/org/apache/cassandra/schema/SchemaDiagnostics.java b/src/java/org/apache/cassandra/schema/SchemaDiagnostics.java
index 29243039da..f7d0ed939f 100644
--- a/src/java/org/apache/cassandra/schema/SchemaDiagnostics.java
+++ b/src/java/org/apache/cassandra/schema/SchemaDiagnostics.java
@@ -23,7 +23,8 @@ import com.google.common.collect.MapDifference;
import org.apache.cassandra.diag.DiagnosticEventService;
import org.apache.cassandra.schema.SchemaEvent.SchemaEventType;
-final class SchemaDiagnostics
+// TODO: re-wire schema diagnostics events
+public final class SchemaDiagnostics
{
private static final DiagnosticEventService service = DiagnosticEventService.instance();
@@ -31,139 +32,140 @@ final class SchemaDiagnostics
{
}
- static void metadataInitialized(Schema schema, KeyspaceMetadata ksmUpdate)
+ static void metadataInitialized(SchemaProvider schema, KeyspaceMetadata ksmUpdate)
{
if (isEnabled(SchemaEventType.KS_METADATA_LOADED))
service.publish(new SchemaEvent(SchemaEventType.KS_METADATA_LOADED, schema, ksmUpdate, null, null, null, null, null, null));
}
- static void metadataReloaded(Schema schema, KeyspaceMetadata previous, KeyspaceMetadata ksmUpdate, Tables.TablesDiff tablesDiff, Views.ViewsDiff viewsDiff, MapDifference<String,TableMetadata> indexesDiff)
+ static void metadataReloaded(SchemaProvider schema, KeyspaceMetadata previous, KeyspaceMetadata ksmUpdate, Tables.TablesDiff tablesDiff, Views.ViewsDiff viewsDiff, MapDifference<String,TableMetadata> indexesDiff)
{
if (isEnabled(SchemaEventType.KS_METADATA_RELOADED))
service.publish(new SchemaEvent(SchemaEventType.KS_METADATA_RELOADED, schema, ksmUpdate, previous,
null, null, tablesDiff, viewsDiff, indexesDiff));
}
- static void metadataRemoved(Schema schema, KeyspaceMetadata ksmUpdate)
+ static void metadataRemoved(SchemaProvider schema, KeyspaceMetadata ksmUpdate)
{
if (isEnabled(SchemaEventType.KS_METADATA_REMOVED))
service.publish(new SchemaEvent(SchemaEventType.KS_METADATA_REMOVED, schema, ksmUpdate,
null, null, null, null, null, null));
}
- static void versionUpdated(Schema schema)
+ public static void versionUpdated(SchemaProvider schema)
{
if (isEnabled(SchemaEventType.VERSION_UPDATED))
service.publish(new SchemaEvent(SchemaEventType.VERSION_UPDATED, schema,
null, null, null, null, null, null, null));
}
- static void keyspaceCreating(Schema schema, KeyspaceMetadata keyspace)
+ static void keyspaceCreating(SchemaProvider schema, KeyspaceMetadata keyspace)
{
if (isEnabled(SchemaEventType.KS_CREATING))
service.publish(new SchemaEvent(SchemaEventType.KS_CREATING, schema, keyspace,
null, null, null, null, null, null));
}
- static void keyspaceCreated(Schema schema, KeyspaceMetadata keyspace)
+ static void keyspaceCreated(SchemaProvider schema, KeyspaceMetadata keyspace)
{
if (isEnabled(SchemaEventType.KS_CREATED))
service.publish(new SchemaEvent(SchemaEventType.KS_CREATED, schema, keyspace,
null, null, null, null, null, null));
}
- static void keyspaceAltering(Schema schema, KeyspaceMetadata.KeyspaceDiff delta)
+ static void keyspaceAltering(SchemaProvider schema, KeyspaceMetadata.KeyspaceDiff delta)
{
if (isEnabled(SchemaEventType.KS_ALTERING))
service.publish(new SchemaEvent(SchemaEventType.KS_ALTERING, schema, delta.after,
delta.before, delta, null, null, null, null));
}
- static void keyspaceAltered(Schema schema, KeyspaceMetadata.KeyspaceDiff delta)
+ static void keyspaceAltered(SchemaProvider schema, KeyspaceMetadata.KeyspaceDiff delta)
{
if (isEnabled(SchemaEventType.KS_ALTERED))
service.publish(new SchemaEvent(SchemaEventType.KS_ALTERED, schema, delta.after,
delta.before, delta, null, null, null, null));
}
- static void keyspaceDropping(Schema schema, KeyspaceMetadata keyspace)
+ static void keyspaceDropping(SchemaProvider schema, KeyspaceMetadata keyspace)
{
if (isEnabled(SchemaEventType.KS_DROPPING))
service.publish(new SchemaEvent(SchemaEventType.KS_DROPPING, schema, keyspace,
null, null, null, null, null, null));
}
- static void keyspaceDropped(Schema schema, KeyspaceMetadata keyspace)
+ static void keyspaceDropped(SchemaProvider schema, KeyspaceMetadata keyspace)
{
if (isEnabled(SchemaEventType.KS_DROPPED))
service.publish(new SchemaEvent(SchemaEventType.KS_DROPPED, schema, keyspace,
null, null, null, null, null, null));
}
- static void schemaLoading(Schema schema)
+ static void schemaLoading(SchemaProvider schema)
{
if (isEnabled(SchemaEventType.SCHEMATA_LOADING))
service.publish(new SchemaEvent(SchemaEventType.SCHEMATA_LOADING, schema, null,
null, null, null, null, null, null));
}
- static void schemaLoaded(Schema schema)
+ // Looks like the author of the patch has meant "schemas" or "schemata", plural. So unless we rename "schemata" everywhere, i'd just leave it as-is
+ static void schemaLoaded(SchemaProvider schema)
{
if (isEnabled(SchemaEventType.SCHEMATA_LOADED))
service.publish(new SchemaEvent(SchemaEventType.SCHEMATA_LOADED, schema, null,
null, null, null, null, null, null));
}
- static void versionAnnounced(Schema schema)
+ static void versionAnnounced(SchemaProvider schema)
{
if (isEnabled(SchemaEventType.VERSION_ANOUNCED))
service.publish(new SchemaEvent(SchemaEventType.VERSION_ANOUNCED, schema, null,
null, null, null, null, null, null));
}
- static void schemaCleared(Schema schema)
+ static void schemaCleared(SchemaProvider schema)
{
if (isEnabled(SchemaEventType.SCHEMATA_CLEARED))
service.publish(new SchemaEvent(SchemaEventType.SCHEMATA_CLEARED, schema, null,
null, null, null, null, null, null));
}
- static void tableCreating(Schema schema, TableMetadata table)
+ static void tableCreating(SchemaProvider schema, TableMetadata table)
{
if (isEnabled(SchemaEventType.TABLE_CREATING))
service.publish(new SchemaEvent(SchemaEventType.TABLE_CREATING, schema, null,
null, null, table, null, null, null));
}
- static void tableCreated(Schema schema, TableMetadata table)
+ static void tableCreated(SchemaProvider schema, TableMetadata table)
{
if (isEnabled(SchemaEventType.TABLE_CREATED))
service.publish(new SchemaEvent(SchemaEventType.TABLE_CREATED, schema, null,
null, null, table, null, null, null));
}
- static void tableAltering(Schema schema, TableMetadata table)
+ static void tableAltering(SchemaProvider schema, TableMetadata table)
{
if (isEnabled(SchemaEventType.TABLE_ALTERING))
service.publish(new SchemaEvent(SchemaEventType.TABLE_ALTERING, schema, null,
null, null, table, null, null, null));
}
- static void tableAltered(Schema schema, TableMetadata table)
+ static void tableAltered(SchemaProvider schema, TableMetadata table)
{
if (isEnabled(SchemaEventType.TABLE_ALTERED))
service.publish(new SchemaEvent(SchemaEventType.TABLE_ALTERED, schema, null,
null, null, table, null, null, null));
}
- static void tableDropping(Schema schema, TableMetadata table)
+ static void tableDropping(SchemaProvider schema, TableMetadata table)
{
if (isEnabled(SchemaEventType.TABLE_DROPPING))
service.publish(new SchemaEvent(SchemaEventType.TABLE_DROPPING, schema, null,
null, null, table, null, null, null));
}
- static void tableDropped(Schema schema, TableMetadata table)
+ static void tableDropped(SchemaProvider schema, TableMetadata table)
{
if (isEnabled(SchemaEventType.TABLE_DROPPED))
service.publish(new SchemaEvent(SchemaEventType.TABLE_DROPPED, schema, null,
diff --git a/src/java/org/apache/cassandra/schema/SchemaEvent.java b/src/java/org/apache/cassandra/schema/SchemaEvent.java
index 8cdd0c8c4b..e65f0a4db4 100644
--- a/src/java/org/apache/cassandra/schema/SchemaEvent.java
+++ b/src/java/org/apache/cassandra/schema/SchemaEvent.java
@@ -30,6 +30,7 @@ import javax.annotation.Nullable;
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.collect.MapDifference;
@@ -87,7 +88,7 @@ public final class SchemaEvent extends DiagnosticEvent
SCHEMATA_CLEARED
}
- SchemaEvent(SchemaEventType type, Schema schema, @Nullable KeyspaceMetadata ksUpdate,
+ SchemaEvent(SchemaEventType type, SchemaProvider schema, @Nullable KeyspaceMetadata ksUpdate,
@Nullable KeyspaceMetadata previous, @Nullable KeyspaceMetadata.KeyspaceDiff ksDiff,
@Nullable TableMetadata tableUpdate, @Nullable Tables.TablesDiff tablesDiff,
@Nullable Views.ViewsDiff viewsDiff, @Nullable MapDifference<String, TableMetadata> indexesDiff)
@@ -101,9 +102,10 @@ public final class SchemaEvent extends DiagnosticEvent
this.viewsDiff = viewsDiff;
this.indexesDiff = indexesDiff;
- this.keyspaces = schema.distributedAndLocalKeyspaces().names();
- this.nonSystemKeyspaces = schema.distributedKeyspaces().names();
- this.userKeyspaces = schema.getUserKeyspaces().names();
+ // todo: no need to copy here
+ this.keyspaces = ImmutableSet.copyOf(schema.distributedAndLocalKeyspaces().names());
+ this.nonSystemKeyspaces = ImmutableSet.copyOf(schema.distributedKeyspaces().names());
+ this.userKeyspaces = ImmutableSet.copyOf(schema.getUserKeyspaces().names());
this.numberOfTables = schema.getNumberOfTables();
this.version = schema.getVersion();
diff --git a/src/java/org/apache/cassandra/schema/SchemaKeyspace.java b/src/java/org/apache/cassandra/schema/SchemaKeyspace.java
index ee33c00096..7110233748 100644
--- a/src/java/org/apache/cassandra/schema/SchemaKeyspace.java
+++ b/src/java/org/apache/cassandra/schema/SchemaKeyspace.java
@@ -65,7 +65,7 @@ import static org.apache.cassandra.utils.Simulate.With.GLOBAL_CLOCK;
* system_schema.* tables and methods for manipulating them.
*
* Please notice this class is _not_ thread safe and all methods which reads or updates the data in schema keyspace
- * should be accessed only from the implementation of {@link SchemaUpdateHandler} in synchronized blocks.
+ * should be accessed only from the implementation of {SchemaUpdateHandler} in synchronized blocks.
*/
@NotThreadSafe
public final class SchemaKeyspace
@@ -262,7 +262,7 @@ public final class SchemaKeyspace
return KeyspaceMetadata.create(SchemaConstants.SCHEMA_KEYSPACE_NAME, KeyspaceParams.local(), org.apache.cassandra.schema.Tables.of(ALL_TABLE_METADATA));
}
- static Collection<Mutation> convertSchemaDiffToMutations(KeyspacesDiff diff, long timestamp)
+ public static Collection<Mutation> convertSchemaDiffToMutations(KeyspacesDiff diff, long timestamp)
{
Map<String, Mutation> mutations = new HashMap<>();
@@ -814,7 +814,7 @@ public final class SchemaKeyspace
.add("argument_names", function.argNames().stream().map((c) -> bbToString(c.bytes)).collect(toList()));
}
- private static String bbToString(ByteBuffer bb)
+ public static String bbToString(ByteBuffer bb)
{
try
{
@@ -862,14 +862,14 @@ public final class SchemaKeyspace
{
String query = format("SELECT keyspace_name FROM %s.%s", SchemaConstants.SCHEMA_KEYSPACE_NAME, KEYSPACES);
- Keyspaces.Builder keyspaces = org.apache.cassandra.schema.Keyspaces.builder();
+ Keyspaces keyspaces = org.apache.cassandra.schema.Keyspaces.NONE;
for (UntypedResultSet.Row row : query(query))
{
String keyspaceName = row.getString("keyspace_name");
if (!excludedKeyspaceNames.contains(keyspaceName))
- keyspaces.add(fetchKeyspace(keyspaceName));
+ keyspaces = keyspaces.with(fetchKeyspace(keyspaceName));
}
- return keyspaces.build();
+ return keyspaces;
}
private static KeyspaceMetadata fetchKeyspace(String keyspaceName)
@@ -1276,7 +1276,7 @@ public final class SchemaKeyspace
.collect(toSet());
}
- static void applyChanges(Collection<Mutation> mutations)
+ public static void applyChanges(Collection<Mutation> mutations)
{
mutations.forEach(Mutation::apply);
if (SchemaKeyspace.FLUSH_SCHEMA_TABLES)
@@ -1291,10 +1291,10 @@ public final class SchemaKeyspace
*/
String query = format("SELECT keyspace_name FROM %s.%s WHERE keyspace_name IN ?", SchemaConstants.SCHEMA_KEYSPACE_NAME, KEYSPACES);
- Keyspaces.Builder keyspaces = org.apache.cassandra.schema.Keyspaces.builder();
+ Keyspaces keyspaces = org.apache.cassandra.schema.Keyspaces.NONE;
for (UntypedResultSet.Row row : query(query, new ArrayList<>(toFetch)))
- keyspaces.add(fetchKeyspace(row.getString("keyspace_name")));
- return keyspaces.build();
+ keyspaces = keyspaces.with(fetchKeyspace(row.getString("keyspace_name")));
+ return keyspaces;
}
@VisibleForTesting
diff --git a/src/java/org/apache/cassandra/schema/SchemaProvider.java b/src/java/org/apache/cassandra/schema/SchemaProvider.java
index cbad42e530..d16818a1f6 100644
--- a/src/java/org/apache/cassandra/schema/SchemaProvider.java
+++ b/src/java/org/apache/cassandra/schema/SchemaProvider.java
@@ -18,35 +18,145 @@
package org.apache.cassandra.schema;
-import java.util.function.Supplier;
+import java.util.*;
import javax.annotation.Nullable;
+import com.google.common.base.Preconditions;
+
+import org.apache.cassandra.cql3.functions.Function;
+import org.apache.cassandra.cql3.functions.FunctionName;
+import org.apache.cassandra.cql3.functions.UserFunction;
+import org.apache.cassandra.db.ColumnFamilyStore;
import org.apache.cassandra.db.Keyspace;
+import org.apache.cassandra.db.KeyspaceNotDefinedException;
+import org.apache.cassandra.db.marshal.AbstractType;
+import org.apache.cassandra.exceptions.InvalidRequestException;
import org.apache.cassandra.exceptions.UnknownTableException;
import org.apache.cassandra.io.sstable.Descriptor;
+import org.apache.cassandra.locator.LocalStrategy;
+import org.apache.cassandra.tcm.ClusterMetadata;
public interface SchemaProvider
{
+ Set<String> getKeyspaces();
+ int getNumberOfTables();
+
+ ClusterMetadata submit(SchemaTransformation transformation);
+
+ default UUID getVersion()
+ {
+ return ClusterMetadata.current().schema.getVersion();
+ }
+
+ Keyspaces localKeyspaces();
+ Keyspaces distributedKeyspaces();
+ Keyspaces distributedAndLocalKeyspaces();
+ Keyspaces getUserKeyspaces();
+
+ void registerListener(SchemaChangeListener listener);
+ void unregisterListener(SchemaChangeListener listener);
+
+ SchemaChangeNotifier schemaChangeNotifier();
+
+ Optional<TableMetadata> getIndexMetadata(String keyspace, String index);
+
+ default TableMetadata getTableMetadata(Descriptor descriptor)
+ {
+ return getTableMetadata(descriptor.ksname, descriptor.cfname);
+ }
+
+ default TableMetadataRef getTableMetadataRef(Descriptor descriptor)
+ {
+ return getTableMetadata(descriptor.ksname, descriptor.cfname).ref;
+ }
+
+ default ViewMetadata getView(String keyspaceName, String viewName)
+ {
+ assert keyspaceName != null;
+ KeyspaceMetadata ksm = distributedKeyspaces().getNullable(keyspaceName);
+ return (ksm == null) ? null : ksm.views.getNullable(viewName);
+ }
+
+ default Keyspaces getNonLocalStrategyKeyspaces()
+ {
+ return distributedKeyspaces().filter(keyspace -> keyspace.params.replication.klass != LocalStrategy.class);
+ }
+
+ default TableMetadata validateTable(String keyspaceName, String tableName)
+ {
+ if (tableName.isEmpty())
+ throw new InvalidRequestException("non-empty table is required");
+
+ KeyspaceMetadata keyspace = getKeyspaceMetadata(keyspaceName);
+ if (keyspace == null)
+ throw new KeyspaceNotDefinedException(String.format("keyspace %s does not exist", keyspaceName));
+
+ TableMetadata metadata = keyspace.getTableOrViewNullable(tableName);
+ if (metadata == null)
+ throw new InvalidRequestException(String.format("table %s does not exist", tableName));
+
+ return metadata;
+ }
+
+ default ColumnFamilyStore getColumnFamilyStoreInstance(TableId id)
+ {
+ TableMetadata metadata = getTableMetadata(id);
+ if (metadata == null)
+ return null;
+
+ Keyspace instance = getKeyspaceInstance(metadata.keyspace);
+ if (instance == null)
+ return null;
+
+ return instance.hasColumnFamilyStore(metadata.id)
+ ? instance.getColumnFamilyStore(metadata.id)
+ : null;
+ }
+
+ /**
+ * Get metadata about keyspace inner ColumnFamilies
+ *
+ * @param keyspaceName The name of the keyspace
+ * @return metadata about ColumnFamilies the belong to the given keyspace
+ */
+ default Iterable<TableMetadata> getTablesAndViews(String keyspaceName)
+ {
+ Preconditions.checkNotNull(keyspaceName);
+ Keyspace ks = getKeyspaceInstance(keyspaceName);
+ Preconditions.checkNotNull(ks, "Keyspace %s not found", keyspaceName);
+ return ks.getMetadata().tablesAndViews();
+ }
+
@Nullable
Keyspace getKeyspaceInstance(String keyspaceName);
- Keyspace maybeAddKeyspaceInstance(String keyspaceName, Supplier<Keyspace> loadFunction);
-
@Nullable
KeyspaceMetadata getKeyspaceMetadata(String keyspaceName);
@Nullable
TableMetadata getTableMetadata(TableId id);
+ @Nullable
+ default TableMetadataRef getTableMetadataRef(TableId id)
+ {
+ return getTableMetadata(id).ref;
+ }
+
@Nullable
TableMetadata getTableMetadata(String keyspace, String table);
+ default TableMetadataRef getTableMetadataRef(String keyspace, String table)
+ {
+ return getTableMetadata(keyspace, table).ref;
+ }
+
default TableMetadata getExistingTableMetadata(TableId id) throws UnknownTableException
{
TableMetadata metadata = getTableMetadata(id);
if (metadata != null)
return metadata;
+ // TODO this should not even be possible anymore.
String message =
String.format("Couldn't find table with id %s. If a table was just created, this is likely due to the schema"
+ "not being fully propagated. Please wait for schema agreement on table creation.",
@@ -54,15 +164,75 @@ public interface SchemaProvider
throw new UnknownTableException(message, id);
}
- @Nullable
- TableMetadataRef getTableMetadataRef(String keyspace, String table);
+ /* Function helpers */
- @Nullable
- TableMetadataRef getTableMetadataRef(TableId id);
+ /**
+ * Get all function overloads with the specified name
+ *
+ * @param name fully qualified function name
+ * @return an empty list if the keyspace or the function name are not found;
+ * a non-empty collection of {@link Function} otherwise
+ */
+ default Collection<UserFunction> getUserFunctions(FunctionName name)
+ {
+ if (!name.hasKeyspace())
+ throw new IllegalArgumentException(String.format("Function name must be fully qualified: got %s", name));
- @Nullable
- default TableMetadataRef getTableMetadataRef(Descriptor descriptor)
+ KeyspaceMetadata ksm = getKeyspaceMetadata(name.keyspace);
+ return ksm == null
+ ? Collections.emptyList()
+ : ksm.userFunctions.get(name);
+ }
+
+ /**
+ * Find the function with the specified name
+ *
+ * @param name fully qualified function name
+ * @param argTypes function argument types
+ * @return an empty {@link Optional} if the keyspace or the function name are not found;
+ * a non-empty optional of {@link Function} otherwise
+ */
+ default Optional<UserFunction> findFunction(FunctionName name, List<AbstractType<?>> argTypes)
{
- return getTableMetadataRef(descriptor.ksname, descriptor.cfname);
+ if (!name.hasKeyspace())
+ throw new IllegalArgumentException(String.format("Function name must be fully quallified: got %s", name));
+
+ KeyspaceMetadata ksm = getKeyspaceMetadata(name.keyspace);
+ return ksm == null
+ ? Optional.empty()
+ : ksm.userFunctions.find(name, argTypes);
+ }
+
+ /**
+ * Compute the largest gc grace seconds amongst all the tables
+ * @return the largest gcgs.
+ */
+ default int largestGcgs()
+ {
+ return distributedAndLocalKeyspaces().stream()
+ .flatMap(ksm -> ksm.tables.stream())
+ .mapToInt(tm -> tm.params.gcGraceSeconds)
+ .max()
+ .orElse(Integer.MIN_VALUE);
+ }
+
+ // TODO: remove?
+ public abstract void saveSystemKeyspace();
+
+ /**
+ * Find the function with the specified name and arguments.
+ *
+ * @param name fully qualified function name
+ * @param argTypes function argument types
+ * @return an empty {@link Optional} if the keyspace or the function name are not found;
+ * a non-empty optional of {@link Function} otherwise
+ */
+ default Optional<UserFunction> findUserFunction(FunctionName name, List<AbstractType<?>> argTypes)
+ {
+ if (!name.hasKeyspace())
+ throw new IllegalArgumentException(String.format("Function name must be fully quallified: got %s", name));
+
+ return Optional.ofNullable(getKeyspaceMetadata(name.keyspace))
+ .flatMap(ksm -> ksm.userFunctions.find(name, argTypes));
}
}
diff --git a/src/java/org/apache/cassandra/schema/SchemaTransformation.java b/src/java/org/apache/cassandra/schema/SchemaTransformation.java
index 2b020a02a0..352715fc89 100644
--- a/src/java/org/apache/cassandra/schema/SchemaTransformation.java
+++ b/src/java/org/apache/cassandra/schema/SchemaTransformation.java
@@ -17,10 +17,23 @@
*/
package org.apache.cassandra.schema;
+import java.io.IOException;
import java.util.Optional;
+import org.apache.cassandra.cql3.CQLStatement;
+import org.apache.cassandra.cql3.QueryProcessor;
+import org.apache.cassandra.db.TypeSizes;
+import org.apache.cassandra.io.util.DataInputPlus;
+import org.apache.cassandra.io.util.DataOutputPlus;
+import org.apache.cassandra.tcm.serialization.MetadataSerializer;
+import org.apache.cassandra.tcm.serialization.Version;
+import org.apache.cassandra.service.ClientState;
+import org.apache.cassandra.tcm.ClusterMetadata;
+
public interface SchemaTransformation
{
+ SchemaTransformationSerializer serializer = new SchemaTransformationSerializer();
+
/**
* Apply a statement transformation to a schema snapshot.
* <p>
@@ -30,7 +43,17 @@ public interface SchemaTransformation
* @param schema Keyspaces to base the transformation on
* @return Keyspaces transformed by the statement
*/
- Keyspaces apply(Keyspaces schema);
+ Keyspaces apply(ClusterMetadata metadata, Keyspaces schema);
+
+ default String cql()
+ {
+ return "null";
+ }
+
+ default String keyspace()
+ {
+ return null;
+ }
/**
* If the transformation should be applied with a certain timestamp, this method should be overriden. This is used
@@ -47,8 +70,8 @@ public interface SchemaTransformation
*/
class SchemaTransformationResult
{
- public final DistributedSchema before;
- public final DistributedSchema after;
+ private final DistributedSchema before;
+ private final DistributedSchema after;
public final Keyspaces.KeyspacesDiff diff;
public SchemaTransformationResult(DistributedSchema before, DistributedSchema after, Keyspaces.KeyspacesDiff diff)
@@ -64,4 +87,37 @@ public interface SchemaTransformation
return String.format("SchemaTransformationResult{%s --> %s, diff=%s}", before.getVersion(), after.getVersion(), diff);
}
}
+
+ class SchemaTransformationSerializer implements MetadataSerializer<SchemaTransformation>
+ {
+ public void serialize(SchemaTransformation transformation, DataOutputPlus out, Version version) throws IOException
+ {
+ boolean hasKeyspace = transformation.keyspace() != null;
+ out.writeBoolean(hasKeyspace);
+ if (hasKeyspace)
+ out.writeUTF(transformation.keyspace());
+ out.writeUTF(transformation.cql());
+ }
+
+ public SchemaTransformation deserialize(DataInputPlus in, Version version) throws IOException
+ {
+ boolean hasKeyspace = in.readBoolean();
+ String keyspace = null;
+ if (hasKeyspace)
+ keyspace = in.readUTF();
+ String cql = in.readUTF();
+ CQLStatement statement = QueryProcessor.getStatement(cql, ClientState.forInternalCalls(keyspace));
+ if (!(statement instanceof SchemaTransformation))
+ throw new IllegalArgumentException("Can not deserialize schema transformation");
+ return (SchemaTransformation) statement;
+ }
+
+ public long serializedSize(SchemaTransformation t, Version version)
+ {
+ long size = TypeSizes.sizeof(true);
+ if (t.keyspace() != null)
+ size += TypeSizes.sizeof(t.keyspace());
+ return size + TypeSizes.sizeof(t.cql());
+ }
+ }
}
diff --git a/src/java/org/apache/cassandra/schema/SchemaTransformations.java b/src/java/org/apache/cassandra/schema/SchemaTransformations.java
index 124f9a6ef6..eb8948aa5d 100644
--- a/src/java/org/apache/cassandra/schema/SchemaTransformations.java
+++ b/src/java/org/apache/cassandra/schema/SchemaTransformations.java
@@ -23,6 +23,10 @@ import java.util.Optional;
import org.apache.cassandra.db.marshal.UserType;
import org.apache.cassandra.exceptions.AlreadyExistsException;
import org.apache.cassandra.exceptions.ConfigurationException;
+import org.apache.cassandra.cql3.CQLStatement;
+import org.apache.cassandra.cql3.QueryProcessor;
+import org.apache.cassandra.service.ClientState;
+import org.apache.cassandra.tcm.ClusterMetadata;
import static org.apache.cassandra.cql3.statements.RequestValidations.invalidRequest;
@@ -31,6 +35,14 @@ import static org.apache.cassandra.cql3.statements.RequestValidations.invalidReq
*/
public class SchemaTransformations
{
+ public static SchemaTransformation fromCql(String cql)
+ {
+ CQLStatement statement = QueryProcessor.getStatement(cql, ClientState.forInternalCalls());
+ if (!(statement instanceof SchemaTransformation))
+ throw new IllegalArgumentException("Can not deserialize schema transformation");
+ return (SchemaTransformation) statement;
+ }
+
/**
* Creates a schema transformation that adds the provided keyspace.
*
@@ -42,7 +54,7 @@ public class SchemaTransformations
*/
public static SchemaTransformation addKeyspace(KeyspaceMetadata keyspace, boolean ignoreIfExists)
{
- return schema ->
+ return (metadata, schema) ->
{
KeyspaceMetadata existing = schema.getNullable(keyspace.name);
if (existing != null)
@@ -68,7 +80,7 @@ public class SchemaTransformations
*/
public static SchemaTransformation addTable(TableMetadata table, boolean ignoreIfExists)
{
- return schema ->
+ return (metadata, schema) ->
{
KeyspaceMetadata keyspace = schema.getNullable(table.keyspace);
if (keyspace == null)
@@ -90,7 +102,7 @@ public class SchemaTransformations
public static SchemaTransformation addTypes(Types toAdd, boolean ignoreIfExists)
{
- return schema ->
+ return (metadata, schema) ->
{
if (toAdd.isEmpty())
return schema;
@@ -128,7 +140,7 @@ public class SchemaTransformations
*/
public static SchemaTransformation addView(ViewMetadata view, boolean ignoreIfExists)
{
- return schema ->
+ return (metadata, schema) ->
{
KeyspaceMetadata keyspace = schema.getNullable(view.keyspace());
if (keyspace == null)
@@ -169,7 +181,7 @@ public class SchemaTransformations
}
@Override
- public Keyspaces apply(Keyspaces schema)
+ public Keyspaces apply(ClusterMetadata metadata, Keyspaces schema)
{
KeyspaceMetadata updatedKeyspace = keyspace;
KeyspaceMetadata curKeyspace = schema.getNullable(keyspace.name);
@@ -207,4 +219,4 @@ public class SchemaTransformations
};
}
-}
\ No newline at end of file
+}
diff --git a/src/java/org/apache/cassandra/schema/SchemaUpdateHandler.java b/src/java/org/apache/cassandra/schema/SchemaUpdateHandler.java
deleted file mode 100644
index f6711f3219..0000000000
--- a/src/java/org/apache/cassandra/schema/SchemaUpdateHandler.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * 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.schema;
-
-import java.time.Duration;
-
-import org.apache.cassandra.schema.SchemaTransformation.SchemaTransformationResult;
-import org.apache.cassandra.utils.concurrent.Awaitable;
-
-/**
- * Schema update handler is responsible for maintaining the shared schema and synchronizing it with other nodes in
- * the cluster, which means pushing and pulling changes, as well as tracking the current version in the cluster.
- * <p/>
- * The interface has been extracted to abstract out that functionality. It allows for various implementations like
- * Gossip based (the default), ETCD, offline, etc., and make it easier for mocking in unit tests.
- */
-public interface SchemaUpdateHandler
-{
- /**
- * Starts actively synchronizing schema with the rest of the cluster. It is called in the very beginning of the
- * node startup. It is not expected to block - to await for the startup completion we have another method
- * {@link #waitUntilReady(Duration)}.
- */
- void start();
-
- /**
- * Waits until the schema update handler is ready and returns the result. If the method returns {@code false} it
- * means that readiness could not be achieved within the specified period of time. The method can be used just to
- * check if schema is ready by passing {@link Duration#ZERO} as the timeout - in such case it returns immediately.
- *
- * @param timeout the maximum time to wait for schema readiness
- * @return whether readiness is achieved
- */
- boolean waitUntilReady(Duration timeout);
-
- /**
- * Applies schema transformation in the underlying storage and synchronizes with other nodes.
- *
- * @param transformation schema transformation to be performed
- * @param local if true, the caller does not require synchronizing schema with other nodes - in practise local is
- * used only in some tests
- * @return transformation result
- */
- SchemaTransformationResult apply(SchemaTransformation transformation, boolean local);
-
- /**
- * Resets the schema either by reloading data from the local storage or from the other nodes. Once the schema is
- * refreshed, the callbacks provided in the factory method are executed, and the updated schema version is announced.
- *
- * @param local whether we should reset with locally stored schema or fetch the schema from other nodes
- */
- void reset(boolean local);
-
- /**
- * Marks the local schema to be cleared and refreshed. Since calling this method, the update handler tries to obtain
- * a fresh schema definition from a remote source. Once the schema definition is received, the local schema is
- * replaced (instead of being merged which usually happens when the update is received).
- * <p/>
- * The returned awaitable is fulfilled when the schema is received and applied.
- */
- Awaitable clear();
-}
diff --git a/src/java/org/apache/cassandra/schema/SchemaUpdateHandlerFactory.java b/src/java/org/apache/cassandra/schema/SchemaUpdateHandlerFactory.java
deleted file mode 100644
index f324a5d6e6..0000000000
--- a/src/java/org/apache/cassandra/schema/SchemaUpdateHandlerFactory.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * 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.schema;
-
-import java.util.function.BiConsumer;
-
-import org.apache.cassandra.schema.SchemaTransformation.SchemaTransformationResult;
-
-public interface SchemaUpdateHandlerFactory
-{
- /**
- * A factory which provides the appropriate schema update handler. The actual implementation may be different for
- * different run modes (client, tool, daemon).
- *
- * @param online whether schema update handler should work online and be aware of the other nodes (when in daemon mode)
- * @param updateSchemaCallback callback which will be called right after the shared schema is updated
- */
- SchemaUpdateHandler getSchemaUpdateHandler(boolean online, BiConsumer<SchemaTransformationResult, Boolean> updateSchemaCallback);
-}
diff --git a/src/java/org/apache/cassandra/schema/SchemaUpdateHandlerFactoryProvider.java b/src/java/org/apache/cassandra/schema/SchemaUpdateHandlerFactoryProvider.java
deleted file mode 100644
index 9411a9285c..0000000000
--- a/src/java/org/apache/cassandra/schema/SchemaUpdateHandlerFactoryProvider.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * 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.schema;
-
-import javax.inject.Provider;
-
-import org.apache.commons.lang3.StringUtils;
-
-import org.apache.cassandra.exceptions.ConfigurationException;
-import org.apache.cassandra.utils.FBUtilities;
-
-/**
- * Provides the instance of SchemaUpdateHandler factory pointed by {@link #SUH_FACTORY_CLASS_PROPERTY} system property.
- * If the property is not defined, the default factory {@link DefaultSchemaUpdateHandler} instance is returned.
- */
-public class SchemaUpdateHandlerFactoryProvider implements Provider<SchemaUpdateHandlerFactory>
-{
- public static final String SUH_FACTORY_CLASS_PROPERTY = "cassandra.schema.update_handler_factory.class";
-
- public final static SchemaUpdateHandlerFactoryProvider instance = new SchemaUpdateHandlerFactoryProvider();
-
- @Override
- public SchemaUpdateHandlerFactory get()
- {
- String suhFactoryClassName = StringUtils.trimToNull(System.getProperty(SUH_FACTORY_CLASS_PROPERTY));
- if (suhFactoryClassName == null)
- {
- return DefaultSchemaUpdateHandlerFactory.instance;
- }
- else
- {
- Class<SchemaUpdateHandlerFactory> suhFactoryClass = FBUtilities.classForName(suhFactoryClassName, "schema update handler factory");
- try
- {
- return suhFactoryClass.newInstance();
- }
- catch (InstantiationException | IllegalAccessException ex)
- {
- throw new ConfigurationException(String.format("Failed to initialize schema update handler factory class %s defined in %s system property.",
- suhFactoryClassName, SUH_FACTORY_CLASS_PROPERTY), ex);
- }
- }
- }
-}
diff --git a/src/java/org/apache/cassandra/schema/TableId.java b/src/java/org/apache/cassandra/schema/TableId.java
index fd47a47e58..47389113b6 100644
--- a/src/java/org/apache/cassandra/schema/TableId.java
+++ b/src/java/org/apache/cassandra/schema/TableId.java
@@ -26,9 +26,8 @@ import org.apache.commons.lang3.ArrayUtils;
import org.apache.cassandra.utils.ByteBufferUtil;
-import static org.apache.cassandra.utils.TimeUUID.Generator.nextTimeUUID;
-
import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.apache.cassandra.utils.TimeUUID.Generator.nextTimeUUID;
/**
* The unique identifier of a table.
@@ -36,7 +35,7 @@ import static java.nio.charset.StandardCharsets.UTF_8;
* This is essentially a UUID, but we wrap it as it's used quite a bit in the code and having a nicely named class make
* the code more readable.
*/
-public class TableId
+public class TableId implements Comparable<TableId>
{
// TODO: should this be a TimeUUID?
private final UUID id;
@@ -124,4 +123,10 @@ public class TableId
{
return new TableId(new UUID(in.readLong(), in.readLong()));
}
+
+ @Override
+ public int compareTo(TableId o)
+ {
+ return id.compareTo(o.id);
+ }
}
diff --git a/src/java/org/apache/cassandra/schema/TableMetadata.java b/src/java/org/apache/cassandra/schema/TableMetadata.java
index fb44034b4f..d183fd0c67 100644
--- a/src/java/org/apache/cassandra/schema/TableMetadata.java
+++ b/src/java/org/apache/cassandra/schema/TableMetadata.java
@@ -17,11 +17,13 @@
*/
package org.apache.cassandra.schema;
+import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
@@ -57,14 +59,20 @@ import org.apache.cassandra.db.marshal.AbstractType;
import org.apache.cassandra.db.marshal.BytesType;
import org.apache.cassandra.db.marshal.CompositeType;
import org.apache.cassandra.db.marshal.EmptyType;
-import org.apache.cassandra.db.marshal.UTF8Type;
import org.apache.cassandra.db.marshal.UserType;
import org.apache.cassandra.dht.IPartitioner;
import org.apache.cassandra.exceptions.ConfigurationException;
import org.apache.cassandra.exceptions.InvalidRequestException;
+import org.apache.cassandra.io.util.DataInputPlus;
+import org.apache.cassandra.io.util.DataOutputPlus;
+import org.apache.cassandra.tcm.Epoch;
+import org.apache.cassandra.tcm.serialization.UDTAwareMetadataSerializer;
+import org.apache.cassandra.tcm.serialization.Version;
import org.apache.cassandra.service.reads.SpeculativeRetryPolicy;
import org.apache.cassandra.transport.ProtocolVersion;
import org.apache.cassandra.utils.AbstractIterator;
+import org.apache.cassandra.utils.ByteBufferUtil;
+import org.apache.cassandra.utils.FBUtilities;
import org.github.jamm.Unmetered;
import static com.google.common.collect.Iterables.any;
@@ -72,11 +80,14 @@ import static com.google.common.collect.Iterables.transform;
import static java.lang.String.format;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toSet;
+import static org.apache.cassandra.db.TypeSizes.sizeof;
import static org.apache.cassandra.schema.IndexMetadata.isNameValid;
@Unmetered
public class TableMetadata implements SchemaElement
{
+ public static final Serializer serializer = new Serializer();
+
private static final Logger logger = LoggerFactory.getLogger(TableMetadata.class);
// Please note that currently the only one truly useful flag is COUNTER, as the rest of the flags were about
@@ -149,6 +160,7 @@ public class TableMetadata implements SchemaElement
public final String keyspace;
public final String name;
public final TableId id;
+ public final Epoch epoch;
public final IPartitioner partitioner;
public final Kind kind;
@@ -180,6 +192,7 @@ public class TableMetadata implements SchemaElement
// performance hacks; TODO see if all are really necessary
public final DataResource resource;
+ public TableMetadataRef ref;
protected TableMetadata(Builder builder)
{
@@ -187,7 +200,7 @@ public class TableMetadata implements SchemaElement
keyspace = builder.keyspace;
name = builder.name;
id = builder.id;
-
+ epoch = builder.epoch;
partitioner = builder.partitioner;
kind = builder.kind;
params = builder.params.build();
@@ -212,6 +225,14 @@ public class TableMetadata implements SchemaElement
comparator = new ClusteringComparator(transform(clusteringColumns, c -> c.type));
resource = DataResource.table(keyspace, name);
+ if (builder.isOffline)
+ ref = TableMetadataRef.forOfflineTools(this);
+ else if (SchemaConstants.isLocalSystemKeyspace(keyspace))
+ ref = TableMetadataRef.forSystemTable(this);
+ else if (isIndex())
+ ref = TableMetadataRef.forIndex(Schema.instance, this, keyspace, indexName, id);
+ else
+ ref = TableMetadataRef.withInitialReference(new TableMetadataRef(Schema.instance, keyspace, name, id), this);
}
public static Builder builder(String keyspace, String table)
@@ -732,6 +753,7 @@ public class TableMetadata implements SchemaElement
final String keyspace;
final String name;
+ private Epoch epoch = Epoch.EMPTY;
private TableId id;
private IPartitioner partitioner;
@@ -743,6 +765,8 @@ public class TableMetadata implements SchemaElement
private Triggers triggers = Triggers.none();
private Indexes indexes = Indexes.none();
+ private boolean isOffline = false;
+
private final Map<ByteBuffer, DroppedColumn> droppedColumns = new HashMap<>();
private final Map<ByteBuffer, ColumnMetadata> columns = new HashMap<>();
private final List<ColumnMetadata> partitionKeyColumns = new ArrayList<>();
@@ -787,6 +811,12 @@ public class TableMetadata implements SchemaElement
return this;
}
+ public Builder epoch(Epoch val)
+ {
+ epoch = val;
+ return this;
+ }
+
public Builder partitioner(IPartitioner val)
{
partitioner = val;
@@ -1120,6 +1150,12 @@ public class TableMetadata implements SchemaElement
return this;
}
+
+ public Builder offline()
+ {
+ this.isOffline = true;
+ return this;
+ }
}
/**
@@ -1576,14 +1612,6 @@ public class TableMetadata implements SchemaElement
return clusteringColumns.get(0).type;
}
- public AbstractType<?> columnDefinitionNameComparator(ColumnMetadata.Kind kind)
- {
- return (Flag.isSuper(this.flags) && kind == ColumnMetadata.Kind.REGULAR) ||
- (isStaticCompactTable() && kind == ColumnMetadata.Kind.STATIC)
- ? staticCompactOrSuperTableColumnNameType()
- : UTF8Type.instance;
- }
-
@Override
public boolean isStaticCompactTable()
{
@@ -1627,7 +1655,104 @@ public class TableMetadata implements SchemaElement
assert columns.regulars.simpleColumnCount() == 1 && columns.regulars.complexColumnCount() == 0;
return columns.regulars.getSimple(0);
}
-
}
+ public static class Serializer implements UDTAwareMetadataSerializer<TableMetadata>
+ {
+ public void serialize(TableMetadata t, DataOutputPlus out, Version version) throws IOException
+ {
+ out.writeUTF(t.keyspace);
+ out.writeUTF(t.name);
+
+ out.writeBoolean(t.epoch != null);
+ if (t.epoch != null)
+ Epoch.serializer.serialize(t.epoch, out, version);
+
+ t.id.serialize(out);
+ out.writeUTF(t.partitioner.getClass().getCanonicalName());
+ out.writeUTF(t.kind.name());
+ TableParams.serializer.serialize(t.params, out, version);
+
+ out.writeInt(t.flags.size());
+ for (Flag f : t.flags)
+ out.writeUTF(f.name());
+
+ out.writeInt(t.columns().size());
+ for (ColumnMetadata cm : t.columns())
+ ColumnMetadata.serializer.serialize(cm, out, version);
+
+ out.writeInt(t.droppedColumns.size());
+ for (DroppedColumn dc : t.droppedColumns.values())
+ DroppedColumn.serializer.serialize(dc, out, version);
+
+ Indexes.serializer.serialize(t.indexes, out, version);
+ Triggers.serializer.serialize(t.triggers, out, version);
+ }
+
+ public TableMetadata deserialize(DataInputPlus in, Types types, Version version) throws IOException
+ {
+ String ks = in.readUTF();
+ String name = in.readUTF();
+
+ boolean hasEpoch = in.readBoolean();
+ Epoch epoch = null;
+ if (hasEpoch)
+ epoch = Epoch.serializer.deserialize(in, version);
+
+ TableId tableId = TableId.deserialize(in);
+ TableMetadata.Builder builder = TableMetadata.builder(ks, name, tableId);
+ builder.epoch(epoch);
+ builder.partitioner(FBUtilities.newPartitioner(in.readUTF()));
+ builder.kind(Kind.valueOf(in.readUTF()));
+ builder.params(TableParams.serializer.deserialize(in, version));
+ int flagCount = in.readInt();
+ Set<Flag> flags = new HashSet<>();
+ for (int i = 0; i < flagCount; i++)
+ flags.add(Flag.valueOf(in.readUTF()));
+ builder.flags(flags);
+ int columnCount = in.readInt();
+ for (int i = 0; i < columnCount; i++)
+ builder.addColumn(ColumnMetadata.serializer.deserialize(in, types, version));
+ int droppedColCount = in.readInt();
+ Map<ByteBuffer, DroppedColumn> droppedColumns = new HashMap<>();
+ for (int i = 0; i < droppedColCount; i++)
+ droppedColumns.put(ByteBufferUtil.readWithShortLength(in), DroppedColumn.serializer.deserialize(in, types, version));
+ builder.droppedColumns(droppedColumns);
+ builder.indexes(Indexes.serializer.deserialize(in, version));
+ builder.triggers(Triggers.serializer.deserialize(in, version));
+ return builder.build();
+ }
+
+ public long serializedSize(TableMetadata t, Version version)
+ {
+ long size = sizeof(t.keyspace) +
+ sizeof(t.name) +
+ t.id.serializedSize() +
+ sizeof(t.partitioner.getClass().getCanonicalName()) +
+ sizeof(t.kind.name()) +
+ TableParams.serializer.serializedSize(t.params, version);
+
+ size += sizeof(t.epoch != null);
+ if (t.epoch != null)
+ size += Epoch.serializer.serializedSize(t.epoch, version);
+
+ size += sizeof(t.flags.size());
+ for (Flag f : t.flags)
+ size += sizeof(f.name());
+
+ size += sizeof(t.columns.size());
+ for (ColumnMetadata cm : t.columns())
+ size += ColumnMetadata.serializer.serializedSize(cm, version);
+
+ size += sizeof(t.droppedColumns.size());
+ for (DroppedColumn dc : t.droppedColumns.values())
+ size += DroppedColumn.serializer.serializedSize(dc, version);
+
+ size += Indexes.serializer.serializedSize(t.indexes, version);
+ size += Triggers.serializer.serializedSize(t.triggers, version);
+
+ return size;
+
+ }
+ }
}
\ No newline at end of file
diff --git a/src/java/org/apache/cassandra/schema/TableMetadataRef.java b/src/java/org/apache/cassandra/schema/TableMetadataRef.java
index dc4ff1da8a..92a17f27da 100644
--- a/src/java/org/apache/cassandra/schema/TableMetadataRef.java
+++ b/src/java/org/apache/cassandra/schema/TableMetadataRef.java
@@ -15,33 +15,62 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package org.apache.cassandra.schema;
-import org.github.jamm.Unmetered;
+import java.util.Optional;
-/**
- * Encapsulates a volatile reference to an immutable {@link TableMetadata} instance.
- *
- * Used in classes that need up-to-date metadata to avoid the cost of looking up {@link Schema} hashmaps.
- */
-@Unmetered
-public final class TableMetadataRef
+import org.apache.cassandra.cql3.ColumnIdentifier;
+
+import static java.lang.String.format;
+
+public class TableMetadataRef
{
+ private final SchemaProvider schema;
public final TableId id;
public final String keyspace;
public final String name;
- private volatile TableMetadata metadata;
private volatile TableMetadata localTableMetadata;
- private volatile CompressionParams localCompressionParams;
- TableMetadataRef(TableMetadata metadata)
+ public static TableMetadataRef forIndex(SchemaProvider schema, TableMetadata initial, String keyspace, String name, TableId id)
{
- this.metadata = metadata;
+ return new TableMetadataRef(schema, keyspace, name, id)
+ {
+ @Override
+ public TableMetadata get()
+ {
+ return getOrDefault(initial);
+ }
- id = metadata.id;
- keyspace = metadata.keyspace;
- name = metadata.name;
+ @Override
+ public TableMetadata getOrDefault(TableMetadata dflt)
+ {
+ Optional<TableMetadata> metadata = schema.getIndexMetadata(keyspace, name);
+ return metadata.orElse(dflt);
+ }
+ };
+ }
+
+ // takes a default metadata instance to provide a ref which can work before the Table has been added to
+ // schema. This is used when creating the initial memtable for a new CFS instance during a schema change
+ // event as the CFS is created before the updated schema is published.
+ public static TableMetadataRef withInitialReference(TableMetadataRef wrapped, TableMetadata initial)
+ {
+ return new TableMetadataRef(Schema.instance, initial.keyspace, initial.name, initial.id)
+ {
+ @Override
+ public TableMetadata get()
+ {
+ return wrapped.getOrDefault(initial);
+ }
+
+ @Override
+ public TableMetadata getOrDefault(TableMetadata dflt)
+ {
+ return wrapped.getOrDefault(dflt);
+ }
+ };
}
/**
@@ -52,39 +81,78 @@ public final class TableMetadataRef
*/
public static TableMetadataRef forOfflineTools(TableMetadata metadata)
{
- return new TableMetadataRef(metadata);
+ return new TableMetadataRef(null, metadata.keyspace, metadata.name, metadata.id)
+ {
+ @Override
+ public TableMetadata get()
+ {
+ return metadata;
+ }
+ };
+ }
+
+ public static TableMetadataRef forSystemTable(TableMetadata metadata)
+ {
+ return new TableMetadataRef(null, metadata.keyspace, metadata.name, metadata.id) {
+ @Override
+ public TableMetadata get()
+ {
+ return metadata;
+ }
+
+ @Override
+ public TableMetadata getOrDefault(TableMetadata dflt)
+ {
+ return metadata;
+ }
+ };
+ }
+
+ public TableMetadataRef(SchemaProvider schema, String keyspace, String name, TableId id)
+ {
+ this.schema = schema;
+ this.keyspace = keyspace;
+ this.name = name;
+ this.id = id;
}
public TableMetadata get()
{
+ TableMetadata metadata = schema.getTableMetadata(keyspace, name);
+ if (metadata == null)
+ throw new IllegalStateException(format("Can't deref metadata for %s.%s.", keyspace, name));
return metadata;
}
+ public TableMetadata getOrDefault(TableMetadata dflt)
+ {
+ TableMetadata tableMetadata = schema.getTableMetadata(keyspace, name);
+
+ if (tableMetadata == null)
+ return dflt;
+
+ return tableMetadata;
+ }
+
/**
* Returns node-local table metadata
*/
public TableMetadata getLocal()
{
if (this.localTableMetadata != null)
+ {
+ TableMetadata global = get();
+ if (!this.localTableMetadata.epoch.equals(global.epoch))
+ {
+ this.localTableMetadata = null;
+ return global;
+ }
return localTableMetadata;
+ }
- return metadata;
- }
-
- /**
- * Update the reference with the most current version of {@link TableMetadata}
- * <p>
- * Must only be used by methods in {@link Schema}, *DO NOT* make public
- * even for testing purposes, it isn't safe.
- */
- void set(TableMetadata metadata)
- {
- metadata.validateCompatibility(get());
- this.metadata = metadata;
- this.localTableMetadata = null;
+ return get();
}
-
public void setLocalOverrides(TableMetadata metadata)
{
metadata.validateCompatibility(get());
@@ -94,6 +162,6 @@ public final class TableMetadataRef
@Override
public String toString()
{
- return get().toString();
+ return format("%s.%s", ColumnIdentifier.maybeQuote(keyspace), ColumnIdentifier.maybeQuote(name));
}
}
diff --git a/src/java/org/apache/cassandra/schema/TableMetadataRefCache.java b/src/java/org/apache/cassandra/schema/TableMetadataRefCache.java
deleted file mode 100644
index d947d1d690..0000000000
--- a/src/java/org/apache/cassandra/schema/TableMetadataRefCache.java
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * 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.schema;
-
-import java.util.Collections;
-import java.util.Map;
-
-import com.google.common.collect.MapDifference;
-import com.google.common.collect.Maps;
-
-import org.apache.cassandra.utils.Pair;
-
-/**
- * Manages the cached {@link TableMetadataRef} objects which holds the references to {@link TableMetadata} objects.
- * <p>
- * The purpose of {@link TableMetadataRef} is that the reference to {@link TableMetadataRef} remains unchanged when
- * the metadata of the table changes. {@link TableMetadata} is immutable, so when it changes, we only switch
- * the reference inside the existing {@link TableMetadataRef} object.
- */
-class TableMetadataRefCache
-{
- public final static TableMetadataRefCache EMPTY = new TableMetadataRefCache(Collections.emptyMap(), Collections.emptyMap(), Collections.emptyMap());
-
- // UUID -> mutable metadata ref map. We have to update these in place every time a table changes.
- private final Map<TableId, TableMetadataRef> metadataRefs;
-
- // keyspace and table names -> mutable metadata ref map.
- private final Map<Pair<String, String>, TableMetadataRef> metadataRefsByName;
-
- // (keyspace name, index name) -> mutable metadata ref map. We have to update these in place every time an index changes.
- private final Map<Pair<String, String>, TableMetadataRef> indexMetadataRefs;
-
- public TableMetadataRefCache(Map<TableId, TableMetadataRef> metadataRefs,
- Map<Pair<String, String>, TableMetadataRef> metadataRefsByName,
- Map<Pair<String, String>, TableMetadataRef> indexMetadataRefs)
- {
- this.metadataRefs = Collections.unmodifiableMap(metadataRefs);
- this.metadataRefsByName = Collections.unmodifiableMap(metadataRefsByName);
- this.indexMetadataRefs = Collections.unmodifiableMap(indexMetadataRefs);
- }
-
- /**
- * Returns cache copy with added the {@link TableMetadataRef} corresponding to the provided keyspace to {@link #metadataRefs} and
- * {@link #indexMetadataRefs}, assuming the keyspace is new (in the sense of not being tracked by the manager yet).
- */
- TableMetadataRefCache withNewRefs(KeyspaceMetadata ksm)
- {
- return withUpdatedRefs(ksm.empty(), ksm);
- }
-
- /**
- * Returns cache copy with updated the {@link TableMetadataRef} in {@link #metadataRefs} and {@link #indexMetadataRefs},
- * for an existing updated keyspace given it's previous and new definition.
- * <p>
- * Note that {@link TableMetadataRef} are not duplicated and table metadata is altered in the existing refs.
- */
- TableMetadataRefCache withUpdatedRefs(KeyspaceMetadata previous, KeyspaceMetadata updated)
- {
- Tables.TablesDiff tablesDiff = Tables.diff(previous.tables, updated.tables);
- Views.ViewsDiff viewsDiff = Views.diff(previous.views, updated.views);
-
- MapDifference<String, TableMetadata> indexesDiff = previous.tables.indexesDiff(updated.tables);
-
- boolean hasCreatedOrDroppedTablesOrViews = tablesDiff.created.size() > 0 || tablesDiff.dropped.size() > 0 || viewsDiff.created.size() > 0 || viewsDiff.dropped.size() > 0;
- boolean hasCreatedOrDroppedIndexes = !indexesDiff.entriesOnlyOnRight().isEmpty() || !indexesDiff.entriesOnlyOnLeft().isEmpty();
-
- Map<TableId, TableMetadataRef> metadataRefs = hasCreatedOrDroppedTablesOrViews ? Maps.newHashMap(this.metadataRefs) : this.metadataRefs;
- Map<Pair<String, String>, TableMetadataRef> metadataRefsByName = hasCreatedOrDroppedTablesOrViews ? Maps.newHashMap(this.metadataRefsByName) : this.metadataRefsByName;
- Map<Pair<String, String>, TableMetadataRef> indexMetadataRefs = hasCreatedOrDroppedIndexes ? Maps.newHashMap(this.indexMetadataRefs) : this.indexMetadataRefs;
-
- // clean up after removed entries
- tablesDiff.dropped.forEach(ref -> removeRef(metadataRefs, metadataRefsByName, ref));
- viewsDiff.dropped.forEach(view -> removeRef(metadataRefs, metadataRefsByName, view.metadata));
- indexesDiff.entriesOnlyOnLeft()
- .values()
- .forEach(indexTable -> indexMetadataRefs.remove(Pair.create(indexTable.keyspace, indexTable.indexName().get())));
-
- // load up new entries
- tablesDiff.created.forEach(table -> putRef(metadataRefs, metadataRefsByName, new TableMetadataRef(table)));
- viewsDiff.created.forEach(view -> putRef(metadataRefs, metadataRefsByName, new TableMetadataRef(view.metadata)));
- indexesDiff.entriesOnlyOnRight()
- .values()
- .forEach(indexTable -> indexMetadataRefs.put(Pair.create(indexTable.keyspace, indexTable.indexName().get()), new TableMetadataRef(indexTable)));
-
- // refresh refs to updated ones
- tablesDiff.altered.forEach(diff -> metadataRefs.get(diff.after.id).set(diff.after));
- viewsDiff.altered.forEach(diff -> metadataRefs.get(diff.after.metadata.id).set(diff.after.metadata));
- indexesDiff.entriesDiffering()
- .values()
- .stream()
- .map(MapDifference.ValueDifference::rightValue)
- .forEach(indexTable -> indexMetadataRefs.get(Pair.create(indexTable.keyspace, indexTable.indexName().get())).set(indexTable));
-
- return new TableMetadataRefCache(metadataRefs, metadataRefsByName, indexMetadataRefs);
- }
-
- private void putRef(Map<TableId, TableMetadataRef> metadataRefs,
- Map<Pair<String, String>, TableMetadataRef> metadataRefsByName,
- TableMetadataRef ref)
- {
- metadataRefs.put(ref.id, ref);
- metadataRefsByName.put(Pair.create(ref.keyspace, ref.name), ref);
- }
-
- private void removeRef(Map<TableId, TableMetadataRef> metadataRefs,
- Map<Pair<String, String>, TableMetadataRef> metadataRefsByName,
- TableMetadata tm)
- {
- metadataRefs.remove(tm.id);
- metadataRefsByName.remove(Pair.create(tm.keyspace, tm.name));
- }
-
- /**
- * Returns cache copy with removed the {@link TableMetadataRef} from {@link #metadataRefs} and {@link #indexMetadataRefs}
- * for the provided (dropped) keyspace.
- */
- TableMetadataRefCache withRemovedRefs(KeyspaceMetadata ksm)
- {
- return withUpdatedRefs(ksm, ksm.empty());
- }
-
- public TableMetadataRef getTableMetadataRef(TableId id)
- {
- return metadataRefs.get(id);
- }
-
- public TableMetadataRef getTableMetadataRef(String keyspace, String table)
- {
- return metadataRefsByName.get(Pair.create(keyspace, table));
- }
-
- public TableMetadataRef getIndexTableMetadataRef(String keyspace, String index)
- {
- return indexMetadataRefs.get(Pair.create(keyspace, index));
- }
-}
diff --git a/src/java/org/apache/cassandra/schema/TableParams.java b/src/java/org/apache/cassandra/schema/TableParams.java
index 8f883f8f47..e5c1f205e6 100644
--- a/src/java/org/apache/cassandra/schema/TableParams.java
+++ b/src/java/org/apache/cassandra/schema/TableParams.java
@@ -17,6 +17,7 @@
*/
package org.apache.cassandra.schema;
+import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Map;
import java.util.Map.Entry;
@@ -24,10 +25,15 @@ import java.util.Map.Entry;
import com.google.common.base.MoreObjects;
import com.google.common.base.Objects;
import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
import org.apache.cassandra.cql3.Attributes;
import org.apache.cassandra.cql3.CqlBuilder;
import org.apache.cassandra.exceptions.ConfigurationException;
+import org.apache.cassandra.io.util.DataInputPlus;
+import org.apache.cassandra.io.util.DataOutputPlus;
+import org.apache.cassandra.tcm.serialization.MetadataSerializer;
+import org.apache.cassandra.tcm.serialization.Version;
import org.apache.cassandra.service.reads.PercentileSpeculativeRetryPolicy;
import org.apache.cassandra.service.reads.SpeculativeRetryPolicy;
import org.apache.cassandra.service.reads.repair.ReadRepairStrategy;
@@ -37,9 +43,11 @@ import org.apache.cassandra.utils.ByteBufferUtil;
import static java.lang.String.format;
import static java.util.stream.Collectors.toMap;
import static org.apache.cassandra.schema.TableParams.Option.*;
+import static org.apache.cassandra.db.TypeSizes.sizeof;
public final class TableParams
{
+ public static final Serializer serializer = new Serializer();
public enum Option
{
ALLOW_AUTO_SNAPSHOT,
@@ -479,4 +487,136 @@ public final class TableParams
return this;
}
}
+
+ public static class Serializer implements MetadataSerializer<TableParams>
+ {
+ public void serialize(TableParams t, DataOutputPlus out, Version version) throws IOException
+ {
+ out.writeUTF(t.comment);
+ out.writeDouble(t.bloomFilterFpChance);
+ out.writeDouble(t.crcCheckChance);
+ out.writeInt(t.gcGraceSeconds);
+ out.writeInt(t.defaultTimeToLive);
+ out.writeInt(t.memtableFlushPeriodInMs);
+ out.writeInt(t.minIndexInterval);
+ out.writeInt(t.maxIndexInterval);
+ out.writeUTF(t.speculativeRetry.toString());
+ out.writeUTF(t.additionalWritePolicy.toString());
+ serializeMap(t.caching.asMap(), out);
+ serializeMap(t.compaction.asMap(), out);
+ serializeMap(t.compression.asMap(), out);
+ serializeMapBB(t.extensions, out);
+ out.writeBoolean(t.cdc);
+ out.writeUTF(t.readRepair.name());
+ }
+
+ public TableParams deserialize(DataInputPlus in, Version version) throws IOException
+ {
+ TableParams.Builder builder = TableParams.builder();
+ builder.comment(in.readUTF())
+ .bloomFilterFpChance(in.readDouble())
+ .crcCheckChance(in.readDouble())
+ .gcGraceSeconds(in.readInt())
+ .defaultTimeToLive(in.readInt())
+ .memtableFlushPeriodInMs(in.readInt())
+ .minIndexInterval(in.readInt())
+ .maxIndexInterval(in.readInt())
+ .speculativeRetry(SpeculativeRetryPolicy.fromString(in.readUTF()))
+ .additionalWritePolicy(SpeculativeRetryPolicy.fromString(in.readUTF()))
+ .caching(CachingParams.fromMap(deserializeMap(in)))
+ .compaction(CompactionParams.fromMap(deserializeMap(in)))
+ .compression(CompressionParams.fromMap(deserializeMap(in)))
+ .extensions(deserializeMapBB(in))
+ .cdc(in.readBoolean())
+ .readRepair(ReadRepairStrategy.fromString(in.readUTF()));
+ return builder.build();
+ }
+
+ public long serializedSize(TableParams t, Version version)
+ {
+ return sizeof(t.comment) +
+ sizeof(t.bloomFilterFpChance) +
+ sizeof(t.crcCheckChance) +
+ sizeof(t.gcGraceSeconds) +
+ sizeof(t.defaultTimeToLive) +
+ sizeof(t.memtableFlushPeriodInMs) +
+ sizeof(t.minIndexInterval) +
+ sizeof(t.maxIndexInterval) +
+ sizeof(t.speculativeRetry.toString()) +
+ sizeof(t.additionalWritePolicy.toString()) +
+ serializedSizeMap(t.caching.asMap()) +
+ serializedSizeMap(t.compaction.asMap()) +
+ serializedSizeMap(t.compression.asMap()) +
+ serializedSizeMapBB(t.extensions) +
+ sizeof(t.cdc) +
+ sizeof(t.readRepair.name());
+ }
+
+ private void serializeMap(Map<String, String> map, DataOutputPlus out) throws IOException
+ {
+ out.writeInt(map.size());
+ for (Map.Entry<String, String> entry : map.entrySet())
+ {
+ out.writeUTF(entry.getKey());
+ out.writeUTF(entry.getValue());
+ }
+ }
+
+ private long serializedSizeMap(Map<String, String> map)
+ {
+ long size = sizeof(map.size());
+ for (Map.Entry<String, String> entry : map.entrySet())
+ {
+ size += sizeof(entry.getKey());
+ size += sizeof(entry.getValue());
+ }
+ return size;
+ }
+
+ private void serializeMapBB(Map<String, ByteBuffer> map, DataOutputPlus out) throws IOException
+ {
+ out.writeInt(map.size());
+ for (Map.Entry<String, ByteBuffer> entry : map.entrySet())
+ {
+ out.writeUTF(entry.getKey());
+ ByteBufferUtil.writeWithVIntLength(entry.getValue(), out);
+ }
+ }
+
+ private long serializedSizeMapBB(Map<String, ByteBuffer> map)
+ {
+ long size = sizeof(map.size());
+ for (Map.Entry<String, ByteBuffer> entry : map.entrySet())
+ {
+ size += sizeof(entry.getKey());
+ size += ByteBufferUtil.serializedSizeWithVIntLength(entry.getValue());
+ }
+ return size;
+ }
+
+ private Map<String, String> deserializeMap(DataInputPlus in) throws IOException
+ {
+ int size = in.readInt();
+ Map<String, String> map = Maps.newHashMapWithExpectedSize(size);
+ for (int i = 0; i < size; i++)
+ {
+ String key = in.readUTF();
+ String value = in.readUTF();
+ map.put(key, value);
+ }
+ return map;
+ }
+ private Map<String, ByteBuffer> deserializeMapBB(DataInputPlus in) throws IOException
+ {
+ int size = in.readInt();
+ Map<String, ByteBuffer> map = Maps.newHashMapWithExpectedSize(size);
+ for (int i = 0; i < size; i++)
+ {
+ String key = in.readUTF();
+ ByteBuffer value = ByteBufferUtil.readWithVIntLength(in);
+ map.put(key, value);
+ }
+ return map;
+ }
+ }
}
diff --git a/src/java/org/apache/cassandra/schema/Tables.java b/src/java/org/apache/cassandra/schema/Tables.java
index 33ed1b8025..f00b8cdfe8 100644
--- a/src/java/org/apache/cassandra/schema/Tables.java
+++ b/src/java/org/apache/cassandra/schema/Tables.java
@@ -17,6 +17,7 @@
*/
package org.apache.cassandra.schema;
+import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.Iterator;
@@ -30,8 +31,13 @@ import javax.annotation.Nullable;
import com.google.common.collect.*;
+import org.apache.cassandra.db.TypeSizes;
import org.apache.cassandra.db.marshal.UserType;
import org.apache.cassandra.index.internal.CassandraIndex;
+import org.apache.cassandra.io.util.DataInputPlus;
+import org.apache.cassandra.io.util.DataOutputPlus;
+import org.apache.cassandra.tcm.serialization.UDTAwareMetadataSerializer;
+import org.apache.cassandra.tcm.serialization.Version;
import static com.google.common.collect.Iterables.any;
import static com.google.common.collect.Iterables.transform;
@@ -41,6 +47,8 @@ import static com.google.common.collect.Iterables.transform;
*/
public final class Tables implements Iterable<TableMetadata>
{
+ public static final Serializer serializer = new Serializer();
+
private static final Tables NONE = builder().build();
private final ImmutableMap<String, TableMetadata> tables;
@@ -285,4 +293,34 @@ public final class Tables implements Iterable<TableMetadata>
return new TablesDiff(created, dropped, altered.build());
}
}
+
+ public static class Serializer implements UDTAwareMetadataSerializer<Tables>
+ {
+ public void serialize(Tables t, DataOutputPlus out, Version version) throws IOException
+ {
+ out.writeInt(t.tables.size());
+ for (TableMetadata tm : t.tables.values())
+ TableMetadata.serializer.serialize(tm, out, version);
+ }
+
+ public Tables deserialize(DataInputPlus in, Types types, Version version) throws IOException
+ {
+ int count = in.readInt();
+ Tables.Builder builder = Tables.builder();
+ for (int i = 0; i < count; i++)
+ {
+ TableMetadata tm = TableMetadata.serializer.deserialize(in, types, version);
+ builder.add(tm);
+ }
+ return builder.build();
+ }
+
+ public long serializedSize(Tables t, Version version)
+ {
+ int size = TypeSizes.sizeof(t.tables.size());
+ for (TableMetadata tm : t.tables.values())
+ size += TableMetadata.serializer.serializedSize(tm, version);
+ return size;
+ }
+ }
}
diff --git a/src/java/org/apache/cassandra/schema/TriggerMetadata.java b/src/java/org/apache/cassandra/schema/TriggerMetadata.java
index d98508130b..05f8d10416 100644
--- a/src/java/org/apache/cassandra/schema/TriggerMetadata.java
+++ b/src/java/org/apache/cassandra/schema/TriggerMetadata.java
@@ -18,11 +18,21 @@
*/
package org.apache.cassandra.schema;
+import java.io.IOException;
+
import com.google.common.base.MoreObjects;
import com.google.common.base.Objects;
+import org.apache.cassandra.io.util.DataInputPlus;
+import org.apache.cassandra.io.util.DataOutputPlus;
+import org.apache.cassandra.tcm.serialization.MetadataSerializer;
+import org.apache.cassandra.tcm.serialization.Version;
+
+import static org.apache.cassandra.db.TypeSizes.sizeof;
+
public final class TriggerMetadata
{
+ public static final Serializer serializer = new Serializer();
public static final String CLASS = "class";
public final String name;
@@ -70,4 +80,26 @@ public final class TriggerMetadata
.add("class", classOption)
.toString();
}
+
+ public static class Serializer implements MetadataSerializer<TriggerMetadata>
+ {
+ public void serialize(TriggerMetadata t, DataOutputPlus out, Version version) throws IOException
+ {
+ out.writeUTF(t.name);
+ out.writeUTF(t.classOption);
+ }
+
+ public TriggerMetadata deserialize(DataInputPlus in, Version version) throws IOException
+ {
+ String name = in.readUTF();
+ String classOption = in.readUTF();
+ return new TriggerMetadata(name, classOption);
+ }
+
+ public long serializedSize(TriggerMetadata t, Version version)
+ {
+ return sizeof(t.name) +
+ sizeof(t.classOption);
+ }
+ }
}
diff --git a/src/java/org/apache/cassandra/schema/Triggers.java b/src/java/org/apache/cassandra/schema/Triggers.java
index 5e107229fd..7ee53eceed 100644
--- a/src/java/org/apache/cassandra/schema/Triggers.java
+++ b/src/java/org/apache/cassandra/schema/Triggers.java
@@ -17,15 +17,24 @@
*/
package org.apache.cassandra.schema;
+import java.io.IOException;
import java.util.Iterator;
import java.util.Optional;
import com.google.common.collect.ImmutableMap;
+import org.apache.cassandra.io.util.DataInputPlus;
+import org.apache.cassandra.io.util.DataOutputPlus;
+import org.apache.cassandra.tcm.serialization.MetadataSerializer;
+import org.apache.cassandra.tcm.serialization.Version;
+
import static com.google.common.collect.Iterables.filter;
+import static org.apache.cassandra.db.TypeSizes.sizeof;
public final class Triggers implements Iterable<TriggerMetadata>
{
+ public static final Serializer serializer = new Serializer();
+
private final ImmutableMap<String, TriggerMetadata> triggers;
private Triggers(Builder builder)
@@ -151,4 +160,34 @@ public final class Triggers implements Iterable<TriggerMetadata>
return this;
}
}
+
+ public static class Serializer implements MetadataSerializer<Triggers>
+ {
+ public void serialize(Triggers t, DataOutputPlus out, Version version) throws IOException
+ {
+ out.writeInt(t.triggers.size());
+ for (TriggerMetadata tm : t.triggers.values())
+ TriggerMetadata.serializer.serialize(tm, out, version);
+ }
+
+ public Triggers deserialize(DataInputPlus in, Version version) throws IOException
+ {
+ int size = in.readInt();
+ Builder builder = builder();
+ for (int i = 0; i < size; i++)
+ {
+ TriggerMetadata tm = TriggerMetadata.serializer.deserialize(in, version);
+ builder.add(tm);
+ }
+ return builder.build();
+ }
+
+ public long serializedSize(Triggers t, Version version)
+ {
+ int size = sizeof(t.triggers.size());
+ for (TriggerMetadata tm : t.triggers.values())
+ size += TriggerMetadata.serializer.serializedSize(tm, version);
+ return size;
+ }
+ }
}
diff --git a/src/java/org/apache/cassandra/schema/Types.java b/src/java/org/apache/cassandra/schema/Types.java
index 0d264c4f49..9b6738e10f 100644
--- a/src/java/org/apache/cassandra/schema/Types.java
+++ b/src/java/org/apache/cassandra/schema/Types.java
@@ -17,6 +17,7 @@
*/
package org.apache.cassandra.schema;
+import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.*;
import java.util.function.Predicate;
@@ -32,6 +33,9 @@ import org.apache.cassandra.cql3.CQL3Type;
import org.apache.cassandra.db.marshal.AbstractType;
import org.apache.cassandra.db.marshal.UserType;
import org.apache.cassandra.exceptions.ConfigurationException;
+import org.apache.cassandra.io.util.DataInputPlus;
+import org.apache.cassandra.io.util.DataOutputPlus;
+import org.apache.cassandra.tcm.serialization.Version;
import static java.lang.String.format;
import static java.util.stream.Collectors.toList;
@@ -39,6 +43,7 @@ import static java.util.stream.Collectors.toList;
import static com.google.common.collect.Iterables.any;
import static com.google.common.collect.Iterables.transform;
+import static org.apache.cassandra.db.TypeSizes.sizeof;
import static org.apache.cassandra.utils.ByteBufferUtil.bytes;
/**
@@ -46,6 +51,8 @@ import static org.apache.cassandra.utils.ByteBufferUtil.bytes;
*/
public final class Types implements Iterable<UserType>
{
+ public static final Serializer serializer = new Serializer();
+
private static final Types NONE = new Types(ImmutableMap.of());
private final Map<ByteBuffer, UserType> types;
@@ -444,4 +451,63 @@ public final class Types implements Iterable<UserType>
return new TypesDiff(created, dropped, altered.build());
}
}
+
+ // Not quite a MetadataSerializer as it needs the keyspace name during deserialization.
+ public static class Serializer
+ {
+ public void serialize(Types t, DataOutputPlus out, Version version) throws IOException
+ {
+ out.writeInt(t.types.size());
+ for (UserType type : t.types.values())
+ {
+ out.writeUTF(type.getNameAsString());
+ List<String> fieldNames = type.fieldNames().stream().map(FieldIdentifier::toString).collect(toList());
+ List<String> fieldTypes = type.fieldTypes().stream().map(AbstractType::asCQL3Type).map(CQL3Type::toString).collect(toList());
+ out.writeInt(fieldNames.size());
+ for (String s : fieldNames)
+ out.writeUTF(s);
+ out.writeInt(fieldTypes.size());
+ for (String s : fieldTypes)
+ out.writeUTF(s);
+ }
+ }
+
+ public Types deserialize(String keyspace, DataInputPlus in, Version version) throws IOException
+ {
+ int count = in.readInt();
+ Types.RawBuilder builder = Types.rawBuilder(keyspace);
+ for (int i = 0; i < count; i++)
+ {
+ String name = in.readUTF();
+ int fieldNamesSize = in.readInt();
+ List<String> fieldNames = new ArrayList<>(fieldNamesSize);
+ for (int x = 0; x < fieldNamesSize; x++)
+ fieldNames.add(in.readUTF());
+ int fieldTypeSize = in.readInt();
+ List<String> fieldTypes = new ArrayList<>(fieldTypeSize);
+ for (int x = 0; x < fieldTypeSize; x++)
+ fieldTypes.add(in.readUTF());
+ builder.add(name, fieldNames, fieldTypes);
+ }
+ return builder.build();
+ }
+
+ public long serializedSize(Types t, Version version)
+ {
+ long size = sizeof(t.types.size());
+ for (UserType type : t.types.values())
+ {
+ size += sizeof(type.getNameAsString());
+ List<String> fieldNames = type.fieldNames().stream().map(FieldIdentifier::toString).collect(toList());
+ List<String> fieldTypes = type.fieldTypes().stream().map(AbstractType::asCQL3Type).map(CQL3Type::toString).collect(toList());
+ size += sizeof(fieldNames.size());
+ for (String s : fieldNames)
+ size += sizeof(s);
+ size += sizeof(fieldTypes.size());
+ for (String s : fieldTypes)
+ size += sizeof(s);
+ }
+ return size;
+ }
+ }
}
diff --git a/src/java/org/apache/cassandra/schema/UserFunctions.java b/src/java/org/apache/cassandra/schema/UserFunctions.java
index b40c704d0b..027fe71bc7 100644
--- a/src/java/org/apache/cassandra/schema/UserFunctions.java
+++ b/src/java/org/apache/cassandra/schema/UserFunctions.java
@@ -17,6 +17,7 @@
*/
package org.apache.cassandra.schema;
+import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.*;
import java.util.function.Predicate;
@@ -28,16 +29,22 @@ import com.google.common.collect.*;
import org.apache.cassandra.cql3.functions.*;
import org.apache.cassandra.db.marshal.AbstractType;
import org.apache.cassandra.db.marshal.UserType;
+import org.apache.cassandra.io.util.DataInputPlus;
+import org.apache.cassandra.io.util.DataOutputPlus;
+import org.apache.cassandra.tcm.serialization.UDTAwareMetadataSerializer;
+import org.apache.cassandra.tcm.serialization.Version;
import static java.util.stream.Collectors.toList;
import static com.google.common.collect.Iterables.any;
+import static org.apache.cassandra.db.TypeSizes.sizeof;
/**
* An immutable container for a keyspace's UDAs and UDFs.
*/
public final class UserFunctions implements Iterable<UserFunction>
{
+ public static final Serializer serializer = new Serializer();
public enum Filter implements Predicate<UserFunction>
{
ALL, UDF, UDA;
@@ -339,4 +346,61 @@ public final class UserFunctions implements Iterable<UserFunction>
return new FunctionsDiff<>(created, dropped, altered.build());
}
}
+
+ public static class Serializer implements UDTAwareMetadataSerializer<UserFunctions>
+ {
+ public void serialize(UserFunctions t, DataOutputPlus out, Version version) throws IOException
+ {
+ List<Function> udfs = t.functions.values().stream().filter(Filter.UDF).collect(Collectors.toList());
+ out.writeInt(udfs.size());
+ for (Function f : udfs)
+ {
+ assert f instanceof UDFunction;
+ UDFunction.serializer.serialize(((UDFunction) f), out, version);
+ }
+ List<Function> udas = t.functions.values().stream().filter(Filter.UDA).collect(Collectors.toList());
+ out.writeInt(udas.size());
+ for (Function f : udas)
+ {
+ assert f instanceof UDAggregate;
+ UDAggregate.serializer.serialize(((UDAggregate)f), out, version);
+ }
+ }
+
+ public UserFunctions deserialize(DataInputPlus in, Types types, Version version) throws IOException
+ {
+ int count = in.readInt();
+ List<UDFunction> udFunctions = new ArrayList<>(count);
+ for (int i = 0; i < count; i++)
+ udFunctions.add(UDFunction.serializer.deserialize(in, types, version));
+ count = in.readInt();
+ List<UDAggregate> udAggregates = new ArrayList<>(count);
+ for (int i = 0; i < count; i++)
+ udAggregates.add(UDAggregate.serializer.deserialize(in, types, udFunctions, version));
+ UserFunctions.Builder builder = UserFunctions.builder();
+ builder.add(udFunctions);
+ builder.add(udAggregates);
+ return builder.build();
+ }
+
+ public long serializedSize(UserFunctions t, Version version)
+ {
+ List<UserFunction> udfs = t.functions.values().stream().filter(Filter.UDF).collect(Collectors.toList());
+ int size = sizeof(udfs.size());
+ for (Function f : udfs)
+ {
+ assert f instanceof UDFunction;
+ size += UDFunction.serializer.serializedSize(((UDFunction) f), version);
+ }
+ List<Function> udas = t.functions.values().stream().filter(Filter.UDA).collect(Collectors.toList());
+ size += sizeof(udas.size());
+ for (Function f : udas)
+ {
+ assert f instanceof UDAggregate;
+ size += UDAggregate.serializer.serializedSize(((UDAggregate)f), version);
+ }
+
+ return size;
+ }
+ }
}
diff --git a/src/java/org/apache/cassandra/schema/ViewMetadata.java b/src/java/org/apache/cassandra/schema/ViewMetadata.java
index 0053249d42..481d1fb751 100644
--- a/src/java/org/apache/cassandra/schema/ViewMetadata.java
+++ b/src/java/org/apache/cassandra/schema/ViewMetadata.java
@@ -17,17 +17,26 @@
*/
package org.apache.cassandra.schema;
+import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Optional;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.antlr.runtime.RecognitionException;
import org.apache.cassandra.cql3.*;
+import org.apache.cassandra.db.TypeSizes;
import org.apache.cassandra.db.marshal.UserType;
+import org.apache.cassandra.io.util.DataInputPlus;
+import org.apache.cassandra.io.util.DataOutputPlus;
+import org.apache.cassandra.tcm.serialization.UDTAwareMetadataSerializer;
+import org.apache.cassandra.tcm.serialization.Version;
public final class ViewMetadata implements SchemaElement
{
+ public static final Serializer serializer = new Serializer();
+
public final TableId baseTableId;
public final String baseTableName;
@@ -226,4 +235,47 @@ public final class ViewMetadata implements SchemaElement
appendCqlTo(builder, withInternals, ifNotExists);
return builder.toString();
}
+
+ public static class Serializer implements UDTAwareMetadataSerializer<ViewMetadata>
+ {
+ public void serialize(ViewMetadata t, DataOutputPlus out, Version version) throws IOException
+ {
+ TableMetadata.serializer.serialize(t.metadata, out, version);
+ out.writeBoolean(t.includeAllColumns);
+ t.baseTableId.serialize(out);
+ out.writeUTF(t.whereClause.toCQLString());
+ out.writeUTF(t.baseTableName);
+ }
+
+ public ViewMetadata deserialize(DataInputPlus in, Types types, Version version) throws IOException
+ {
+ TableMetadata meta = TableMetadata.serializer.deserialize(in, types, version);
+ boolean includeAllColumns = in.readBoolean();
+ TableId tableId = TableId.deserialize(in);
+ String whereClause = in.readUTF();
+ String baseTableName = in.readUTF();
+
+ WhereClause wc;
+ try
+ {
+ wc = WhereClause.parse(whereClause);
+ }
+ catch (RecognitionException e)
+ {
+ throw new RuntimeException(String.format("Unexpected error while parsing materialized view's where clause for '%s' (got %s)", meta.name, whereClause));
+ }
+
+ return new ViewMetadata(tableId, baseTableName, includeAllColumns, wc, meta);
+ }
+
+ public long serializedSize(ViewMetadata t, Version version)
+ {
+ long size = TableMetadata.serializer.serializedSize(t.metadata, version);
+ size += TypeSizes.sizeof(t.includeAllColumns);
+ size += t.baseTableId.serializedSize();
+ size += TypeSizes.sizeof(t.whereClause.toCQLString());
+ size += TypeSizes.sizeof(t.baseTableName);
+ return size;
+ }
+ }
}
diff --git a/src/java/org/apache/cassandra/schema/Views.java b/src/java/org/apache/cassandra/schema/Views.java
index 15d13f35af..08cf3f7641 100644
--- a/src/java/org/apache/cassandra/schema/Views.java
+++ b/src/java/org/apache/cassandra/schema/Views.java
@@ -17,6 +17,7 @@
*/
package org.apache.cassandra.schema;
+import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
@@ -30,12 +31,19 @@ import javax.annotation.Nullable;
import com.google.common.collect.*;
import org.apache.cassandra.db.marshal.UserType;
+import org.apache.cassandra.io.util.DataInputPlus;
+import org.apache.cassandra.io.util.DataOutputPlus;
+import org.apache.cassandra.tcm.serialization.UDTAwareMetadataSerializer;
+import org.apache.cassandra.tcm.serialization.Version;
import static com.google.common.collect.Iterables.any;
import static com.google.common.collect.Iterables.transform;
+import static org.apache.cassandra.db.TypeSizes.sizeof;
public final class Views implements Iterable<ViewMetadata>
{
+ public static final Serializer serializer = new Serializer();
+
private static final Views NONE = builder().build();
private final ImmutableMap<String, ViewMetadata> views;
@@ -252,4 +260,34 @@ public final class Views implements Iterable<ViewMetadata>
return new ViewsDiff(created, dropped, altered.build());
}
}
+
+ public static class Serializer implements UDTAwareMetadataSerializer<Views>
+ {
+ public void serialize(Views t, DataOutputPlus out, Version version) throws IOException
+ {
+ out.writeInt(t.views.size());
+ for (ViewMetadata vm : t.views.values())
+ ViewMetadata.serializer.serialize(vm, out, version);
+ }
+
+ public Views deserialize(DataInputPlus in, Types types, Version version) throws IOException
+ {
+ int size = in.readInt();
+ Views.Builder builder = Views.builder();
+ for (int i = 0; i < size; i++)
+ {
+ ViewMetadata vm = ViewMetadata.serializer.deserialize(in, types, version);
+ builder.put(vm);
+ }
+ return builder.build();
+ }
+
+ public long serializedSize(Views t, Version version)
+ {
+ long size = sizeof(t.views.size());
+ for (ViewMetadata vm : t.views.values())
+ size += ViewMetadata.serializer.serializedSize(vm, version);
+ return size;
+ }
+ }
}
diff --git a/src/java/org/apache/cassandra/service/CassandraDaemon.java b/src/java/org/apache/cassandra/service/CassandraDaemon.java
index 8b7c761889..5ea65c6915 100644
--- a/src/java/org/apache/cassandra/service/CassandraDaemon.java
+++ b/src/java/org/apache/cassandra/service/CassandraDaemon.java
@@ -28,6 +28,7 @@ import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.List;
+import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;
@@ -80,6 +81,7 @@ import org.apache.cassandra.schema.TableMetadata;
import org.apache.cassandra.security.ThreadAwareSecurityManager;
import org.apache.cassandra.streaming.StreamManager;
import org.apache.cassandra.service.paxos.PaxosState;
+import org.apache.cassandra.tcm.Startup;
import org.apache.cassandra.utils.FBUtilities;
import org.apache.cassandra.utils.JMXServerUtils;
import org.apache.cassandra.utils.JVMStabilityInspector;
@@ -254,9 +256,25 @@ public class CassandraDaemon
NativeLibrary.tryMlockall();
+ DatabaseDescriptor.createAllDirectories();
+
+ Keyspace.setInitialized();
+
CommitLog.instance.start();
- runStartupChecks();
+ try
+ {
+ Startup.initialize(DatabaseDescriptor.getSeeds());
+ }
+ catch (InterruptedException | ExecutionException e)
+ {
+ throw new AssertionError("Can't initialize cluster metadata service");
+ }
+
+ QueryProcessor.registerStatementInvalidatingListener();
+
+ //TODO disabled b/c this involves checking schema but log replay hasn't run yet so it hasn't been constructed
+// runStartupChecks();
try
{
@@ -278,16 +296,16 @@ public class CassandraDaemon
// Populate token metadata before flushing, for token-aware sstable partitioning (#6696)
StorageService.instance.populateTokenMetadata();
- try
- {
- // load schema from disk
- Schema.instance.loadFromDisk();
- }
- catch (Exception e)
- {
- logger.error("Error while loading schema: ", e);
- throw e;
- }
+// try
+// {
+// // load schema from disk
+// Schema.instance.loadFromDisk();
+// }
+// catch (Exception e)
+// {
+// logger.error("Error while loading schema: ", e);
+// throw e;
+// }
setupVirtualKeyspaces();
diff --git a/src/java/org/apache/cassandra/service/StorageService.java b/src/java/org/apache/cassandra/service/StorageService.java
index 326af6b984..f9362a018d 100644
--- a/src/java/org/apache/cassandra/service/StorageService.java
+++ b/src/java/org/apache/cassandra/service/StorageService.java
@@ -22,7 +22,6 @@ import java.io.DataInputStream;
import java.io.IOError;
import java.io.IOException;
import java.net.InetAddress;
-import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.nio.file.Paths;
@@ -89,6 +88,27 @@ import com.google.common.collect.Sets;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.RateLimiter;
import com.google.common.util.concurrent.Uninterruptibles;
+
+import org.apache.cassandra.config.CassandraRelevantProperties;
+import org.apache.cassandra.config.DataStorageSpec;
+import org.apache.cassandra.cql3.QueryHandler;
+import org.apache.cassandra.dht.RangeStreamer.FetchReplica;
+import org.apache.cassandra.fql.FullQueryLogger;
+import org.apache.cassandra.fql.FullQueryLoggerOptions;
+import org.apache.cassandra.fql.FullQueryLoggerOptionsCompositeData;
+import org.apache.cassandra.io.util.File;
+import org.apache.cassandra.locator.ReplicaCollection.Builder.Conflict;
+import org.apache.cassandra.metrics.Sampler;
+import org.apache.cassandra.metrics.SamplingManager;
+import org.apache.cassandra.schema.Keyspaces;
+import org.apache.cassandra.service.disk.usage.DiskUsageBroadcaster;
+import org.apache.cassandra.tcm.ClusterMetadata;
+import org.apache.cassandra.tcm.ClusterMetadataService;
+import org.apache.cassandra.tcm.Startup;
+import org.apache.cassandra.utils.concurrent.Future;
+import org.apache.cassandra.schema.TableId;
+import org.apache.cassandra.utils.concurrent.FutureCombiner;
+import org.apache.cassandra.utils.concurrent.ImmediateFuture;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -211,6 +231,9 @@ import org.apache.cassandra.tcm.ClusterMetadata;
import org.apache.cassandra.tcm.ClusterMetadataService;
import org.apache.cassandra.tcm.Startup;
import org.apache.cassandra.tracing.TraceKeyspace;
+import org.apache.cassandra.net.AsyncOneResponse;
+import org.apache.cassandra.net.MessagingService;
+import org.apache.cassandra.streaming.*;
import org.apache.cassandra.transport.ClientResourceLimits;
import org.apache.cassandra.transport.ProtocolVersion;
import org.apache.cassandra.utils.Clock;
@@ -1161,7 +1184,6 @@ public class StorageService extends NotificationBroadcasterSupport implements IE
// gossip snitch infos (local DC and rack)
gossipSnitchInfo();
- Schema.instance.startSync();
LoadBroadcaster.instance.startBroadcasting();
DiskUsageBroadcaster.instance.startBroadcasting();
HintsService.instance.startDispatch();
@@ -1176,17 +1198,6 @@ public class StorageService extends NotificationBroadcasterSupport implements IE
snapshotManager.start();
}
- public void waitForSchema(long schemaTimeoutMillis, long ringTimeoutMillis)
- {
- Instant deadline = FBUtilities.now().plus(java.time.Duration.ofMillis(ringTimeoutMillis));
-
- while (Schema.instance.isEmpty() && FBUtilities.now().isBefore(deadline))
- Uninterruptibles.sleepUninterruptibly(1, TimeUnit.SECONDS);
-
- if (!Schema.instance.waitUntilReady(java.time.Duration.ofMillis(schemaTimeoutMillis)))
- throw new IllegalStateException("Could not achieve schema readiness in " + java.time.Duration.ofMillis(schemaTimeoutMillis));
- }
-
private void joinTokenRing(long schemaTimeoutMillis, long ringTimeoutMillis) throws ConfigurationException
{
joinTokenRing(!isSurveyMode, shouldBootstrap(), schemaTimeoutMillis, INDEFINITE, ringTimeoutMillis);
@@ -1247,8 +1258,6 @@ public class StorageService extends NotificationBroadcasterSupport implements IE
}
}
- setUpDistributedSystemKeyspaces();
-
if (finishJoiningRing)
{
if (dataAvailable)
@@ -1365,7 +1374,8 @@ public class StorageService extends NotificationBroadcasterSupport implements IE
{
if (setUpSchema)
{
- Schema.instance.transform(SchemaTransformations.updateSystemKeyspace(AuthKeyspace.metadata(), AuthKeyspace.GENERATION));
+ // TODO: this is not serializable!
+ Schema.instance.submit(SchemaTransformations.updateSystemKeyspace(AuthKeyspace.metadata(), AuthKeyspace.GENERATION));
}
DatabaseDescriptor.getRoleManager().setup();
@@ -1390,17 +1400,6 @@ public class StorageService extends NotificationBroadcasterSupport implements IE
return authSetupCalled.get();
}
-
- @VisibleForTesting
- public void setUpDistributedSystemKeyspaces()
- {
- Schema.instance.transform(SchemaTransformations.updateSystemKeyspace(TraceKeyspace.metadata(), TraceKeyspace.GENERATION));
- Schema.instance.transform(SchemaTransformations.updateSystemKeyspace(SystemDistributedKeyspace.metadata(), SystemDistributedKeyspace.GENERATION));
- Schema.instance.transform(SchemaTransformations.updateSystemKeyspace(AuthKeyspace.metadata(), AuthKeyspace.GENERATION));
-
- Schema.instance.transform(SchemaTransformations.updateSystemKeyspace(DistributedMetadataLogKeyspace.metadata(), 1));
- }
-
public boolean isJoined()
{
return tokenMetadata.isMember(FBUtilities.getBroadcastAddressAndPort()) && !isSurveyMode;
@@ -1949,7 +1948,6 @@ public class StorageService extends NotificationBroadcasterSupport implements IE
else
SystemKeyspace.setBootstrapState(SystemKeyspace.BootstrapState.IN_PROGRESS);
setMode(Mode.JOINING, "waiting for ring information", true);
- waitForSchema(schemaTimeoutMillis, ringTimeoutMillis);
setMode(Mode.JOINING, "schema complete, ready to bootstrap", true);
setMode(Mode.JOINING, "waiting for pending range calculation", true);
PendingRangeCalculatorService.instance.blockUntilFinished();
@@ -6368,12 +6366,14 @@ public class StorageService extends NotificationBroadcasterSupport implements IE
public void resetLocalSchema() throws IOException
{
- Schema.instance.resetLocalSchema();
+ // TODO remove from MBean
+ throw new RuntimeException("Deprecated");
}
public void reloadLocalSchema()
{
- Schema.instance.reloadSchemaAndAnnounceVersion();
+ // TODO remove from MBean
+ throw new RuntimeException("Deprecated");
}
public void setTraceProbability(double probability)
@@ -6846,17 +6846,15 @@ public class StorageService extends NotificationBroadcasterSupport implements IE
@Override
public Map<String, Set<InetAddress>> getOutstandingSchemaVersions()
{
- Map<UUID, Set<InetAddressAndPort>> outstanding = Schema.instance.getOutstandingSchemaVersions();
- return outstanding.entrySet().stream().collect(Collectors.toMap(e -> e.getKey().toString(),
- e -> e.getValue().stream().map(InetSocketAddress::getAddress).collect(Collectors.toSet())));
+ // TODO remove from MBean
+ throw new RuntimeException("Deprecated");
}
@Override
public Map<String, Set<String>> getOutstandingSchemaVersionsWithPort()
{
- Map<UUID, Set<InetAddressAndPort>> outstanding = Schema.instance.getOutstandingSchemaVersions();
- return outstanding.entrySet().stream().collect(Collectors.toMap(e -> e.getKey().toString(),
- e -> e.getValue().stream().map(Object::toString).collect(Collectors.toSet())));
+ // TODO remove from MBean
+ throw new RuntimeException("Deprecated");
}
public boolean autoOptimiseIncRepairStreams()
diff --git a/src/java/org/apache/cassandra/tcm/ClusterMetadata.java b/src/java/org/apache/cassandra/tcm/ClusterMetadata.java
index b1eb2316f3..4d9f5a28cf 100644
--- a/src/java/org/apache/cassandra/tcm/ClusterMetadata.java
+++ b/src/java/org/apache/cassandra/tcm/ClusterMetadata.java
@@ -35,6 +35,7 @@ import org.apache.cassandra.io.util.DataInputPlus;
import org.apache.cassandra.io.util.DataOutputPlus;
import org.apache.cassandra.locator.EndpointsForRange;
import org.apache.cassandra.locator.InetAddressAndPort;
+import org.apache.cassandra.schema.DistributedSchema;
import org.apache.cassandra.tcm.extensions.ExtensionKey;
import org.apache.cassandra.tcm.extensions.ExtensionValue;
import org.apache.cassandra.tcm.serialization.MetadataSerializer;
@@ -56,6 +57,7 @@ public class ClusterMetadata
public final IPartitioner partitioner; // Set during (initial) construction and not modifiable via Transformer
public final ImmutableMap<ExtensionKey<?,?>, ExtensionValue<?>> extensions;
+ public final DistributedSchema schema;
public final EndpointsForRange cmsReplicas;
public final ImmutableSet<InetAddressAndPort> cmsMembers;
@@ -65,6 +67,7 @@ public class ClusterMetadata
Period.EMPTY,
true,
partitioner,
+ DistributedSchema.first(),
ImmutableSet.of(),
ImmutableMap.of());
}
@@ -73,6 +76,7 @@ public class ClusterMetadata
long period,
boolean lastInPeriod,
IPartitioner partitioner,
+ DistributedSchema schema,
Set<InetAddressAndPort> cmsMembers,
Map<ExtensionKey<?, ?>, ExtensionValue<?>> extensions)
{
@@ -80,6 +84,7 @@ public class ClusterMetadata
this.period = period;
this.lastInPeriod = lastInPeriod;
this.partitioner = partitioner;
+ this.schema = schema;
this.cmsMembers = ImmutableSet.copyOf(cmsMembers);
this.extensions = ImmutableMap.copyOf(extensions);
@@ -110,6 +115,17 @@ public class ClusterMetadata
return new Transformer(this, this.nextEpoch(), sealPeriod);
}
+ public ClusterMetadata forceEpoch(Epoch epoch)
+ {
+ return new ClusterMetadata(epoch,
+ period,
+ lastInPeriod,
+ partitioner,
+ schema,
+ cmsMembers,
+ extensions);
+ }
+
public Epoch nextEpoch()
{
return epoch.nextEpoch();
@@ -127,6 +143,7 @@ public class ClusterMetadata
private final long period;
private final boolean lastInPeriod;
private final IPartitioner partitioner;
+ private DistributedSchema schema;
private final Set<InetAddressAndPort> cmsMembers;
private final Map<ExtensionKey<?, ?>, ExtensionValue<?>> extensions;
private final Set<MetadataKey> modifiedKeys;
@@ -138,11 +155,18 @@ public class ClusterMetadata
this.period = metadata.nextPeriod();
this.lastInPeriod = lastInPeriod;
this.partitioner = metadata.partitioner;
+ this.schema = metadata.schema;
this.cmsMembers = new HashSet<>(metadata.cmsMembers);
extensions = new HashMap<>(metadata.extensions);
modifiedKeys = new HashSet<>();
}
+ public Transformer with(DistributedSchema schema)
+ {
+ this.schema = schema;
+ return this;
+ }
+
public Transformer withCMSMember(InetAddressAndPort member)
{
cmsMembers.add(member);
@@ -199,10 +223,17 @@ public class ClusterMetadata
mutable.withLastModified(epoch);
}
+ if (schema != base.schema)
+ {
+ modifiedKeys.add(MetadataKeys.SCHEMA);
+ schema = schema.withLastModified(epoch);
+ }
+
return new Transformed(new ClusterMetadata(epoch,
period,
lastInPeriod,
partitioner,
+ schema,
cmsMembers,
extensions),
ImmutableSet.copyOf(modifiedKeys));
@@ -216,6 +247,7 @@ public class ClusterMetadata
", epoch=" + epoch +
", lastInPeriod=" + lastInPeriod +
", partitioner=" + partitioner +
+ ", schema=" + schema +
", extensions=" + extensions +
", cmsMembers=" + cmsMembers +
", modifiedKeys=" + modifiedKeys +
@@ -286,6 +318,7 @@ public class ClusterMetadata
out.writeUnsignedVInt(metadata.period);
out.writeBoolean(metadata.lastInPeriod);
out.writeUTF(metadata.partitioner.getClass().getCanonicalName());
+ DistributedSchema.serializer.serialize(metadata.schema, out, version);
out.writeInt(metadata.extensions.size());
for (Map.Entry<ExtensionKey<?, ?>, ExtensionValue<?>> entry : metadata.extensions.entrySet())
{
@@ -307,6 +340,7 @@ public class ClusterMetadata
long period = in.readUnsignedVInt();
boolean lastInPeriod = in.readBoolean();
IPartitioner partitioner = FBUtilities.newPartitioner(in.readUTF());
+ DistributedSchema schema = DistributedSchema.serializer.deserialize(in, version);
int items = in.readInt();
Map<ExtensionKey<?, ?>, ExtensionValue<?>> extensions = new HashMap<>(items);
for (int i = 0; i < items; i++)
@@ -324,6 +358,7 @@ public class ClusterMetadata
period,
lastInPeriod,
partitioner,
+ schema,
members,
extensions);
}
@@ -339,7 +374,8 @@ public class ClusterMetadata
size += Epoch.serializer.serializedSize(metadata.epoch) +
VIntCoding.computeUnsignedVIntSize(metadata.period) +
TypeSizes.BOOL_SIZE +
- sizeof(metadata.partitioner.getClass().getCanonicalName());
+ sizeof(metadata.partitioner.getClass().getCanonicalName()) +
+ DistributedSchema.serializer.serializedSize(metadata.schema, version);
size += TypeSizes.INT_SIZE;
for (InetAddressAndPort member : metadata.cmsMembers)
diff --git a/src/java/org/apache/cassandra/tcm/ClusterMetadataService.java b/src/java/org/apache/cassandra/tcm/ClusterMetadataService.java
index 4638d62f58..4a7464c3a3 100644
--- a/src/java/org/apache/cassandra/tcm/ClusterMetadataService.java
+++ b/src/java/org/apache/cassandra/tcm/ClusterMetadataService.java
@@ -35,6 +35,7 @@ import org.apache.cassandra.locator.InetAddressAndPort;
import org.apache.cassandra.net.IVerbHandler;
import org.apache.cassandra.net.Message;
import org.apache.cassandra.net.NoPayload;
+import org.apache.cassandra.schema.DistributedSchema;
import org.apache.cassandra.service.StorageService;
import org.apache.cassandra.tcm.log.Entry;
import org.apache.cassandra.tcm.log.LocalLog;
@@ -43,6 +44,7 @@ import org.apache.cassandra.tcm.transformations.SealPeriod;
import org.apache.cassandra.utils.FBUtilities;
import static org.apache.cassandra.tcm.ClusterMetadataService.State.LOCAL;
+import static org.apache.cassandra.tcm.compatibility.GossipHelper.emptyWithSchemaFromSystemTables;
public class ClusterMetadataService
@@ -159,6 +161,25 @@ public class ClusterMetadataService
commitRequestHandler = isMemberOfOwnershipGroup ? new Commit.Handler(processor, replicator) : null;
}
+ @SuppressWarnings("resource")
+ public static void initializeForTools(boolean loadSSTables)
+ {
+ if (instance != null)
+ return;
+ ClusterMetadata emptyFromSystemTables = emptyWithSchemaFromSystemTables();
+ emptyFromSystemTables.schema.initializeKeyspaceInstances(DistributedSchema.empty(), loadSSTables);
+ emptyFromSystemTables = emptyFromSystemTables.forceEpoch(Epoch.EMPTY);
+ LocalLog log = LocalLog.sync(emptyFromSystemTables, new AtomicLongBackedProcessor.InMemoryStorage(), true);
+ ClusterMetadataService cms = new ClusterMetadataService(MetadataSnapshots.NO_OP,
+ log,
+ new AtomicLongBackedProcessor(log),
+ Commit.Replicator.NO_OP,
+ false);
+ log.bootstrap(FBUtilities.getBroadcastAddressAndPort());
+ ClusterMetadataService.setInstance(cms);
+ }
+
+
public boolean isCurrentMember(InetAddressAndPort peer)
{
return ClusterMetadata.current().isCMSMember(peer);
diff --git a/src/java/org/apache/cassandra/tcm/MetadataKeys.java b/src/java/org/apache/cassandra/tcm/MetadataKeys.java
index b4dbdba5f9..6db00c2964 100644
--- a/src/java/org/apache/cassandra/tcm/MetadataKeys.java
+++ b/src/java/org/apache/cassandra/tcm/MetadataKeys.java
@@ -25,7 +25,10 @@ import com.google.common.collect.ImmutableSet;
public class MetadataKeys
{
public static final String CORE_NS = MetadataKeys.class.getPackage().getName().toLowerCase(Locale.ROOT);
- public static final ImmutableSet<MetadataKey> CORE_METADATA = ImmutableSet.of();
+
+ public static final MetadataKey SCHEMA = make(CORE_NS, "schema", "dist_schema");
+
+ public static final ImmutableSet<MetadataKey> CORE_METADATA = ImmutableSet.of(SCHEMA);
public static MetadataKey make(String...parts)
{
diff --git a/src/java/org/apache/cassandra/tcm/Transformation.java b/src/java/org/apache/cassandra/tcm/Transformation.java
index 41dbbffad4..1a9db06611 100644
--- a/src/java/org/apache/cassandra/tcm/Transformation.java
+++ b/src/java/org/apache/cassandra/tcm/Transformation.java
@@ -33,6 +33,7 @@ import org.apache.cassandra.io.util.DataOutputPlus;
import org.apache.cassandra.tcm.serialization.AsymmetricMetadataSerializer;
import org.apache.cassandra.tcm.serialization.VerboseMetadataSerializer;
import org.apache.cassandra.tcm.serialization.Version;
+import org.apache.cassandra.tcm.transformations.AlterSchema;
import org.apache.cassandra.tcm.transformations.CustomTransformation;
import org.apache.cassandra.tcm.transformations.ForceSnapshot;
import org.apache.cassandra.tcm.transformations.SealPeriod;
@@ -137,7 +138,7 @@ public interface Transformation
INITIALIZE_CMS(() -> Initialize.serializer),
FORCE_SNAPSHOT(() -> ForceSnapshot.serializer),
SEAL_PERIOD(() -> SealPeriod.serializer),
-
+ SCHEMA_CHANGE(() -> AlterSchema.serializer),
CUSTOM(() -> CustomTransformation.serializer);
private final Supplier<AsymmetricMetadataSerializer<Transformation, ? extends Transformation>> serializer;
diff --git a/src/java/org/apache/cassandra/tcm/compatibility/GossipHelper.java b/src/java/org/apache/cassandra/tcm/compatibility/GossipHelper.java
new file mode 100644
index 0000000000..9ff241f72b
--- /dev/null
+++ b/src/java/org/apache/cassandra/tcm/compatibility/GossipHelper.java
@@ -0,0 +1,52 @@
+/*
+ * 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.tcm.compatibility;
+
+import java.util.Collections;
+import java.util.UUID;
+
+import org.apache.cassandra.config.DatabaseDescriptor;
+import org.apache.cassandra.gms.ApplicationState;
+import org.apache.cassandra.gms.Gossiper;
+import org.apache.cassandra.schema.DistributedSchema;
+import org.apache.cassandra.schema.SchemaKeyspace;
+import org.apache.cassandra.service.StorageService;
+import org.apache.cassandra.tcm.ClusterMetadata;
+import org.apache.cassandra.tcm.Epoch;
+import org.apache.cassandra.tcm.Period;
+
+public class GossipHelper
+{
+ public static void updateSchemaVersionInGossip(UUID version)
+ {
+ Gossiper.instance.addLocalApplicationState(ApplicationState.SCHEMA,
+ StorageService.instance.valueFactory.schema(version));
+ }
+
+ public static ClusterMetadata emptyWithSchemaFromSystemTables()
+ {
+ return new ClusterMetadata(Epoch.UPGRADE_STARTUP,
+ Period.EMPTY,
+ true,
+ DatabaseDescriptor.getPartitioner(),
+ DistributedSchema.fromSystemTables(SchemaKeyspace.fetchNonSystemKeyspaces()),
+ Collections.emptySet(),
+ Collections.emptyMap());
+ }
+}
diff --git a/src/java/org/apache/cassandra/tcm/listeners/SchemaListener.java b/src/java/org/apache/cassandra/tcm/listeners/SchemaListener.java
new file mode 100644
index 0000000000..516c7450c8
--- /dev/null
+++ b/src/java/org/apache/cassandra/tcm/listeners/SchemaListener.java
@@ -0,0 +1,48 @@
+/*
+ * 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.tcm.listeners;
+
+import org.apache.cassandra.db.SystemKeyspace;
+import org.apache.cassandra.gms.ApplicationState;
+import org.apache.cassandra.gms.Gossiper;
+import org.apache.cassandra.schema.Schema;
+import org.apache.cassandra.schema.SchemaDiagnostics;
+import org.apache.cassandra.service.StorageService;
+import org.apache.cassandra.tcm.ClusterMetadata;
+
+public class SchemaListener implements ChangeListener
+{
+ @Override
+ public void notifyPreCommit(ClusterMetadata prev, ClusterMetadata next)
+ {
+ if (!next.schema.lastModified().equals(prev.schema.lastModified()))
+ next.schema.initializeKeyspaceInstances(prev.schema);
+ }
+
+ @Override
+ public void notifyPostCommit(ClusterMetadata prev, ClusterMetadata next)
+ {
+ if (!next.schema.lastModified().equals(prev.schema.lastModified()))
+ {
+ SchemaDiagnostics.versionUpdated(Schema.instance);
+ Gossiper.instance.addLocalApplicationState(ApplicationState.SCHEMA, StorageService.instance.valueFactory.schema(next.schema.getVersion()));
+ SystemKeyspace.updateSchemaVersion(next.schema.getVersion());
+ }
+ }
+}
diff --git a/src/java/org/apache/cassandra/tcm/log/LocalLog.java b/src/java/org/apache/cassandra/tcm/log/LocalLog.java
index e8acf61f41..d6165386ba 100644
--- a/src/java/org/apache/cassandra/tcm/log/LocalLog.java
+++ b/src/java/org/apache/cassandra/tcm/log/LocalLog.java
@@ -48,6 +48,7 @@ import org.apache.cassandra.tcm.listeners.ChangeListener;
import org.apache.cassandra.tcm.listeners.InitializationListener;
import org.apache.cassandra.tcm.listeners.LogListener;
import org.apache.cassandra.tcm.listeners.MetadataSnapshotListener;
+import org.apache.cassandra.tcm.listeners.SchemaListener;
import org.apache.cassandra.tcm.transformations.ForceSnapshot;
import org.apache.cassandra.tcm.transformations.cms.PreInitialize;
import org.apache.cassandra.utils.Closeable;
@@ -542,6 +543,7 @@ public abstract class LocalLog implements Closeable
{
addListener(snapshotListener());
addListener(new InitializationListener());
+ addListener(new SchemaListener());
addListener(new MetadataSnapshotListener());
}
diff --git a/src/java/org/apache/cassandra/tcm/transformations/AlterSchema.java b/src/java/org/apache/cassandra/tcm/transformations/AlterSchema.java
new file mode 100644
index 0000000000..af96eba01c
--- /dev/null
+++ b/src/java/org/apache/cassandra/tcm/transformations/AlterSchema.java
@@ -0,0 +1,107 @@
+/*
+ * 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.tcm.transformations;
+
+import java.io.IOException;
+
+import org.apache.cassandra.exceptions.ConfigurationException;
+import org.apache.cassandra.exceptions.InvalidRequestException;
+import org.apache.cassandra.io.util.DataInputPlus;
+import org.apache.cassandra.io.util.DataOutputPlus;
+import org.apache.cassandra.schema.DistributedSchema;
+import org.apache.cassandra.schema.Keyspaces;
+import org.apache.cassandra.schema.Schema;
+import org.apache.cassandra.schema.SchemaProvider;
+import org.apache.cassandra.schema.SchemaTransformation;
+import org.apache.cassandra.tcm.ClusterMetadata;
+import org.apache.cassandra.tcm.Transformation;
+import org.apache.cassandra.tcm.serialization.AsymmetricMetadataSerializer;
+import org.apache.cassandra.tcm.serialization.Version;
+
+public class AlterSchema implements Transformation
+{
+ public static final Serializer serializer = new Serializer();
+
+ protected final SchemaTransformation schemaTransformation;
+ protected final SchemaProvider schemaProvider;
+
+ public AlterSchema(SchemaTransformation schemaTransformation,
+ SchemaProvider schemaProvider)
+ {
+ this.schemaTransformation = schemaTransformation;
+ this.schemaProvider = schemaProvider;
+ }
+
+ @Override
+ public Kind kind()
+ {
+ return Kind.SCHEMA_CHANGE;
+ }
+
+ @Override
+ public final Result execute(ClusterMetadata prev)
+ {
+ Keyspaces newKeyspaces;
+
+ try
+ {
+ newKeyspaces = schemaTransformation.apply(prev, prev.schema.getKeyspaces());
+ }
+ catch (ConfigurationException | InvalidRequestException t)
+ {
+ return new Rejected(t.getMessage());
+ }
+
+ DistributedSchema snapshotAfter = new DistributedSchema(newKeyspaces);
+ ClusterMetadata.Transformer next = prev.transformer().with(snapshotAfter);
+
+ return success(next);
+ }
+
+ static class Serializer implements AsymmetricMetadataSerializer<Transformation, AlterSchema>
+ {
+ @Override
+ public void serialize(Transformation t, DataOutputPlus out, Version version) throws IOException
+ {
+ SchemaTransformation.serializer.serialize(((AlterSchema) t).schemaTransformation, out, version);
+ }
+
+ @Override
+ public AlterSchema deserialize(DataInputPlus in, Version version) throws IOException
+ {
+ return new AlterSchema(SchemaTransformation.serializer.deserialize(in, version),
+ Schema.instance);
+
+ }
+
+ @Override
+ public long serializedSize(Transformation t, Version version)
+ {
+ return SchemaTransformation.serializer.serializedSize(((AlterSchema) t).schemaTransformation, version);
+ }
+ }
+
+ @Override
+ public String toString()
+ {
+ return "SchemaChangeRequest{" +
+ ", schemaTransformation=" + schemaTransformation +
+ '}';
+ }
+}
diff --git a/src/java/org/apache/cassandra/tools/SSTableExpiredBlockers.java b/src/java/org/apache/cassandra/tools/SSTableExpiredBlockers.java
index 04d77a9d4a..81a239fd92 100644
--- a/src/java/org/apache/cassandra/tools/SSTableExpiredBlockers.java
+++ b/src/java/org/apache/cassandra/tools/SSTableExpiredBlockers.java
@@ -35,6 +35,7 @@ import org.apache.cassandra.io.sstable.format.SSTableFormat.Components;
import org.apache.cassandra.io.sstable.format.SSTableReader;
import org.apache.cassandra.schema.Schema;
import org.apache.cassandra.schema.TableMetadata;
+import org.apache.cassandra.tcm.ClusterMetadataService;
import static org.apache.cassandra.utils.Clock.Global.currentTimeMillis;
@@ -58,10 +59,9 @@ public class SSTableExpiredBlockers
}
Util.initDatabaseDescriptor();
-
+ ClusterMetadataService.initializeForTools(false);
String keyspace = args[args.length - 2];
String columnfamily = args[args.length - 1];
- Schema.instance.loadFromDisk();
TableMetadata metadata = Schema.instance.validateTable(keyspace, columnfamily);
diff --git a/src/java/org/apache/cassandra/tools/SSTableExport.java b/src/java/org/apache/cassandra/tools/SSTableExport.java
index 43fda169ef..ef8e4b5e49 100644
--- a/src/java/org/apache/cassandra/tools/SSTableExport.java
+++ b/src/java/org/apache/cassandra/tools/SSTableExport.java
@@ -48,6 +48,7 @@ import org.apache.cassandra.io.sstable.format.SSTableReader;
import org.apache.cassandra.io.util.File;
import org.apache.cassandra.schema.TableMetadata;
import org.apache.cassandra.schema.TableMetadataRef;
+
import org.apache.cassandra.utils.FBUtilities;
/**
diff --git a/src/java/org/apache/cassandra/tools/SSTableLevelResetter.java b/src/java/org/apache/cassandra/tools/SSTableLevelResetter.java
index 0aa9e10814..6010777018 100644
--- a/src/java/org/apache/cassandra/tools/SSTableLevelResetter.java
+++ b/src/java/org/apache/cassandra/tools/SSTableLevelResetter.java
@@ -31,6 +31,7 @@ import org.apache.cassandra.io.sstable.format.SSTableFormat.Components;
import org.apache.cassandra.io.sstable.format.StatsComponent;
import org.apache.cassandra.io.sstable.metadata.StatsMetadata;
import org.apache.cassandra.schema.Schema;
+import org.apache.cassandra.tcm.ClusterMetadataService;
import org.apache.cassandra.utils.JVMStabilityInspector;
/**
@@ -60,18 +61,15 @@ public class SSTableLevelResetter
}
Util.initDatabaseDescriptor();
-
+ ClusterMetadataService.initializeForTools(false);
// TODO several daemon threads will run from here.
// So we have to explicitly call System.exit.
try
{
- // load keyspace descriptions.
- Schema.instance.loadFromDisk();
-
String keyspaceName = args[1];
String columnfamily = args[2];
// validate columnfamily
- if (Schema.instance.getTableMetadataRef(keyspaceName, columnfamily) == null)
+ if (Schema.instance.getTableMetadata(keyspaceName, columnfamily) == null)
{
System.err.println("ColumnFamily not found: " + keyspaceName + "/" + columnfamily);
System.exit(1);
diff --git a/src/java/org/apache/cassandra/tools/SSTableOfflineRelevel.java b/src/java/org/apache/cassandra/tools/SSTableOfflineRelevel.java
index 16faa9283b..42fe40c0c4 100644
--- a/src/java/org/apache/cassandra/tools/SSTableOfflineRelevel.java
+++ b/src/java/org/apache/cassandra/tools/SSTableOfflineRelevel.java
@@ -44,6 +44,7 @@ import org.apache.cassandra.io.sstable.format.SSTableFormat.Components;
import org.apache.cassandra.io.sstable.format.SSTableReader;
import org.apache.cassandra.io.util.File;
import org.apache.cassandra.schema.Schema;
+import org.apache.cassandra.tcm.ClusterMetadataService;
/**
* Create a decent leveling for the given keyspace/column family
@@ -88,13 +89,12 @@ public class SSTableOfflineRelevel
}
Util.initDatabaseDescriptor();
-
+ ClusterMetadataService.initializeForTools(false);
boolean dryRun = args[0].equals("--dry-run");
String keyspace = args[args.length - 2];
String columnfamily = args[args.length - 1];
- Schema.instance.loadFromDisk();
- if (Schema.instance.getTableMetadataRef(keyspace, columnfamily) == null)
+ if (Schema.instance.getTableMetadata(keyspace, columnfamily) == null)
throw new IllegalArgumentException(String.format("Unknown keyspace/table %s.%s",
keyspace,
columnfamily));
diff --git a/src/java/org/apache/cassandra/tools/StandaloneSSTableUtil.java b/src/java/org/apache/cassandra/tools/StandaloneSSTableUtil.java
index 4d2acd20d4..1dd0ba53d6 100644
--- a/src/java/org/apache/cassandra/tools/StandaloneSSTableUtil.java
+++ b/src/java/org/apache/cassandra/tools/StandaloneSSTableUtil.java
@@ -18,10 +18,11 @@
*/
package org.apache.cassandra.tools;
-import org.apache.cassandra.schema.TableMetadata;
import org.apache.cassandra.schema.Schema;
+import org.apache.cassandra.schema.TableMetadata;
import org.apache.cassandra.db.Directories;
import org.apache.cassandra.db.lifecycle.LifecycleTransaction;
+import org.apache.cassandra.tcm.ClusterMetadataService;
import org.apache.cassandra.utils.OutputHandler;
import org.apache.commons.cli.*;
@@ -43,13 +44,13 @@ public class StandaloneSSTableUtil
public static void main(String args[])
{
+
Options options = Options.parseArgs(args);
try
{
// load keyspace descriptions.
Util.initDatabaseDescriptor();
- Schema.instance.loadFromDisk();
-
+ ClusterMetadataService.initializeForTools(false);
TableMetadata metadata = Schema.instance.getTableMetadata(options.keyspaceName, options.cfName);
if (metadata == null)
throw new IllegalArgumentException(String.format("Unknown keyspace/table %s.%s",
diff --git a/src/java/org/apache/cassandra/tools/StandaloneScrubber.java b/src/java/org/apache/cassandra/tools/StandaloneScrubber.java
index 7a458683bf..70464c3007 100644
--- a/src/java/org/apache/cassandra/tools/StandaloneScrubber.java
+++ b/src/java/org/apache/cassandra/tools/StandaloneScrubber.java
@@ -51,6 +51,7 @@ import org.apache.cassandra.io.sstable.format.SSTableFormat.Components;
import org.apache.cassandra.io.sstable.format.SSTableReader;
import org.apache.cassandra.io.util.File;
import org.apache.cassandra.schema.Schema;
+import org.apache.cassandra.tcm.ClusterMetadataService;
import org.apache.cassandra.tools.BulkLoader.CmdLineOptions;
import org.apache.cassandra.utils.JVMStabilityInspector;
import org.apache.cassandra.utils.OutputHandler;
@@ -84,12 +85,10 @@ public class StandaloneScrubber
DatabaseDescriptor.toolInitialization(false); //Necessary for testing
else
Util.initDatabaseDescriptor();
+ ClusterMetadataService.initializeForTools(false);
try
{
- // load keyspace descriptions.
- Schema.instance.loadFromDisk();
-
if (Schema.instance.getKeyspaceMetadata(options.keyspaceName) == null)
throw new IllegalArgumentException(String.format("Unknown keyspace %s", options.keyspaceName));
diff --git a/src/java/org/apache/cassandra/tools/StandaloneSplitter.java b/src/java/org/apache/cassandra/tools/StandaloneSplitter.java
index 5229c7764a..ede03e3789 100644
--- a/src/java/org/apache/cassandra/tools/StandaloneSplitter.java
+++ b/src/java/org/apache/cassandra/tools/StandaloneSplitter.java
@@ -46,7 +46,7 @@ import org.apache.cassandra.io.sstable.Descriptor;
import org.apache.cassandra.io.sstable.SSTable;
import org.apache.cassandra.io.sstable.format.SSTableReader;
import org.apache.cassandra.io.util.File;
-import org.apache.cassandra.schema.Schema;
+import org.apache.cassandra.tcm.ClusterMetadataService;
import org.apache.cassandra.utils.JVMStabilityInspector;
import static org.apache.cassandra.tools.BulkLoader.CmdLineOptions;
@@ -72,9 +72,7 @@ public class StandaloneSplitter
try
{
- // load keyspace descriptions.
- Schema.instance.loadFromDisk();
-
+ ClusterMetadataService.initializeForTools(false);
String ksName = null;
String cfName = null;
Map<Descriptor, Set<Component>> parsedFilenames = new HashMap<Descriptor, Set<Component>>();
diff --git a/src/java/org/apache/cassandra/tools/StandaloneUpgrader.java b/src/java/org/apache/cassandra/tools/StandaloneUpgrader.java
index 657ec05d65..ed899ef263 100644
--- a/src/java/org/apache/cassandra/tools/StandaloneUpgrader.java
+++ b/src/java/org/apache/cassandra/tools/StandaloneUpgrader.java
@@ -42,6 +42,7 @@ import org.apache.cassandra.io.sstable.Descriptor;
import org.apache.cassandra.io.sstable.format.SSTableFormat;
import org.apache.cassandra.io.sstable.format.SSTableReader;
import org.apache.cassandra.schema.Schema;
+import org.apache.cassandra.tcm.ClusterMetadataService;
import org.apache.cassandra.utils.JVMStabilityInspector;
import org.apache.cassandra.utils.OutputHandler;
@@ -61,13 +62,10 @@ public class StandaloneUpgrader
DatabaseDescriptor.toolInitialization(false); //Necessary for testing
else
Util.initDatabaseDescriptor();
-
+ ClusterMetadataService.initializeForTools(false);
try
{
- // load keyspace descriptions.
- Schema.instance.loadFromDisk();
-
- if (Schema.instance.getTableMetadataRef(options.keyspace, options.cf) == null)
+ if (Schema.instance.getTableMetadata(options.keyspace, options.cf) == null)
throw new IllegalArgumentException(String.format("Unknown keyspace/table %s.%s",
options.keyspace,
options.cf));
diff --git a/src/java/org/apache/cassandra/tools/StandaloneVerifier.java b/src/java/org/apache/cassandra/tools/StandaloneVerifier.java
index b32d81bb13..57e73cb8b9 100644
--- a/src/java/org/apache/cassandra/tools/StandaloneVerifier.java
+++ b/src/java/org/apache/cassandra/tools/StandaloneVerifier.java
@@ -47,6 +47,7 @@ import org.apache.cassandra.io.sstable.Descriptor;
import org.apache.cassandra.io.sstable.IVerifier;
import org.apache.cassandra.io.sstable.format.SSTableReader;
import org.apache.cassandra.schema.Schema;
+import org.apache.cassandra.tcm.ClusterMetadataService;
import org.apache.cassandra.utils.JVMStabilityInspector;
import org.apache.cassandra.utils.OutputHandler;
import org.apache.cassandra.utils.Throwables;
@@ -76,19 +77,16 @@ public class StandaloneVerifier
System.exit(1);
}
initDatabaseDescriptorForTool();
-
+ ClusterMetadataService.initializeForTools(false);
System.out.println("sstableverify using the following options: " + options);
List<SSTableReader> sstables = new ArrayList<>();
int exitCode = 0;
try
{
- // load keyspace descriptions.
- Schema.instance.loadFromDisk();
-
boolean hasFailed = false;
- if (Schema.instance.getTableMetadataRef(options.keyspaceName, options.cfName) == null)
+ if (Schema.instance.getTableMetadata(options.keyspaceName, options.cfName) == null)
throw new IllegalArgumentException(String.format("Unknown keyspace/table %s.%s",
options.keyspaceName,
options.cfName));
diff --git a/src/java/org/apache/cassandra/tools/Util.java b/src/java/org/apache/cassandra/tools/Util.java
index 414d04ad4c..234ba86473 100644
--- a/src/java/org/apache/cassandra/tools/Util.java
+++ b/src/java/org/apache/cassandra/tools/Util.java
@@ -316,8 +316,9 @@ public final class Util
SerializationHeader.Component header = statsComponent.serializationHeader();
IPartitioner partitioner = FBUtilities.newPartitioner(desc);
+ DatabaseDescriptor.setPartitionerUnsafe(partitioner);
- TableMetadata.Builder builder = TableMetadata.builder("keyspace", "table").partitioner(partitioner);
+ TableMetadata.Builder builder = TableMetadata.builder("keyspace", "table").partitioner(partitioner).offline();
header.getStaticColumns().entrySet().stream()
.forEach(entry -> {
ColumnIdentifier ident = ColumnIdentifier.getInterned(UTF8Type.instance.getString(entry.getKey()), true);
diff --git a/src/java/org/apache/cassandra/utils/FBUtilities.java b/src/java/org/apache/cassandra/utils/FBUtilities.java
index c760659405..411034db3e 100644
--- a/src/java/org/apache/cassandra/utils/FBUtilities.java
+++ b/src/java/org/apache/cassandra/utils/FBUtilities.java
@@ -35,6 +35,7 @@ import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
+import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Collection;
@@ -59,14 +60,18 @@ import javax.annotation.Nullable;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
+import com.fasterxml.jackson.databind.SerializationFeature;
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
+import com.google.common.base.Preconditions;
+import org.apache.cassandra.io.util.File;
+import org.apache.cassandra.io.util.FileInputStreamPlus;
+import org.apache.cassandra.io.util.FileOutputStreamPlus;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.SerializationFeature;
-import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.apache.cassandra.audit.IAuditLogger;
import org.apache.cassandra.auth.AllowAllNetworkAuthorizer;
import org.apache.cassandra.auth.IAuthenticator;
@@ -87,9 +92,6 @@ import org.apache.cassandra.io.sstable.format.StatsComponent;
import org.apache.cassandra.io.sstable.metadata.MetadataType;
import org.apache.cassandra.io.util.DataOutputBuffer;
import org.apache.cassandra.io.util.DataOutputBufferFixed;
-import org.apache.cassandra.io.util.File;
-import org.apache.cassandra.io.util.FileInputStreamPlus;
-import org.apache.cassandra.io.util.FileOutputStreamPlus;
import org.apache.cassandra.io.util.FileUtils;
import org.apache.cassandra.locator.InetAddressAndPort;
import org.apache.cassandra.security.ISslContextFactory;
@@ -549,6 +551,7 @@ public class FBUtilities
}
catch (ExecutionException ee)
{
+ logger.info("Exception occurred in async code", ee);
throw Throwables.cleaned(ee);
}
catch (InterruptedException ie)
@@ -557,6 +560,28 @@ public class FBUtilities
}
}
+ public static <T> T waitOnFuture(Future<T> future, Duration timeout)
+ {
+ Preconditions.checkArgument(!timeout.isNegative(), "Timeout must not be negative, provided %s", timeout);
+ try
+ {
+ return future.get(timeout.toNanos(), TimeUnit.NANOSECONDS);
+ }
+ catch (ExecutionException ee)
+ {
+ logger.info("Exception occurred in async code", ee);
+ throw Throwables.cleaned(ee);
+ }
+ catch (InterruptedException ie)
+ {
+ throw new AssertionError(ie);
+ }
+ catch (TimeoutException e)
+ {
+ throw new RuntimeException("Timeout - task did not finish in " + timeout);
+ }
+ }
+
public static <T, F extends Future<? extends T>> F waitOnFirstFuture(Iterable<? extends F> futures)
{
return waitOnFirstFuture(futures, 100);
@@ -943,9 +968,9 @@ public class FBUtilities
sb.append(str).append(lineSep);
while ((str = err.readLine()) != null)
sb.append(str).append(lineSep);
- throw new IOException("Exception while executing the command: "+ StringUtils.join(pb.command(), " ") +
+ throw new IOException("Exception while executing the command: " + StringUtils.join(pb.command(), " ") +
", command error Code: " + errCode +
- ", command output: "+ sb.toString());
+ ", command output: " + sb);
}
}
}
diff --git a/src/java/org/apache/cassandra/utils/btree/AbstractBTreeMap.java b/src/java/org/apache/cassandra/utils/btree/AbstractBTreeMap.java
new file mode 100644
index 0000000000..0db33a52cc
--- /dev/null
+++ b/src/java/org/apache/cassandra/utils/btree/AbstractBTreeMap.java
@@ -0,0 +1,157 @@
+/*
+ * 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.utils.btree;
+
+import java.util.AbstractMap;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import com.google.common.collect.ImmutableSet;
+
+public abstract class AbstractBTreeMap<K, V> extends AbstractMap<K, V>
+{
+ protected final Object[] tree;
+ protected final KeyComparator<K, V> comparator;
+
+ protected AbstractBTreeMap(Object[] tree, KeyComparator<K, V> comparator)
+ {
+ this.tree = tree;
+ this.comparator = comparator;
+ }
+
+ /**
+ * return a new map containing provided key and value
+ *
+ * @throws IllegalStateException if the key already exists in the map
+ */
+ public abstract AbstractBTreeMap<K, V> with(K key, V value);
+
+ /**
+ * returns a new map containing provided key and value - replaces existing key
+ */
+ public abstract AbstractBTreeMap<K, V> withForce(K key, V value);
+
+ /**
+ * returns a new map without key
+ */
+ public abstract AbstractBTreeMap<K, V> without(K key);
+
+ @Override
+ public int size()
+ {
+ return BTree.size(tree);
+ }
+
+ @Override
+ public boolean isEmpty()
+ {
+ return BTree.isEmpty(tree);
+ }
+
+ @Override
+ public boolean containsKey(Object key)
+ {
+ return get(key) != null;
+ }
+
+ @Override
+ public boolean containsValue(Object value)
+ {
+ Iterator<Entry<K, V>> iter = BTree.iterator(tree);
+ while (iter.hasNext())
+ {
+ Entry<K, V> entry = iter.next();
+ if (entry.getValue().equals(value))
+ return true;
+ }
+ return false;
+ }
+
+ @SuppressWarnings("unchecked")
+ public V get(Object key)
+ {
+ if (key == null)
+ throw new NullPointerException();
+ Entry<K, V> entry = BTree.find(tree, comparator, new Entry<>((K)key, null));
+ if (entry != null)
+ return entry.getValue();
+ return null;
+ }
+
+ private Set<K> keySet = null;
+ @Override
+ public Set<K> keySet()
+ {
+ if (keySet == null)
+ keySet = BTreeSet.wrap(BTree.transformAndFilter(tree, (entry) -> ((Map.Entry<K, V>)entry).getKey()), comparator.keyComparator);
+ return keySet;
+ }
+
+ @Override
+ public Set<V> values()
+ {
+ ImmutableSet.Builder<V> b = ImmutableSet.builder();
+ for (Map.Entry<K, V> e : entrySet())
+ b.add(e.getValue());
+ return b.build();
+ }
+
+ @Override
+ public Set<Map.Entry<K, V>> entrySet()
+ {
+ return BTreeSet.wrap(tree, comparator);
+ }
+
+ public V put(K key, V value) { throw new UnsupportedOperationException(); }
+ public V forcePut(K ignoredKey, V ignoredVal) { throw new UnsupportedOperationException(); }
+ public V remove(Object key) { throw new UnsupportedOperationException();}
+ public void putAll(Map<? extends K, ? extends V> m) { throw new UnsupportedOperationException(); }
+ public void clear() { throw new UnsupportedOperationException(); }
+ public Map.Entry<K, V> pollFirstEntry() { throw new UnsupportedOperationException(); }
+ public Map.Entry<K, V> pollLastEntry() { throw new UnsupportedOperationException(); }
+
+ protected static class KeyComparator<K, V> implements Comparator<Map.Entry<K, V>>
+ {
+ protected final Comparator<K> keyComparator;
+
+ protected KeyComparator(Comparator<K> keyComparator)
+ {
+ this.keyComparator = keyComparator;
+ }
+
+ @Override
+ public int compare(Map.Entry<K, V> o1, Map.Entry<K, V> o2)
+ {
+ return keyComparator.compare(o1.getKey(), o2.getKey());
+ }
+ }
+
+ static class Entry<K, V> extends AbstractMap.SimpleEntry<K, V>
+ {
+ public Entry(K key, V value)
+ {
+ super(key, value);
+ }
+
+ public V setValue(V value) { throw new UnsupportedOperationException(); }
+
+ }
+}
diff --git a/src/java/org/apache/cassandra/utils/btree/BTreeMap.java b/src/java/org/apache/cassandra/utils/btree/BTreeMap.java
new file mode 100644
index 0000000000..2d8e92a298
--- /dev/null
+++ b/src/java/org/apache/cassandra/utils/btree/BTreeMap.java
@@ -0,0 +1,232 @@
+/*
+ * 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.utils.btree;
+
+import java.util.Comparator;
+import java.util.Map;
+import java.util.NavigableMap;
+import java.util.NavigableSet;
+import java.util.SortedMap;
+
+import org.apache.cassandra.utils.BulkIterator;
+
+import static java.util.Comparator.naturalOrder;
+
+
+public class BTreeMap<K, V> extends AbstractBTreeMap<K, V> implements NavigableMap<K, V>
+{
+ protected static <K, V> BTreeMap<K, V> withComparator(Object[] tree, Comparator<K> comparator)
+ {
+ return new BTreeMap<>(tree, new KeyComparator<>(comparator));
+ }
+
+ protected BTreeMap(Object[] tree, KeyComparator<K, V> comparator)
+ {
+ super(tree, comparator);
+ }
+
+ public static <K, V> BTreeMap<K, V> empty(Comparator<K> comparator)
+ {
+ return withComparator(BTree.empty(), comparator);
+ }
+
+ public static <K extends Comparable<K>, V> BTreeMap<K, V> empty()
+ {
+ return BTreeMap.<K, V>empty(naturalOrder());
+ }
+
+ @Override
+ public BTreeMap<K, V> with(K key, V value)
+ {
+ if (key == null || value == null)
+ throw new NullPointerException();
+
+ AbstractBTreeMap.Entry<K, V> entry = new AbstractBTreeMap.Entry<>(key, value);
+ AbstractBTreeMap.Entry<K, V> existing;
+ if ((existing = BTree.find(tree, comparator, entry)) != null && !existing.equals(entry))
+ throw new IllegalStateException("Map already contains " + key);
+ return new BTreeMap<>(BTree.update(tree, new Object[]{ entry }, comparator, UpdateFunction.noOp()), comparator);
+ }
+
+ public BTreeMap<K, V> withForce(K key, V value)
+ {
+ if (key == null || value == null)
+ throw new NullPointerException();
+ AbstractBTreeMap.Entry<K, V> entry = new AbstractBTreeMap.Entry<>(key, value);
+ return new BTreeMap<>(BTree.update(tree, new Object[] { entry }, comparator, UpdateFunction.Simple.of((a, b) -> b)), comparator);
+ }
+
+ public BTreeMap<K, V> without(K key)
+ {
+ if (key == null)
+ throw new NullPointerException();
+
+ return new BTreeMap<>(BTreeRemoval.remove(tree, comparator, new AbstractBTreeMap.Entry<>(key, null)), comparator);
+ }
+
+ @Override
+ public Map.Entry<K, V> lowerEntry(K key)
+ {
+ return BTree.lower(tree, comparator, new AbstractBTreeMap.Entry<>(key, null));
+ }
+
+ @Override
+ public K lowerKey(K key)
+ {
+ Map.Entry<K, V> entry = lowerEntry(key);
+ return entry == null ? null : entry.getKey();
+ }
+
+ @Override
+ public Map.Entry<K, V> floorEntry(K key)
+ {
+ return BTree.floor(tree, comparator, new AbstractBTreeMap.Entry<>(key, null));
+ }
+
+ @Override
+ public K floorKey(K key)
+ {
+ Map.Entry<K, V> entry = floorEntry(key);
+ return entry == null ? null : entry.getKey();
+ }
+
+ @Override
+ public Map.Entry<K, V> ceilingEntry(K key)
+ {
+ return BTree.ceil(tree, comparator, new AbstractBTreeMap.Entry<>(key, null));
+ }
+
+ @Override
+ public K ceilingKey(K key)
+ {
+ Map.Entry<K, V> entry = ceilingEntry(key);
+ return entry == null ? null : entry.getKey();
+ }
+
+ @Override
+ public Map.Entry<K, V> higherEntry(K key)
+ {
+ return BTree.higher(tree, comparator, new AbstractBTreeMap.Entry<>(key, null));
+ }
+
+ @Override
+ public K higherKey(K key)
+ {
+ Map.Entry<K, V> entry = higherEntry(key);
+ return entry == null ? null : entry.getKey();
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public Map.Entry<K, V> firstEntry()
+ {
+ if (isEmpty())
+ return null;
+ return (AbstractBTreeMap.Entry<K, V>) BTree.iterator(tree).next();
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public Map.Entry<K, V> lastEntry()
+ {
+ return getEntry(size() - 1);
+ }
+
+ @Override
+ public NavigableMap<K, V> descendingMap()
+ {
+ return new BTreeMap<>(BTree.build(BulkIterator.of(BTree.iterable(tree, BTree.Dir.DESC).iterator()), BTree.size(tree), UpdateFunction.noOp),
+ new KeyComparator<>(comparator.keyComparator.reversed()));
+ }
+
+ @Override
+ public NavigableSet<K> navigableKeySet()
+ {
+ throw new UnsupportedOperationException("todo");
+ }
+
+ @Override
+ public NavigableSet<K> descendingKeySet()
+ {
+ throw new UnsupportedOperationException("todo");
+ }
+
+ @Override
+ public NavigableMap<K, V> subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive)
+ {
+ throw new UnsupportedOperationException("todo");
+ }
+
+ @Override
+ public NavigableMap<K, V> headMap(K toKey, boolean inclusive)
+ {
+ throw new UnsupportedOperationException("todo");
+ }
+
+ @Override
+ public NavigableMap<K, V> tailMap(K fromKey, boolean inclusive)
+ {
+ throw new UnsupportedOperationException("todo");
+ }
+
+ @Override
+ public Comparator<K> comparator()
+ {
+ return comparator.keyComparator;
+ }
+
+ @Override
+ public SortedMap<K, V> subMap(K fromKey, K toKey)
+ {
+ throw new UnsupportedOperationException("todo");
+ }
+
+ @Override
+ public SortedMap<K, V> headMap(K toKey)
+ {
+ throw new UnsupportedOperationException("todo");
+ }
+
+ @Override
+ public SortedMap<K, V> tailMap(K fromKey)
+ {
+ throw new UnsupportedOperationException("todo");
+ }
+
+ @Override
+ public K firstKey()
+ {
+ if (BTree.isEmpty(tree))
+ return null;
+ return BTree.<Map.Entry<K, V>>findByIndex(tree, 0).getKey();
+ }
+
+ @Override
+ public K lastKey()
+ {
+ if (BTree.isEmpty(tree))
+ return null;
+ return getEntry(size() - 1).getKey();
+ }
+
+ private Map.Entry<K, V> getEntry(int idx)
+ {
+ return BTree.findByIndex(tree, idx);
+ }
+}
diff --git a/test/distributed/org/apache/cassandra/distributed/action/GossipHelper.java b/test/distributed/org/apache/cassandra/distributed/action/GossipHelper.java
index 5b23b8654e..3920993d80 100644
--- a/test/distributed/org/apache/cassandra/distributed/action/GossipHelper.java
+++ b/test/distributed/org/apache/cassandra/distributed/action/GossipHelper.java
@@ -46,13 +46,11 @@ import org.apache.cassandra.io.util.DataInputBuffer;
import org.apache.cassandra.io.util.DataOutputBuffer;
import org.apache.cassandra.locator.InetAddressAndPort;
import org.apache.cassandra.net.MessagingService;
-import org.apache.cassandra.schema.Schema;
import org.apache.cassandra.service.StorageService;
import org.apache.cassandra.utils.FBUtilities;
import static org.apache.cassandra.distributed.impl.DistributedTestSnitch.toCassandraInetAddressAndPort;
import static org.apache.cassandra.utils.Clock.Global.currentTimeMillis;
-import static org.junit.Assert.assertTrue;
public class GossipHelper
{
@@ -161,7 +159,7 @@ public class GossipHelper
public static InstanceAction pullSchemaFrom(IInvokableInstance pullFrom)
{
- return new PullSchemaFrom(pullFrom);
+ return inst -> {};
}
private static InstanceAction disableBinary()
@@ -227,26 +225,6 @@ public class GossipHelper
}
}
- private static class PullSchemaFrom implements InstanceAction
- {
- final InetSocketAddress pullFrom;
-
- public PullSchemaFrom(IInvokableInstance pullFrom)
- {
- this.pullFrom = pullFrom.broadcastAddress();;
- }
-
- public void accept(IInvokableInstance pullTo)
- {
- pullTo.acceptsOnInstance((InetSocketAddress pullFrom) -> {
- InetAddressAndPort endpoint = toCassandraInetAddressAndPort(pullFrom);
- EndpointState state = Gossiper.instance.getEndpointStateForEndpoint(endpoint);
- Gossiper.instance.doOnChangeNotifications(endpoint, ApplicationState.SCHEMA, state.getApplicationState(ApplicationState.SCHEMA));
- assertTrue("schema is ready", Schema.instance.waitUntilReady(Duration.ofSeconds(10)));
- }).accept(pullFrom);
- }
- }
-
private static class BootstrapAction implements InstanceAction, Serializable
{
private final boolean joinRing;
@@ -276,7 +254,6 @@ public class GossipHelper
assert collisions.size() == 0 : String.format("Didn't expect any replacements but got %s", collisions);
boolean isBootstrapSuccessful = StorageService.instance.bootstrap(tokens, waitForBootstrap.toMillis());
assert isBootstrapSuccessful : "Bootstrap did not complete successfully";
- StorageService.instance.setUpDistributedSystemKeyspaces();
if (joinRing)
StorageService.instance.finishJoiningRing(true, tokens);
}
diff --git a/test/distributed/org/apache/cassandra/distributed/impl/Instance.java b/test/distributed/org/apache/cassandra/distributed/impl/Instance.java
index 3822b02f79..3e142af11d 100644
--- a/test/distributed/org/apache/cassandra/distributed/impl/Instance.java
+++ b/test/distributed/org/apache/cassandra/distributed/impl/Instance.java
@@ -35,7 +35,6 @@ import java.util.UUID;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Stream;
@@ -110,7 +109,6 @@ import org.apache.cassandra.net.Message;
import org.apache.cassandra.net.MessagingService;
import org.apache.cassandra.net.NoPayload;
import org.apache.cassandra.net.Verb;
-import org.apache.cassandra.schema.MigrationCoordinator;
import org.apache.cassandra.schema.Schema;
import org.apache.cassandra.schema.SchemaConstants;
import org.apache.cassandra.service.ActiveRepairService;
@@ -158,7 +156,6 @@ import static org.apache.cassandra.distributed.api.Feature.NETWORK;
import static org.apache.cassandra.distributed.impl.DistributedTestSnitch.fromCassandraInetAddressAndPort;
import static org.apache.cassandra.distributed.impl.DistributedTestSnitch.toCassandraInetAddressAndPort;
import static org.apache.cassandra.net.Verb.BATCH_STORE_REQ;
-import static org.apache.cassandra.utils.Clock.Global.nanoTime;
/**
* This class is instantiated on the relevant classloader, so its methods invoke the correct target classes automatically
@@ -612,7 +609,7 @@ public class Instance extends IsolatedExecutor implements IInvokableInstance
try
{
// load schema from disk
- Schema.instance.loadFromDisk();
+// Schema.instance.loadFromDisk();
}
catch (Exception e)
{
@@ -676,7 +673,6 @@ public class Instance extends IsolatedExecutor implements IInvokableInstance
StorageService.instance.registerDaemon(CassandraDaemon.getInstanceForTesting());
if (config.has(GOSSIP))
{
- MigrationCoordinator.setUptimeFn(() -> TimeUnit.NANOSECONDS.toMillis(nanoTime() - startedAt.get()));
try
{
StorageService.instance.initServer();
@@ -695,7 +691,7 @@ public class Instance extends IsolatedExecutor implements IInvokableInstance
}
else
{
- Schema.instance.startSync();
+// Schema.instance.startSync();
Stream peers = cluster.stream().filter(instance -> ((IInstance) instance).isValid());
SystemKeyspace.setLocalHostId(config.hostId());
if (config.has(BLANK_GOSSIP))
@@ -705,7 +701,7 @@ public class Instance extends IsolatedExecutor implements IInvokableInstance
else
peers.forEach(peer -> GossipHelper.unsafeStatusToNormal(this, (IInstance) peer));
- StorageService.instance.setUpDistributedSystemKeyspaces();
+// StorageService.instance.setUpDistributedSystemKeyspaces();
StorageService.instance.setNormalModeUnsafe();
Gossiper.instance.register(StorageService.instance);
StorageService.instance.startSnapshotManager();
diff --git a/test/distributed/org/apache/cassandra/distributed/test/SchemaTest.java b/test/distributed/org/apache/cassandra/distributed/test/SchemaTest.java
deleted file mode 100644
index 142b66b581..0000000000
--- a/test/distributed/org/apache/cassandra/distributed/test/SchemaTest.java
+++ /dev/null
@@ -1,200 +0,0 @@
-/*
- * 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 java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
-
-import org.junit.Test;
-
-import org.apache.cassandra.config.CassandraRelevantProperties;
-import org.apache.cassandra.db.ColumnFamilyStore;
-import org.apache.cassandra.distributed.Cluster;
-import org.apache.cassandra.distributed.api.ConsistencyLevel;
-import org.apache.cassandra.distributed.api.Feature;
-import org.apache.cassandra.distributed.api.IInvokableInstance;
-import org.apache.cassandra.distributed.api.IIsolatedExecutor.SerializableCallable;
-import org.apache.cassandra.gms.Gossiper;
-import org.apache.cassandra.schema.Schema;
-import org.apache.cassandra.schema.SchemaConstants;
-import org.assertj.core.api.Assertions;
-import org.awaitility.Awaitility;
-import org.awaitility.core.ConditionFactory;
-
-import static java.time.Duration.ofSeconds;
-import static org.apache.cassandra.utils.FBUtilities.getBroadcastAddressAndPort;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-public class SchemaTest extends TestBaseImpl
-{
- public static final String TABLE_ONE = "tbl_one";
- public static final String TABLE_TWO = "tbl_two";
-
- @Test
- public void readRepair() throws Throwable
- {
- try (Cluster cluster = init(Cluster.build(2).start()))
- {
- cluster.schemaChange("CREATE TABLE " + KEYSPACE + ".tbl (pk int, ck int, v1 int, v2 int, primary key (pk, ck))");
- String name = "aaa";
- cluster.get(1).schemaChangeInternal("ALTER TABLE " + KEYSPACE + ".tbl ADD " + name + " list<int>");
- cluster.get(1).executeInternal("INSERT INTO " + KEYSPACE + ".tbl (pk, ck, v1, v2) values (?,1,1,1)", 1);
- selectSilent(cluster, name);
-
- cluster.get(2).flush(KEYSPACE);
- cluster.get(2).schemaChangeInternal("ALTER TABLE " + KEYSPACE + ".tbl ADD " + name + " list<int>");
- cluster.get(2).shutdown().get();
- cluster.get(2).startup();
- cluster.get(2).forceCompact(KEYSPACE, "tbl");
- }
- }
-
- @Test
- public void readRepairWithCompaction() throws Throwable
- {
- try (Cluster cluster = init(Cluster.build(2).start()))
- {
- cluster.schemaChange("CREATE TABLE " + KEYSPACE + ".tbl (pk int, ck int, v1 int, v2 int, primary key (pk, ck))");
- String name = "v10";
- cluster.get(1).schemaChangeInternal("ALTER TABLE " + KEYSPACE + ".tbl ADD " + name + " list<int>");
- cluster.get(1).executeInternal("INSERT INTO " + KEYSPACE + ".tbl (pk, ck, v1, v2) values (?,1,1,1)", 1);
- selectSilent(cluster, name);
- cluster.get(2).flush(KEYSPACE);
- cluster.get(2).schemaChangeInternal("ALTER TABLE " + KEYSPACE + ".tbl ADD " + name + " list<int>");
- cluster.get(2).executeInternal("INSERT INTO " + KEYSPACE + ".tbl (pk, ck, v1, v2, " + name + ") values (?,1,1,1,[1])", 1);
- cluster.get(2).flush(KEYSPACE);
- cluster.get(2).forceCompact(KEYSPACE, "tbl");
- cluster.get(2).shutdown().get();
- cluster.get(2).startup();
- cluster.get(2).forceCompact(KEYSPACE, "tbl");
- }
- }
-
- private void selectSilent(Cluster cluster, String name)
- {
- try
- {
- cluster.coordinator(1).execute(withKeyspace("SELECT * FROM %s.tbl WHERE pk = ?"), ConsistencyLevel.ALL, 1);
- }
- catch (Exception e)
- {
- boolean causeIsUnknownColumn = false;
- Throwable cause = e;
- while (cause != null)
- {
- if (cause.getMessage() != null && cause.getMessage().contains("Unknown column " + name + " during deserialization"))
- causeIsUnknownColumn = true;
- cause = cause.getCause();
- }
- assertTrue(causeIsUnknownColumn);
- }
- }
-
- /**
- * The purpose of this test is to verify manual schema reset functinality.
- * <p>
- * There is a 2-node cluster and a TABLE_ONE created. The schema version is agreed on both nodes. Then the 2nd node
- * is shutdown. We introduce a disagreement by dropping TABLE_ONE and creating TABLE_TWO on the 1st node. Therefore,
- * the 1st node has a newer schema version with TABLE_TWO, while the shutdown 2nd node has older schema version with
- * TABLE_ONE.
- * <p>
- * At this point, if we just started the 2nd node, it would sync its schema by getting fresh mutations from the 1st
- * node which would result in both nodes having only the definition of TABLE_TWO.
- * <p>
- * However, before starting the 2nd node the schema is reset on the 1st node, so the 1st node will discard its local
- * schema whenever it manages to fetch a schema definition from some other node (the 2nd node in this case).
- * It is expected to end up with both nodes having only the definition of TABLE_ONE.
- * <p>
- * In the second phase of the test we simply break the schema on the 1st node and call reset to fetch the schema
- * definition it from the 2nd node.
- */
- @Test
- public void schemaReset() throws Throwable
- {
- CassandraRelevantProperties.MIGRATION_DELAY.setLong(10000);
- CassandraRelevantProperties.SCHEMA_PULL_INTERVAL_MS.setLong(10000);
- try (Cluster cluster = init(Cluster.build(2).withConfig(cfg -> cfg.with(Feature.GOSSIP, Feature.NETWORK)).start()))
- {
- // create TABLE_ONE and make sure it is propagated
- cluster.schemaChange(String.format("CREATE TABLE %s.%s (pk INT PRIMARY KEY, v TEXT)", KEYSPACE, TABLE_ONE));
- assertTrue(checkTablesPropagated(cluster.get(1), true, false));
- assertTrue(checkTablesPropagated(cluster.get(2), true, false));
-
- // shutdown the 2nd node and make sure that the 1st does not see it any longer as alive
- cluster.get(2).shutdown().get();
- await(30).until(() -> cluster.get(1).callOnInstance(() -> {
- return Gossiper.instance.getLiveMembers()
- .stream()
- .allMatch(e -> e.equals(getBroadcastAddressAndPort()));
- }));
-
- // when there is no node to fetch the schema from, reset local schema should immediately fail
- Assertions.assertThatExceptionOfType(RuntimeException.class).isThrownBy(() -> {
- cluster.get(1).runOnInstance(() -> Schema.instance.resetLocalSchema());
- }).withMessageContaining("Cannot reset local schema when there are no other live nodes");
-
- // now, let's make a disagreement, the shutdown node 2 has a definition of TABLE_ONE, while the running
- // node 1 will have a definition of TABLE_TWO
- cluster.coordinator(1).execute(String.format("DROP TABLE %s.%s", KEYSPACE, TABLE_ONE), ConsistencyLevel.ONE);
- cluster.coordinator(1).execute(String.format("CREATE TABLE %s.%s (pk INT PRIMARY KEY, v TEXT)", KEYSPACE, TABLE_TWO), ConsistencyLevel.ONE);
- await(30).until(() -> checkTablesPropagated(cluster.get(1), false, true));
-
- // Schema.resetLocalSchema is guarded by some conditions which would not let us reset schema if there is no
- // live node in the cluster, therefore we simply call SchemaUpdateHandler.clear (this is the only real thing
- // being done by Schema.resetLocalSchema under the hood)
- SerializableCallable<Boolean> clear = () -> Schema.instance.updateHandler.clear().awaitUninterruptibly(1, TimeUnit.MINUTES);
- Future<Boolean> clear1 = cluster.get(1).asyncCallsOnInstance(clear).call();
- assertFalse(clear1.isDone());
-
- // when the 2nd node is started, schema should be back in sync
- cluster.get(2).startup();
- await(30).until(() -> clear1.isDone() && clear1.get());
-
- // this proves that reset schema works on the 1st node - the most recent change should be discarded because
- // it receives the schema from the 2nd node and applies it on empty schema
- await(60).until(() -> checkTablesPropagated(cluster.get(1), true, false));
-
- // now let's break schema locally and let it be reset
- cluster.get(1).runOnInstance(() -> Schema.instance.getLocalKeyspaces()
- .get(SchemaConstants.SCHEMA_KEYSPACE_NAME)
- .get().tables.forEach(t -> ColumnFamilyStore.getIfExists(t.keyspace, t.name).truncateBlockingWithoutSnapshot()));
-
- // when schema is removed and there is a node to fetch it from, the 1st node should immediately restore it
- cluster.get(1).runOnInstance(() -> Schema.instance.resetLocalSchema());
- // note that we should not wait for this to be true because resetLocalSchema is blocking
- // and after successfully completing it, the schema should be already back in sync
- assertTrue(checkTablesPropagated(cluster.get(1), true, false));
- assertTrue(checkTablesPropagated(cluster.get(2), true, false));
- }
- }
-
- private static ConditionFactory await(int seconds)
- {
- return Awaitility.await().atMost(ofSeconds(seconds)).pollDelay(ofSeconds(1));
- }
-
- private static boolean checkTablesPropagated(IInvokableInstance instance, boolean one, boolean two)
- {
- return instance.callOnInstance(() -> {
- return (Schema.instance.getTableMetadata(KEYSPACE, TABLE_ONE) != null ^ !one)
- && (Schema.instance.getTableMetadata(KEYSPACE, TABLE_TWO) != null ^ !two);
- });
- }
-}
diff --git a/test/distributed/org/apache/cassandra/distributed/test/ring/NodeNotInRingTest.java b/test/distributed/org/apache/cassandra/distributed/test/ring/NodeNotInRingTest.java
index e973e33cd9..71466b273f 100644
--- a/test/distributed/org/apache/cassandra/distributed/test/ring/NodeNotInRingTest.java
+++ b/test/distributed/org/apache/cassandra/distributed/test/ring/NodeNotInRingTest.java
@@ -24,7 +24,6 @@ import org.junit.Assert;
import org.junit.Test;
import org.apache.cassandra.distributed.Cluster;
-import org.apache.cassandra.distributed.action.GossipHelper;
import org.apache.cassandra.distributed.api.ConsistencyLevel;
import org.apache.cassandra.distributed.api.ICluster;
import org.apache.cassandra.distributed.test.TestBaseImpl;
@@ -52,7 +51,8 @@ public class NodeNotInRingTest extends TestBaseImpl
.outbound()
.drop()
.on();
- cluster.run(GossipHelper.removeFromRing(cluster.get(3)), 1, 2);
+ // todo: this can probably use an event like "unsafe assassinate" or similar?
+// cluster.run(GossipHelper.removeFromRing(cluster.get(3)), 1, 2);
cluster.run(inst -> inst.runsOnInstance(() -> {
Assert.assertEquals("There should be 2 remaining nodes in ring",
2, StorageService.instance.effectiveOwnershipWithPort(KEYSPACE).size());
diff --git a/test/distributed/org/apache/cassandra/distributed/test/ring/PendingWritesTest.java b/test/distributed/org/apache/cassandra/distributed/test/ring/PendingWritesTest.java
deleted file mode 100644
index 2e702b219b..0000000000
--- a/test/distributed/org/apache/cassandra/distributed/test/ring/PendingWritesTest.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * 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.ring;
-
-import java.net.InetSocketAddress;
-import java.time.Duration;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-
-import org.junit.Assert;
-import org.junit.Test;
-
-import org.apache.cassandra.dht.Range;
-import org.apache.cassandra.dht.Token;
-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.impl.DistributedTestSnitch;
-import org.apache.cassandra.distributed.shared.NetworkTopology;
-import org.apache.cassandra.distributed.test.TestBaseImpl;
-import org.apache.cassandra.locator.EndpointsForRange;
-import org.apache.cassandra.locator.InetAddressAndPort;
-import org.apache.cassandra.service.PendingRangeCalculatorService;
-import org.apache.cassandra.service.StorageService;
-
-import static org.apache.cassandra.distributed.action.GossipHelper.bootstrap;
-import static org.apache.cassandra.distributed.action.GossipHelper.disseminateGossipState;
-import static org.apache.cassandra.distributed.action.GossipHelper.statusToBootstrap;
-import static org.apache.cassandra.distributed.action.GossipHelper.withProperty;
-import static org.apache.cassandra.distributed.api.Feature.GOSSIP;
-import static org.apache.cassandra.distributed.api.Feature.NETWORK;
-
-public class PendingWritesTest extends TestBaseImpl
-{
- @Test
- public void testPendingWrites() throws Throwable
- {
- int originalNodeCount = 2;
- int expandedNodeCount = originalNodeCount + 1;
-
- try (Cluster cluster = builder().withNodes(originalNodeCount)
- .withTokenSupplier(TokenSupplier.evenlyDistributedTokens(expandedNodeCount))
- .withNodeIdTopology(NetworkTopology.singleDcNetworkTopology(expandedNodeCount, "dc0", "rack0"))
- .withConfig(config -> config.with(NETWORK, GOSSIP))
- .start())
- {
- BootstrapTest.populate(cluster, 0, 100);
- IInstanceConfig config = cluster.newInstanceConfig();
- IInvokableInstance newInstance = cluster.bootstrap(config);
- withProperty("cassandra.join_ring", false,
- () -> newInstance.startup(cluster));
-
- cluster.forEach(statusToBootstrap(newInstance));
- cluster.run(bootstrap(false, Duration.ofSeconds(60), Duration.ofSeconds(60)), newInstance.config().num());
-
- cluster.get(1).acceptsOnInstance((InetSocketAddress ip) -> {
- Set<InetAddressAndPort> set = new HashSet<>();
- for (Map.Entry<Range<Token>, EndpointsForRange.Builder> e : StorageService.instance.getTokenMetadata().getPendingRanges(KEYSPACE))
- {
- set.addAll(e.getValue().build().endpoints());
- }
- Assert.assertEquals(set.size(), 1);
- Assert.assertTrue(String.format("%s should contain %s", set, ip),
- set.contains(DistributedTestSnitch.toCassandraInetAddressAndPort(ip)));
- }).accept(cluster.get(3).broadcastAddress());
-
- BootstrapTest.populate(cluster, 100, 150);
-
- newInstance.nodetoolResult("join").asserts().success();
-
- cluster.run(disseminateGossipState(newInstance),1, 2);
-
- cluster.run((instance) -> {
- instance.runOnInstance(() -> {
- PendingRangeCalculatorService.instance.update();
- PendingRangeCalculatorService.instance.blockUntilFinished();
- });
- }, 1, 2);
-
- cluster.get(1).acceptsOnInstance((InetSocketAddress ip) -> {
- Set<InetAddressAndPort> set = new HashSet<>();
- for (Map.Entry<Range<Token>, EndpointsForRange.Builder> e : StorageService.instance.getTokenMetadata().getPendingRanges(KEYSPACE))
- set.addAll(e.getValue().build().endpoints());
- assert set.size() == 0 : set;
- }).accept(cluster.get(3).broadcastAddress());
-
- for (Map.Entry<Integer, Long> e : BootstrapTest.count(cluster).entrySet())
- Assert.assertEquals("Node " + e.getKey() + " has incorrect row state", e.getValue().longValue(), 150L);
- }
- }
-}
diff --git a/test/simulator/main/org/apache/cassandra/simulator/cluster/OnInstanceSyncSchemaForBootstrap.java b/test/simulator/main/org/apache/cassandra/simulator/cluster/OnInstanceSyncSchemaForBootstrap.java
index c8bc9f741e..9114391af2 100644
--- a/test/simulator/main/org/apache/cassandra/simulator/cluster/OnInstanceSyncSchemaForBootstrap.java
+++ b/test/simulator/main/org/apache/cassandra/simulator/cluster/OnInstanceSyncSchemaForBootstrap.java
@@ -18,20 +18,16 @@
package org.apache.cassandra.simulator.cluster;
-import java.time.Duration;
-
-import org.apache.cassandra.schema.Schema;
import org.apache.cassandra.simulator.systems.SimulatedActionTask;
import static org.apache.cassandra.simulator.Action.Modifier.DISPLAY_ORIGIN;
import static org.apache.cassandra.simulator.Action.Modifiers.RELIABLE_NO_TIMEOUTS;
-import static org.junit.Assert.assertTrue;
class OnInstanceSyncSchemaForBootstrap extends SimulatedActionTask
{
public OnInstanceSyncSchemaForBootstrap(ClusterActions actions, int node)
{
super("Sync Schema on " + node, RELIABLE_NO_TIMEOUTS.with(DISPLAY_ORIGIN), RELIABLE_NO_TIMEOUTS, actions, actions.cluster.get(node),
- () -> assertTrue("schema is ready", Schema.instance.waitUntilReady(Duration.ofMinutes(10))));
+ () -> {});
}
}
diff --git a/test/unit/org/apache/cassandra/cql3/KeyCacheCqlTest.java b/test/unit/org/apache/cassandra/cql3/KeyCacheCqlTest.java
index d40b001e90..00d9ed358f 100644
--- a/test/unit/org/apache/cassandra/cql3/KeyCacheCqlTest.java
+++ b/test/unit/org/apache/cassandra/cql3/KeyCacheCqlTest.java
@@ -316,7 +316,7 @@ public class KeyCacheCqlTest extends CQLTester
}
dropTable("DROP TABLE %s");
- assert Schema.instance.isSameVersion(SchemaKeyspace.calculateSchemaDigest());
+ assertEquals(Schema.instance.getVersion(), SchemaKeyspace.calculateSchemaDigest());
//Test loading for a dropped 2i/table
CacheService.instance.keyCache.clear();
diff --git a/test/unit/org/apache/cassandra/db/CleanupTest.java b/test/unit/org/apache/cassandra/db/CleanupTest.java
index d82eef8850..d4e99a429d 100644
--- a/test/unit/org/apache/cassandra/db/CleanupTest.java
+++ b/test/unit/org/apache/cassandra/db/CleanupTest.java
@@ -28,7 +28,6 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
-import java.util.UUID;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
@@ -38,22 +37,19 @@ import org.junit.Test;
import org.apache.cassandra.SchemaLoader;
import org.apache.cassandra.Util;
-import org.apache.cassandra.locator.InetAddressAndPort;
import org.apache.cassandra.config.DatabaseDescriptor;
-import org.apache.cassandra.schema.ColumnMetadata;
-import org.apache.cassandra.schema.KeyspaceMetadata;
import org.apache.cassandra.cql3.Operator;
import org.apache.cassandra.db.compaction.CompactionManager;
import org.apache.cassandra.db.filter.RowFilter;
import org.apache.cassandra.dht.ByteOrderedPartitioner.BytesToken;
import org.apache.cassandra.dht.Range;
+import org.apache.cassandra.dht.Token;
import org.apache.cassandra.exceptions.ConfigurationException;
import org.apache.cassandra.io.sstable.format.SSTableReader;
-import org.apache.cassandra.dht.Token;
import org.apache.cassandra.locator.AbstractNetworkTopologySnitch;
-import org.apache.cassandra.locator.TokenMetadata;
+import org.apache.cassandra.locator.InetAddressAndPort;
+import org.apache.cassandra.schema.ColumnMetadata;
import org.apache.cassandra.schema.KeyspaceParams;
-import org.apache.cassandra.service.StorageService;
import org.apache.cassandra.utils.ByteBufferUtil;
import static org.apache.cassandra.utils.Clock.Global.nanoTime;
@@ -119,10 +115,6 @@ public class CleanupTest
@Test
public void testCleanup() throws ExecutionException, InterruptedException, UnknownHostException
{
- TokenMetadata tmd = StorageService.instance.getTokenMetadata();
- tmd.clearUnsafe();
- tmd.updateNormalToken(token(new byte[]{ 50 }), InetAddressAndPort.getByName("127.0.0.1"));
-
Keyspace keyspace = Keyspace.open(KEYSPACE1);
ColumnFamilyStore cfs = keyspace.getColumnFamilyStore(CF_STANDARD1);
@@ -168,12 +160,11 @@ public class CleanupTest
// we don't allow cleanup when the local host has no range to avoid wipping up all data when a node has not join the ring.
// So to make sure cleanup erase everything here, we give the localhost the tiniest possible range.
- TokenMetadata tmd = StorageService.instance.getTokenMetadata();
byte[] tk1 = new byte[1], tk2 = new byte[1];
tk1[0] = 2;
tk2[0] = 1;
- tmd.updateNormalToken(new BytesToken(tk1), InetAddressAndPort.getByName("127.0.0.1"));
- tmd.updateNormalToken(new BytesToken(tk2), InetAddressAndPort.getByName("127.0.0.2"));
+// tmd.updateNormalToken(new BytesToken(tk1), InetAddressAndPort.getByName("127.0.0.1"));
+// tmd.updateNormalToken(new BytesToken(tk2), InetAddressAndPort.getByName("127.0.0.2"));
CompactionManager.instance.performCleanup(cfs, 2);
@@ -190,8 +181,6 @@ public class CleanupTest
@Test
public void testCleanupWithNewToken() throws ExecutionException, InterruptedException, UnknownHostException
{
- StorageService.instance.getTokenMetadata().clearUnsafe();
-
Keyspace keyspace = Keyspace.open(KEYSPACE1);
ColumnFamilyStore cfs = keyspace.getColumnFamilyStore(CF_STANDARD1);
@@ -199,13 +188,12 @@ public class CleanupTest
fillCF(cfs, "val", LOOPS);
assertEquals(LOOPS, Util.getAll(Util.cmd(cfs).build()).size());
- TokenMetadata tmd = StorageService.instance.getTokenMetadata();
byte[] tk1 = new byte[1], tk2 = new byte[1];
tk1[0] = 2;
tk2[0] = 1;
- tmd.updateNormalToken(new BytesToken(tk1), InetAddressAndPort.getByName("127.0.0.1"));
- tmd.updateNormalToken(new BytesToken(tk2), InetAddressAndPort.getByName("127.0.0.2"));
+// tmd.updateNormalToken(new BytesToken(tk1), InetAddressAndPort.getByName("127.0.0.1"));
+// tmd.updateNormalToken(new BytesToken(tk2), InetAddressAndPort.getByName("127.0.0.2"));
CompactionManager.instance.performCleanup(cfs, 2);
assertEquals(0, Util.getAll(Util.cmd(cfs).build()).size());
@@ -226,15 +214,13 @@ public class CleanupTest
private void testCleanupWithNoTokenRange(boolean isUserDefined) throws Exception
{
- TokenMetadata tmd = StorageService.instance.getTokenMetadata();
- tmd.clearUnsafe();
- tmd.updateHostId(UUID.randomUUID(), InetAddressAndPort.getByName("127.0.0.1"));
+// tmd.updateHostId(UUID.randomUUID(), InetAddressAndPort.getByName("127.0.0.1"));
byte[] tk1 = {2};
- tmd.updateNormalToken(new BytesToken(tk1), InetAddressAndPort.getByName("127.0.0.1"));
+// tmd.updateNormalToken(new BytesToken(tk1), InetAddressAndPort.getByName("127.0.0.1"));
Keyspace keyspace = Keyspace.open(KEYSPACE2);
- keyspace.setMetadata(KeyspaceMetadata.create(KEYSPACE2, KeyspaceParams.nts("DC1", 1)));
+// keyspace.setMetadata(KeyspaceMetadata.create(KEYSPACE2, KeyspaceParams.nts("DC1", 1)));
ColumnFamilyStore cfs = keyspace.getColumnFamilyStore(CF_STANDARD2);
// insert data and verify we get it back w/ range query
@@ -242,7 +228,7 @@ public class CleanupTest
assertEquals(LOOPS, Util.getAll(Util.cmd(cfs).build()).size());
// remove replication on DC1
- keyspace.setMetadata(KeyspaceMetadata.create(KEYSPACE2, KeyspaceParams.nts("DC1", 0)));
+// keyspace.setMetadata(KeyspaceMetadata.create(KEYSPACE2, KeyspaceParams.nts("DC1", 0)));
// clear token range for localhost on DC1
if (isUserDefined)
@@ -275,9 +261,8 @@ public class CleanupTest
Keyspace keyspace = Keyspace.open(KEYSPACE3);
ColumnFamilyStore cfs = keyspace.getColumnFamilyStore(CF_STANDARD3);
cfs.disableAutoCompaction();
- TokenMetadata tmd = StorageService.instance.getTokenMetadata();
- tmd.clearUnsafe();
- tmd.updateNormalToken(token(new byte[]{ 50 }), InetAddressAndPort.getByName("127.0.0.1"));
+// tmd.clearUnsafe();
+// tmd.updateNormalToken(token(new byte[]{ 50 }), InetAddressAndPort.getByName("127.0.0.1"));
for (byte i = 0; i < 100; i++)
{
@@ -307,7 +292,7 @@ public class CleanupTest
// single token - 127.0.0.1 owns everything, cleanup should be noop
cfs.forceCleanup(2);
assertEquals(beforeFirstCleanup, cfs.getLiveSSTables());
- tmd.updateNormalToken(token(new byte[]{ 120 }), InetAddressAndPort.getByName("127.0.0.2"));
+// tmd.updateNormalToken(token(new byte[]{ 120 }), InetAddressAndPort.getByName("127.0.0.2"));
cfs.forceCleanup(2);
for (SSTableReader sstable : cfs.getLiveSSTables())
@@ -324,7 +309,7 @@ public class CleanupTest
@Test
public void testuserDefinedCleanupWithNewToken() throws ExecutionException, InterruptedException, UnknownHostException
{
- StorageService.instance.getTokenMetadata().clearUnsafe();
+// StorageService.instance.getTokenMetadata().clearUnsafe();
Keyspace keyspace = Keyspace.open(KEYSPACE1);
ColumnFamilyStore cfs = keyspace.getColumnFamilyStore(CF_STANDARD1);
@@ -333,14 +318,13 @@ public class CleanupTest
fillCF(cfs, "val", LOOPS);
assertEquals(LOOPS, Util.getAll(Util.cmd(cfs).build()).size());
- TokenMetadata tmd = StorageService.instance.getTokenMetadata();
... 1290 lines suppressed ...
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@cassandra.apache.org
For additional commands, e-mail: commits-help@cassandra.apache.org