diff --git a/CHANGES.txt b/CHANGES.txt index e2262d4f38..77caf7cb25 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,5 @@ 5.1 + * Reduce performance impact of TableMetadataRef.get and KeyspaceMetadataRef.get (CASSANDRA-20465) * Improve CMS initialization (CASSANDRA-21036) * Introducing comments and security labels for schema elements (CASSANDRA-20943) * Extend nodetool tablestats for dictionary memory usage (CASSANDRA-20940) diff --git a/src/java/org/apache/cassandra/db/Keyspace.java b/src/java/org/apache/cassandra/db/Keyspace.java index cbf6039e42..02b554fd7e 100644 --- a/src/java/org/apache/cassandra/db/Keyspace.java +++ b/src/java/org/apache/cassandra/db/Keyspace.java @@ -25,6 +25,7 @@ import java.util.Iterator; import java.util.List; import java.util.Objects; import java.util.Set; +import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicLong; @@ -723,6 +724,20 @@ public class Keyspace private final String name; private final SchemaProvider provider; + private volatile KeyspaceMetadataCache cachedKeyspaceMetadata; + + private static class KeyspaceMetadataCache + { + private final UUID lastSeenSchemaVersion; + private final KeyspaceMetadata keyspaceMetadata; + + private KeyspaceMetadataCache(UUID lastSeenSchemaVersion, KeyspaceMetadata keyspaceMetadata) + { + this.lastSeenSchemaVersion = lastSeenSchemaVersion; + this.keyspaceMetadata = keyspaceMetadata; + } + } + public KeyspaceMetadataRef(KeyspaceMetadata initial, SchemaProvider provider) { this.initial = initial; @@ -734,7 +749,30 @@ public class Keyspace { if (initial != null) return initial; - return provider.getKeyspaceMetadata(name); + return getWithCaching(); + } + + private KeyspaceMetadata getWithCaching() + { + UUID schemaVersion = provider.getVersion(); + if (schemaVersion == null) + return provider.getKeyspaceMetadata(name); + + KeyspaceMetadataCache cache = cachedKeyspaceMetadata; + // we assume that local keyspaces and virtual keyspaces are immutable, so we need to track only a distributed schema version + KeyspaceMetadata metadata; + if (cache != null && schemaVersion.equals(cache.lastSeenSchemaVersion) && cache.keyspaceMetadata != null) + metadata = cache.keyspaceMetadata; + else + { + // we always retrieve metadata after schema version and assume they are changed coherently + // we may put new metadata + old schema version to the cache but not vice versa + // it we put non-latest schema version + latest metadata then it will be just updated on the next get() invocation + metadata = provider.getKeyspaceMetadata(name); + if (metadata != null) + cachedKeyspaceMetadata = new KeyspaceMetadataCache(schemaVersion, metadata); + } + return metadata; } public void unsetInitial() diff --git a/src/java/org/apache/cassandra/db/memtable/ShardedSkipListMemtable.java b/src/java/org/apache/cassandra/db/memtable/ShardedSkipListMemtable.java index 9b9a531a2b..8c0897e1d4 100644 --- a/src/java/org/apache/cassandra/db/memtable/ShardedSkipListMemtable.java +++ b/src/java/org/apache/cassandra/db/memtable/ShardedSkipListMemtable.java @@ -284,6 +284,7 @@ public class ShardedSkipListMemtable extends AbstractShardedMemtable { long keySize = 0; int keyCount = 0; + TableMetadata currentTableMetadata = metadata(); for (Iterator it = getPartitionIterator(from, true, to,false); it.hasNext();) { @@ -297,6 +298,8 @@ public class ShardedSkipListMemtable extends AbstractShardedMemtable return new AbstractFlushablePartitionSet() { + private final TableMetadata tableMetadata = currentTableMetadata; + public Memtable memtable() { return ShardedSkipListMemtable.this; @@ -326,6 +329,12 @@ public class ShardedSkipListMemtable extends AbstractShardedMemtable { return partitionKeySize; } + + @Override + public TableMetadata metadata() + { + return tableMetadata; + } }; } diff --git a/src/java/org/apache/cassandra/db/memtable/SkipListMemtable.java b/src/java/org/apache/cassandra/db/memtable/SkipListMemtable.java index 985dd310fd..9271000f05 100644 --- a/src/java/org/apache/cassandra/db/memtable/SkipListMemtable.java +++ b/src/java/org/apache/cassandra/db/memtable/SkipListMemtable.java @@ -257,6 +257,7 @@ public class SkipListMemtable extends AbstractAllocatorMemtable Map toFlush = getPartitionsSubMap(from, true, to, false); long keysSize = 0; long keyCount = 0; + TableMetadata currentTableMetadata = metadata(); boolean trackContention = logger.isTraceEnabled(); if (trackContention) @@ -289,6 +290,8 @@ public class SkipListMemtable extends AbstractAllocatorMemtable return new AbstractFlushablePartitionSet() { + private final TableMetadata tableMetadata = currentTableMetadata; + @Override public Memtable memtable() { @@ -324,6 +327,12 @@ public class SkipListMemtable extends AbstractAllocatorMemtable { return partitionKeysSize; } + + @Override + public TableMetadata metadata() + { + return tableMetadata; + } }; } diff --git a/src/java/org/apache/cassandra/db/memtable/TrieMemtable.java b/src/java/org/apache/cassandra/db/memtable/TrieMemtable.java index 3e183c572e..66b1e8cb3a 100644 --- a/src/java/org/apache/cassandra/db/memtable/TrieMemtable.java +++ b/src/java/org/apache/cassandra/db/memtable/TrieMemtable.java @@ -448,6 +448,8 @@ public class TrieMemtable extends AbstractShardedMemtable return new AbstractFlushablePartitionSet() { + private final TableMetadata tableMetadata = TrieMemtable.this.metadata(); + public Memtable memtable() { return TrieMemtable.this; @@ -480,6 +482,12 @@ public class TrieMemtable extends AbstractShardedMemtable { return partitionKeySize; } + + @Override + public TableMetadata metadata() + { + return tableMetadata; + } }; } diff --git a/src/java/org/apache/cassandra/io/sstable/SSTableIdentityIterator.java b/src/java/org/apache/cassandra/io/sstable/SSTableIdentityIterator.java index d5a1ae8bcc..c2d7d4b7f9 100644 --- a/src/java/org/apache/cassandra/io/sstable/SSTableIdentityIterator.java +++ b/src/java/org/apache/cassandra/io/sstable/SSTableIdentityIterator.java @@ -61,14 +61,19 @@ public class SSTableIdentityIterator implements Comparable();