Author: jbellis
Date: Tue Feb 16 02:47:57 2010
New Revision: 910380
URL: http://svn.apache.org/viewvc?rev=910380&view=rev
Log:
fix range queries and Range/Bounds intersections. patch by jbellis; tested by
Jack Culpepper for CASSANDRA-781
Added:
incubator/cassandra/trunk/test/unit/org/apache/cassandra/dht/BoundsTest.java
(with props)
incubator/cassandra/trunk/test/unit/org/apache/cassandra/dht/RangeIntersectionTest.java
(with props)
Modified:
incubator/cassandra/trunk/src/java/org/apache/cassandra/db/ColumnFamilyStore.java
incubator/cassandra/trunk/src/java/org/apache/cassandra/db/RangeSliceReply.java
incubator/cassandra/trunk/src/java/org/apache/cassandra/dht/AbstractBounds.java
incubator/cassandra/trunk/src/java/org/apache/cassandra/dht/Bounds.java
incubator/cassandra/trunk/src/java/org/apache/cassandra/dht/Range.java
incubator/cassandra/trunk/src/java/org/apache/cassandra/service/RangeSliceResponseResolver.java
incubator/cassandra/trunk/src/java/org/apache/cassandra/service/StorageProxy.java
incubator/cassandra/trunk/src/java/org/apache/cassandra/thrift/CassandraServer.java
incubator/cassandra/trunk/src/java/org/apache/cassandra/thrift/ThriftValidation.java
incubator/cassandra/trunk/test/system/test_server.py
incubator/cassandra/trunk/test/unit/org/apache/cassandra/dht/RangeTest.java
Modified:
incubator/cassandra/trunk/src/java/org/apache/cassandra/db/ColumnFamilyStore.java
URL:
http://svn.apache.org/viewvc/incubator/cassandra/trunk/src/java/org/apache/cassandra/db/ColumnFamilyStore.java?rev=910380&r1=910379&r2=910380&view=diff
==============================================================================
---
incubator/cassandra/trunk/src/java/org/apache/cassandra/db/ColumnFamilyStore.java
(original)
+++
incubator/cassandra/trunk/src/java/org/apache/cassandra/db/ColumnFamilyStore.java
Tue Feb 16 02:47:57 2010
@@ -44,6 +44,7 @@
import org.apache.cassandra.dht.AbstractBounds;
import org.apache.cassandra.dht.Bounds;
import org.apache.cassandra.dht.Range;
+import org.apache.cassandra.dht.Token;
import org.apache.cassandra.io.*;
import org.apache.cassandra.io.util.FileUtils;
@@ -1062,12 +1063,13 @@
else
{
// wrapped range
- Range first = new Range(range.left,
StorageService.getPartitioner().getMinimumToken());
+ Token min = StorageService.getPartitioner().getMinimumToken();
+ Range first = new Range(range.left, min);
completed = getKeyRange(keys, first, keyMax);
- if (!completed)
+ if (!completed && min.compareTo(range.right) < 0)
{
- Range second = new
Range(StorageService.getPartitioner().getMinimumToken(), range.right);
- completed = getKeyRange(keys, second, keyMax);
+ Range second = new Range(min, range.right);
+ getKeyRange(keys, second, keyMax);
}
}
List<Row> rows = new ArrayList<Row>(keys.size());
@@ -1081,7 +1083,7 @@
rows.add(new Row(key, getColumnFamily(filter)));
}
- return new RangeSliceReply(rows, completed);
+ return new RangeSliceReply(rows);
}
public AbstractType getComparator()
Modified:
incubator/cassandra/trunk/src/java/org/apache/cassandra/db/RangeSliceReply.java
URL:
http://svn.apache.org/viewvc/incubator/cassandra/trunk/src/java/org/apache/cassandra/db/RangeSliceReply.java?rev=910380&r1=910379&r2=910380&view=diff
==============================================================================
---
incubator/cassandra/trunk/src/java/org/apache/cassandra/db/RangeSliceReply.java
(original)
+++
incubator/cassandra/trunk/src/java/org/apache/cassandra/db/RangeSliceReply.java
Tue Feb 16 02:47:57 2010
@@ -31,18 +31,15 @@
public class RangeSliceReply
{
public final List<Row> rows;
- public final boolean rangeCompletedLocally;
- public RangeSliceReply(List<Row> rows, boolean rangeCompletedLocally)
+ public RangeSliceReply(List<Row> rows)
{
this.rows = rows;
- this.rangeCompletedLocally = rangeCompletedLocally;
}
public Message getReply(Message originalMessage) throws IOException
{
DataOutputBuffer dob = new DataOutputBuffer();
- dob.writeBoolean(rangeCompletedLocally);
dob.writeInt(rows.size());
for (Row row : rows)
{
@@ -57,7 +54,6 @@
{
return "RangeSliceReply{" +
"rows=" + StringUtils.join(rows, ",") +
- ", rangeCompletedLocally=" + rangeCompletedLocally +
'}';
}
@@ -65,13 +61,12 @@
{
ByteArrayInputStream bufIn = new ByteArrayInputStream(body);
DataInputStream dis = new DataInputStream(bufIn);
- boolean completed = dis.readBoolean();
int rowCount = dis.readInt();
List<Row> rows = new ArrayList<Row>(rowCount);
for (int i = 0; i < rowCount; i++)
{
rows.add(Row.serializer().deserialize(dis));
}
- return new RangeSliceReply(rows, completed);
+ return new RangeSliceReply(rows);
}
}
Modified:
incubator/cassandra/trunk/src/java/org/apache/cassandra/dht/AbstractBounds.java
URL:
http://svn.apache.org/viewvc/incubator/cassandra/trunk/src/java/org/apache/cassandra/dht/AbstractBounds.java?rev=910380&r1=910379&r2=910380&view=diff
==============================================================================
---
incubator/cassandra/trunk/src/java/org/apache/cassandra/dht/AbstractBounds.java
(original)
+++
incubator/cassandra/trunk/src/java/org/apache/cassandra/dht/AbstractBounds.java
Tue Feb 16 02:47:57 2010
@@ -1,7 +1,11 @@
package org.apache.cassandra.dht;
-import java.io.*;
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.io.Serializable;
import java.util.List;
+import java.util.Set;
import org.apache.cassandra.io.ICompactSerializer2;
@@ -23,13 +27,29 @@
public final Token left;
public final Token right;
- public AbstractBounds(Token left, Token right)
+ protected transient final IPartitioner partitioner;
+
+ public AbstractBounds(Token left, Token right, IPartitioner partitioner)
{
this.left = left;
this.right = right;
+ this.partitioner = partitioner;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ return toString().hashCode();
}
- public abstract List<AbstractBounds> restrictTo(Range range);
+ @Override
+ public abstract boolean equals(Object obj);
+
+ public abstract boolean contains(Token start);
+
+ public abstract Set<AbstractBounds> restrictTo(Range range);
+
+ public abstract List<AbstractBounds> unwrap();
private static class AbstractBoundsSerializer implements
ICompactSerializer2<AbstractBounds>
{
Modified:
incubator/cassandra/trunk/src/java/org/apache/cassandra/dht/Bounds.java
URL:
http://svn.apache.org/viewvc/incubator/cassandra/trunk/src/java/org/apache/cassandra/dht/Bounds.java?rev=910380&r1=910379&r2=910380&view=diff
==============================================================================
--- incubator/cassandra/trunk/src/java/org/apache/cassandra/dht/Bounds.java
(original)
+++ incubator/cassandra/trunk/src/java/org/apache/cassandra/dht/Bounds.java Tue
Feb 16 02:47:57 2010
@@ -1,9 +1,6 @@
package org.apache.cassandra.dht;
-import java.util.Arrays;
-import java.util.List;
-
-import org.apache.commons.lang.ObjectUtils;
+import java.util.*;
import org.apache.cassandra.service.StorageService;
@@ -11,27 +8,62 @@
{
public Bounds(Token left, Token right)
{
- super(left, right);
+ this(left, right, StorageService.getPartitioner());
+ }
+
+ Bounds(Token left, Token right, IPartitioner partitioner)
+ {
+ super(left, right, partitioner);
// unlike a Range, a Bounds may not wrap
- assert left.compareTo(right) <= 0 ||
right.equals(StorageService.getPartitioner().getMinimumToken());
+ assert left.compareTo(right) <= 0 ||
right.equals(partitioner.getMinimumToken()) : "[" + left + "," + right + "]";
}
- public List<AbstractBounds> restrictTo(Range range)
+ @Override
+ public boolean contains(Token token)
{
- Token left, right;
- if (range.left.equals(range.right))
- {
- left = this.left;
- right = this.right;
- }
- else
+ return Range.contains(left, right, token) || left.equals(token);
+ }
+
+ public Set<AbstractBounds> restrictTo(Range range)
+ {
+ Token min = partitioner.getMinimumToken();
+
+ // special case Bounds where left=right (single Token)
+ if (this.left.equals(this.right) && !this.right.equals(min))
+ return range.contains(this.left)
+ ? Collections.unmodifiableSet(new
HashSet<AbstractBounds>(Arrays.asList(this)))
+ : Collections.<AbstractBounds>emptySet();
+
+ // get the intersection of a Range w/ same left & right
+ Set<Range> ranges = range.intersectionWith(new Range(this.left,
this.right));
+ // if range doesn't contain left token anyway, that's the correct
answer
+ if (!range.contains(this.left))
+ return (Set) ranges;
+ // otherwise, add back in the left token
+ Set<AbstractBounds> S = new HashSet<AbstractBounds>(ranges.size());
+ for (Range restricted : ranges)
{
- left = (Token) ObjectUtils.max(this.left, range.left);
- right =
this.right.equals(StorageService.getPartitioner().getMinimumToken())
- ? range.right
- : (Token) ObjectUtils.min(this.right, range.right);
+ if (restricted.left.equals(this.left))
+ S.add(new Bounds(restricted.left, restricted.right));
+ else
+ S.add(restricted);
}
- return (List) Arrays.asList(new Bounds(left, right));
+ return Collections.unmodifiableSet(S);
+ }
+
+ public List<AbstractBounds> unwrap()
+ {
+ // Bounds objects never wrap
+ return (List)Arrays.asList(this);
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if (!(o instanceof Bounds))
+ return false;
+ Bounds rhs = (Bounds)o;
+ return left.equals(rhs.left) && right.equals(rhs.right);
}
public String toString()
Modified: incubator/cassandra/trunk/src/java/org/apache/cassandra/dht/Range.java
URL:
http://svn.apache.org/viewvc/incubator/cassandra/trunk/src/java/org/apache/cassandra/dht/Range.java?rev=910380&r1=910379&r2=910380&view=diff
==============================================================================
--- incubator/cassandra/trunk/src/java/org/apache/cassandra/dht/Range.java
(original)
+++ incubator/cassandra/trunk/src/java/org/apache/cassandra/dht/Range.java Tue
Feb 16 02:47:57 2010
@@ -19,13 +19,12 @@
package org.apache.cassandra.dht;
import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
+import java.util.*;
import org.apache.commons.lang.ObjectUtils;
+import org.apache.cassandra.service.StorageService;
+
/**
* A representation of the range that a node is responsible for on the DHT
ring.
@@ -38,7 +37,12 @@
public Range(Token left, Token right)
{
- super(left, right);
+ this(left, right, StorageService.getPartitioner());
+ }
+
+ public Range(Token left, Token right, IPartitioner partitioner)
+ {
+ super(left, right, partitioner);
}
public static boolean contains(Token left, Token right, Token bi)
@@ -68,18 +72,30 @@
public boolean contains(Range that)
{
+ if (this.left.equals(this.right))
+ {
+ // full ring always contains all other ranges
+ return true;
+ }
+
boolean thiswraps = isWrapAround(left, right);
boolean thatwraps = isWrapAround(that.left, that.right);
if (thiswraps == thatwraps)
- return left.compareTo(that.left) <= 0 &&
- that.right.compareTo(right) <= 0;
+ {
+ return left.compareTo(that.left) <= 0 &&
that.right.compareTo(right) <= 0;
+ }
else if (thiswraps)
+ {
// wrapping might contain non-wrapping
- return left.compareTo(that.left) <= 0 ||
- that.right.compareTo(right) <= 0;
- else // (thatwraps)
+ // that is contained if both its tokens are in one of our wrap
segments
+ return left.compareTo(that.left) <= 0 ||
that.right.compareTo(right) <= 0;
+ }
+ else
+ {
+ // (thatwraps)
// non-wrapping cannot contain wrapping
return false;
+ }
}
/**
@@ -102,22 +118,49 @@
return intersectionWith(that).size() > 0;
}
- public List<Range> intersectionWith(Range that)
+ public static Set<Range> rangeSet(Range ... ranges)
+ {
+ return Collections.unmodifiableSet(new
HashSet<Range>(Arrays.asList(ranges)));
+ }
+
+ /**
+ * @param that
+ * @return the intersection of the two Ranges. this can be two disjoint
Ranges if one is wrapping and one is not.
+ * say you have nodes G and M, with query range (D,T]; the intersection is
(M-T] and (D-G].
+ * If there is no intersection, an empty list is returned.
+ */
+ public Set<Range> intersectionWith(Range that)
{
+ if (this.contains(that))
+ return rangeSet(that);
+ if (that.contains(this))
+ return rangeSet(this);
+
boolean thiswraps = isWrapAround(left, right);
boolean thatwraps = isWrapAround(that.left, that.right);
- if (thiswraps && thatwraps)
- {
- // there is always an intersection when both wrap
- return Arrays.asList(new Range((Token)ObjectUtils.max(this.left,
that.left),
- (Token)ObjectUtils.min(this.right,
that.right)));
- }
if (!thiswraps && !thatwraps)
{
+ // neither wraps. the straightforward case.
if (!(left.compareTo(that.right) < 0 && that.left.compareTo(right)
< 0))
- return Collections.emptyList();
- return Arrays.asList(new Range((Token)ObjectUtils.max(this.left,
that.left),
- (Token)ObjectUtils.min(this.right,
that.right)));
+ return Collections.emptySet();
+ return rangeSet(new Range((Token)ObjectUtils.max(this.left,
that.left),
+ (Token)ObjectUtils.min(this.right,
that.right)));
+ }
+ if (thiswraps && thatwraps)
+ {
+ // if the starts are the same, one contains the other, which we
have already ruled out.
+ assert !this.left.equals(that.left);
+ // two wrapping ranges always intersect.
+ // since we have already determined that neither this nor that
contains the other, we have 2 cases,
+ // and mirror images of those case.
+ // (1) both of that's (1, 2] endpoints lie in this's (A, B] right
segment:
+ // ---------B--------A--1----2------>
+ // (2) only that's start endpoint lies in this's right segment:
+ // ---------B----1---A-------2------>
+ // or, we have the same cases on the left segement, which we can
handle by swapping this and that.
+ return this.left.compareTo(that.left) < 0
+ ? intersectionBothWrapping(this, that)
+ : intersectionBothWrapping(that, this);
}
if (thiswraps && !thatwraps)
return intersectionOneWrapping(this, that);
@@ -125,23 +168,39 @@
return intersectionOneWrapping(that, this);
}
- public List<AbstractBounds> restrictTo(Range range)
+ private static Set<Range> intersectionBothWrapping(Range first, Range that)
{
- return (List) intersectionWith(range);
+ Set<Range> intersection = new HashSet<Range>(2);
+ if (that.right.compareTo(first.left) > 0)
+ intersection.add(new Range(first.left, that.right));
+ intersection.add(new Range(that.left, first.right));
+ return Collections.unmodifiableSet(intersection);
}
- private static List<Range> intersectionOneWrapping(Range wrapping, Range
other)
+ private static Set<Range> intersectionOneWrapping(Range wrapping, Range
other)
{
- List<Range> intersection = new ArrayList<Range>(2);
- if (wrapping.contains(other))
- {
- return Arrays.asList(other);
- }
- if (other.contains(wrapping.right) || other.left.equals(wrapping.left))
+ Set<Range> intersection = new HashSet<Range>(2);
+ if (other.contains(wrapping.right))
intersection.add(new Range(other.left, wrapping.right));
+ // need the extra compareto here because ranges are asymmetrical;
wrapping.left _is not_ contained by the wrapping range
if (other.contains(wrapping.left) &&
wrapping.left.compareTo(other.right) < 0)
intersection.add(new Range(wrapping.left, other.right));
- return Collections.unmodifiableList(intersection);
+ return Collections.unmodifiableSet(intersection);
+ }
+
+ public Set<AbstractBounds> restrictTo(Range range)
+ {
+ return (Set) intersectionWith(range);
+ }
+
+ public List<AbstractBounds> unwrap()
+ {
+ if (!isWrapAround() || right.equals(partitioner.getMinimumToken()))
+ return (List)Arrays.asList(this);
+ List<AbstractBounds> unwrapped = new ArrayList<AbstractBounds>(2);
+ unwrapped.add(new Range(left, partitioner.getMinimumToken()));
+ unwrapped.add(new Range(partitioner.getMinimumToken(), right));
+ return unwrapped;
}
/**
@@ -190,12 +249,6 @@
return left.equals(rhs.left) && right.equals(rhs.right);
}
- @Override
- public int hashCode()
- {
- return toString().hashCode();
- }
-
public String toString()
{
return "(" + left + "," + right + "]";
Modified:
incubator/cassandra/trunk/src/java/org/apache/cassandra/service/RangeSliceResponseResolver.java
URL:
http://svn.apache.org/viewvc/incubator/cassandra/trunk/src/java/org/apache/cassandra/service/RangeSliceResponseResolver.java?rev=910380&r1=910379&r2=910380&view=diff
==============================================================================
---
incubator/cassandra/trunk/src/java/org/apache/cassandra/service/RangeSliceResponseResolver.java
(original)
+++
incubator/cassandra/trunk/src/java/org/apache/cassandra/service/RangeSliceResponseResolver.java
Tue Feb 16 02:47:57 2010
@@ -24,63 +24,78 @@
import org.apache.log4j.Logger;
+import org.apache.commons.collections.iterators.CollatingIterator;
+
+import com.google.common.collect.AbstractIterator;
import org.apache.cassandra.db.ColumnFamily;
import org.apache.cassandra.db.RangeSliceReply;
import org.apache.cassandra.db.Row;
-import org.apache.cassandra.dht.Range;
import org.apache.cassandra.net.Message;
+import org.apache.cassandra.utils.Pair;
+import org.apache.cassandra.utils.ReducingIterator;
/**
* Turns RangeSliceReply objects into row (string -> CF) maps, resolving
* to the most recent ColumnFamily and setting up read repairs as necessary.
*/
-public class RangeSliceResponseResolver implements
IResponseResolver<Map<String, ColumnFamily>>
+public class RangeSliceResponseResolver implements IResponseResolver<List<Row>>
{
private static final Logger logger_ =
Logger.getLogger(RangeSliceResponseResolver.class);
private final String table;
- private final Range range;
private final List<InetAddress> sources;
- private boolean isCompleted;
- public RangeSliceResponseResolver(String table, Range range,
List<InetAddress> sources)
+ public RangeSliceResponseResolver(String table, List<InetAddress> sources)
{
assert sources.size() > 0;
this.sources = sources;
- this.range = range;
this.table = table;
}
- public Map<String, ColumnFamily> resolve(List<Message> responses) throws
DigestMismatchException, IOException
+ public List<Row> resolve(List<Message> responses) throws
DigestMismatchException, IOException
{
- Map<InetAddress, Map<String, ColumnFamily>> replies = new
HashMap<InetAddress, Map<String, ColumnFamily>>(responses.size());
- Set<String> allKeys = new HashSet<String>();
- for (Message response : responses)
+ CollatingIterator collator = new CollatingIterator(new
Comparator<Pair<Row,InetAddress>>()
{
- RangeSliceReply reply =
RangeSliceReply.read(response.getMessageBody());
- isCompleted &= reply.rangeCompletedLocally;
- Map<String, ColumnFamily> rows = new HashMap<String,
ColumnFamily>(reply.rows.size());
- for (Row row : reply.rows)
+ public int compare(Pair<Row,InetAddress> o1, Pair<Row,InetAddress>
o2)
{
- rows.put(row.key, row.cf);
- allKeys.add(row.key);
+ return o1.left.key.compareTo(o2.left.key);
}
- replies.put(response.getFrom(), rows);
+ });
+
+ int n = 0;
+ for (Message response : responses)
+ {
+ RangeSliceReply reply =
RangeSliceReply.read(response.getMessageBody());
+ n = Math.max(n, reply.rows.size());
+ collator.addIterator(new RowIterator(reply.rows.iterator(),
response.getFrom()));
}
// for each row, compute the combination of all different versions
seen, and repair incomplete versions
- // TODO since the rows all arrive in sorted order, we should be able
to do this more efficiently w/o all the Map conversion
- Map<String, ColumnFamily> resolvedRows = new HashMap<String,
ColumnFamily>(allKeys.size());
- for (String key : allKeys)
+ ReducingIterator<Pair<Row,InetAddress>, Row> iter = new
ReducingIterator<Pair<Row,InetAddress>, Row>(collator)
{
List<ColumnFamily> versions = new
ArrayList<ColumnFamily>(sources.size());
- for (InetAddress endpoint : sources)
+ List<InetAddress> versionSources = new
ArrayList<InetAddress>(sources.size());
+ String key;
+
+ public void reduce(Pair<Row,InetAddress> current)
{
- versions.add(replies.get(endpoint).get(key));
+ key = current.left.key;
+ versions.add(current.left.cf);
+ versionSources.add(current.right);
}
- ColumnFamily resolved =
ReadResponseResolver.resolveSuperset(versions);
- ReadResponseResolver.maybeScheduleRepairs(resolved, table, key,
versions, sources);
- resolvedRows.put(key, resolved);
- }
+
+ protected Row getReduced()
+ {
+ ColumnFamily resolved =
ReadResponseResolver.resolveSuperset(versions);
+ ReadResponseResolver.maybeScheduleRepairs(resolved, table,
key, versions, versionSources);
+ versions.clear();
+ return new Row(key, resolved);
+ }
+ };
+
+ List<Row> resolvedRows = new ArrayList<Row>(n);
+ while (iter.hasNext())
+ resolvedRows.add(iter.next());
+
return resolvedRows;
}
@@ -89,11 +104,21 @@
return responses.size() >= sources.size();
}
- /**
- * only valid after resolve has been called (typically via QRH.get)
- */
- public boolean completed()
+ private static class RowIterator extends
AbstractIterator<Pair<Row,InetAddress>>
{
- return isCompleted;
+ private final Iterator<Row> iter;
+ private final InetAddress source;
+
+ private RowIterator(Iterator<Row> iter, InetAddress source)
+ {
+ this.iter = iter;
+ this.source = source;
+ }
+
+ @Override
+ protected Pair<Row,InetAddress> computeNext()
+ {
+ return iter.hasNext() ? new Pair<Row, InetAddress>(iter.next(),
source) : endOfData();
+ }
}
}
\ No newline at end of file
Modified:
incubator/cassandra/trunk/src/java/org/apache/cassandra/service/StorageProxy.java
URL:
http://svn.apache.org/viewvc/incubator/cassandra/trunk/src/java/org/apache/cassandra/service/StorageProxy.java?rev=910380&r1=910379&r2=910380&view=diff
==============================================================================
---
incubator/cassandra/trunk/src/java/org/apache/cassandra/service/StorageProxy.java
(original)
+++
incubator/cassandra/trunk/src/java/org/apache/cassandra/service/StorageProxy.java
Tue Feb 16 02:47:57 2010
@@ -29,6 +29,9 @@
import org.apache.commons.lang.StringUtils;
+import com.google.common.collect.AbstractIterator;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Iterators;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.db.*;
import java.net.InetAddress;
@@ -530,81 +533,154 @@
return rows;
}
- public static List<Pair<String, ColumnFamily>>
getRangeSlice(RangeSliceCommand command, ConsistencyLevel consistency_level)
+ public static List<Row> getRangeSlice(RangeSliceCommand command,
ConsistencyLevel consistency_level)
throws IOException, UnavailableException, TimeoutException
{
+ if (logger.isDebugEnabled())
+ logger.debug(command);
long startTime = System.nanoTime();
- TokenMetadata tokenMetadata =
StorageService.instance.getTokenMetadata();
- Iterator<Token> iter =
TokenMetadata.ringIterator(tokenMetadata.sortedTokens(), command.range.left);
final String table = command.keyspace;
int responseCount =
determineBlockFor(DatabaseDescriptor.getReplicationFactor(table),
DatabaseDescriptor.getReplicationFactor(table), consistency_level);
- // starting with the range containing the start key, scan until either
we have enough results,
- // or the node scan reports that it was done (i.e., encountered a key
outside the desired range).
- Map<String, ColumnFamily> rows = new HashMap<String,
ColumnFamily>(command.max_keys);
- outer:
- while (iter.hasNext())
+ List<Pair<AbstractBounds, List<InetAddress>>> ranges =
getRestrictedRanges(command.range, command.keyspace, responseCount);
+
+ // now scan until we have enough results
+ List<Row> rows = new ArrayList<Row>(command.max_keys);
+ for (Pair<AbstractBounds, List<InetAddress>> pair :
getRangeIterator(ranges, command.range.left))
{
- Token currentToken = iter.next();
- Range currentRange = new
Range(tokenMetadata.getPredecessor(currentToken), currentToken);
- List<InetAddress> endpoints =
StorageService.instance.getLiveNaturalEndpoints(command.keyspace, currentToken);
- if (endpoints.size() < responseCount)
- throw new UnavailableException();
-
DatabaseDescriptor.getEndPointSnitch(command.keyspace).sortByProximity(FBUtilities.getLocalAddress(),
endpoints);
+ AbstractBounds range = pair.left;
+ List<InetAddress> endpoints = pair.right;
+ RangeSliceCommand c2 = new RangeSliceCommand(command.keyspace,
command.column_family, command.super_column, command.predicate, range,
command.max_keys);
+ Message message = c2.getMessage();
- // make sure we only get keys from the current range (and not
other replicas that might be on the nodes).
- // usually this will be only one range, but sometimes the
intersection of a wrapping Range with a non-wrapping
- // is two disjoint, non-wrapping Ranges separated by a gap.
- List<AbstractBounds> restricted =
command.range.restrictTo(currentRange);
+ // collect replies and resolve according to consistency level
+ RangeSliceResponseResolver resolver = new
RangeSliceResponseResolver(command.keyspace, endpoints);
+ QuorumResponseHandler<List<Row>> handler = new
QuorumResponseHandler<List<Row>>(responseCount, resolver);
- for (AbstractBounds range : restricted)
+ for (InetAddress endpoint : endpoints)
{
- RangeSliceCommand c2 = new RangeSliceCommand(command.keyspace,
command.column_family, command.super_column, command.predicate, range,
command.max_keys);
- Message message = c2.getMessage();
-
- // collect replies and resolve according to consistency level
- List<InetAddress> endpointsforCL = endpoints.subList(0,
responseCount);
- RangeSliceResponseResolver resolver = new
RangeSliceResponseResolver(command.keyspace, currentRange, endpointsforCL);
- QuorumResponseHandler<Map<String, ColumnFamily>> handler = new
QuorumResponseHandler<Map<String, ColumnFamily>>(responseCount, resolver);
-
- for (InetAddress endpoint : endpointsforCL)
- {
- MessagingService.instance.sendRR(message, endpoint,
handler);
- if (logger.isDebugEnabled())
- logger.debug("reading " + c2 + " for " + range + "
from " + message.getMessageId() + "@" + endpoint);
- }
- // TODO read repair on remaining replicas?
+ MessagingService.instance.sendRR(message, endpoint, handler);
+ if (logger.isDebugEnabled())
+ logger.debug("reading " + c2 + " from " +
message.getMessageId() + "@" + endpoint);
+ }
+ // TODO read repair on remaining replicas?
- // if we're done, great, otherwise, move to the next range
- try
- {
- rows.putAll(handler.get());
- }
- catch (DigestMismatchException e)
+ // if we're done, great, otherwise, move to the next range
+ try
+ {
+ if (logger.isDebugEnabled())
{
- throw new AssertionError(e); // no digests in range slices
yet
+ for (Row row : handler.get())
+ {
+ logger.debug("range slices read " + row.key);
+ }
}
- if (rows.size() >= command.max_keys || resolver.completed())
- break outer;
+ rows.addAll(handler.get());
+ }
+ catch (DigestMismatchException e)
+ {
+ throw new AssertionError(e); // no digests in range slices yet
}
+ if (rows.size() >= command.max_keys)
+ break;
}
- List<Pair<String, ColumnFamily>> results = new ArrayList<Pair<String,
ColumnFamily>>(rows.size());
- for (Map.Entry<String, ColumnFamily> entry : rows.entrySet())
+ rangeStats.addNano(System.nanoTime() - startTime);
+ return rows.size() > command.max_keys ? rows.subList(0,
command.max_keys) : rows;
+ }
+
+ /**
+ * returns an iterator that will return ranges in ring order, starting
with the one that contains the start token
+ */
+ private static Iterable<Pair<AbstractBounds, List<InetAddress>>>
getRangeIterator(final List<Pair<AbstractBounds, List<InetAddress>>> ranges,
Token start)
+ {
+ // sort ranges in ring order
+ Comparator<Pair<AbstractBounds, List<InetAddress>>> comparator = new
Comparator<Pair<AbstractBounds, List<InetAddress>>>()
+ {
+ public int compare(Pair<AbstractBounds, List<InetAddress>> o1,
Pair<AbstractBounds, List<InetAddress>> o2)
+ {
+ // no restricted ranges will overlap so we don't need to worry
about inclusive vs exclusive left,
+ // just sort by raw token position.
+ return o1.left.left.compareTo(o2.left.left);
+ }
+ };
+ Collections.sort(ranges, comparator);
+
+ // find the one to start with
+ int i;
+ for (i = 0; i < ranges.size(); i++)
{
- ColumnFamily cf = entry.getValue();
- results.add(new Pair<String, ColumnFamily>(entry.getKey(), cf));
+ AbstractBounds range = ranges.get(i).left;
+ if (range.contains(start) || range.left.equals(start))
+ break;
}
- Collections.sort(results, new Comparator<Pair<String, ColumnFamily>>()
+ AbstractBounds range = ranges.get(i).left;
+ assert range.contains(start) || range.left.equals(start); // make sure
the loop didn't just end b/c ranges were exhausted
+
+ // return an iterable that starts w/ the correct range and iterates
the rest in ring order
+ final int begin = i;
+ return new Iterable<Pair<AbstractBounds, List<InetAddress>>>()
{
- public int compare(Pair<String, ColumnFamily> o1, Pair<String,
ColumnFamily> o2)
+ public Iterator<Pair<AbstractBounds, List<InetAddress>>> iterator()
{
- return keyComparator.compare(o1.left, o2.left);
+ return new AbstractIterator<Pair<AbstractBounds,
List<InetAddress>>>()
+ {
+ int n = 0;
+
+ protected Pair<AbstractBounds, List<InetAddress>>
computeNext()
+ {
+ if (n == ranges.size())
+ return endOfData();
+ return ranges.get((begin + n++) % ranges.size());
+ }
+ };
}
- });
- rangeStats.addNano(System.nanoTime() - startTime);
- return results;
+ };
+ }
+
+ /**
+ * compute all ranges we're going to query, in sorted order, so that we
get the correct results back.
+ * 1) computing range intersections is necessary because nodes can be
replica destinations for many ranges,
+ * so if we do not restrict each scan to the specific range we want we
will get duplicate results.
+ * 2) sorting the intersection ranges is necessary because wraparound
node ranges can be discontiguous.
+ * Consider a 2-node ring, (D, T] and (T, D]. A query for [A, Z] will
intersect the 2nd node twice,
+ * at [A, D] and (T, Z]. We need to scan the (D, T] range in between
those, or we will skip those
+ * results entirely if the limit is low enough.
+ * 3) we unwrap the intersection ranges because otherwise we get results
in the wrong order.
+ * Consider a 2-node ring, (D, T] and (T, D]. A query for [D, Z] will
get results in the wrong
+ * order if we use (T, D] directly -- we need to start with that
range, because our query starts with
+ * D, but we don't want any other results from it until after the (D,
T] range. Unwrapping so that
+ * the ranges we consider are (D, T], (T, MIN], (MIN, D] fixes this.
+ */
+ private static List<Pair<AbstractBounds, List<InetAddress>>>
getRestrictedRanges(AbstractBounds queryRange, String keyspace, int
responseCount)
+ throws UnavailableException
+ {
+ TokenMetadata tokenMetadata =
StorageService.instance.getTokenMetadata();
+ Iterator<Token> iter =
TokenMetadata.ringIterator(tokenMetadata.sortedTokens(), queryRange.left);
+ List<Pair<AbstractBounds, List<InetAddress>>> ranges = new
ArrayList<Pair<AbstractBounds, List<InetAddress>>>();
+ while (iter.hasNext())
+ {
+ Token nodeToken = iter.next();
+ Range nodeRange = new
Range(tokenMetadata.getPredecessor(nodeToken), nodeToken);
+ List<InetAddress> endpoints =
StorageService.instance.getLiveNaturalEndpoints(keyspace, nodeToken);
+ if (endpoints.size() < responseCount)
+ throw new UnavailableException();
+
+
DatabaseDescriptor.getEndPointSnitch(keyspace).sortByProximity(FBUtilities.getLocalAddress(),
endpoints);
+ List<InetAddress> endpointsForCL = endpoints.subList(0,
responseCount);
+ Set<AbstractBounds> restrictedRanges =
queryRange.restrictTo(nodeRange);
+ for (AbstractBounds range : restrictedRanges)
+ {
+ for (AbstractBounds unwrapped : range.unwrap())
+ {
+ if (logger.isDebugEnabled())
+ logger.debug("Adding to restricted ranges " +
unwrapped + " for " + nodeRange);
+ ranges.add(new Pair<AbstractBounds,
List<InetAddress>>(unwrapped, endpointsForCL));
+ }
+ }
+ }
+ return ranges;
}
public long getReadOperations()
Modified:
incubator/cassandra/trunk/src/java/org/apache/cassandra/thrift/CassandraServer.java
URL:
http://svn.apache.org/viewvc/incubator/cassandra/trunk/src/java/org/apache/cassandra/thrift/CassandraServer.java?rev=910380&r1=910379&r2=910380&view=diff
==============================================================================
---
incubator/cassandra/trunk/src/java/org/apache/cassandra/thrift/CassandraServer.java
(original)
+++
incubator/cassandra/trunk/src/java/org/apache/cassandra/thrift/CassandraServer.java
Tue Feb 16 02:47:57 2010
@@ -35,6 +35,7 @@
import org.apache.cassandra.db.marshal.MarshalException;
import org.apache.cassandra.dht.AbstractBounds;
import org.apache.cassandra.dht.Bounds;
+import org.apache.cassandra.dht.IPartitioner;
import org.apache.cassandra.dht.Range;
import org.apache.cassandra.dht.Token;
import org.apache.cassandra.service.StorageProxy;
@@ -543,7 +544,7 @@
throws InvalidRequestException, UnavailableException, TException,
TimedOutException
{
if (logger.isDebugEnabled())
- logger.debug("range_slice");
+ logger.debug("get_range_slice " + start_key + " to " + finish_key);
KeyRange range = new
KeyRange().setStart_key(start_key).setEnd_key(finish_key).setCount(maxRows);
return getRangeSlicesInternal(keyspace, column_parent, predicate,
range, consistency_level);
@@ -567,21 +568,21 @@
ThriftValidation.validatePredicate(keyspace, column_parent, predicate);
ThriftValidation.validateKeyRange(range);
- List<Pair<String, ColumnFamily>> rows;
+ List<Row> rows;
try
{
+ IPartitioner p = StorageService.getPartitioner();
AbstractBounds bounds;
if (range.start_key == null)
{
- Token.TokenFactory tokenFactory =
StorageService.getPartitioner().getTokenFactory();
+ Token.TokenFactory tokenFactory = p.getTokenFactory();
Token left = tokenFactory.fromString(range.start_token);
Token right = tokenFactory.fromString(range.end_token);
bounds = new Range(left, right);
}
else
{
- bounds = new
Bounds(StorageService.getPartitioner().decorateKey(range.start_key).token,
-
StorageService.getPartitioner().decorateKey(range.end_key).token);
+ bounds = new Bounds(p.decorateKey(range.start_key).token,
p.decorateKey(range.end_key).token);
}
rows = StorageProxy.getRangeSlice(new RangeSliceCommand(keyspace,
column_parent, predicate, bounds, range.count), consistency_level);
assert rows != null;
@@ -597,10 +598,10 @@
List<KeySlice> keySlices = new ArrayList<KeySlice>(rows.size());
boolean reversed = predicate.slice_range != null &&
predicate.slice_range.reversed;
- for (Pair<String, ColumnFamily> row : rows)
+ for (Row row : rows)
{
- List<ColumnOrSuperColumn> thriftifiedColumns =
thriftifyColumnFamily(row.right, column_parent.super_column != null, reversed);
- keySlices.add(new KeySlice(row.left, thriftifiedColumns));
+ List<ColumnOrSuperColumn> thriftifiedColumns =
thriftifyColumnFamily(row.cf, column_parent.super_column != null, reversed);
+ keySlices.add(new KeySlice(row.key, thriftifiedColumns));
}
return keySlices;
Modified:
incubator/cassandra/trunk/src/java/org/apache/cassandra/thrift/ThriftValidation.java
URL:
http://svn.apache.org/viewvc/incubator/cassandra/trunk/src/java/org/apache/cassandra/thrift/ThriftValidation.java?rev=910380&r1=910379&r2=910380&view=diff
==============================================================================
---
incubator/cassandra/trunk/src/java/org/apache/cassandra/thrift/ThriftValidation.java
(original)
+++
incubator/cassandra/trunk/src/java/org/apache/cassandra/thrift/ThriftValidation.java
Tue Feb 16 02:47:57 2010
@@ -314,6 +314,12 @@
throw new InvalidRequestException("exactly one of {start key, end
key} or {start token, end token} must be specified");
}
+ if (range.start_key != null)
+ {
+ if (range.start_key.compareTo(range.end_key) > 0 &&
!range.end_key.isEmpty())
+ throw new InvalidRequestException("start key must sort before
(or equal to) finish key in your partitioner!");
+ }
+
if (range.count <= 0)
{
throw new InvalidRequestException("maxRows must be positive");
Modified: incubator/cassandra/trunk/test/system/test_server.py
URL:
http://svn.apache.org/viewvc/incubator/cassandra/trunk/test/system/test_server.py?rev=910380&r1=910379&r2=910380&view=diff
==============================================================================
--- incubator/cassandra/trunk/test/system/test_server.py (original)
+++ incubator/cassandra/trunk/test/system/test_server.py Tue Feb 16 02:47:57
2010
@@ -533,6 +533,8 @@
column_parent = ColumnParent('Super1', 'sc1')
_expect_exception(lambda: client.get_slice('Keyspace1', 'key1',
column_parent, p, ConsistencyLevel.ONE),
InvalidRequestException)
+ # start > finish, key version
+ _expect_exception(lambda: client.get_range_slice('Keyspace1',
ColumnParent('Standard1'), SlicePredicate(column_names=['']), 'z', 'a', 1,
ConsistencyLevel.ONE), InvalidRequestException)
def test_batch_insert_super(self):
cfmap = {'Super1': [ColumnOrSuperColumn(super_column=c) for c in
_SUPER_COLUMNS],
@@ -795,7 +797,7 @@
# test column_names predicate
result = client.get_range_slice("Keyspace1", cp,
SlicePredicate(column_names=['col1', 'col3']), 'key2', 'key4', 5,
ConsistencyLevel.ONE)
- assert len(result) == 3
+ assert len(result) == 3, result
assert result[0].columns[0].column.name == 'col1'
assert result[0].columns[1].column.name == 'col3'
Added:
incubator/cassandra/trunk/test/unit/org/apache/cassandra/dht/BoundsTest.java
URL:
http://svn.apache.org/viewvc/incubator/cassandra/trunk/test/unit/org/apache/cassandra/dht/BoundsTest.java?rev=910380&view=auto
==============================================================================
---
incubator/cassandra/trunk/test/unit/org/apache/cassandra/dht/BoundsTest.java
(added)
+++
incubator/cassandra/trunk/test/unit/org/apache/cassandra/dht/BoundsTest.java
Tue Feb 16 02:47:57 2010
@@ -0,0 +1,73 @@
+package org.apache.cassandra.dht;
+
+import java.util.*;
+
+import junit.framework.TestCase;
+import org.apache.cassandra.utils.FBUtilities;
+
+public class BoundsTest extends TestCase
+{
+ public void testRestrictTo() throws Exception
+ {
+ IPartitioner p = new OrderPreservingPartitioner();
+ Token min = p.getMinimumToken();
+ Range wraps = new Range(new StringToken("m"), new StringToken("e"));
+ Range normal = new Range(wraps.right, wraps.left);
+ Bounds all = new Bounds(min, min, p);
+ Bounds almostAll = new Bounds(new StringToken("a"), min, p);
+
+ Set<AbstractBounds> S;
+ Set<AbstractBounds> S2;
+
+ S = all.restrictTo(wraps);
+ assert S.equals(new HashSet<AbstractBounds>(Arrays.asList(wraps)));
+
+ S = almostAll.restrictTo(wraps);
+ S2 = new HashSet<AbstractBounds>(Arrays.asList(new Bounds(new
StringToken("a"), new StringToken("e"), p),
+ new Range(new
StringToken("m"), min)));
+ assert S.equals(S2);
+
+ S = all.restrictTo(normal);
+ assert S.equals(new HashSet<AbstractBounds>(Arrays.asList(normal)));
+ }
+
+ public void testNoIntersectionWrapped()
+ {
+ IPartitioner p = new OrderPreservingPartitioner();
+ Range node = new Range(new StringToken("z"), new StringToken("a"));
+ Bounds bounds;
+
+ bounds = new Bounds(new StringToken("m"), new StringToken("n"), p);
+ assert
bounds.restrictTo(node).equals(Collections.<AbstractBounds>emptySet());
+
+ bounds = new Bounds(new StringToken("b"), node.left, p);
+ assert
bounds.restrictTo(node).equals(Collections.<AbstractBounds>emptySet());
+ }
+
+ public void testSmallBoundsFullRange()
+ {
+ IPartitioner p = new OrderPreservingPartitioner();
+ Range node;
+ Bounds bounds = new Bounds(new StringToken("b"), new StringToken("c"),
p);
+
+ node = new Range(new StringToken("d"), new StringToken("d"));
+ assert bounds.restrictTo(node).equals(new
HashSet(Arrays.asList(bounds)));
+ }
+
+ public void testNoIntersectionUnwrapped()
+ {
+ IPartitioner p = new OrderPreservingPartitioner();
+ Token min = p.getMinimumToken();
+ Range node = new Range(new StringToken("m"), new StringToken("n"));
+ Bounds bounds;
+
+ bounds = new Bounds(new StringToken("z"), min, p);
+ assert
bounds.restrictTo(node).equals(Collections.<AbstractBounds>emptySet());
+
+ bounds = new Bounds(new StringToken("a"), node.left, p);
+ assert
bounds.restrictTo(node).equals(Collections.<AbstractBounds>emptySet());
+
+ bounds = new Bounds(min, new StringToken("b"), p);
+ assert
bounds.restrictTo(node).equals(Collections.<AbstractBounds>emptySet());
+ }
+}
Propchange:
incubator/cassandra/trunk/test/unit/org/apache/cassandra/dht/BoundsTest.java
------------------------------------------------------------------------------
svn:eol-style = native
Added:
incubator/cassandra/trunk/test/unit/org/apache/cassandra/dht/RangeIntersectionTest.java
URL:
http://svn.apache.org/viewvc/incubator/cassandra/trunk/test/unit/org/apache/cassandra/dht/RangeIntersectionTest.java?rev=910380&view=auto
==============================================================================
---
incubator/cassandra/trunk/test/unit/org/apache/cassandra/dht/RangeIntersectionTest.java
(added)
+++
incubator/cassandra/trunk/test/unit/org/apache/cassandra/dht/RangeIntersectionTest.java
Tue Feb 16 02:47:57 2010
@@ -0,0 +1,131 @@
+package org.apache.cassandra.dht;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.commons.lang.StringUtils;
+import org.junit.Test;
+
+public class RangeIntersectionTest
+{
+ static void assertIntersection(Range one, Range two, Range ... ranges)
+ {
+ Set<Range> correct = Range.rangeSet(ranges);
+ Set<Range> result1 = one.intersectionWith(two);
+ assert result1.equals(correct) : String.format("%s != %s",
+
StringUtils.join(result1, ","),
+
StringUtils.join(correct, ","));
+ Set<Range> result2 = two.intersectionWith(one);
+ assert result2.equals(correct) : String.format("%s != %s",
+
StringUtils.join(result2, ","),
+
StringUtils.join(correct, ","));
+ }
+
+ private void assertNoIntersection(Range wraps1, Range nowrap3)
+ {
+ assertIntersection(wraps1, nowrap3);
+ }
+
+ @Test
+ public void testIntersectionWithAll()
+ {
+ Range all0 = new Range(new BigIntegerToken("0"), new
BigIntegerToken("0"));
+ Range all10 = new Range(new BigIntegerToken("10"), new
BigIntegerToken("10"));
+ Range all100 = new Range(new BigIntegerToken("100"), new
BigIntegerToken("100"));
+ Range all1000 = new Range(new BigIntegerToken("1000"), new
BigIntegerToken("1000"));
+ Range wraps = new Range(new BigIntegerToken("100"), new
BigIntegerToken("10"));
+
+ assertIntersection(all0, wraps, wraps);
+ assertIntersection(all10, wraps, wraps);
+ assertIntersection(all100, wraps, wraps);
+ assertIntersection(all1000, wraps, wraps);
+ }
+
+ @Test
+ public void testIntersectionContains()
+ {
+ Range wraps1 = new Range(new BigIntegerToken("100"), new
BigIntegerToken("10"));
+ Range wraps2 = new Range(new BigIntegerToken("90"), new
BigIntegerToken("20"));
+ Range wraps3 = new Range(new BigIntegerToken("90"), new
BigIntegerToken("0"));
+ Range nowrap1 = new Range(new BigIntegerToken("100"), new
BigIntegerToken("110"));
+ Range nowrap2 = new Range(new BigIntegerToken("0"), new
BigIntegerToken("10"));
+ Range nowrap3 = new Range(new BigIntegerToken("0"), new
BigIntegerToken("9"));
+
+ assertIntersection(wraps1, wraps2, wraps1);
+ assertIntersection(wraps3, wraps2, wraps3);
+
+ assertIntersection(wraps1, nowrap1, nowrap1);
+ assertIntersection(wraps1, nowrap2, nowrap2);
+ assertIntersection(nowrap2, nowrap3, nowrap3);
+
+ assertIntersection(wraps1, wraps1, wraps1);
+ assertIntersection(nowrap1, nowrap1, nowrap1);
+ assertIntersection(nowrap2, nowrap2, nowrap2);
+ assertIntersection(wraps3, wraps3, wraps3);
+ }
+
+ @Test
+ public void testNoIntersection()
+ {
+ Range wraps1 = new Range(new BigIntegerToken("100"), new
BigIntegerToken("10"));
+ Range wraps2 = new Range(new BigIntegerToken("100"), new
BigIntegerToken("0"));
+ Range nowrap1 = new Range(new BigIntegerToken("0"), new
BigIntegerToken("100"));
+ Range nowrap2 = new Range(new BigIntegerToken("100"), new
BigIntegerToken("200"));
+ Range nowrap3 = new Range(new BigIntegerToken("10"), new
BigIntegerToken("100"));
+
+ assertNoIntersection(wraps1, nowrap3);
+ assertNoIntersection(wraps2, nowrap1);
+ assertNoIntersection(nowrap1, nowrap2);
+ }
+
+ @Test
+ public void testIntersectionOneWraps()
+ {
+ Range wraps1 = new Range(new BigIntegerToken("100"), new
BigIntegerToken("10"));
+ Range wraps2 = new Range(new BigIntegerToken("100"), new
BigIntegerToken("0"));
+ Range nowrap1 = new Range(new BigIntegerToken("0"), new
BigIntegerToken("200"));
+ Range nowrap2 = new Range(new BigIntegerToken("0"), new
BigIntegerToken("100"));
+
+ assertIntersection(wraps1,
+ nowrap1,
+ new Range(new BigIntegerToken("0"), new
BigIntegerToken("10")),
+ new Range(new BigIntegerToken("100"), new
BigIntegerToken("200")));
+ assertIntersection(wraps2,
+ nowrap1,
+ new Range(new BigIntegerToken("100"), new
BigIntegerToken("200")));
+ assertIntersection(wraps1,
+ nowrap2,
+ new Range(new BigIntegerToken("0"), new
BigIntegerToken("10")));
+ }
+
+ @Test
+ public void testIntersectionTwoWraps()
+ {
+ Range wraps1 = new Range(new BigIntegerToken("100"), new
BigIntegerToken("20"));
+ Range wraps2 = new Range(new BigIntegerToken("120"), new
BigIntegerToken("90"));
+ Range wraps3 = new Range(new BigIntegerToken("120"), new
BigIntegerToken("110"));
+ Range wraps4 = new Range(new BigIntegerToken("10"), new
BigIntegerToken("0"));
+ Range wraps5 = new Range(new BigIntegerToken("10"), new
BigIntegerToken("1"));
+ Range wraps6 = new Range(new BigIntegerToken("30"), new
BigIntegerToken("10"));
+
+ assertIntersection(wraps1,
+ wraps2,
+ new Range(new BigIntegerToken("120"), new
BigIntegerToken("20")));
+ assertIntersection(wraps1,
+ wraps3,
+ new Range(new BigIntegerToken("120"), new
BigIntegerToken("20")),
+ new Range(new BigIntegerToken("100"), new
BigIntegerToken("110")));
+ assertIntersection(wraps1,
+ wraps4,
+ new Range(new BigIntegerToken("10"), new
BigIntegerToken("20")),
+ new Range(new BigIntegerToken("100"), new
BigIntegerToken("0")));
+ assertIntersection(wraps1,
+ wraps5,
+ new Range(new BigIntegerToken("10"), new
BigIntegerToken("20")),
+ new Range(new BigIntegerToken("100"), new
BigIntegerToken("1")));
+ assertIntersection(wraps1,
+ wraps6,
+ new Range(new BigIntegerToken("100"), new
BigIntegerToken("10")));
+ }
+}
Propchange:
incubator/cassandra/trunk/test/unit/org/apache/cassandra/dht/RangeIntersectionTest.java
------------------------------------------------------------------------------
svn:eol-style = native
Modified:
incubator/cassandra/trunk/test/unit/org/apache/cassandra/dht/RangeTest.java
URL:
http://svn.apache.org/viewvc/incubator/cassandra/trunk/test/unit/org/apache/cassandra/dht/RangeTest.java?rev=910380&r1=910379&r2=910380&view=diff
==============================================================================
--- incubator/cassandra/trunk/test/unit/org/apache/cassandra/dht/RangeTest.java
(original)
+++ incubator/cassandra/trunk/test/unit/org/apache/cassandra/dht/RangeTest.java
Tue Feb 16 02:47:57 2010
@@ -18,6 +18,9 @@
*/
package org.apache.cassandra.dht;
+import java.util.Arrays;
+import java.util.List;
+
import org.junit.Test;
public class RangeTest
@@ -80,6 +83,7 @@
Range two = new Range(new BigIntegerToken("5"), new
BigIntegerToken("3"));
Range thr = new Range(new BigIntegerToken("10"), new
BigIntegerToken("12"));
Range fou = new Range(new BigIntegerToken("2"), new
BigIntegerToken("6"));
+ Range fiv = new Range(new BigIntegerToken("0"), new
BigIntegerToken("0"));
assert !one.contains(two);
assert one.contains(thr);
@@ -96,6 +100,30 @@
assert !fou.contains(one);
assert !fou.contains(two);
assert !fou.contains(thr);
+
+ assert fiv.contains(one);
+ assert fiv.contains(two);
+ assert fiv.contains(thr);
+ assert fiv.contains(fou);
+ }
+
+ @Test
+ public void testContainsRangeOneWrapping()
+ {
+ Range wrap1 = new Range(new BigIntegerToken("0"), new
BigIntegerToken("0"));
+ Range wrap2 = new Range(new BigIntegerToken("10"), new
BigIntegerToken("2"));
+
+ Range nowrap1 = new Range(new BigIntegerToken("0"), new
BigIntegerToken("2"));
+ Range nowrap2 = new Range(new BigIntegerToken("2"), new
BigIntegerToken("10"));
+ Range nowrap3 = new Range(new BigIntegerToken("10"), new
BigIntegerToken("100"));
+
+ assert wrap1.contains(nowrap1);
+ assert wrap1.contains(nowrap2);
+ assert wrap1.contains(nowrap3);
+
+ assert wrap2.contains(nowrap1);
+ assert !wrap2.contains(nowrap2);
+ assert wrap2.contains(nowrap3);
}
@Test
@@ -124,12 +152,14 @@
{
Range onewrap = new Range(new BigIntegerToken("10"), new
BigIntegerToken("2"));
Range onecomplement = new Range(onewrap.right, onewrap.left);
- Range oneadjoins = new Range(onewrap.left, new BigIntegerToken("12"));
+ Range onestartswith = new Range(onewrap.left, new
BigIntegerToken("12"));
+ Range oneendswith = new Range(new BigIntegerToken("1"), onewrap.right);
Range twowrap = new Range(new BigIntegerToken("5"), new
BigIntegerToken("3"));
Range not = new Range(new BigIntegerToken("2"), new
BigIntegerToken("6"));
assert !onewrap.intersects(onecomplement);
- assert onewrap.intersects(oneadjoins);
+ assert onewrap.intersects(onestartswith);
+ assert onewrap.intersects(oneendswith);
assert onewrap.intersects(twowrap);
assert twowrap.intersects(onewrap);