Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add range assertions and docs #35

Open
wants to merge 6 commits into
base: trunk
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion accord-core/src/main/java/accord/api/Key.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import accord.primitives.Range;
import accord.primitives.RoutableKey;
import accord.primitives.Seekable;
import accord.utils.Invariants;

/**
* A key we can find in both the cluster and on disk
Expand All @@ -31,7 +32,11 @@ public interface Key extends Seekable, RoutableKey
default Key asKey() { return this; }

@Override
default Key slice(Range range) { return this; }
default Key slice(Range range)
{
Invariants.checkArgument(range.contains(this));
return this;
}

@Override
default Range asRange() { throw new UnsupportedOperationException(); }
Expand Down
18 changes: 18 additions & 0 deletions accord-core/src/main/java/accord/api/Read.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,26 @@
*/
public interface Read
{
/**
* Returns the scope of the transaction, that is, the keys or ranges that will be read
* // TODO maybe this method should be renamed to readScope, or just scope, or something else which would not mention keys as it can be ranges as well?
*/
Seekables<?, ?> keys();

/**
* The method is called when Accord reads data.
*
* @param key denotes which data to read; it is guaranteed that the key passed there is one of the items covered by {@link #keys()}.
*/
AsyncChain<Data> read(Seekable key, Txn.Kind kind, SafeCommandStore commandStore, Timestamp executeAt, DataStore store);

/**
* Returns a new read operation whose scope is an intersection of {@link #keys()} and the provided ranges.
*/
Read slice(Ranges ranges);

/**
* Returns a new read operation whose scope is a union of the scopes of this and the provided read operations.
*/
Read merge(Read other);
}
21 changes: 17 additions & 4 deletions accord-core/src/main/java/accord/api/Update.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,34 @@
package accord.api;

import accord.primitives.Ranges;
import accord.primitives.Keys;
import accord.primitives.Seekables;

import javax.annotation.Nullable;

/**
* A client-defined update operation (the write equivalent of a query).
* Takes as input the data returned by {@code Read}, and returns a {@code Write}
* representing new information to distributed to each shard's stores.
*/
public interface Update
{
/**
* Returns the scope of the transaction, that is, the keys or ranges that will be written
*/
//TODO maybe this method should be renamed to updateScope, or just scope, or something else which would not mention keys as it can be ranges as well?
Seekables<?, ?> keys();
// null is provided only if nothing was read

/**
* Takes as input the data returned by {@link Read}, and returns a {@link Write} representing new information
* to be distributed to each shard's stores. Accepts {@code null} if nothing was read.
*/
Write apply(@Nullable Data data);

/**
* Returns a new update operation whose scope is an intersection of {@link #keys()} and the provided ranges.
*/
Update slice(Ranges ranges);

/**
* Returns a new update operation whose scope is a union of the scopes of this and the provided update operations.
*/
Update merge(Update other);
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@

@SuppressWarnings("rawtypes")
// TODO (desired, efficiency): check that foldl call-sites are inlined and optimised by HotSpot
public abstract class AbstractKeys<K extends RoutableKey, KS extends Routables<K, ?>> implements Iterable<K>, Routables<K, KS>
public abstract class AbstractKeys<K extends RoutableKey, KS extends Routables<K, KS>> implements Iterable<K>, Routables<K, KS>
{
final K[] keys;

Expand Down
10 changes: 9 additions & 1 deletion accord-core/src/main/java/accord/primitives/AbstractRanges.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,21 @@
import static accord.utils.SortedArrays.Search.FAST;
import static accord.utils.SortedArrays.swapHighLow32b;

public abstract class AbstractRanges<RS extends Routables<Range, ?>> implements Iterable<Range>, Routables<Range, RS>
public abstract class AbstractRanges<RS extends Routables<Range, RS>> implements Iterable<Range>, Routables<Range, RS>
{
static final Range[] NO_RANGES = new Range[0];

final Range[] ranges;

AbstractRanges(@Nonnull Range[] ranges)
{
if (ranges.length > 0)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't usually permit double nesting of no braces, the outer statement is usually braced (esp. when mixing if+for)

{
// check that ranges are of the same type
for (int i = 1; i < ranges.length; i++)
ranges[0].checkRangeType(ranges[i]);
}

// TODO (simple, validation): check ranges are non-overlapping (or make sure it's safe for all methods that they aren't)
this.ranges = Invariants.nonNull(ranges);
}
Expand Down Expand Up @@ -110,6 +117,7 @@ public boolean intersectsAll(AbstractRanges<?> that)
{
if (this.isEmpty()) return that.isEmpty();
if (that.isEmpty()) return true;
this.ranges[0].checkRangeType(that.ranges[0]);
return Routables.rangeFoldl(that, this, (p, v, from, to) -> v + (to - from), 0, 0, 0) == that.size();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
import java.util.Arrays;

// TODO: do we need this class?
public abstract class AbstractUnseekableKeys<KS extends Unseekables<RoutingKey, ?>> extends AbstractKeys<RoutingKey, KS> implements Iterable<RoutingKey>, Unseekables<RoutingKey, KS>
public abstract class AbstractUnseekableKeys<KS extends Unseekables<RoutingKey, KS>> extends AbstractKeys<RoutingKey, KS> implements Iterable<RoutingKey>, Unseekables<RoutingKey, KS>
{
AbstractUnseekableKeys(RoutingKey[] keys)
{
Expand Down
2 changes: 1 addition & 1 deletion accord-core/src/main/java/accord/primitives/FullRoute.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

package accord.primitives;

public interface FullRoute<T extends Unseekable> extends Route<T>, Unseekables<T, Route<T>>
public interface FullRoute<T extends Unseekable> extends Route<T>
{
@Override default FullRoute<T> union(Route<T> route) { return this; }
@Override default Ranges sliceCovering(Ranges newRanges, Slice slice) { return newRanges; }
Expand Down
50 changes: 25 additions & 25 deletions accord-core/src/main/java/accord/primitives/Range.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
import static accord.utils.SortedArrays.Search.FAST;

/**
* A range of keys
* A range of keys.
*/
public abstract class Range implements Comparable<RoutableKey>, Unseekable, Seekable
{
Expand All @@ -52,13 +52,13 @@ public int compareTo(RoutableKey key)
}

@Override
public boolean startInclusive()
public final boolean startInclusive()
{
return false;
}

@Override
public boolean endInclusive()
public final boolean endInclusive()
{
return true;
}
Expand Down Expand Up @@ -94,13 +94,13 @@ public int compareTo(RoutableKey key)
}

@Override
public boolean startInclusive()
public final boolean startInclusive()
{
return true;
}

@Override
public boolean endInclusive()
public final boolean endInclusive()
{
return false;
}
Expand Down Expand Up @@ -244,8 +244,7 @@ public boolean contains(RoutableKey key)
*/
public int compareIntersecting(Range that)
{
if (that.getClass() != this.getClass())
throw new IllegalArgumentException("Cannot mix Range of different types");
checkRangeType(that);
if (this.start.compareTo(that.end) >= 0)
return 1;
if (this.end.compareTo(that.start) <= 0)
Expand All @@ -258,8 +257,7 @@ public int compareIntersecting(Range that)
*/
public int compare(Range that)
{
if (that.getClass() != this.getClass())
throw new IllegalArgumentException("Cannot mix Range of different types");
checkRangeType(that);
int c = this.start.compareTo(that.start);
if (c == 0) c = this.end.compareTo(that.end);
return c;
Expand All @@ -272,10 +270,9 @@ public boolean contains(Range that)

public Range slice(Range truncateTo)
{
int cs = start.compareTo(truncateTo.start);
int ce = end.compareTo(truncateTo.end);
if (cs >= 0 && ce <= 0) return this;
return newRange(cs >= 0 ? start : truncateTo.start, ce <= 0 ? end : truncateTo.end);
Range intersection = this.intersection(truncateTo);
Invariants.checkArgument(intersection != null);
return intersection;
}

public boolean intersects(AbstractKeys<?, ?> keys)
Expand All @@ -289,11 +286,20 @@ public boolean intersects(AbstractKeys<?, ?> keys)
*/
public Range intersection(Range that)
{
checkRangeType(that);

if (this.compareIntersecting(that) != 0)
return null;

RoutingKey start = this.start.compareTo(that.start) > 0 ? this.start : that.start;
RoutingKey end = this.end.compareTo(that.end) < 0 ? this.end : that.end;

if (start == this.start && end == this.end)
return this;

if (start == that.start && end == that.end)
return that;

return newRange(start, end);
}

Expand Down Expand Up @@ -349,21 +355,15 @@ public RoutingKey someIntersectingRoutingKey(Ranges ranges)
}
}

public static Range slice(Range bound, Range toSlice)
{
Invariants.checkArgument(bound.compareIntersecting(toSlice) == 0);
if (bound.contains(toSlice))
return toSlice;

return toSlice.newRange(
toSlice.start().compareTo(bound.start()) >= 0 ? toSlice.start() : bound.start(),
toSlice.end().compareTo(bound.end()) <= 0 ? toSlice.end() : bound.end()
);
}

@Override
public Range toUnseekable()
{
return this;
}

final void checkRangeType(Range that)
{
if (that.getClass() != this.getClass())
throw new IllegalArgumentException(String.format("Cannot mix Range of different types (%s and %s)", this.getClass().getSimpleName(), that.getClass().getSimpleName()));
}
}
7 changes: 2 additions & 5 deletions accord-core/src/main/java/accord/primitives/Routables.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,15 @@
import accord.utils.IndexedFoldToLong;
import accord.utils.IndexedRangeFoldToLong;
import accord.utils.SortedArrays;
import net.nicoulaj.compilecommand.annotations.Inline;

import java.util.function.BiFunction;
import java.util.function.Function;
import net.nicoulaj.compilecommand.annotations.Inline;

import static accord.primitives.Routables.Slice.Overlapping;
import static accord.utils.SortedArrays.Search.FLOOR;

/**
* A collection of either Seekable or Unseekable
*/
public interface Routables<K extends Routable, U extends Routables<K, ?>> extends Iterable<K>
public interface Routables<K extends Routable, U extends Routables<K, U>> extends Iterable<K>
{
enum Slice
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@

import static accord.utils.ArrayBuffers.cachedRoutingKeys;

public class RoutingKeys extends AbstractUnseekableKeys<AbstractUnseekableKeys<?>> implements Unseekables<RoutingKey, AbstractUnseekableKeys<?>>
public class RoutingKeys extends AbstractUnseekableKeys<RoutingKeys>
{
public static class SerializationSupport
{
Expand Down
15 changes: 14 additions & 1 deletion accord-core/src/main/java/accord/primitives/Seekable.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,24 @@
import accord.api.Key;

/**
* Something that can be found within the cluster AND found on disk, queried and returned
* Something that can be found within the cluster AND found on disk, queried and returned - in particular, it includes
* enough information to be found on disk (in constrast to {@link Unseekable}.
*/
public interface Seekable extends Routable
{
/**
* If this is a key, it will return itself, otherwise it throw an exception
*/
Key asKey();

/**
* If this is a range, it will return itself, otherwise it throw an exception
*/
Range asRange();

/**
* Returns a {@link Seekable} of the same type as this one truncated to the given range (intersection).
* If the provided range does not intersect with this {@link Seekable}, an exception is thrown.
*/
Seekable slice(Range range);
}
2 changes: 1 addition & 1 deletion accord-core/src/main/java/accord/primitives/Seekables.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
/**
* Either a Route or a collection of Routable
*/
public interface Seekables<K extends Seekable, U extends Seekables<K, ?>> extends Routables<K, U>
public interface Seekables<K extends Seekable, U extends Seekables<K, U>> extends Routables<K, U>
{
@Override
default U slice(Ranges ranges) { return slice(ranges, Overlapping); }
Expand Down
5 changes: 4 additions & 1 deletion accord-core/src/main/java/accord/primitives/Unseekable.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@
package accord.primitives;

/**
* Something that can only be routed, i.e. is NOT a Seekable.
* Something that can only be routed.
* In particular, it purposely does not contain enough information to found it on disk (in contrast to {@link Seekable}).
* Those two interfaces were created to explicitly distinguish what information it is intended to be serialized when
* exchanging data between nodes. {@link Seekable} contains more information, but it is not always necessary to send it.
*/
public interface Unseekable extends Routable
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
/**
* Either a Route or a simple collection of keys or ranges
*/
public interface Unseekables<K extends Unseekable, U extends Unseekables<K, ?>> extends Iterable<K>, Routables<K, U>
public interface Unseekables<K extends Unseekable, U extends Unseekables<K, U>> extends Iterable<K>, Routables<K, U>
{
enum UnseekablesKind
{
Expand Down