Merge branch 'cassandra-2.0' into trunk Conflicts: src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java src/java/org/apache/cassandra/service/StorageProxy.java
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/7ce5e062 Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/7ce5e062 Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/7ce5e062 Branch: refs/heads/trunk Commit: 7ce5e062ed602dd1c9593a03b554c11ff3cc52d5 Parents: fc01759 e59ef16 Author: Sylvain Lebresne <sylv...@datastax.com> Authored: Thu Feb 6 08:50:34 2014 +0100 Committer: Sylvain Lebresne <sylv...@datastax.com> Committed: Thu Feb 6 08:50:34 2014 +0100 ---------------------------------------------------------------------- CHANGES.txt | 1 + .../cql3/statements/ModificationStatement.java | 109 +++++++++++++++---- .../apache/cassandra/service/CASConditions.java | 38 +++++++ .../apache/cassandra/service/StorageProxy.java | 76 ++----------- .../cassandra/thrift/CassandraServer.java | 65 ++++++++++- 5 files changed, 200 insertions(+), 89 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cassandra/blob/7ce5e062/CHANGES.txt ---------------------------------------------------------------------- diff --cc CHANGES.txt index a139fdc,7ba8044..657bf9e --- a/CHANGES.txt +++ b/CHANGES.txt @@@ -1,34 -1,5 +1,35 @@@ +2.1 + * add listsnapshots command to nodetool (CASSANDRA-5742) + * Introduce AtomicBTreeColumns (CASSANDRA-6271) + * Multithreaded commitlog (CASSANDRA-3578) + * allocate fixed index summary memory pool and resample cold index summaries + to use less memory (CASSANDRA-5519) + * Removed multithreaded compaction (CASSANDRA-6142) + * Parallelize fetching rows for low-cardinality indexes (CASSANDRA-1337) + * change logging from log4j to logback (CASSANDRA-5883) + * switch to LZ4 compression for internode communication (CASSANDRA-5887) + * Stop using Thrift-generated Index* classes internally (CASSANDRA-5971) + * Remove 1.2 network compatibility code (CASSANDRA-5960) + * Remove leveled json manifest migration code (CASSANDRA-5996) + * Remove CFDefinition (CASSANDRA-6253) + * Use AtomicIntegerFieldUpdater in RefCountedMemory (CASSANDRA-6278) + * User-defined types for CQL3 (CASSANDRA-5590) + * Use of o.a.c.metrics in nodetool (CASSANDRA-5871, 6406) + * Batch read from OTC's queue and cleanup (CASSANDRA-1632) + * Secondary index support for collections (CASSANDRA-4511, 6383) + * SSTable metadata(Stats.db) format change (CASSANDRA-6356) + * Push composites support in the storage engine + (CASSANDRA-5417, CASSANDRA-6520) + * Add snapshot space used to cfstats (CASSANDRA-6231) + * Add cardinality estimator for key count estimation (CASSANDRA-5906) + * CF id is changed to be non-deterministic. Data dir/key cache are created + uniquely for CF id (CASSANDRA-5202) + * New counters implementation (CASSANDRA-6504) + * Replace UnsortedColumns usage with ArrayBackedSortedColumns (CASSANDRA-6630) + + 2.0.6 + * Correctly handle null with IF conditions and TTL (CASSANDRA-6623) Merged from 1.2: * Fix partition and range deletes not triggering flush (CASSANDRA-6655) http://git-wip-us.apache.org/repos/asf/cassandra/blob/7ce5e062/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java ---------------------------------------------------------------------- diff --cc src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java index e551187,2567043..f35690b --- a/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java +++ b/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java @@@ -24,13 -24,13 +24,14 @@@ import org.github.jamm.MemoryMeter import org.apache.cassandra.auth.Permission; import org.apache.cassandra.config.CFMetaData; +import org.apache.cassandra.config.ColumnDefinition; import org.apache.cassandra.cql3.*; import org.apache.cassandra.db.*; +import org.apache.cassandra.db.composites.CBuilder; +import org.apache.cassandra.db.composites.Composite; import org.apache.cassandra.db.filter.ColumnSlice; + import org.apache.cassandra.db.filter.IDiskAtomFilter; import org.apache.cassandra.db.filter.SliceQueryFilter; -import org.apache.cassandra.db.marshal.CompositeType; -import org.apache.cassandra.db.marshal.UTF8Type; import org.apache.cassandra.db.marshal.ListType; import org.apache.cassandra.db.marshal.BooleanType; import org.apache.cassandra.exceptions.*; @@@ -533,29 -539,96 +535,96 @@@ public abstract class ModificationState } else { - rm = new RowMutation(cfm.ksName, key, cf); + mutation = new Mutation(cfm.ksName, key, cf); } - return isCounter() ? new CounterMutation(rm, cl) : rm; + return isCounter() ? new CounterMutation(mutation, cl) : mutation; } - private ColumnFamily buildConditions(ByteBuffer key, Composite clusteringPrefix, UpdateParameters params) - throws InvalidRequestException + private static abstract class CQL3CasConditions implements CASConditions { - if (ifNotExists) - return null; - protected final ColumnNameBuilder rowPrefix; ++ protected final Composite rowPrefix; + protected final long now; - ColumnFamily cf = TreeMapBackedSortedColumns.factory.create(cfm); - protected CQL3CasConditions(ColumnNameBuilder rowPrefix, long now) ++ protected CQL3CasConditions(Composite rowPrefix, long now) + { + this.rowPrefix = rowPrefix; + this.now = now; + } - // CQL row marker - if (cfm.isCQL3Table()) - cf.addColumn(params.makeColumn(cfm.comparator.rowMarker(clusteringPrefix), ByteBufferUtil.EMPTY_BYTE_BUFFER)); + public IDiskAtomFilter readFilter() + { + // We always read the row entirely as on CAS failure we want to be able to distinguish between "row exists + // but all values on why there were conditions are null" and "row doesn't exists", and we can't rely on the + // row marker for that (see #6623) - return new SliceQueryFilter(rowPrefix.build(), rowPrefix.buildAsEndOfRange(), false, 1, rowPrefix.componentCount()); ++ return new SliceQueryFilter(rowPrefix.slice(), false, 1, rowPrefix.size()); + } + } - // Conditions - for (Operation condition : columnConditions) - condition.execute(key, cf, clusteringPrefix, params); + private static class NotExistCondition extends CQL3CasConditions + { - private NotExistCondition(ColumnNameBuilder rowPrefix, long now) ++ private NotExistCondition(Composite rowPrefix, long now) + { + super(rowPrefix, now); + } + + public boolean appliesTo(ColumnFamily current) + { + return current == null || current.hasOnlyTombstones(now); + } + } + + private static class ColumnsConditions extends CQL3CasConditions + { + private final ColumnFamily expected; - assert !cf.isEmpty(); - return cf; - private ColumnsConditions(ColumnNameBuilder rowPrefix, ++ private ColumnsConditions(Composite rowPrefix, + CFMetaData cfm, + ByteBuffer key, + Collection<Operation> conditions, + List<ByteBuffer> variables, + long now) throws InvalidRequestException + { + super(rowPrefix, now); + this.expected = TreeMapBackedSortedColumns.factory.create(cfm); + + // When building the conditions, we should not use a TTL. It's not useful, and if a very low ttl (1 seconds) is used, it's possible + // for it to expire before the actual build of the conditions which would break since we would then testing for the presence of tombstones. + UpdateParameters params = new UpdateParameters(cfm, variables, now, 0, null); + + // Conditions + for (Operation condition : conditions) - condition.execute(key, expected, rowPrefix.copy(), params); ++ condition.execute(key, expected, rowPrefix, params); + } + + public boolean appliesTo(ColumnFamily current) + { + if (current == null) + return false; + - for (Column e : expected) ++ for (Cell e : expected) + { - Column c = current.getColumn(e.name()); ++ Cell c = current.getColumn(e.name()); + if (e.isLive(now)) + { + if (c == null || !c.isLive(now) || !c.value().equals(e.value())) + return false; + } + else + { + // If we have a tombstone in expected, it means the condition tests that the column is + // null, so check that we have no value + if (c != null && c.isLive(now)) + return false; + } + } + return true; + } + + @Override + public String toString() + { + return expected.toString(); + } } public static abstract class Parsed extends CFStatement http://git-wip-us.apache.org/repos/asf/cassandra/blob/7ce5e062/src/java/org/apache/cassandra/service/StorageProxy.java ---------------------------------------------------------------------- diff --cc src/java/org/apache/cassandra/service/StorageProxy.java index 6e8d231,8d1f913..69d7123 --- a/src/java/org/apache/cassandra/service/StorageProxy.java +++ b/src/java/org/apache/cassandra/service/StorageProxy.java @@@ -43,12 -43,6 +43,8 @@@ import org.apache.cassandra.config.Data import org.apache.cassandra.config.Schema; import org.apache.cassandra.db.*; import org.apache.cassandra.db.Keyspace; - import org.apache.cassandra.db.composites.Composite; - import org.apache.cassandra.db.filter.ColumnSlice; - import org.apache.cassandra.db.filter.NamesQueryFilter; - import org.apache.cassandra.db.filter.SliceQueryFilter; +import org.apache.cassandra.db.index.SecondaryIndex; +import org.apache.cassandra.db.index.SecondaryIndexSearcher; import org.apache.cassandra.db.marshal.UUIDType; import org.apache.cassandra.dht.AbstractBounds; import org.apache.cassandra.dht.Bounds; http://git-wip-us.apache.org/repos/asf/cassandra/blob/7ce5e062/src/java/org/apache/cassandra/thrift/CassandraServer.java ---------------------------------------------------------------------- diff --cc src/java/org/apache/cassandra/thrift/CassandraServer.java index 44fb22e,ef5eeb8..650c74e --- a/src/java/org/apache/cassandra/thrift/CassandraServer.java +++ b/src/java/org/apache/cassandra/thrift/CassandraServer.java @@@ -47,8 -47,7 +48,9 @@@ import org.apache.cassandra.cql.CQLStat import org.apache.cassandra.cql.QueryProcessor; import org.apache.cassandra.cql3.QueryOptions; import org.apache.cassandra.db.*; +import org.apache.cassandra.db.composites.*; import org.apache.cassandra.db.context.CounterContext; ++import org.apache.cassandra.db.filter.ColumnSlice; import org.apache.cassandra.db.filter.IDiskAtomFilter; import org.apache.cassandra.db.filter.NamesQueryFilter; import org.apache.cassandra.db.filter.SliceQueryFilter; @@@ -2171,5 -2159,62 +2173,62 @@@ public class CassandraServer implement } }); } - // main method moved to CassandraDaemon + + private static class ThriftCASConditions implements CASConditions + { + private final ColumnFamily expected; + + private ThriftCASConditions(ColumnFamily expected) + { + this.expected = expected; + } + + public IDiskAtomFilter readFilter() + { + return expected == null || expected.isEmpty() - ? new SliceQueryFilter(ByteBufferUtil.EMPTY_BYTE_BUFFER, ByteBufferUtil.EMPTY_BYTE_BUFFER, false, 1) ++ ? new SliceQueryFilter(ColumnSlice.ALL_COLUMNS_ARRAY, false, 1) + : new NamesQueryFilter(ImmutableSortedSet.copyOf(expected.getComparator(), expected.getColumnNames())); + } + + public boolean appliesTo(ColumnFamily current) + { + long now = System.currentTimeMillis(); + - if (!hasLiveColumns(expected, now)) - return !hasLiveColumns(current, now); - else if (!hasLiveColumns(current, now)) ++ if (!hasLiveCells(expected, now)) ++ return !hasLiveCells(current, now); ++ else if (!hasLiveCells(current, now)) + return false; + + // current has been built from expected, so we know that it can't have columns + // that excepted don't have. So we just check that for each columns in expected: + // - if it is a tombstone, whether current has no column or a tombstone; + // - otherwise, that current has a live column with the same value. - for (org.apache.cassandra.db.Column e : expected) ++ for (Cell e : expected) + { - org.apache.cassandra.db.Column c = current.getColumn(e.name()); ++ Cell c = current.getColumn(e.name()); + if (e.isLive(now)) + { + if (c == null || !c.isLive(now) || !c.value().equals(e.value())) + return false; + } + else + { + if (c != null && c.isLive(now)) + return false; + } + } + return true; + } + - private static boolean hasLiveColumns(ColumnFamily cf, long now) ++ private static boolean hasLiveCells(ColumnFamily cf, long now) + { + return cf != null && !cf.hasOnlyTombstones(now); + } + + @Override + public String toString() + { + return expected.toString(); + } + } }