mirror of https://github.com/apache/cassandra
Accord fixes:
- Fix erroneous call to registerWithDelay with absolute deadline - ExecuteSyncPoint should discount votes from unbootstrapped replicas - Bootstrap no longer needs to first ensure durability (now ExecuteSyncPoint discounts votes from unbootstrapped replicas) - Progress stall with home shard stopping before Stable is propagated to all shards - Only upgrade Durability.HasPhase if we have queried all shards - Incorrectly inferring Durability in CheckStatus.finish - Incorrectly inferring/propagating stable from fast unstable reply; clarify to prevent further mistakes - MaxDecidedRX should track separate hlc bounds for filtering non-RX dependencies - minGcBefore.hlc() can move backwards across epochs, even if each shard moves forwards; must track minHlc directly - slowTimeout init in ExecuteTxn.LocalExecute - TxnId.parse for RV Also improve: - Don't query recovery state if fast path durably decided - Support deferred partial dep deserialisation - Support both orientations of KeyDeps/RangeDeps - Support partialDeps as ByteBuffer Also improve in Cassandra: - Deps serialization and Journal skipping - Don't inflate when reading from cache for CommandsForRanges patch by Benedict; reviewed by Alex Petrov for CASSANDRA-20847
This commit is contained in:
parent
9d5cef7f8c
commit
fdff319739
|
|
@ -1 +1 @@
|
||||||
Subproject commit a8916a18a012063f4da74aaccd39f0b828c99da0
|
Subproject commit 5cbe8d62f15cc7d66af2c7686f14b8fa52b1d35b
|
||||||
|
|
@ -1031,7 +1031,7 @@ public class CompactionIterator extends CompactionInfo.Holder implements Unfilte
|
||||||
{
|
{
|
||||||
row = null;
|
row = null;
|
||||||
modified = false;
|
modified = false;
|
||||||
builder.clear();
|
builder.reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ import java.util.function.Consumer;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
import accord.local.MaxDecidedRX;
|
||||||
import accord.primitives.Timestamp;
|
import accord.primitives.Timestamp;
|
||||||
import accord.primitives.TxnId;
|
import accord.primitives.TxnId;
|
||||||
import org.apache.cassandra.db.Clustering;
|
import org.apache.cassandra.db.Clustering;
|
||||||
|
|
@ -68,21 +69,21 @@ public class MemtableIndex
|
||||||
}
|
}
|
||||||
|
|
||||||
public void search(int storeId, TableId tableId, byte[] start, byte[] end,
|
public void search(int storeId, TableId tableId, byte[] start, byte[] end,
|
||||||
TxnId minTxnId, Timestamp maxTxnId, @Nullable TxnId minDecidedId,
|
TxnId minTxnId, Timestamp maxTxnId, @Nullable MaxDecidedRX.DecidedRX decidedRX,
|
||||||
Consumer<ByteBuffer> onMatch)
|
Consumer<ByteBuffer> onMatch)
|
||||||
{
|
{
|
||||||
memoryIndex.search(storeId, tableId,
|
memoryIndex.search(storeId, tableId,
|
||||||
start, end,
|
start, end,
|
||||||
minTxnId, maxTxnId, minDecidedId,
|
minTxnId, maxTxnId, decidedRX,
|
||||||
onMatch);
|
onMatch);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void search(int storeId, TableId tableId, byte[] key,
|
public void search(int storeId, TableId tableId, byte[] key,
|
||||||
TxnId minTxnId, Timestamp maxTxnId, @Nullable TxnId minDecidedId,
|
TxnId minTxnId, Timestamp maxTxnId, @Nullable MaxDecidedRX.DecidedRX decidedRX,
|
||||||
Consumer<ByteBuffer> onMatch)
|
Consumer<ByteBuffer> onMatch)
|
||||||
{
|
{
|
||||||
memoryIndex.search(storeId, tableId, key,
|
memoryIndex.search(storeId, tableId, key,
|
||||||
minTxnId, maxTxnId, minDecidedId,
|
minTxnId, maxTxnId, decidedRX,
|
||||||
onMatch);
|
onMatch);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ import java.util.function.Consumer;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
import accord.local.MaxDecidedRX.DecidedRX;
|
||||||
import accord.primitives.Timestamp;
|
import accord.primitives.Timestamp;
|
||||||
import accord.primitives.TxnId;
|
import accord.primitives.TxnId;
|
||||||
import org.apache.cassandra.db.DecoratedKey;
|
import org.apache.cassandra.db.DecoratedKey;
|
||||||
|
|
@ -42,10 +43,10 @@ public interface MemtableIndexManager
|
||||||
void renewMemtable(Memtable renewed);
|
void renewMemtable(Memtable renewed);
|
||||||
|
|
||||||
void search(int storeId, TableId tableId, byte[] start, byte[] end,
|
void search(int storeId, TableId tableId, byte[] start, byte[] end,
|
||||||
TxnId minTxnId, Timestamp maxTxnId, @Nullable TxnId minDecidedId,
|
TxnId minTxnId, Timestamp maxTxnId, @Nullable DecidedRX decidedRX,
|
||||||
Consumer<ByteBuffer> onMatch);
|
Consumer<ByteBuffer> onMatch);
|
||||||
|
|
||||||
void search(int storeId, TableId tableId, byte[] key,
|
void search(int storeId, TableId tableId, byte[] key,
|
||||||
TxnId minTxnId, Timestamp maxTxnId, @Nullable TxnId minDecidedId,
|
TxnId minTxnId, Timestamp maxTxnId, @Nullable DecidedRX decidedRX,
|
||||||
Consumer<ByteBuffer> onMatch);
|
Consumer<ByteBuffer> onMatch);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ import javax.annotation.concurrent.GuardedBy;
|
||||||
|
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
|
||||||
|
import accord.local.MaxDecidedRX.DecidedRX;
|
||||||
import accord.primitives.Participants;
|
import accord.primitives.Participants;
|
||||||
import accord.primitives.Routable;
|
import accord.primitives.Routable;
|
||||||
import accord.primitives.Timestamp;
|
import accord.primitives.Timestamp;
|
||||||
|
|
@ -86,12 +87,12 @@ public class RangeMemoryIndex
|
||||||
}
|
}
|
||||||
|
|
||||||
void search(byte[] start, byte[] end,
|
void search(byte[] start, byte[] end,
|
||||||
TxnId minTxnId, Timestamp maxTxnId, @Nullable TxnId minDecidedId,
|
TxnId minTxnId, Timestamp maxTxnId, @Nullable DecidedRX decidedRX,
|
||||||
Consumer<Map.Entry<RangeMemoryIndex.Range, DecoratedKey>> fn)
|
Consumer<Map.Entry<RangeMemoryIndex.Range, DecoratedKey>> fn)
|
||||||
{
|
{
|
||||||
if (this.minTxnId.compareTo(maxTxnId) > 0 || this.maxTxnId.compareTo(minTxnId) < 0)
|
if (this.minTxnId.compareTo(maxTxnId) > 0 || this.maxTxnId.compareTo(minTxnId) < 0)
|
||||||
return;
|
return;
|
||||||
if (maxRXId != null && !RouteIndexFormat.includeByMinDecidedId(minDecidedId, maxRXId))
|
if (maxRXId != null && !RouteIndexFormat.includeByDecidedRX(decidedRX, maxRXId))
|
||||||
return;
|
return;
|
||||||
tree.search(new Range(start, end), e -> {
|
tree.search(new Range(start, end), e -> {
|
||||||
TxnId id = AccordKeyspace.JournalColumns.getJournalKey(e.getValue()).id;
|
TxnId id = AccordKeyspace.JournalColumns.getJournalKey(e.getValue()).id;
|
||||||
|
|
@ -101,12 +102,12 @@ public class RangeMemoryIndex
|
||||||
}
|
}
|
||||||
|
|
||||||
void searchToken(byte[] key,
|
void searchToken(byte[] key,
|
||||||
TxnId minTxnId, Timestamp maxTxnId, @Nullable TxnId minDecidedId,
|
TxnId minTxnId, Timestamp maxTxnId, @Nullable DecidedRX decidedRX,
|
||||||
Consumer<Map.Entry<RangeMemoryIndex.Range, DecoratedKey>> fn)
|
Consumer<Map.Entry<RangeMemoryIndex.Range, DecoratedKey>> fn)
|
||||||
{
|
{
|
||||||
if (this.minTxnId.compareTo(maxTxnId) > 0 || this.maxTxnId.compareTo(minTxnId) < 0)
|
if (this.minTxnId.compareTo(maxTxnId) > 0 || this.maxTxnId.compareTo(minTxnId) < 0)
|
||||||
return;
|
return;
|
||||||
if (maxRXId != null && !RouteIndexFormat.includeByMinDecidedId(minDecidedId, maxRXId))
|
if (maxRXId != null && !RouteIndexFormat.includeByDecidedRX(decidedRX, maxRXId))
|
||||||
return;
|
return;
|
||||||
tree.searchToken(key, e -> {
|
tree.searchToken(key, e -> {
|
||||||
TxnId id = AccordKeyspace.JournalColumns.getJournalKey(e.getValue()).id;
|
TxnId id = AccordKeyspace.JournalColumns.getJournalKey(e.getValue()).id;
|
||||||
|
|
@ -198,25 +199,25 @@ public class RangeMemoryIndex
|
||||||
|
|
||||||
public synchronized void search(int storeId, TableId tableId,
|
public synchronized void search(int storeId, TableId tableId,
|
||||||
byte[] start, byte[] end,
|
byte[] start, byte[] end,
|
||||||
TxnId minTxnId, Timestamp maxTxnId, @Nullable TxnId minDecidedId,
|
TxnId minTxnId, Timestamp maxTxnId, @Nullable DecidedRX decidedRX,
|
||||||
Consumer<ByteBuffer> onMatch)
|
Consumer<ByteBuffer> onMatch)
|
||||||
{
|
{
|
||||||
Group group = map.get(new Key(storeId, tableId));
|
Group group = map.get(new Key(storeId, tableId));
|
||||||
if (group == null) return;
|
if (group == null) return;
|
||||||
if (group.tree.isEmpty()) return;
|
if (group.tree.isEmpty()) return;
|
||||||
|
|
||||||
group.search(start, end, minTxnId, maxTxnId, minDecidedId, e -> onMatch.accept(e.getValue().getKey()));
|
group.search(start, end, minTxnId, maxTxnId, decidedRX, e -> onMatch.accept(e.getValue().getKey()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void search(int storeId, TableId tableId, byte[] key,
|
public synchronized void search(int storeId, TableId tableId, byte[] key,
|
||||||
TxnId minTxnId, Timestamp maxTxnId, @Nullable TxnId minDecidedId,
|
TxnId minTxnId, Timestamp maxTxnId, @Nullable DecidedRX decidedRX,
|
||||||
Consumer<ByteBuffer> onMatch)
|
Consumer<ByteBuffer> onMatch)
|
||||||
{
|
{
|
||||||
Group group = map.get(new Key(storeId, tableId));
|
Group group = map.get(new Key(storeId, tableId));
|
||||||
if (group == null) return;
|
if (group == null) return;
|
||||||
if (group.tree.isEmpty()) return;
|
if (group.tree.isEmpty()) return;
|
||||||
|
|
||||||
group.searchToken(key, minTxnId, maxTxnId, minDecidedId, e -> onMatch.accept(e.getValue().getKey()));
|
group.searchToken(key, minTxnId, maxTxnId, decidedRX, e -> onMatch.accept(e.getValue().getKey()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized boolean isEmpty()
|
public synchronized boolean isEmpty()
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@ import javax.annotation.Nullable;
|
||||||
|
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
|
|
||||||
|
import accord.local.MaxDecidedRX;
|
||||||
import accord.local.StoreParticipants;
|
import accord.local.StoreParticipants;
|
||||||
import accord.primitives.Participants;
|
import accord.primitives.Participants;
|
||||||
import accord.primitives.Txn;
|
import accord.primitives.Txn;
|
||||||
|
|
@ -92,10 +93,10 @@ public class RouteIndexFormat
|
||||||
return touches.deserialize(bytes);
|
return touches.deserialize(bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean includeByMinDecidedId(@Nullable TxnId minDecidedId, TxnId txnId)
|
public static boolean includeByDecidedRX(@Nullable MaxDecidedRX.DecidedRX decidedRX, TxnId txnId)
|
||||||
{
|
{
|
||||||
if (minDecidedId == null || txnId.equals(TxnId.NONE) || !txnId.is(Txn.Kind.ExclusiveSyncPoint)) return true;
|
if (decidedRX == null || txnId.equals(TxnId.NONE) || !txnId.is(Txn.Kind.ExclusiveSyncPoint)) return true;
|
||||||
return txnId.compareTo(minDecidedId) >= 0;
|
return decidedRX.includeDecided(txnId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface Writer extends SSTableFlushObserver
|
public interface Writer extends SSTableFlushObserver
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ import com.google.common.base.Splitter;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import accord.local.MaxDecidedRX.DecidedRX;
|
||||||
import accord.primitives.Timestamp;
|
import accord.primitives.Timestamp;
|
||||||
import accord.primitives.TxnId;
|
import accord.primitives.TxnId;
|
||||||
import org.apache.cassandra.config.DatabaseDescriptor;
|
import org.apache.cassandra.config.DatabaseDescriptor;
|
||||||
|
|
@ -390,7 +391,6 @@ public class RouteJournalIndex implements Index, INotificationConsumer
|
||||||
Integer storeId = null;
|
Integer storeId = null;
|
||||||
TxnId minTxnId = TxnId.NONE;
|
TxnId minTxnId = TxnId.NONE;
|
||||||
Timestamp maxTxnId = TxnId.MAX;
|
Timestamp maxTxnId = TxnId.MAX;
|
||||||
@Nullable TxnId minDecidedId = null;
|
|
||||||
for (RowFilter.Expression e : expressions)
|
for (RowFilter.Expression e : expressions)
|
||||||
{
|
{
|
||||||
if (e.column() == AccordJournalTable.SyntheticColumn.participants.metadata)
|
if (e.column() == AccordJournalTable.SyntheticColumn.participants.metadata)
|
||||||
|
|
@ -429,10 +429,6 @@ public class RouteJournalIndex implements Index, INotificationConsumer
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (e.column() == AccordJournalTable.SyntheticColumn.min_decided_id.metadata)
|
|
||||||
{
|
|
||||||
minDecidedId = CommandSerializers.txnId.deserialize(e.getIndexValue());
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
String cqlString;
|
String cqlString;
|
||||||
|
|
@ -450,12 +446,12 @@ public class RouteJournalIndex implements Index, INotificationConsumer
|
||||||
if (start == null || end == null || storeId == null)
|
if (start == null || end == null || storeId == null)
|
||||||
return null;
|
return null;
|
||||||
if (start.equals(end))
|
if (start.equals(end))
|
||||||
return keySearcher(command, storeId, start, minTxnId, maxTxnId, minDecidedId);
|
return keySearcher(command, storeId, start, minTxnId, maxTxnId, null);
|
||||||
return rangeSearcher(command, storeId, start, end, minTxnId, maxTxnId, minDecidedId);
|
return rangeSearcher(command, storeId, start, end, minTxnId, maxTxnId, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Searcher keySearcher(ReadCommand command, Integer storeId, ByteBuffer key,
|
private Searcher keySearcher(ReadCommand command, Integer storeId, ByteBuffer key,
|
||||||
TxnId minTxnId, Timestamp maxTxnId, @Nullable TxnId minDecidedId)
|
TxnId minTxnId, Timestamp maxTxnId, @Nullable DecidedRX decidedRX)
|
||||||
{
|
{
|
||||||
return new Searcher()
|
return new Searcher()
|
||||||
{
|
{
|
||||||
|
|
@ -470,13 +466,13 @@ public class RouteJournalIndex implements Index, INotificationConsumer
|
||||||
{
|
{
|
||||||
// find all partitions from memtable / sstable
|
// find all partitions from memtable / sstable
|
||||||
NavigableSet<ByteBuffer> partitions = search(storeId, key,
|
NavigableSet<ByteBuffer> partitions = search(storeId, key,
|
||||||
minTxnId, maxTxnId, minDecidedId);
|
minTxnId, maxTxnId, decidedRX);
|
||||||
// do SinglePartitionReadCommand per partition
|
// do SinglePartitionReadCommand per partition
|
||||||
return new SearchIterator(command, partitions);
|
return new SearchIterator(command, partitions);
|
||||||
}
|
}
|
||||||
|
|
||||||
NavigableSet<ByteBuffer> search(int storeId, ByteBuffer key,
|
NavigableSet<ByteBuffer> search(int storeId, ByteBuffer key,
|
||||||
TxnId minTxnId, Timestamp maxTxnId, @Nullable TxnId minDecidedId)
|
TxnId minTxnId, Timestamp maxTxnId, @Nullable DecidedRX decidedRX)
|
||||||
{
|
{
|
||||||
TableId tableId;
|
TableId tableId;
|
||||||
byte[] start;
|
byte[] start;
|
||||||
|
|
@ -487,9 +483,9 @@ public class RouteJournalIndex implements Index, INotificationConsumer
|
||||||
}
|
}
|
||||||
// store matches in a hash set so add is O(1), and the sorting is done after collecting all matches
|
// store matches in a hash set so add is O(1), and the sorting is done after collecting all matches
|
||||||
Set<ByteBuffer> matches = new HashSet<>();
|
Set<ByteBuffer> matches = new HashSet<>();
|
||||||
sstableManager.search(storeId, tableId, start, minTxnId, maxTxnId, minDecidedId, matches::add);
|
sstableManager.search(storeId, tableId, start, minTxnId, maxTxnId, decidedRX, matches::add);
|
||||||
memtableIndexManager.search(storeId, tableId, start,
|
memtableIndexManager.search(storeId, tableId, start,
|
||||||
minTxnId, maxTxnId, minDecidedId,
|
minTxnId, maxTxnId, decidedRX,
|
||||||
matches::add);
|
matches::add);
|
||||||
return new TreeSet<>(matches);
|
return new TreeSet<>(matches);
|
||||||
}
|
}
|
||||||
|
|
@ -498,7 +494,7 @@ public class RouteJournalIndex implements Index, INotificationConsumer
|
||||||
|
|
||||||
private Searcher rangeSearcher(ReadCommand command, int storeId,
|
private Searcher rangeSearcher(ReadCommand command, int storeId,
|
||||||
ByteBuffer start, ByteBuffer end,
|
ByteBuffer start, ByteBuffer end,
|
||||||
TxnId minTxnId, Timestamp maxTxnId, @Nullable TxnId minDecidedId)
|
TxnId minTxnId, Timestamp maxTxnId, @Nullable DecidedRX decidedRX)
|
||||||
{
|
{
|
||||||
return new Searcher()
|
return new Searcher()
|
||||||
{
|
{
|
||||||
|
|
@ -514,14 +510,14 @@ public class RouteJournalIndex implements Index, INotificationConsumer
|
||||||
// find all partitions from memtable / sstable
|
// find all partitions from memtable / sstable
|
||||||
NavigableSet<ByteBuffer> partitions = search(storeId,
|
NavigableSet<ByteBuffer> partitions = search(storeId,
|
||||||
start, end,
|
start, end,
|
||||||
minTxnId, maxTxnId, minDecidedId);
|
minTxnId, maxTxnId, decidedRX);
|
||||||
// do SinglePartitionReadCommand per partition
|
// do SinglePartitionReadCommand per partition
|
||||||
return new SearchIterator(command, partitions);
|
return new SearchIterator(command, partitions);
|
||||||
}
|
}
|
||||||
|
|
||||||
NavigableSet<ByteBuffer> search(int storeId,
|
NavigableSet<ByteBuffer> search(int storeId,
|
||||||
ByteBuffer startTableWithToken, ByteBuffer endTableWithToken,
|
ByteBuffer startTableWithToken, ByteBuffer endTableWithToken,
|
||||||
TxnId minTxnId, Timestamp maxTxnId, @Nullable TxnId minDecidedId)
|
TxnId minTxnId, Timestamp maxTxnId, @Nullable DecidedRX decidedRX)
|
||||||
{
|
{
|
||||||
TableId tableId;
|
TableId tableId;
|
||||||
byte[] start;
|
byte[] start;
|
||||||
|
|
@ -534,8 +530,8 @@ public class RouteJournalIndex implements Index, INotificationConsumer
|
||||||
byte[] end = OrderedRouteSerializer.serializeTokenOnly(OrderedRouteSerializer.deserialize(endTableWithToken));
|
byte[] end = OrderedRouteSerializer.serializeTokenOnly(OrderedRouteSerializer.deserialize(endTableWithToken));
|
||||||
// store matches in a hash set so add is O(1), and the sorting is done after collecting all matches
|
// store matches in a hash set so add is O(1), and the sorting is done after collecting all matches
|
||||||
Set<ByteBuffer> matches = new HashSet<>();
|
Set<ByteBuffer> matches = new HashSet<>();
|
||||||
sstableManager.search(storeId, tableId, start, end, minTxnId, maxTxnId, minDecidedId, matches::add);
|
sstableManager.search(storeId, tableId, start, end, minTxnId, maxTxnId, decidedRX, matches::add);
|
||||||
memtableIndexManager.search(storeId, tableId, start, end, minTxnId, maxTxnId, minDecidedId, matches::add);
|
memtableIndexManager.search(storeId, tableId, start, end, minTxnId, maxTxnId, decidedRX, matches::add);
|
||||||
return new TreeSet<>(matches);
|
return new TreeSet<>(matches);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ import java.util.function.Consumer;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
import accord.local.MaxDecidedRX.DecidedRX;
|
||||||
import accord.primitives.Timestamp;
|
import accord.primitives.Timestamp;
|
||||||
import accord.primitives.TxnId;
|
import accord.primitives.TxnId;
|
||||||
import org.apache.cassandra.db.DecoratedKey;
|
import org.apache.cassandra.db.DecoratedKey;
|
||||||
|
|
@ -106,22 +107,22 @@ public class RouteMemtableIndexManager implements MemtableIndexManager
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void search(int storeId, TableId tableId, byte[] start, byte[] end,
|
public void search(int storeId, TableId tableId, byte[] start, byte[] end,
|
||||||
TxnId minTxnId, Timestamp maxTxnId, @Nullable TxnId minDecidedId,
|
TxnId minTxnId, Timestamp maxTxnId, @Nullable DecidedRX decidedRX,
|
||||||
Consumer<ByteBuffer> onMatch)
|
Consumer<ByteBuffer> onMatch)
|
||||||
{
|
{
|
||||||
liveMemtableIndexMap.values().forEach(m -> m.search(storeId, tableId,
|
liveMemtableIndexMap.values().forEach(m -> m.search(storeId, tableId,
|
||||||
start, end,
|
start, end,
|
||||||
minTxnId, maxTxnId, minDecidedId,
|
minTxnId, maxTxnId, decidedRX,
|
||||||
onMatch));
|
onMatch));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void search(int storeId, TableId tableId, byte[] key,
|
public void search(int storeId, TableId tableId, byte[] key,
|
||||||
TxnId minTxnId, Timestamp maxTxnId, @Nullable TxnId minDecidedId,
|
TxnId minTxnId, Timestamp maxTxnId, @Nullable DecidedRX decidedRX,
|
||||||
Consumer<ByteBuffer> onMatch)
|
Consumer<ByteBuffer> onMatch)
|
||||||
{
|
{
|
||||||
liveMemtableIndexMap.values().forEach(m -> m.search(storeId, tableId, key,
|
liveMemtableIndexMap.values().forEach(m -> m.search(storeId, tableId, key,
|
||||||
minTxnId, maxTxnId, minDecidedId,
|
minTxnId, maxTxnId, decidedRX,
|
||||||
onMatch));
|
onMatch));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ import java.util.function.Consumer;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
import accord.local.MaxDecidedRX;
|
||||||
import accord.primitives.Timestamp;
|
import accord.primitives.Timestamp;
|
||||||
import accord.primitives.TxnId;
|
import accord.primitives.TxnId;
|
||||||
import org.apache.cassandra.io.FSReadError;
|
import org.apache.cassandra.io.FSReadError;
|
||||||
|
|
@ -72,6 +73,7 @@ public class RouteSSTableManager implements SSTableManager
|
||||||
}
|
}
|
||||||
catch (IOException e)
|
catch (IOException e)
|
||||||
{
|
{
|
||||||
|
if (notComplete == null) notComplete = new ArrayList<>();
|
||||||
notComplete.add(sstable);
|
notComplete.add(sstable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -88,7 +90,7 @@ public class RouteSSTableManager implements SSTableManager
|
||||||
@Override
|
@Override
|
||||||
public synchronized void search(int storeId, TableId tableId,
|
public synchronized void search(int storeId, TableId tableId,
|
||||||
byte[] start, byte[] end,
|
byte[] start, byte[] end,
|
||||||
TxnId minTxnId, Timestamp maxTxnId, @Nullable TxnId minDecidedId,
|
TxnId minTxnId, Timestamp maxTxnId, @Nullable MaxDecidedRX.DecidedRX decidedRX,
|
||||||
Consumer<ByteBuffer> onMatch)
|
Consumer<ByteBuffer> onMatch)
|
||||||
{
|
{
|
||||||
Key group = new Key(storeId, tableId);
|
Key group = new Key(storeId, tableId);
|
||||||
|
|
@ -96,7 +98,7 @@ public class RouteSSTableManager implements SSTableManager
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
index.search(group, start, end, minTxnId, maxTxnId, minDecidedId, onMatch);
|
index.search(group, start, end, minTxnId, maxTxnId, decidedRX, onMatch);
|
||||||
}
|
}
|
||||||
catch (Throwable t)
|
catch (Throwable t)
|
||||||
{
|
{
|
||||||
|
|
@ -107,10 +109,10 @@ public class RouteSSTableManager implements SSTableManager
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized void search(int storeId, TableId tableId, byte[] key, TxnId minTxnId, Timestamp maxTxnId, @Nullable TxnId minDecidedId, Consumer<ByteBuffer> onMatch)
|
public synchronized void search(int storeId, TableId tableId, byte[] key, TxnId minTxnId, Timestamp maxTxnId, @Nullable MaxDecidedRX.DecidedRX decidedRX, Consumer<ByteBuffer> onMatch)
|
||||||
{
|
{
|
||||||
Key group = new Key(storeId, tableId);
|
Key group = new Key(storeId, tableId);
|
||||||
for (SSTableIndex index : sstables.values())
|
for (SSTableIndex index : sstables.values())
|
||||||
index.search(group, key, minTxnId, maxTxnId, minDecidedId, onMatch);
|
index.search(group, key, minTxnId, maxTxnId, decidedRX, onMatch);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ import java.util.stream.Collectors;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
import accord.local.MaxDecidedRX.DecidedRX;
|
||||||
import accord.primitives.Timestamp;
|
import accord.primitives.Timestamp;
|
||||||
import accord.primitives.TxnId;
|
import accord.primitives.TxnId;
|
||||||
import org.apache.cassandra.index.accord.CheckpointIntervalArrayIndex.SegmentSearcher;
|
import org.apache.cassandra.index.accord.CheckpointIntervalArrayIndex.SegmentSearcher;
|
||||||
|
|
@ -82,14 +83,14 @@ public class SSTableIndex extends SharedCloseableImpl
|
||||||
return new SSTableIndex(id, files, segments, cleanup);
|
return new SSTableIndex(id, files, segments, cleanup);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void search(Key group, byte[] key, TxnId minTxnId, Timestamp maxTxnId, @Nullable TxnId minDecidedId, Consumer<ByteBuffer> onMatch)
|
public void search(Key group, byte[] key, TxnId minTxnId, Timestamp maxTxnId, @Nullable DecidedRX decidedRX, Consumer<ByteBuffer> onMatch)
|
||||||
{
|
{
|
||||||
List<Segment> matches = segments.stream().filter(s -> {
|
List<Segment> matches = segments.stream().filter(s -> {
|
||||||
Segment.Metadata metadata = s.groups.get(group);
|
Segment.Metadata metadata = s.groups.get(group);
|
||||||
if (metadata == null) return false;
|
if (metadata == null) return false;
|
||||||
if (metadata.maxTxnId.compareTo(minTxnId) < 0 || metadata.minTxnId.compareTo(maxTxnId) > 0)
|
if (metadata.maxTxnId.compareTo(minTxnId) < 0 || metadata.minTxnId.compareTo(maxTxnId) > 0)
|
||||||
return false;
|
return false;
|
||||||
if (!RouteIndexFormat.includeByMinDecidedId(minDecidedId, metadata.maxRxId))
|
if (!RouteIndexFormat.includeByDecidedRX(decidedRX, metadata.maxRxId))
|
||||||
return false;
|
return false;
|
||||||
return ByteArrayUtil.compareUnsigned(metadata.minTerm, key) < 0
|
return ByteArrayUtil.compareUnsigned(metadata.minTerm, key) < 0
|
||||||
&& ByteArrayUtil.compareUnsigned(metadata.maxTerm, key) >= 0;
|
&& ByteArrayUtil.compareUnsigned(metadata.maxTerm, key) >= 0;
|
||||||
|
|
@ -115,14 +116,14 @@ public class SSTableIndex extends SharedCloseableImpl
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void search(Key group, byte[] start, byte[] end, TxnId minTxnId, Timestamp maxTxnId, @Nullable TxnId minDecidedId, Consumer<ByteBuffer> onMatch)
|
public void search(Key group, byte[] start, byte[] end, TxnId minTxnId, Timestamp maxTxnId, @Nullable DecidedRX decidedRX, Consumer<ByteBuffer> onMatch)
|
||||||
{
|
{
|
||||||
List<Segment> matches = segments.stream().filter(s -> {
|
List<Segment> matches = segments.stream().filter(s -> {
|
||||||
Segment.Metadata metadata = s.groups.get(group);
|
Segment.Metadata metadata = s.groups.get(group);
|
||||||
if (metadata == null) return false;
|
if (metadata == null) return false;
|
||||||
if (metadata.maxTxnId.compareTo(minTxnId) < 0 || metadata.minTxnId.compareTo(maxTxnId) > 0)
|
if (metadata.maxTxnId.compareTo(minTxnId) < 0 || metadata.minTxnId.compareTo(maxTxnId) > 0)
|
||||||
return false;
|
return false;
|
||||||
if (!RouteIndexFormat.includeByMinDecidedId(minDecidedId, metadata.maxRxId))
|
if (!RouteIndexFormat.includeByDecidedRX(decidedRX, metadata.maxRxId))
|
||||||
return false;
|
return false;
|
||||||
if (ByteArrayUtil.compareUnsigned(metadata.minTerm, end) >= 0)
|
if (ByteArrayUtil.compareUnsigned(metadata.minTerm, end) >= 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ import java.util.function.Consumer;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
import accord.local.MaxDecidedRX;
|
||||||
import accord.primitives.Timestamp;
|
import accord.primitives.Timestamp;
|
||||||
import accord.primitives.TxnId;
|
import accord.primitives.TxnId;
|
||||||
import org.apache.cassandra.io.sstable.format.SSTableReader;
|
import org.apache.cassandra.io.sstable.format.SSTableReader;
|
||||||
|
|
@ -34,6 +35,6 @@ public interface SSTableManager
|
||||||
void onSSTableChanged(Collection<SSTableReader> removed, Iterable<SSTableReader> added);
|
void onSSTableChanged(Collection<SSTableReader> removed, Iterable<SSTableReader> added);
|
||||||
boolean isIndexComplete(SSTableReader reader);
|
boolean isIndexComplete(SSTableReader reader);
|
||||||
|
|
||||||
void search(int storeId, TableId tableId, byte[] start, byte[] end, TxnId minTxnId, Timestamp maxTxnId, @Nullable TxnId minDecidedId, Consumer<ByteBuffer> onMatch);
|
void search(int storeId, TableId tableId, byte[] start, byte[] end, TxnId minTxnId, Timestamp maxTxnId, @Nullable MaxDecidedRX.DecidedRX decidedRX, Consumer<ByteBuffer> onMatch);
|
||||||
void search(int storeId, TableId tableId, byte[] key, TxnId minTxnId, Timestamp maxTxnId, @Nullable TxnId minDecidedId, Consumer<ByteBuffer> onMatch);
|
void search(int storeId, TableId tableId, byte[] key, TxnId minTxnId, Timestamp maxTxnId, @Nullable MaxDecidedRX.DecidedRX decidedRX, Consumer<ByteBuffer> onMatch);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,11 @@ public interface AsymmetricParameterisedVersionedSerializer<In, P, Out, Version>
|
||||||
|
|
||||||
Out deserialize(P p, DataInputPlus in, Version version) throws IOException;
|
Out deserialize(P p, DataInputPlus in, Version version) throws IOException;
|
||||||
|
|
||||||
|
default void skip(P p, DataInputPlus in, Version version) throws IOException
|
||||||
|
{
|
||||||
|
deserialize(p, in, version);
|
||||||
|
}
|
||||||
|
|
||||||
default Out deserialize(P p, ByteBuffer buffer, Version version) throws IOException
|
default Out deserialize(P p, ByteBuffer buffer, Version version) throws IOException
|
||||||
{
|
{
|
||||||
try (DataInputBuffer in = new DataInputBuffer(buffer, true))
|
try (DataInputBuffer in = new DataInputBuffer(buffer, true))
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,12 @@ public interface AsymmetricVersionedSerializer<In, Out, Version>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Out deserialize(DataInputPlus in, Version version) throws IOException;
|
Out deserialize(DataInputPlus in, Version version) throws IOException;
|
||||||
|
|
||||||
|
default void skip(DataInputPlus in, Version version) throws IOException
|
||||||
|
{
|
||||||
|
deserialize(in, version);
|
||||||
|
}
|
||||||
|
|
||||||
default Out deserialize(ByteBuffer buffer, Version version) throws IOException
|
default Out deserialize(ByteBuffer buffer, Version version) throws IOException
|
||||||
{
|
{
|
||||||
try (DataInputBuffer in = new DataInputBuffer(buffer, true))
|
try (DataInputBuffer in = new DataInputBuffer(buffer, true))
|
||||||
|
|
|
||||||
|
|
@ -67,6 +67,11 @@ public class DataInputBuffer extends RebufferingInputStream
|
||||||
return buffer.remaining();
|
return buffer.remaining();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ByteBuffer buffer()
|
||||||
|
{
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() {}
|
public void close() {}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -383,6 +383,13 @@ public class AccordCacheEntry<K, V> extends IntrusiveLinkedListNode
|
||||||
return (V)unwrap();
|
return (V)unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Object getOrShrunkExclusive()
|
||||||
|
{
|
||||||
|
Invariants.require(owner == null || owner.commandStore == null || owner.commandStore.executor().isOwningThread());
|
||||||
|
Invariants.require(isLoaded(), "%s", this);
|
||||||
|
return unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
public V tryGetExclusive()
|
public V tryGetExclusive()
|
||||||
{
|
{
|
||||||
Invariants.require(owner == null || owner.commandStore == null || owner.commandStore.executor().isOwningThread());
|
Invariants.require(owner == null || owner.commandStore == null || owner.commandStore.executor().isOwningThread());
|
||||||
|
|
|
||||||
|
|
@ -79,7 +79,6 @@ import org.apache.cassandra.utils.Clock;
|
||||||
|
|
||||||
import static accord.api.Journal.CommandUpdate;
|
import static accord.api.Journal.CommandUpdate;
|
||||||
import static accord.api.Journal.FieldUpdates;
|
import static accord.api.Journal.FieldUpdates;
|
||||||
import static accord.api.Journal.Load.MINIMAL;
|
|
||||||
import static accord.utils.Invariants.require;
|
import static accord.utils.Invariants.require;
|
||||||
import static org.apache.cassandra.journal.Params.ReplayMode.ONLY_NON_DURABLE;
|
import static org.apache.cassandra.journal.Params.ReplayMode.ONLY_NON_DURABLE;
|
||||||
|
|
||||||
|
|
@ -435,7 +434,12 @@ public class AccordCommandStore extends CommandStore
|
||||||
|
|
||||||
public Command.Minimal loadMinimal(TxnId txnId)
|
public Command.Minimal loadMinimal(TxnId txnId)
|
||||||
{
|
{
|
||||||
return journal.loadMinimal(id, txnId, MINIMAL, unsafeGetRedundantBefore(), durableBefore());
|
return journal.loadMinimal(id, txnId, unsafeGetRedundantBefore(), durableBefore());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Command.MinimalWithDeps loadMinimalWithDeps(TxnId txnId)
|
||||||
|
{
|
||||||
|
return journal.loadMinimalWithDeps(id, txnId, unsafeGetRedundantBefore(), durableBefore());
|
||||||
}
|
}
|
||||||
|
|
||||||
public AccordCompactionInfo getCompactionInfo()
|
public AccordCompactionInfo getCompactionInfo()
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,8 @@ import accord.local.DurableBefore;
|
||||||
import accord.local.Node;
|
import accord.local.Node;
|
||||||
import accord.local.RedundantBefore;
|
import accord.local.RedundantBefore;
|
||||||
import accord.primitives.EpochSupplier;
|
import accord.primitives.EpochSupplier;
|
||||||
|
import accord.primitives.PartialDeps;
|
||||||
|
import accord.primitives.PartialTxn;
|
||||||
import accord.primitives.Ranges;
|
import accord.primitives.Ranges;
|
||||||
import accord.primitives.Route;
|
import accord.primitives.Route;
|
||||||
import accord.primitives.SaveStatus;
|
import accord.primitives.SaveStatus;
|
||||||
|
|
@ -67,6 +69,7 @@ import org.apache.cassandra.concurrent.Shutdownable;
|
||||||
import org.apache.cassandra.config.DatabaseDescriptor;
|
import org.apache.cassandra.config.DatabaseDescriptor;
|
||||||
import org.apache.cassandra.db.ColumnFamilyStore;
|
import org.apache.cassandra.db.ColumnFamilyStore;
|
||||||
import org.apache.cassandra.db.Keyspace;
|
import org.apache.cassandra.db.Keyspace;
|
||||||
|
import org.apache.cassandra.io.util.DataInputBuffer;
|
||||||
import org.apache.cassandra.io.util.DataInputPlus;
|
import org.apache.cassandra.io.util.DataInputPlus;
|
||||||
import org.apache.cassandra.io.util.DataOutputBuffer;
|
import org.apache.cassandra.io.util.DataOutputBuffer;
|
||||||
import org.apache.cassandra.io.util.DataOutputPlus;
|
import org.apache.cassandra.io.util.DataOutputPlus;
|
||||||
|
|
@ -94,6 +97,9 @@ import org.apache.cassandra.utils.CloseableIterator;
|
||||||
import org.apache.cassandra.utils.ExecutorUtils;
|
import org.apache.cassandra.utils.ExecutorUtils;
|
||||||
import org.apache.cassandra.utils.concurrent.Semaphore;
|
import org.apache.cassandra.utils.concurrent.Semaphore;
|
||||||
|
|
||||||
|
import static accord.api.Journal.Load.ALL;
|
||||||
|
import static accord.api.Journal.Load.MINIMAL;
|
||||||
|
import static accord.api.Journal.Load.MINIMAL_WITH_DEPS;
|
||||||
import static accord.impl.CommandChange.Field.CLEANUP;
|
import static accord.impl.CommandChange.Field.CLEANUP;
|
||||||
import static accord.impl.CommandChange.anyFieldChanged;
|
import static accord.impl.CommandChange.anyFieldChanged;
|
||||||
import static accord.impl.CommandChange.describeFlags;
|
import static accord.impl.CommandChange.describeFlags;
|
||||||
|
|
@ -278,10 +284,9 @@ public class AccordJournal implements accord.api.Journal, RangeSearcher.Supplier
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
// applies cleanup and returns null if no command should be returned
|
||||||
public Command.Minimal loadMinimal(int commandStoreId, TxnId txnId, Load load, RedundantBefore redundantBefore, DurableBefore durableBefore)
|
public static Builder cleanupAndFilter(Builder builder, RedundantBefore redundantBefore, DurableBefore durableBefore)
|
||||||
{
|
{
|
||||||
Builder builder = loadDiffs(commandStoreId, txnId, load);
|
|
||||||
if (builder.isEmpty())
|
if (builder.isEmpty())
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
|
|
@ -294,7 +299,21 @@ public class AccordJournal implements accord.api.Journal, RangeSearcher.Supplier
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
Invariants.require(builder.saveStatus() != null, "No saveSatus loaded, but next was called and cleanup was not: %s", builder);
|
Invariants.require(builder.saveStatus() != null, "No saveSatus loaded, but next was called and cleanup was not: %s", builder);
|
||||||
return builder.asMinimal();
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Command.Minimal loadMinimal(int commandStoreId, TxnId txnId, RedundantBefore redundantBefore, DurableBefore durableBefore)
|
||||||
|
{
|
||||||
|
Builder builder = cleanupAndFilter(loadDiffs(commandStoreId, txnId, MINIMAL), redundantBefore, durableBefore);
|
||||||
|
return builder == null ? null : builder.asMinimal();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Command.MinimalWithDeps loadMinimalWithDeps(int commandStoreId, TxnId txnId, RedundantBefore redundantBefore, DurableBefore durableBefore)
|
||||||
|
{
|
||||||
|
Builder builder = cleanupAndFilter(loadDiffs(commandStoreId, txnId, MINIMAL_WITH_DEPS), redundantBefore, durableBefore);
|
||||||
|
return builder == null ? null : builder.asMinimalWithDeps();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -831,7 +850,7 @@ public class AccordJournal implements accord.api.Journal, RangeSearcher.Supplier
|
||||||
CommandSerializers.partialTxn.serialize(command.partialTxn(), out, userVersion);
|
CommandSerializers.partialTxn.serialize(command.partialTxn(), out, userVersion);
|
||||||
break;
|
break;
|
||||||
case PARTIAL_DEPS:
|
case PARTIAL_DEPS:
|
||||||
DepsSerializers.partialDeps.serialize(command.partialDeps(), out);
|
DepsSerializers.partialDepsById.serialize(command.partialDeps(), out);
|
||||||
break;
|
break;
|
||||||
case WAITING_ON:
|
case WAITING_ON:
|
||||||
Command.WaitingOn waitingOn = command.waitingOn();
|
Command.WaitingOn waitingOn = command.waitingOn();
|
||||||
|
|
@ -878,6 +897,8 @@ public class AccordJournal implements accord.api.Journal, RangeSearcher.Supplier
|
||||||
|
|
||||||
public static class Builder extends CommandChange.Builder implements FlyweightImage
|
public static class Builder extends CommandChange.Builder implements FlyweightImage
|
||||||
{
|
{
|
||||||
|
private final boolean deserializeDeps;
|
||||||
|
|
||||||
public Builder()
|
public Builder()
|
||||||
{
|
{
|
||||||
this(Load.ALL);
|
this(Load.ALL);
|
||||||
|
|
@ -885,17 +906,35 @@ public class AccordJournal implements accord.api.Journal, RangeSearcher.Supplier
|
||||||
|
|
||||||
public Builder(Load load)
|
public Builder(Load load)
|
||||||
{
|
{
|
||||||
super(null, load);
|
this(null, load);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder(TxnId txnId)
|
public Builder(TxnId txnId)
|
||||||
{
|
{
|
||||||
super(txnId, Load.ALL);
|
this(txnId, Load.ALL);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder(TxnId txnId, Load load)
|
public Builder(TxnId txnId, Load load)
|
||||||
{
|
{
|
||||||
super(txnId, load);
|
super(txnId, load);
|
||||||
|
deserializeDeps = load == ALL;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PartialDeps partialDeps()
|
||||||
|
{
|
||||||
|
if (partialDeps instanceof ByteBuffer)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
partialDeps = DepsSerializers.partialDepsById.deserialize((ByteBuffer) partialDeps);
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
throw new IllegalStateException("Failed to materialise partially deserialised deps", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (PartialDeps) partialDeps;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void reset(JournalKey key)
|
public void reset(JournalKey key)
|
||||||
|
|
@ -970,15 +1009,17 @@ public class AccordJournal implements accord.api.Journal, RangeSearcher.Supplier
|
||||||
break;
|
break;
|
||||||
case PARTIAL_TXN:
|
case PARTIAL_TXN:
|
||||||
Invariants.require(partialTxn != null, "%s", this);
|
Invariants.require(partialTxn != null, "%s", this);
|
||||||
CommandSerializers.partialTxn.serialize(partialTxn, out, userVersion);
|
if (partialTxn instanceof ByteBuffer) out.write(((ByteBuffer) partialTxn).duplicate());
|
||||||
|
else CommandSerializers.partialTxn.serialize((PartialTxn) partialTxn, out, userVersion);
|
||||||
break;
|
break;
|
||||||
case PARTIAL_DEPS:
|
case PARTIAL_DEPS:
|
||||||
Invariants.require(partialDeps != null, "%s", this);
|
Invariants.require(partialDeps != null, "%s", this);
|
||||||
DepsSerializers.partialDeps.serialize(partialDeps, out);
|
if (partialDeps instanceof ByteBuffer) out.write(((ByteBuffer) partialDeps).duplicate());
|
||||||
|
else DepsSerializers.partialDepsById.serialize((PartialDeps) partialDeps, out);
|
||||||
break;
|
break;
|
||||||
case WAITING_ON:
|
case WAITING_ON:
|
||||||
Invariants.require(waitingOn != null, "%s", this);
|
Invariants.require(waitingOn != null, "%s", this);
|
||||||
((WaitingOnSerializer.Provider)waitingOn).reserialize(out);
|
((WaitingOnSerializer.WaitingOnBitSetsAndLength)waitingOn).reserialize(out);
|
||||||
break;
|
break;
|
||||||
case WRITES:
|
case WRITES:
|
||||||
Invariants.require(writes != null, "%s", this);
|
Invariants.require(writes != null, "%s", this);
|
||||||
|
|
@ -1050,10 +1091,24 @@ public class AccordJournal implements accord.api.Journal, RangeSearcher.Supplier
|
||||||
partialTxn = CommandSerializers.partialTxn.deserialize(in, userVersion);
|
partialTxn = CommandSerializers.partialTxn.deserialize(in, userVersion);
|
||||||
break;
|
break;
|
||||||
case PARTIAL_DEPS:
|
case PARTIAL_DEPS:
|
||||||
partialDeps = DepsSerializers.partialDeps.deserialize(in);
|
// TODO (required): this optimisation will be easily disabled;
|
||||||
|
// should either operate natively on ByteBuffer
|
||||||
|
// or else use some explicit API for copying bytes while skipping
|
||||||
|
if (deserializeDeps || !(in instanceof DataInputBuffer))
|
||||||
|
{
|
||||||
|
partialDeps = DepsSerializers.partialDepsById.deserialize(in);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ByteBuffer buf = ((DataInputBuffer)in).buffer();
|
||||||
|
int start = buf.position();
|
||||||
|
DepsSerializers.partialDepsById.skip(in);
|
||||||
|
int end = buf.position();
|
||||||
|
partialDeps = buf.duplicate().position(start).limit(end);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case WAITING_ON:
|
case WAITING_ON:
|
||||||
waitingOn = WaitingOnSerializer.deserializeProvider(txnId, in);
|
waitingOn = WaitingOnSerializer.deserializeBitSets(txnId, in);
|
||||||
break;
|
break;
|
||||||
case WRITES:
|
case WRITES:
|
||||||
writes = CommandSerializers.writes.deserialize(in, userVersion);
|
writes = CommandSerializers.writes.deserialize(in, userVersion);
|
||||||
|
|
@ -1093,25 +1148,24 @@ public class AccordJournal implements accord.api.Journal, RangeSearcher.Supplier
|
||||||
CommandSerializers.ballot.skip(in);
|
CommandSerializers.ballot.skip(in);
|
||||||
break;
|
break;
|
||||||
case PARTICIPANTS:
|
case PARTICIPANTS:
|
||||||
CommandSerializers.participants.deserialize(in);
|
CommandSerializers.participants.skip(in);
|
||||||
break;
|
break;
|
||||||
case PARTIAL_TXN:
|
case PARTIAL_TXN:
|
||||||
CommandSerializers.partialTxn.deserialize(in, userVersion);
|
CommandSerializers.partialTxn.skip(in, userVersion);
|
||||||
break;
|
break;
|
||||||
case PARTIAL_DEPS:
|
case PARTIAL_DEPS:
|
||||||
// TODO (expected): skip
|
DepsSerializers.partialDepsById.skip(in);
|
||||||
DepsSerializers.partialDeps.deserialize(in);
|
|
||||||
break;
|
break;
|
||||||
case WAITING_ON:
|
case WAITING_ON:
|
||||||
WaitingOnSerializer.skip(txnId, in);
|
WaitingOnSerializer.skip(txnId, in);
|
||||||
break;
|
break;
|
||||||
case WRITES:
|
case WRITES:
|
||||||
// TODO (expected): skip
|
// TODO (expected): skip
|
||||||
CommandSerializers.writes.deserialize(in, userVersion);
|
CommandSerializers.writes.skip(in, userVersion);
|
||||||
break;
|
break;
|
||||||
case RESULT:
|
case RESULT:
|
||||||
// TODO (expected): skip
|
// TODO (expected): skip
|
||||||
ResultSerializers.result.deserialize(in);
|
ResultSerializers.result.skip(in);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ import com.google.common.collect.AbstractIterator;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import accord.local.MaxDecidedRX;
|
||||||
import accord.primitives.Timestamp;
|
import accord.primitives.Timestamp;
|
||||||
import accord.primitives.TxnId;
|
import accord.primitives.TxnId;
|
||||||
import accord.utils.Invariants;
|
import accord.utils.Invariants;
|
||||||
|
|
@ -230,8 +231,7 @@ public class AccordJournalTable<K extends JournalKey, V> implements RangeSearche
|
||||||
{
|
{
|
||||||
participants("participants", BytesType.instance),
|
participants("participants", BytesType.instance),
|
||||||
store_id("store_id", Int32Type.instance),
|
store_id("store_id", Int32Type.instance),
|
||||||
txn_id("txn_id", BytesType.instance),
|
txn_id("txn_id", BytesType.instance);
|
||||||
min_decided_id("min_decided_id", BytesType.instance);
|
|
||||||
|
|
||||||
public final ColumnMetadata metadata;
|
public final ColumnMetadata metadata;
|
||||||
|
|
||||||
|
|
@ -253,22 +253,22 @@ public class AccordJournalTable<K extends JournalKey, V> implements RangeSearche
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Result search(int commandStoreId, TokenRange range, TxnId minTxnId, Timestamp maxTxnId, @Nullable TxnId minDecidedId)
|
public Result search(int commandStoreId, TokenRange range, TxnId minTxnId, Timestamp maxTxnId, @Nullable MaxDecidedRX.DecidedRX decidedRX)
|
||||||
{
|
{
|
||||||
CloseableIterator<TxnId> inMemory = index.search(commandStoreId, range, minTxnId, maxTxnId, minDecidedId).results();
|
CloseableIterator<TxnId> inMemory = index.search(commandStoreId, range, minTxnId, maxTxnId, decidedRX).results();
|
||||||
CloseableIterator<TxnId> table = tableSearch(commandStoreId, range.start(), range.end(), minTxnId, maxTxnId, minDecidedId);
|
CloseableIterator<TxnId> table = tableSearch(commandStoreId, range.start(), range.end(), minTxnId, maxTxnId, decidedRX);
|
||||||
return new DefaultResult(minTxnId, maxTxnId, minDecidedId, MergeIterator.get(Arrays.asList(inMemory, table)));
|
return new DefaultResult(minTxnId, maxTxnId, decidedRX, MergeIterator.get(Arrays.asList(inMemory, table)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Result search(int commandStoreId, TokenKey key, TxnId minTxnId, Timestamp maxTxnId, @Nullable TxnId minDecidedId)
|
public Result search(int commandStoreId, TokenKey key, TxnId minTxnId, Timestamp maxTxnId, @Nullable MaxDecidedRX.DecidedRX decidedRX)
|
||||||
{
|
{
|
||||||
CloseableIterator<TxnId> inMemory = index.search(commandStoreId, key, minTxnId, maxTxnId, minDecidedId).results();
|
CloseableIterator<TxnId> inMemory = index.search(commandStoreId, key, minTxnId, maxTxnId, decidedRX).results();
|
||||||
CloseableIterator<TxnId> table = tableSearch(commandStoreId, key, minTxnId, maxTxnId, minDecidedId);
|
CloseableIterator<TxnId> table = tableSearch(commandStoreId, key, minTxnId, maxTxnId);
|
||||||
return new DefaultResult(minTxnId, maxTxnId, minDecidedId, MergeIterator.get(Arrays.asList(inMemory, table)));
|
return new DefaultResult(minTxnId, maxTxnId, decidedRX, MergeIterator.get(Arrays.asList(inMemory, table)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private CloseableIterator<TxnId> tableSearch(int store, TokenKey start, TokenKey end, TxnId minTxnId, Timestamp maxTxnId, @Nullable TxnId minDecidedId)
|
private CloseableIterator<TxnId> tableSearch(int store, TokenKey start, TokenKey end, TxnId minTxnId, Timestamp maxTxnId, @Nullable MaxDecidedRX.DecidedRX decidedRX)
|
||||||
{
|
{
|
||||||
RowFilter rowFilter = RowFilter.create(false);
|
RowFilter rowFilter = RowFilter.create(false);
|
||||||
rowFilter.add(AccordJournalTable.SyntheticColumn.participants.metadata, Operator.GT, OrderedRouteSerializer.serialize(start));
|
rowFilter.add(AccordJournalTable.SyntheticColumn.participants.metadata, Operator.GT, OrderedRouteSerializer.serialize(start));
|
||||||
|
|
@ -276,13 +276,10 @@ public class AccordJournalTable<K extends JournalKey, V> implements RangeSearche
|
||||||
rowFilter.add(AccordJournalTable.SyntheticColumn.store_id.metadata, Operator.EQ, Int32Type.instance.decompose(store));
|
rowFilter.add(AccordJournalTable.SyntheticColumn.store_id.metadata, Operator.EQ, Int32Type.instance.decompose(store));
|
||||||
rowFilter.add(AccordJournalTable.SyntheticColumn.txn_id.metadata, Operator.GTE, CommandSerializers.txnId.serialize(minTxnId));
|
rowFilter.add(AccordJournalTable.SyntheticColumn.txn_id.metadata, Operator.GTE, CommandSerializers.txnId.serialize(minTxnId));
|
||||||
rowFilter.add(AccordJournalTable.SyntheticColumn.txn_id.metadata, Operator.LTE, CommandSerializers.timestamp.serialize(maxTxnId));
|
rowFilter.add(AccordJournalTable.SyntheticColumn.txn_id.metadata, Operator.LTE, CommandSerializers.timestamp.serialize(maxTxnId));
|
||||||
if (minDecidedId != null)
|
|
||||||
rowFilter.add(AccordJournalTable.SyntheticColumn.min_decided_id.metadata, Operator.GTE, CommandSerializers.txnId.serialize(minDecidedId));
|
|
||||||
|
|
||||||
return process(store, rowFilter);
|
return process(store, rowFilter);
|
||||||
}
|
}
|
||||||
|
|
||||||
private CloseableIterator<TxnId> tableSearch(int store, TokenKey key, TxnId minTxnId, Timestamp maxTxnId, @Nullable TxnId minDecidedId)
|
private CloseableIterator<TxnId> tableSearch(int store, TokenKey key, TxnId minTxnId, Timestamp maxTxnId)
|
||||||
{
|
{
|
||||||
RowFilter rowFilter = RowFilter.create(false);
|
RowFilter rowFilter = RowFilter.create(false);
|
||||||
rowFilter.add(AccordJournalTable.SyntheticColumn.participants.metadata, Operator.GTE, OrderedRouteSerializer.serialize(key));
|
rowFilter.add(AccordJournalTable.SyntheticColumn.participants.metadata, Operator.GTE, OrderedRouteSerializer.serialize(key));
|
||||||
|
|
@ -290,9 +287,6 @@ public class AccordJournalTable<K extends JournalKey, V> implements RangeSearche
|
||||||
rowFilter.add(AccordJournalTable.SyntheticColumn.store_id.metadata, Operator.EQ, Int32Type.instance.decompose(store));
|
rowFilter.add(AccordJournalTable.SyntheticColumn.store_id.metadata, Operator.EQ, Int32Type.instance.decompose(store));
|
||||||
rowFilter.add(AccordJournalTable.SyntheticColumn.txn_id.metadata, Operator.GTE, CommandSerializers.txnId.serialize(minTxnId));
|
rowFilter.add(AccordJournalTable.SyntheticColumn.txn_id.metadata, Operator.GTE, CommandSerializers.txnId.serialize(minTxnId));
|
||||||
rowFilter.add(AccordJournalTable.SyntheticColumn.txn_id.metadata, Operator.LTE, CommandSerializers.timestamp.serialize(maxTxnId));
|
rowFilter.add(AccordJournalTable.SyntheticColumn.txn_id.metadata, Operator.LTE, CommandSerializers.timestamp.serialize(maxTxnId));
|
||||||
if (minDecidedId != null)
|
|
||||||
rowFilter.add(AccordJournalTable.SyntheticColumn.min_decided_id.metadata, Operator.GTE, CommandSerializers.txnId.serialize(minDecidedId));
|
|
||||||
|
|
||||||
return process(store, rowFilter);
|
return process(store, rowFilter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -270,13 +270,10 @@ public class AccordObjectSizes
|
||||||
size += range(dependencies.rangeDeps.range(i));
|
size += range(dependencies.rangeDeps.range(i));
|
||||||
size += ObjectSizes.sizeOfReferenceArray(dependencies.rangeDeps.rangeCount());
|
size += ObjectSizes.sizeOfReferenceArray(dependencies.rangeDeps.rangeCount());
|
||||||
|
|
||||||
for (int i = 0 ; i < dependencies.keyDeps.txnIdCount() ; ++i)
|
size += dependencies.keyDeps.txnIdCount() * TIMESTAMP_SIZE;
|
||||||
size += timestamp(dependencies.keyDeps.txnId(i));
|
size += dependencies.rangeDeps.txnIdCount() * TIMESTAMP_SIZE;
|
||||||
for (int i = 0 ; i < dependencies.rangeDeps.txnIdCount() ; ++i)
|
size += KeyDeps.SerializerSupport.keysToTxnIds(dependencies.keyDeps).length * 4L;
|
||||||
size += timestamp(dependencies.rangeDeps.txnId(i));
|
size += RangeDeps.SerializerSupport.rangesToTxnIds(dependencies.rangeDeps).length * 4L;
|
||||||
|
|
||||||
size += KeyDeps.SerializerSupport.keysToTxnIdsCount(dependencies.keyDeps) * 4L;
|
|
||||||
size += RangeDeps.SerializerSupport.rangesToTxnIdsCount(dependencies.rangeDeps) * 4L;
|
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,8 @@
|
||||||
|
|
||||||
package org.apache.cassandra.service.accord;
|
package org.apache.cassandra.service.accord;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
|
@ -31,6 +33,7 @@ import java.util.concurrent.locks.ReentrantLock;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
import accord.api.Journal;
|
||||||
import accord.api.RoutingKey;
|
import accord.api.RoutingKey;
|
||||||
import accord.local.Command;
|
import accord.local.Command;
|
||||||
import accord.local.CommandSummaries;
|
import accord.local.CommandSummaries;
|
||||||
|
|
@ -54,12 +57,17 @@ import accord.utils.Invariants;
|
||||||
import accord.utils.SymmetricComparator;
|
import accord.utils.SymmetricComparator;
|
||||||
import accord.utils.UnhandledEnum;
|
import accord.utils.UnhandledEnum;
|
||||||
import org.agrona.collections.Object2ObjectHashMap;
|
import org.agrona.collections.Object2ObjectHashMap;
|
||||||
|
import org.apache.cassandra.exceptions.UnknownTableException;
|
||||||
|
import org.apache.cassandra.io.util.DataInputBuffer;
|
||||||
import org.apache.cassandra.service.accord.api.TokenKey;
|
import org.apache.cassandra.service.accord.api.TokenKey;
|
||||||
|
import org.apache.cassandra.service.accord.serializers.Version;
|
||||||
import org.apache.cassandra.utils.btree.BTree;
|
import org.apache.cassandra.utils.btree.BTree;
|
||||||
import org.apache.cassandra.utils.btree.BTreeSet;
|
import org.apache.cassandra.utils.btree.BTreeSet;
|
||||||
import org.apache.cassandra.utils.btree.IntervalBTree;
|
import org.apache.cassandra.utils.btree.IntervalBTree;
|
||||||
import org.apache.cassandra.utils.concurrent.IntrusiveStack;
|
import org.apache.cassandra.utils.concurrent.IntrusiveStack;
|
||||||
|
|
||||||
|
import static accord.api.Journal.Load.MINIMAL;
|
||||||
|
import static accord.api.Journal.Load.MINIMAL_WITH_DEPS;
|
||||||
import static org.apache.cassandra.utils.btree.IntervalBTree.InclusiveEndHelper.endWithStart;
|
import static org.apache.cassandra.utils.btree.IntervalBTree.InclusiveEndHelper.endWithStart;
|
||||||
import static org.apache.cassandra.utils.btree.IntervalBTree.InclusiveEndHelper.keyEndWithStart;
|
import static org.apache.cassandra.utils.btree.IntervalBTree.InclusiveEndHelper.keyEndWithStart;
|
||||||
import static org.apache.cassandra.utils.btree.IntervalBTree.InclusiveEndHelper.keyStartWithEnd;
|
import static org.apache.cassandra.utils.btree.IntervalBTree.InclusiveEndHelper.keyStartWithEnd;
|
||||||
|
|
@ -375,11 +383,11 @@ public class CommandsForRanges extends TreeMap<Timestamp, Summary> implements Co
|
||||||
{
|
{
|
||||||
case Range:
|
case Range:
|
||||||
for (Unseekable range : searchKeysOrRanges)
|
for (Unseekable range : searchKeysOrRanges)
|
||||||
manager.searcher.search(manager.commandStore.id(), (TokenRange) range, minTxnId, maxTxnId, minDecidedId).consume(forEach);
|
manager.searcher.search(manager.commandStore.id(), (TokenRange) range, minTxnId, maxTxnId, decidedRx).consume(forEach);
|
||||||
break;
|
break;
|
||||||
case Key:
|
case Key:
|
||||||
for (Unseekable key : searchKeysOrRanges)
|
for (Unseekable key : searchKeysOrRanges)
|
||||||
manager.searcher.search(manager.commandStore.id(), (TokenKey) key, minTxnId, maxTxnId, minDecidedId).consume(forEach);
|
manager.searcher.search(manager.commandStore.id(), (TokenKey) key, minTxnId, maxTxnId, decidedRx).consume(forEach);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -448,7 +456,7 @@ public class CommandsForRanges extends TreeMap<Timestamp, Summary> implements Co
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Command cmd = manager.commandStore.loadCommand(txnId);
|
Command.MinimalWithDeps cmd = manager.commandStore.loadMinimalWithDeps(txnId);
|
||||||
if (cmd != null)
|
if (cmd != null)
|
||||||
return ifRelevant(cmd);
|
return ifRelevant(cmd);
|
||||||
}
|
}
|
||||||
|
|
@ -477,19 +485,38 @@ public class CommandsForRanges extends TreeMap<Timestamp, Summary> implements Co
|
||||||
}
|
}
|
||||||
|
|
||||||
TxnId txnId = state.key();
|
TxnId txnId = state.key();
|
||||||
if (!txnId.isVisible() || txnId.compareTo(minTxnId) < 0 || txnId.compareTo(maxTxnId) >= 0)
|
if (!isMaybeRelevant(txnId))
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
Command command = state.getExclusive();
|
Object command = state.getOrShrunkExclusive();
|
||||||
if (command == null)
|
if (command == null)
|
||||||
return null;
|
return null;
|
||||||
return ifRelevant(command);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Summary ifRelevant(Command.Minimal cmd)
|
if (command instanceof Command)
|
||||||
{
|
return ifRelevant((Command) command);
|
||||||
Invariants.require(findAsDep == null);
|
|
||||||
return ifRelevant(cmd.txnId, cmd.executeAt, cmd.saveStatus, cmd.durability, cmd.participants, null);
|
Invariants.require(command instanceof ByteBuffer);
|
||||||
|
AccordJournal.Builder builder = new AccordJournal.Builder(txnId, findAsDep == null ? MINIMAL : MINIMAL_WITH_DEPS);
|
||||||
|
ByteBuffer buffer = (ByteBuffer) command;
|
||||||
|
buffer.mark();
|
||||||
|
try (DataInputBuffer buf = new DataInputBuffer(buffer, false))
|
||||||
|
{
|
||||||
|
builder.deserializeNext(buf, Version.LATEST);
|
||||||
|
if (findAsDep == null) return ifRelevant(builder.asMinimal());
|
||||||
|
else return ifRelevant(builder.asMinimalWithDeps());
|
||||||
|
}
|
||||||
|
catch (UnknownTableException e)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
buffer.reset();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ import java.util.function.Consumer;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
import accord.local.MaxDecidedRX;
|
||||||
import accord.primitives.Timestamp;
|
import accord.primitives.Timestamp;
|
||||||
import accord.primitives.TxnId;
|
import accord.primitives.TxnId;
|
||||||
import org.apache.cassandra.index.accord.RouteIndexFormat;
|
import org.apache.cassandra.index.accord.RouteIndexFormat;
|
||||||
|
|
@ -30,8 +31,8 @@ import org.apache.cassandra.utils.CloseableIterator;
|
||||||
|
|
||||||
public interface RangeSearcher
|
public interface RangeSearcher
|
||||||
{
|
{
|
||||||
Result search(int commandStoreId, TokenRange range, TxnId minTxnId, Timestamp maxTxnId, @Nullable TxnId minDecidedId);
|
Result search(int commandStoreId, TokenRange range, TxnId minTxnId, Timestamp maxTxnId, @Nullable MaxDecidedRX.DecidedRX decidedRX);
|
||||||
Result search(int commandStoreId, TokenKey key, TxnId minTxnId, Timestamp maxTxnId, @Nullable TxnId minDecidedId);
|
Result search(int commandStoreId, TokenKey key, TxnId minTxnId, Timestamp maxTxnId, @Nullable MaxDecidedRX.DecidedRX decidedRX);
|
||||||
|
|
||||||
static RangeSearcher extractRangeSearcher(Object o)
|
static RangeSearcher extractRangeSearcher(Object o)
|
||||||
{
|
{
|
||||||
|
|
@ -55,15 +56,15 @@ public interface RangeSearcher
|
||||||
{
|
{
|
||||||
private final TxnId minTxnId;
|
private final TxnId minTxnId;
|
||||||
private final Timestamp maxTxnId;
|
private final Timestamp maxTxnId;
|
||||||
private final @Nullable TxnId minDecidedId;
|
private final @Nullable MaxDecidedRX.DecidedRX decidedRX;
|
||||||
private final CloseableIterator<TxnId> results;
|
private final CloseableIterator<TxnId> results;
|
||||||
private boolean consumed = false;
|
private boolean consumed = false;
|
||||||
|
|
||||||
public DefaultResult(TxnId minTxnId, Timestamp maxTxnId, @Nullable TxnId minDecidedId, CloseableIterator<TxnId> results)
|
public DefaultResult(TxnId minTxnId, Timestamp maxTxnId, @Nullable MaxDecidedRX.DecidedRX decidedRX, CloseableIterator<TxnId> results)
|
||||||
{
|
{
|
||||||
this.minTxnId = minTxnId;
|
this.minTxnId = minTxnId;
|
||||||
this.maxTxnId = maxTxnId;
|
this.maxTxnId = maxTxnId;
|
||||||
this.minDecidedId = minDecidedId;
|
this.decidedRX = decidedRX;
|
||||||
this.results = results;
|
this.results = results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -84,7 +85,7 @@ public interface RangeSearcher
|
||||||
{
|
{
|
||||||
TxnId next = results.next();
|
TxnId next = results.next();
|
||||||
|
|
||||||
if (next.compareTo(minTxnId) >= 0 && next.compareTo(maxTxnId) < 0 && RouteIndexFormat.includeByMinDecidedId(minDecidedId, next))
|
if (next.compareTo(minTxnId) >= 0 && next.compareTo(maxTxnId) < 0 && RouteIndexFormat.includeByDecidedRX(decidedRX, next))
|
||||||
forEach.accept(next);
|
forEach.accept(next);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -120,13 +121,13 @@ public interface RangeSearcher
|
||||||
instance;
|
instance;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Result search(int commandStoreId, TokenRange range, TxnId minTxnId, Timestamp maxTxnId, @Nullable TxnId minDecidedId)
|
public Result search(int commandStoreId, TokenRange range, TxnId minTxnId, Timestamp maxTxnId, @Nullable MaxDecidedRX.DecidedRX decidedRX)
|
||||||
{
|
{
|
||||||
return NoopResult.instance;
|
return NoopResult.instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Result search(int commandStoreId, TokenKey key, TxnId minTxnId, Timestamp maxTxnId, @Nullable TxnId minDecidedId)
|
public Result search(int commandStoreId, TokenKey key, TxnId minTxnId, Timestamp maxTxnId, @Nullable MaxDecidedRX.DecidedRX decidedRX)
|
||||||
{
|
{
|
||||||
return NoopResult.instance;
|
return NoopResult.instance;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,8 @@ import javax.annotation.Nullable;
|
||||||
|
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
|
||||||
|
import accord.local.MaxDecidedRX;
|
||||||
|
import accord.local.MaxDecidedRX.DecidedRX;
|
||||||
import accord.primitives.Route;
|
import accord.primitives.Route;
|
||||||
import accord.primitives.Timestamp;
|
import accord.primitives.Timestamp;
|
||||||
import accord.primitives.Txn;
|
import accord.primitives.Txn;
|
||||||
|
|
@ -81,35 +83,35 @@ public class RouteInMemoryIndex<V> implements RangeSearcher
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public RangeSearcher.Result search(int commandStoreId, TokenRange range, TxnId minTxnId, Timestamp maxTxnId, @Nullable TxnId minDecidedId)
|
public RangeSearcher.Result search(int commandStoreId, TokenRange range, TxnId minTxnId, Timestamp maxTxnId, @Nullable DecidedRX decidedRX)
|
||||||
{
|
{
|
||||||
NavigableSet<TxnId> result = search(commandStoreId, range.table(),
|
NavigableSet<TxnId> result = search(commandStoreId, range.table(),
|
||||||
OrderedRouteSerializer.serializeTokenOnly(range.start()),
|
OrderedRouteSerializer.serializeTokenOnly(range.start()),
|
||||||
OrderedRouteSerializer.serializeTokenOnly(range.end()),
|
OrderedRouteSerializer.serializeTokenOnly(range.end()),
|
||||||
minTxnId, maxTxnId, minDecidedId);
|
minTxnId, maxTxnId, decidedRX);
|
||||||
return new DefaultResult(minTxnId, maxTxnId, minDecidedId, CloseableIterator.wrap(result.iterator()));
|
return new DefaultResult(minTxnId, maxTxnId, decidedRX, CloseableIterator.wrap(result.iterator()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized NavigableSet<TxnId> search(int storeId, TableId tableId, byte[] start, byte[] end, TxnId minTxnId, Timestamp maxTxnId, @Nullable TxnId minDecidedId)
|
private synchronized NavigableSet<TxnId> search(int storeId, TableId tableId, byte[] start, byte[] end, TxnId minTxnId, Timestamp maxTxnId, @Nullable DecidedRX decidedRX)
|
||||||
{
|
{
|
||||||
// store matches in a hash set so add is O(1), and the sorting is done after collecting all matches
|
// store matches in a hash set so add is O(1), and the sorting is done after collecting all matches
|
||||||
Set<TxnId> matches = new HashSet<>();
|
Set<TxnId> matches = new HashSet<>();
|
||||||
segmentIndexes.values().forEach(s -> s.search(storeId, tableId, start, end, minTxnId, maxTxnId, minDecidedId, e -> matches.add(e.getValue())));
|
segmentIndexes.values().forEach(s -> s.search(storeId, tableId, start, end, minTxnId, maxTxnId, decidedRX, e -> matches.add(e.getValue())));
|
||||||
return matches.isEmpty() ? Collections.emptyNavigableSet() : new TreeSet<>(matches);
|
return matches.isEmpty() ? Collections.emptyNavigableSet() : new TreeSet<>(matches);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public RangeSearcher.Result search(int commandStoreId, TokenKey key, TxnId minTxnId, Timestamp maxTxnId, @Nullable TxnId minDecidedId)
|
public RangeSearcher.Result search(int commandStoreId, TokenKey key, TxnId minTxnId, Timestamp maxTxnId, @Nullable MaxDecidedRX.DecidedRX decidedRX)
|
||||||
{
|
{
|
||||||
NavigableSet<TxnId> result = search(commandStoreId, key.table(), OrderedRouteSerializer.serializeTokenOnly(key), minTxnId, maxTxnId, minDecidedId);
|
NavigableSet<TxnId> result = search(commandStoreId, key.table(), OrderedRouteSerializer.serializeTokenOnly(key), minTxnId, maxTxnId, decidedRX);
|
||||||
return new DefaultResult(minTxnId, maxTxnId, minDecidedId, CloseableIterator.wrap(result.iterator()));
|
return new DefaultResult(minTxnId, maxTxnId, decidedRX, CloseableIterator.wrap(result.iterator()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized NavigableSet<TxnId> search(int storeId, TableId tableId, byte[] key, TxnId minTxnId, Timestamp maxTxnId, @Nullable TxnId minDecidedId)
|
private synchronized NavigableSet<TxnId> search(int storeId, TableId tableId, byte[] key, TxnId minTxnId, Timestamp maxTxnId, @Nullable MaxDecidedRX.DecidedRX decidedRX)
|
||||||
{
|
{
|
||||||
// store matches in a hash set so add is O(1), and the sorting is done after collecting all matches
|
// store matches in a hash set so add is O(1), and the sorting is done after collecting all matches
|
||||||
Set<TxnId> matches = new HashSet<>();
|
Set<TxnId> matches = new HashSet<>();
|
||||||
segmentIndexes.values().forEach(s -> s.search(storeId, tableId, key, minTxnId, maxTxnId, minDecidedId, e -> matches.add(e.getValue())));
|
segmentIndexes.values().forEach(s -> s.search(storeId, tableId, key, minTxnId, maxTxnId, decidedRX, e -> matches.add(e.getValue())));
|
||||||
return matches.isEmpty() ? Collections.emptyNavigableSet() : new TreeSet<>(matches);
|
return matches.isEmpty() ? Collections.emptyNavigableSet() : new TreeSet<>(matches);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -133,22 +135,22 @@ public class RouteInMemoryIndex<V> implements RangeSearcher
|
||||||
|
|
||||||
private void search(int storeId, TableId tableId,
|
private void search(int storeId, TableId tableId,
|
||||||
byte[] start, byte[] end,
|
byte[] start, byte[] end,
|
||||||
TxnId minTxnId, Timestamp maxTxnId, @Nullable TxnId minDecidedId,
|
TxnId minTxnId, Timestamp maxTxnId, @Nullable DecidedRX decidedRX,
|
||||||
Consumer<Map.Entry<IndexRange, TxnId>> fn)
|
Consumer<Map.Entry<IndexRange, TxnId>> fn)
|
||||||
{
|
{
|
||||||
StoreIndex idx = storeIndexes.get(storeId);
|
StoreIndex idx = storeIndexes.get(storeId);
|
||||||
if (idx == null) return;
|
if (idx == null) return;
|
||||||
idx.search(tableId, start, end, minTxnId, maxTxnId, minDecidedId, fn);
|
idx.search(tableId, start, end, minTxnId, maxTxnId, decidedRX, fn);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void search(int storeId, TableId tableId,
|
private void search(int storeId, TableId tableId,
|
||||||
byte[] key,
|
byte[] key,
|
||||||
TxnId minTxnId, Timestamp maxTxnId, @Nullable TxnId minDecidedId,
|
TxnId minTxnId, Timestamp maxTxnId, @Nullable DecidedRX decidedRX,
|
||||||
Consumer<Map.Entry<IndexRange, TxnId>> fn)
|
Consumer<Map.Entry<IndexRange, TxnId>> fn)
|
||||||
{
|
{
|
||||||
StoreIndex idx = storeIndexes.get(storeId);
|
StoreIndex idx = storeIndexes.get(storeId);
|
||||||
if (idx == null) return;
|
if (idx == null) return;
|
||||||
idx.search(tableId, key, minTxnId, maxTxnId, minDecidedId, fn);
|
idx.search(tableId, key, minTxnId, maxTxnId, decidedRX, fn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -176,22 +178,22 @@ public class RouteInMemoryIndex<V> implements RangeSearcher
|
||||||
|
|
||||||
public void search(TableId tableId,
|
public void search(TableId tableId,
|
||||||
byte[] start, byte[] end,
|
byte[] start, byte[] end,
|
||||||
TxnId minTxnId, Timestamp maxTxnId, @Nullable TxnId minDecidedId,
|
TxnId minTxnId, Timestamp maxTxnId, @Nullable DecidedRX decidedRX,
|
||||||
Consumer<Map.Entry<IndexRange, TxnId>> fn)
|
Consumer<Map.Entry<IndexRange, TxnId>> fn)
|
||||||
{
|
{
|
||||||
TableIndex index = tableIndex.get(tableId);
|
TableIndex index = tableIndex.get(tableId);
|
||||||
if (index == null) return;
|
if (index == null) return;
|
||||||
index.search(start, end, minTxnId, maxTxnId, minDecidedId, fn);
|
index.search(start, end, minTxnId, maxTxnId, decidedRX, fn);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void search(TableId tableId,
|
public void search(TableId tableId,
|
||||||
byte[] key,
|
byte[] key,
|
||||||
TxnId minTxnId, Timestamp maxTxnId, @Nullable TxnId minDecidedId,
|
TxnId minTxnId, Timestamp maxTxnId, @Nullable MaxDecidedRX.DecidedRX decidedRX,
|
||||||
Consumer<Map.Entry<IndexRange, TxnId>> fn)
|
Consumer<Map.Entry<IndexRange, TxnId>> fn)
|
||||||
{
|
{
|
||||||
TableIndex index = tableIndex.get(tableId);
|
TableIndex index = tableIndex.get(tableId);
|
||||||
if (index == null) return;
|
if (index == null) return;
|
||||||
index.search(key, minTxnId, maxTxnId, minDecidedId, fn);
|
index.search(key, minTxnId, maxTxnId, decidedRX, fn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -225,12 +227,12 @@ public class RouteInMemoryIndex<V> implements RangeSearcher
|
||||||
}
|
}
|
||||||
|
|
||||||
private void search(byte[] start, byte[] end,
|
private void search(byte[] start, byte[] end,
|
||||||
TxnId minTxnId, Timestamp maxTxnId, @Nullable TxnId minDecidedId,
|
TxnId minTxnId, Timestamp maxTxnId, @Nullable DecidedRX decidedRX,
|
||||||
Consumer<Map.Entry<IndexRange, TxnId>> fn)
|
Consumer<Map.Entry<IndexRange, TxnId>> fn)
|
||||||
{
|
{
|
||||||
if (minTxnId.compareTo(max) > 0) return;
|
if (minTxnId.compareTo(max) > 0) return;
|
||||||
if (maxTxnId.compareTo(min) < 0) return;
|
if (maxTxnId.compareTo(min) < 0) return;
|
||||||
if (maxRX != null && !RouteIndexFormat.includeByMinDecidedId(min, maxRX)) return;
|
if (maxRX != null && !RouteIndexFormat.includeByDecidedRX(decidedRX, maxRX)) return;
|
||||||
index.search(new IndexRange(start, end), e -> {
|
index.search(new IndexRange(start, end), e -> {
|
||||||
if (minTxnId.compareTo(e.getValue()) > 0) return;
|
if (minTxnId.compareTo(e.getValue()) > 0) return;
|
||||||
if (maxTxnId.compareTo(e.getValue()) < 0) return;
|
if (maxTxnId.compareTo(e.getValue()) < 0) return;
|
||||||
|
|
@ -239,12 +241,12 @@ public class RouteInMemoryIndex<V> implements RangeSearcher
|
||||||
}
|
}
|
||||||
|
|
||||||
private void search(byte[] key,
|
private void search(byte[] key,
|
||||||
TxnId minTxnId, Timestamp maxTxnId, @Nullable TxnId minDecidedId,
|
TxnId minTxnId, Timestamp maxTxnId, @Nullable MaxDecidedRX.DecidedRX decidedRX,
|
||||||
Consumer<Map.Entry<IndexRange, TxnId>> fn)
|
Consumer<Map.Entry<IndexRange, TxnId>> fn)
|
||||||
{
|
{
|
||||||
if (minTxnId.compareTo(max) > 0) return;
|
if (minTxnId.compareTo(max) > 0) return;
|
||||||
if (maxTxnId.compareTo(min) < 0) return;
|
if (maxTxnId.compareTo(min) < 0) return;
|
||||||
if (maxRX != null && !RouteIndexFormat.includeByMinDecidedId(min, maxRX)) return;
|
if (maxRX != null && !RouteIndexFormat.includeByDecidedRX(decidedRX, maxRX)) return;
|
||||||
index.searchToken(key, e -> {
|
index.searchToken(key, e -> {
|
||||||
if (minTxnId.compareTo(e.getValue()) > 0) return;
|
if (minTxnId.compareTo(e.getValue()) > 0) return;
|
||||||
if (maxTxnId.compareTo(e.getValue()) < 0) return;
|
if (maxTxnId.compareTo(e.getValue()) < 0) return;
|
||||||
|
|
|
||||||
|
|
@ -453,7 +453,7 @@ public class AccordInteropExecution implements ReadCoordinator
|
||||||
// Provide request callbacks with a way to send maximal commits on Insufficient responses
|
// Provide request callbacks with a way to send maximal commits on Insufficient responses
|
||||||
public void sendMaximalCommit(Id to)
|
public void sendMaximalCommit(Id to)
|
||||||
{
|
{
|
||||||
Commit.stableMaximal(node, to, txn, txnId, executeAt, route, deps);
|
node.send(to, new Commit(Kind.StableWithTxnAndDeps, to, allTopologies, txnId, txn, route, ballot, executeAt, deps));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void maybeUpdateUniqueHlc(long uniqueHlc)
|
public void maybeUpdateUniqueHlc(long uniqueHlc)
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@
|
||||||
package org.apache.cassandra.service.accord.serializers;
|
package org.apache.cassandra.service.accord.serializers;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
import accord.api.Result;
|
import accord.api.Result;
|
||||||
import accord.api.RoutingKey;
|
import accord.api.RoutingKey;
|
||||||
|
|
@ -31,7 +32,7 @@ import accord.messages.CheckStatus.CheckStatusReply;
|
||||||
import accord.primitives.Ballot;
|
import accord.primitives.Ballot;
|
||||||
import accord.primitives.Known;
|
import accord.primitives.Known;
|
||||||
import accord.primitives.KnownMap;
|
import accord.primitives.KnownMap;
|
||||||
import accord.primitives.KnownMap.MinMax;
|
import accord.primitives.KnownMap.MinAndMaxKnown;
|
||||||
import accord.primitives.PartialDeps;
|
import accord.primitives.PartialDeps;
|
||||||
import accord.primitives.PartialTxn;
|
import accord.primitives.PartialTxn;
|
||||||
import accord.primitives.Participants;
|
import accord.primitives.Participants;
|
||||||
|
|
@ -63,17 +64,17 @@ public class CheckStatusSerializers
|
||||||
KeySerializers.routingKey.serialize(knownMap.startAt(i), out);
|
KeySerializers.routingKey.serialize(knownMap.startAt(i), out);
|
||||||
for (int i = 0 ; i < size ; ++i)
|
for (int i = 0 ; i < size ; ++i)
|
||||||
{
|
{
|
||||||
KnownMap.MinMax minMax = knownMap.valueAt(i);
|
MinAndMaxKnown minAndMax = knownMap.valueAt(i);
|
||||||
if (minMax == null)
|
if (minAndMax == null)
|
||||||
{
|
{
|
||||||
out.writeByte(0);
|
out.writeByte(0);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
boolean equal = minMax.min.equals(minMax);
|
boolean equal = Objects.equals(minAndMax.minOwned, minAndMax.max);
|
||||||
out.writeByte(equal ? 1 : 2);
|
out.writeByte(equal ? 1 : 2);
|
||||||
known.serialize(minMax.min, out);
|
known.serialize(minAndMax.minOwned, out);
|
||||||
if (!equal)
|
if (!equal)
|
||||||
known.serialize(minMax, out);
|
known.serialize(minAndMax.max, out);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -84,7 +85,7 @@ public class CheckStatusSerializers
|
||||||
RoutingKey[] starts = new RoutingKey[size + 1];
|
RoutingKey[] starts = new RoutingKey[size + 1];
|
||||||
for (int i = 0 ; i <= size ; ++i)
|
for (int i = 0 ; i <= size ; ++i)
|
||||||
starts[i] = KeySerializers.routingKey.deserialize(in);
|
starts[i] = KeySerializers.routingKey.deserialize(in);
|
||||||
MinMax[] values = new MinMax[size];
|
MinAndMaxKnown[] values = new MinAndMaxKnown[size];
|
||||||
for (int i = 0 ; i < size ; ++i)
|
for (int i = 0 ; i < size ; ++i)
|
||||||
{
|
{
|
||||||
int kind = in.readByte();
|
int kind = in.readByte();
|
||||||
|
|
@ -92,7 +93,7 @@ public class CheckStatusSerializers
|
||||||
continue;
|
continue;
|
||||||
Known min = known.deserialize(in);
|
Known min = known.deserialize(in);
|
||||||
Known max = kind == 1 ? min : known.deserialize(in);
|
Known max = kind == 1 ? min : known.deserialize(in);
|
||||||
values[i] = new KnownMap.MinMax(min, max);
|
values[i] = new MinAndMaxKnown(min, max);
|
||||||
}
|
}
|
||||||
return KnownMap.SerializerSupport.create(true, starts, values);
|
return KnownMap.SerializerSupport.create(true, starts, values);
|
||||||
}
|
}
|
||||||
|
|
@ -106,14 +107,14 @@ public class CheckStatusSerializers
|
||||||
result += KeySerializers.routingKey.serializedSize(knownMap.startAt(i));
|
result += KeySerializers.routingKey.serializedSize(knownMap.startAt(i));
|
||||||
for (int i = 0 ; i < size ; ++i)
|
for (int i = 0 ; i < size ; ++i)
|
||||||
{
|
{
|
||||||
KnownMap.MinMax minMax = knownMap.valueAt(i);
|
MinAndMaxKnown minMax = knownMap.valueAt(i);
|
||||||
result += TypeSizes.BYTE_SIZE;
|
result += TypeSizes.BYTE_SIZE;
|
||||||
if (minMax == null)
|
if (minMax == null)
|
||||||
continue;
|
continue;
|
||||||
boolean equal = minMax.min.equals(minMax);
|
boolean equal = Objects.equals(minMax.minOwned, minMax.max);
|
||||||
result += known.serializedSize(minMax.min);
|
result += known.serializedSize(minMax.minOwned);
|
||||||
if (!equal)
|
if (!equal)
|
||||||
result += known.serializedSize(minMax);
|
result += known.serializedSize(minMax.max);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ package org.apache.cassandra.service.accord.serializers;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.function.IntFunction;
|
||||||
|
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
|
||||||
|
|
@ -47,7 +48,10 @@ import accord.primitives.Txn;
|
||||||
import accord.primitives.TxnId;
|
import accord.primitives.TxnId;
|
||||||
import accord.primitives.Unseekables;
|
import accord.primitives.Unseekables;
|
||||||
import accord.primitives.Writes;
|
import accord.primitives.Writes;
|
||||||
|
import accord.utils.ArrayBuffers;
|
||||||
|
import accord.utils.BitUtils;
|
||||||
import accord.utils.Invariants;
|
import accord.utils.Invariants;
|
||||||
|
import accord.utils.VIntCoding;
|
||||||
import org.apache.cassandra.db.TypeSizes;
|
import org.apache.cassandra.db.TypeSizes;
|
||||||
import org.apache.cassandra.db.marshal.ByteBufferAccessor;
|
import org.apache.cassandra.db.marshal.ByteBufferAccessor;
|
||||||
import org.apache.cassandra.db.marshal.ValueAccessor;
|
import org.apache.cassandra.db.marshal.ValueAccessor;
|
||||||
|
|
@ -62,14 +66,16 @@ import org.apache.cassandra.service.accord.txn.TxnRead;
|
||||||
import org.apache.cassandra.service.accord.txn.TxnWrite;
|
import org.apache.cassandra.service.accord.txn.TxnWrite;
|
||||||
import org.apache.cassandra.utils.NullableSerializer;
|
import org.apache.cassandra.utils.NullableSerializer;
|
||||||
|
|
||||||
|
import static org.apache.cassandra.service.accord.serializers.SerializePacked.serializedPackedBitsSize;
|
||||||
|
|
||||||
public class CommandSerializers
|
public class CommandSerializers
|
||||||
{
|
{
|
||||||
private CommandSerializers()
|
private CommandSerializers()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final VariableWidthTimestampSerializer<TxnId> txnId = new VariableWidthTimestampSerializer<>(TxnId::fromValues);
|
public static final VariableWidthTimestampSerializer<TxnId> txnId = new VariableWidthTimestampSerializer<>(TxnId::fromValues, TxnId::fromBits, TxnId[]::new);
|
||||||
public static final VariableWidthTimestampSerializer<Timestamp> timestamp = new VariableWidthTimestampSerializer<>(Timestamp::fromValues);
|
public static final VariableWidthTimestampSerializer<Timestamp> timestamp = new VariableWidthTimestampSerializer<>(Timestamp::fromValues, Timestamp::fromBits, Timestamp[]::new);
|
||||||
public static final BallotSerializer ballot = new BallotSerializer(); // permits null
|
public static final BallotSerializer ballot = new BallotSerializer(); // permits null
|
||||||
public static final UnversionedSerializer<Txn.Kind> kind = EncodeAsVInt32.of(Txn.Kind.class);
|
public static final UnversionedSerializer<Txn.Kind> kind = EncodeAsVInt32.of(Txn.Kind.class);
|
||||||
public static final StoreParticipantsSerializer participants = new StoreParticipantsSerializer();
|
public static final StoreParticipantsSerializer participants = new StoreParticipantsSerializer();
|
||||||
|
|
@ -490,12 +496,9 @@ public class CommandSerializers
|
||||||
Invariants.require(EPOCH_SHIFT + Integer.bitCount(EPOCH_MASK) < 8);
|
Invariants.require(EPOCH_SHIFT + Integer.bitCount(EPOCH_MASK) < 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Factory<T extends Timestamp>
|
private final Timestamp.ValueFactory<T> factory;
|
||||||
{
|
private final Timestamp.RawFactory<T> rawFactory;
|
||||||
T create(long epoch, long hlc, int flags, Node.Id node);
|
private final IntFunction<T[]> allocator;
|
||||||
}
|
|
||||||
|
|
||||||
private final VariableWidthTimestampSerializer.Factory<T> factory;
|
|
||||||
|
|
||||||
T decodeSpecial(int encodingFlags)
|
T decodeSpecial(int encodingFlags)
|
||||||
{
|
{
|
||||||
|
|
@ -510,9 +513,11 @@ public class CommandSerializers
|
||||||
return NULL_BYTE;
|
return NULL_BYTE;
|
||||||
}
|
}
|
||||||
|
|
||||||
private VariableWidthTimestampSerializer(VariableWidthTimestampSerializer.Factory<T> factory)
|
private VariableWidthTimestampSerializer(Timestamp.ValueFactory<T> factory, Timestamp.RawFactory<T> rawFactory, IntFunction<T[]> allocator)
|
||||||
{
|
{
|
||||||
this.factory = factory;
|
this.factory = factory;
|
||||||
|
this.rawFactory = rawFactory;
|
||||||
|
this.allocator = allocator;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -546,6 +551,73 @@ public class CommandSerializers
|
||||||
out.writeLeastSignificantBytes(ts.node.id, nodeLength);
|
out.writeLeastSignificantBytes(ts.node.id, nodeLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void serializeArray(T[] ts, DataOutputPlus out) throws IOException
|
||||||
|
{
|
||||||
|
out.writeUnsignedVInt32(ts.length);
|
||||||
|
if (ts.length == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
long minEpoch = Long.MAX_VALUE, maxEpoch = 0;
|
||||||
|
long minHlc = Long.MAX_VALUE, maxHlc = 0;
|
||||||
|
int minFlags = 0xFFFF, maxFlags = 0;
|
||||||
|
int minNodeId = Integer.MAX_VALUE, maxNodeId = 0;
|
||||||
|
for (int i = 0; i < ts.length; i++)
|
||||||
|
{
|
||||||
|
T t = ts[i];
|
||||||
|
long epoch = t.epoch();
|
||||||
|
minEpoch = Math.min(epoch, minEpoch);
|
||||||
|
maxEpoch = Math.max(epoch, maxEpoch);
|
||||||
|
long hlc = t.hlc();
|
||||||
|
minHlc = Math.min(hlc, minHlc);
|
||||||
|
maxHlc = Math.max(hlc, maxHlc);
|
||||||
|
int flags = t.flags();
|
||||||
|
minFlags = Math.min(flags, minFlags);
|
||||||
|
maxFlags = Math.max(flags, maxFlags);
|
||||||
|
int nodeId = t.node.id;
|
||||||
|
minNodeId = Math.min(nodeId, minNodeId);
|
||||||
|
maxNodeId = Math.max(nodeId, maxNodeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
int epochBits = BitUtils.numberOfBitsToRepresent(maxEpoch - minEpoch);
|
||||||
|
int hlcBits = BitUtils.numberOfBitsToRepresent(maxHlc - minHlc);
|
||||||
|
int flagBits = BitUtils.numberOfBitsToRepresent(maxFlags - minFlags);
|
||||||
|
int nodeBits = BitUtils.numberOfBitsToRepresent(maxNodeId - minNodeId);
|
||||||
|
|
||||||
|
// we could pack these a bit more tightly if we wanted to
|
||||||
|
out.writeUnsignedVInt(minEpoch);
|
||||||
|
out.writeUnsignedVInt(minHlc);
|
||||||
|
out.writeUnsignedVInt32(minFlags);
|
||||||
|
out.writeUnsignedVInt32(minNodeId);
|
||||||
|
out.writeByte(epochBits);
|
||||||
|
out.writeByte(hlcBits);
|
||||||
|
out.writeByte(flagBits);
|
||||||
|
out.writeByte(nodeBits);
|
||||||
|
|
||||||
|
long finalMinEpoch = minEpoch;
|
||||||
|
SerializePacked.serializePacked((in, i) -> in[i].epoch() - finalMinEpoch, ts, 0, ts.length, maxEpoch - minEpoch, out);
|
||||||
|
long finalMinHlc = minHlc;
|
||||||
|
SerializePacked.serializePacked((in, i) -> in[i].hlc() - finalMinHlc, ts, 0, ts.length, maxHlc - minHlc, out);
|
||||||
|
long finalMinFlags = minFlags;
|
||||||
|
SerializePacked.serializePacked((in, i) -> in[i].flags() - finalMinFlags, ts, 0, ts.length, maxFlags - minFlags, out);
|
||||||
|
long finalMinNodeId = minNodeId;
|
||||||
|
SerializePacked.serializePacked((in, i) -> in[i].node.id - finalMinNodeId, ts, 0, ts.length, maxNodeId - minNodeId, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int flags(T ts)
|
||||||
|
{
|
||||||
|
long epoch = ts.epoch();
|
||||||
|
long hlc = ts.hlc();
|
||||||
|
int flags = ts.flags();
|
||||||
|
int epochLength = length(epoch, EPOCH_MIN_LENGTH);
|
||||||
|
int hlcLength = length(hlc, HLC_MIN_LENGTH);
|
||||||
|
int flagsLength = length(flags, FLAGS_MIN_LENGTH);
|
||||||
|
int nodeLength = length(ts.node.id, NODE_MIN_LENGTH);
|
||||||
|
return encodeLength(epochLength, EPOCH_SHIFT, EPOCH_MIN_LENGTH, EPOCH_MASK)
|
||||||
|
| encodeLength(hlcLength, HLC_SHIFT, HLC_MIN_LENGTH, HLC_MASK)
|
||||||
|
| encodeLength(flagsLength, FLAGS_SHIFT, FLAGS_MIN_LENGTH, FLAGS_MASK)
|
||||||
|
| encodeLength(nodeLength, NODE_SHIFT, NODE_MIN_LENGTH, NODE_MASK);
|
||||||
|
}
|
||||||
|
|
||||||
// exactly the same fundamental format as serialize(), only we interleave the length bits with the values, maintaining ordering
|
// exactly the same fundamental format as serialize(), only we interleave the length bits with the values, maintaining ordering
|
||||||
public <V> int serializeComparable(T ts, V dst, ValueAccessor<V> accessor, int offset)
|
public <V> int serializeComparable(T ts, V dst, ValueAccessor<V> accessor, int offset)
|
||||||
{
|
{
|
||||||
|
|
@ -635,11 +707,16 @@ public class CommandSerializers
|
||||||
int encodingFlags = in.readByte();
|
int encodingFlags = in.readByte();
|
||||||
if (encodingFlags < 0)
|
if (encodingFlags < 0)
|
||||||
return;
|
return;
|
||||||
|
in.skipBytesFully(lengthWithFlags(encodingFlags));
|
||||||
|
}
|
||||||
|
|
||||||
|
public int lengthWithFlags(int encodingFlags)
|
||||||
|
{
|
||||||
int epochLength = decodeLength(encodingFlags, EPOCH_SHIFT, EPOCH_MIN_LENGTH, EPOCH_MASK);
|
int epochLength = decodeLength(encodingFlags, EPOCH_SHIFT, EPOCH_MIN_LENGTH, EPOCH_MASK);
|
||||||
int hlcLength = decodeLength(encodingFlags, HLC_SHIFT, HLC_MIN_LENGTH, HLC_MASK);
|
int hlcLength = decodeLength(encodingFlags, HLC_SHIFT, HLC_MIN_LENGTH, HLC_MASK);
|
||||||
int flagsLength = decodeLength(encodingFlags, FLAGS_SHIFT, FLAGS_MIN_LENGTH, FLAGS_MASK);
|
int flagsLength = decodeLength(encodingFlags, FLAGS_SHIFT, FLAGS_MIN_LENGTH, FLAGS_MASK);
|
||||||
int nodeLength = decodeLength(encodingFlags, NODE_SHIFT, NODE_MIN_LENGTH, NODE_MASK);
|
int nodeLength = decodeLength(encodingFlags, NODE_SHIFT, NODE_MIN_LENGTH, NODE_MASK);
|
||||||
in.skipBytesFully(epochLength + hlcLength + flagsLength + nodeLength);
|
return epochLength + hlcLength + flagsLength + nodeLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -648,10 +725,59 @@ public class CommandSerializers
|
||||||
int encodingFlags = in.readByte();
|
int encodingFlags = in.readByte();
|
||||||
if (encodingFlags < 0)
|
if (encodingFlags < 0)
|
||||||
return decodeSpecial(encodingFlags);
|
return decodeSpecial(encodingFlags);
|
||||||
|
return deserializeFixed(encodingFlags, in);
|
||||||
|
}
|
||||||
|
|
||||||
|
public T[] deserializeArray(DataInputPlus in) throws IOException
|
||||||
|
{
|
||||||
|
int length = in.readUnsignedVInt32();
|
||||||
|
if (length == 0)
|
||||||
|
return allocator.apply(0);
|
||||||
|
|
||||||
|
// we could pack these a bit more tightly if we wanted to
|
||||||
|
long minEpoch = in.readUnsignedVInt();
|
||||||
|
long minHlc = in.readUnsignedVInt();
|
||||||
|
int minFlags = in.readUnsignedVInt32();
|
||||||
|
int minNodeId = in.readUnsignedVInt32();
|
||||||
|
int epochBits = in.readByte();
|
||||||
|
int hlcBits = in.readByte();
|
||||||
|
int flagBits = in.readByte();
|
||||||
|
int nodeBits = in.readByte();
|
||||||
|
|
||||||
|
long[] bits = ArrayBuffers.cachedLongs().getLongs(length * 2);
|
||||||
|
SerializePacked.deserializePacked((out, i, v) -> out[i*2] = Timestamp.epochMsb(minEpoch + v), bits, 0, length, mask(epochBits), in);
|
||||||
|
SerializePacked.deserializePacked((out, i, v) -> {
|
||||||
|
long hlc = minHlc + v;
|
||||||
|
out[i*2] |= Timestamp.hlcMsb(hlc);
|
||||||
|
out[i*2+1] = Timestamp.hlcLsb(hlc);
|
||||||
|
}, bits, 0, length, mask(hlcBits), in);
|
||||||
|
SerializePacked.deserializePacked((out, i, v) -> out[i*2 + 1] |= minFlags + v, bits, 0, length, mask(flagBits), in);
|
||||||
|
T[] ts = allocator.apply(length);
|
||||||
|
SerializePacked.deserializePacked((out, i, v) -> {
|
||||||
|
Node.Id id = new Node.Id(minNodeId + (int)v);
|
||||||
|
ts[i] = rawFactory.create(bits[i*2], bits[i*2 + 1], id);
|
||||||
|
}, bits, 0, length, mask(nodeBits), in);
|
||||||
|
ArrayBuffers.cachedLongs().forceDiscard(bits);
|
||||||
|
return ts;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static long mask(int bits)
|
||||||
|
{
|
||||||
|
return bits == 0 ? 0 : -1L >>> (64 - bits);
|
||||||
|
}
|
||||||
|
|
||||||
|
public T deserializeFixed(int encodingFlags, DataInputPlus in) throws IOException
|
||||||
|
{
|
||||||
|
Invariants.require(((byte)encodingFlags) >= 0);
|
||||||
int epochLength = decodeLength(encodingFlags, EPOCH_SHIFT, EPOCH_MIN_LENGTH, EPOCH_MASK);
|
int epochLength = decodeLength(encodingFlags, EPOCH_SHIFT, EPOCH_MIN_LENGTH, EPOCH_MASK);
|
||||||
int hlcLength = decodeLength(encodingFlags, HLC_SHIFT, HLC_MIN_LENGTH, HLC_MASK);
|
int hlcLength = decodeLength(encodingFlags, HLC_SHIFT, HLC_MIN_LENGTH, HLC_MASK);
|
||||||
int flagsLength = decodeLength(encodingFlags, FLAGS_SHIFT, FLAGS_MIN_LENGTH, FLAGS_MASK);
|
int flagsLength = decodeLength(encodingFlags, FLAGS_SHIFT, FLAGS_MIN_LENGTH, FLAGS_MASK);
|
||||||
int nodeLength = decodeLength(encodingFlags, NODE_SHIFT, NODE_MIN_LENGTH, NODE_MASK);
|
int nodeLength = decodeLength(encodingFlags, NODE_SHIFT, NODE_MIN_LENGTH, NODE_MASK);
|
||||||
|
return deserialize(epochLength, hlcLength, flagsLength, nodeLength, in);
|
||||||
|
}
|
||||||
|
|
||||||
|
private T deserialize(int epochLength, int hlcLength, int flagsLength, int nodeLength, DataInputPlus in) throws IOException
|
||||||
|
{
|
||||||
long epoch = in.readLeastSignificantBytes(epochLength);
|
long epoch = in.readLeastSignificantBytes(epochLength);
|
||||||
long hlc = in.readLeastSignificantBytes(hlcLength);
|
long hlc = in.readLeastSignificantBytes(hlcLength);
|
||||||
int flags = Math.toIntExact(in.readLeastSignificantBytes(flagsLength));
|
int flags = Math.toIntExact(in.readLeastSignificantBytes(flagsLength));
|
||||||
|
|
@ -728,6 +854,49 @@ public class CommandSerializers
|
||||||
return 1 + epochLength + hlcLength + flagsLength + nodeLength;
|
return 1 + epochLength + hlcLength + flagsLength + nodeLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public long serializedArraySize(T[] ts)
|
||||||
|
{
|
||||||
|
if (ts.length == 0)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
long minEpoch = Long.MAX_VALUE, maxEpoch = 0;
|
||||||
|
long minHlc = Long.MAX_VALUE, maxHlc = 0;
|
||||||
|
int minFlags = 0xFFFF, maxFlags = 0;
|
||||||
|
int minNodeId = Integer.MAX_VALUE, maxNodeId = 0;
|
||||||
|
for (int i = 0; i < ts.length; i++)
|
||||||
|
{
|
||||||
|
T t = ts[i];
|
||||||
|
long epoch = t.epoch();
|
||||||
|
minEpoch = Math.min(epoch, minEpoch);
|
||||||
|
maxEpoch = Math.max(epoch, maxEpoch);
|
||||||
|
long hlc = t.hlc();
|
||||||
|
minHlc = Math.min(hlc, minHlc);
|
||||||
|
maxHlc = Math.max(hlc, maxHlc);
|
||||||
|
int flags = t.flags();
|
||||||
|
minFlags = Math.min(flags, minFlags);
|
||||||
|
maxFlags = Math.max(flags, maxFlags);
|
||||||
|
int nodeId = t.node.id;
|
||||||
|
minNodeId = Math.min(nodeId, minNodeId);
|
||||||
|
maxNodeId = Math.max(nodeId, maxNodeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
int epochBits = BitUtils.numberOfBitsToRepresent(maxEpoch - minEpoch);
|
||||||
|
int hlcBits = BitUtils.numberOfBitsToRepresent(maxHlc - minHlc);
|
||||||
|
int flagBits = BitUtils.numberOfBitsToRepresent(maxFlags - minFlags);
|
||||||
|
int nodeBits = BitUtils.numberOfBitsToRepresent(maxNodeId - minNodeId);
|
||||||
|
|
||||||
|
return VIntCoding.sizeOfUnsignedVInt(ts.length)
|
||||||
|
+ VIntCoding.sizeOfUnsignedVInt(minEpoch)
|
||||||
|
+ VIntCoding.sizeOfUnsignedVInt(minHlc)
|
||||||
|
+ VIntCoding.sizeOfUnsignedVInt(minFlags)
|
||||||
|
+ VIntCoding.sizeOfUnsignedVInt(minNodeId)
|
||||||
|
+ 4
|
||||||
|
+ serializedPackedBitsSize(ts.length, epochBits)
|
||||||
|
+ serializedPackedBitsSize(ts.length, hlcBits)
|
||||||
|
+ serializedPackedBitsSize(ts.length, flagBits)
|
||||||
|
+ serializedPackedBitsSize(ts.length, nodeBits);
|
||||||
|
}
|
||||||
|
|
||||||
private static int length(long value, int minLength)
|
private static int length(long value, int minLength)
|
||||||
{
|
{
|
||||||
int length = ((64 + 7) - Long.numberOfLeadingZeros(value))/8;
|
int length = ((64 + 7) - Long.numberOfLeadingZeros(value))/8;
|
||||||
|
|
@ -747,6 +916,12 @@ public class CommandSerializers
|
||||||
return encoded << shift;
|
return encoded << shift;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static int reencodePartDecodedLength(int length, int shift, int mask)
|
||||||
|
{
|
||||||
|
Invariants.require(length <= mask);
|
||||||
|
return length << shift;
|
||||||
|
}
|
||||||
|
|
||||||
private static long packLength(int length, int shift, int minLength, int mask)
|
private static long packLength(int length, int shift, int minLength, int mask)
|
||||||
{
|
{
|
||||||
int encoded = length - minLength;
|
int encoded = length - minLength;
|
||||||
|
|
@ -758,6 +933,11 @@ public class CommandSerializers
|
||||||
{
|
{
|
||||||
return minLength + ((encodingFlags >>> shift) & mask);
|
return minLength + ((encodingFlags >>> shift) & mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static int maxPartDecoded(int flagsa, int flagsb, int shift, int mask)
|
||||||
|
{
|
||||||
|
return Math.max(((flagsa >>> shift) & mask), (flagsb >>> shift) & mask);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class BallotSerializer extends VariableWidthTimestampSerializer<Ballot>
|
public static class BallotSerializer extends VariableWidthTimestampSerializer<Ballot>
|
||||||
|
|
@ -766,7 +946,7 @@ public class CommandSerializers
|
||||||
private static final byte MAX_BYTE = (byte) 0x82;
|
private static final byte MAX_BYTE = (byte) 0x82;
|
||||||
private BallotSerializer()
|
private BallotSerializer()
|
||||||
{
|
{
|
||||||
super(Ballot::fromValues);
|
super(Ballot::fromValues, Ballot::fromBits, Ballot[]::new);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -923,9 +1103,9 @@ public class CommandSerializers
|
||||||
{
|
{
|
||||||
txnId.serialize(writes.txnId, out);
|
txnId.serialize(writes.txnId, out);
|
||||||
ExecuteAtSerializer.serialize(writes.txnId, writes.executeAt, out);
|
ExecuteAtSerializer.serialize(writes.txnId, writes.executeAt, out);
|
||||||
KeySerializers.seekables.serialize(writes.keys, out);
|
|
||||||
boolean hasWrite = writes.write != null;
|
boolean hasWrite = writes.write != null;
|
||||||
out.writeBoolean(hasWrite);
|
out.writeBoolean(hasWrite);
|
||||||
|
KeySerializers.seekables.serialize(writes.keys, out);
|
||||||
if (hasWrite)
|
if (hasWrite)
|
||||||
CommandSerializers.write.serialize(writes.write, writes.keys, out, version);
|
CommandSerializers.write.serialize(writes.write, writes.keys, out, version);
|
||||||
}
|
}
|
||||||
|
|
@ -935,14 +1115,28 @@ public class CommandSerializers
|
||||||
{
|
{
|
||||||
TxnId id = txnId.deserialize(in);
|
TxnId id = txnId.deserialize(in);
|
||||||
Timestamp executeAt = ExecuteAtSerializer.deserialize(id, in);
|
Timestamp executeAt = ExecuteAtSerializer.deserialize(id, in);
|
||||||
Seekables seekables = KeySerializers.seekables.deserialize(in);
|
|
||||||
boolean hasWrite = in.readBoolean();
|
boolean hasWrite = in.readBoolean();
|
||||||
|
Seekables seekables = KeySerializers.seekables.deserialize(in);
|
||||||
Write write = null;
|
Write write = null;
|
||||||
if (hasWrite)
|
if (hasWrite)
|
||||||
write = CommandSerializers.write.deserialize(seekables, in, version);
|
write = CommandSerializers.write.deserialize(seekables, in, version);
|
||||||
return new Writes(id, executeAt, seekables, write);
|
return new Writes(id, executeAt, seekables, write);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void skip(DataInputPlus in, Version version) throws IOException
|
||||||
|
{
|
||||||
|
txnId.skip(in);
|
||||||
|
ExecuteAtSerializer.skip(null, in);
|
||||||
|
boolean hasWrite = in.readBoolean();
|
||||||
|
if (hasWrite)
|
||||||
|
{
|
||||||
|
Seekables seekables = KeySerializers.seekables.deserialize(in);
|
||||||
|
CommandSerializers.write.skip(seekables, in, version);
|
||||||
|
}
|
||||||
|
else KeySerializers.seekables.skip(in);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long serializedSize(Writes writes, Version version)
|
public long serializedSize(Writes writes, Version version)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,6 @@ package org.apache.cassandra.service.accord.serializers;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
import com.google.common.primitives.Ints;
|
|
||||||
|
|
||||||
import accord.primitives.Deps;
|
import accord.primitives.Deps;
|
||||||
import accord.primitives.KeyDeps;
|
import accord.primitives.KeyDeps;
|
||||||
|
|
@ -30,6 +29,8 @@ import accord.primitives.Range;
|
||||||
import accord.primitives.RangeDeps;
|
import accord.primitives.RangeDeps;
|
||||||
import accord.primitives.RoutingKeys;
|
import accord.primitives.RoutingKeys;
|
||||||
import accord.primitives.TxnId;
|
import accord.primitives.TxnId;
|
||||||
|
import accord.utils.Invariants;
|
||||||
|
import accord.utils.VIntCoding;
|
||||||
import org.apache.cassandra.io.UnversionedSerializer;
|
import org.apache.cassandra.io.UnversionedSerializer;
|
||||||
import org.apache.cassandra.io.util.DataInputPlus;
|
import org.apache.cassandra.io.util.DataInputPlus;
|
||||||
import org.apache.cassandra.io.util.DataOutputPlus;
|
import org.apache.cassandra.io.util.DataOutputPlus;
|
||||||
|
|
@ -37,17 +38,21 @@ import org.apache.cassandra.service.accord.TokenRange;
|
||||||
import org.apache.cassandra.utils.NullableSerializer;
|
import org.apache.cassandra.utils.NullableSerializer;
|
||||||
|
|
||||||
import static accord.primitives.KeyDeps.SerializerSupport.keysToTxnIds;
|
import static accord.primitives.KeyDeps.SerializerSupport.keysToTxnIds;
|
||||||
import static accord.primitives.KeyDeps.SerializerSupport.keysToTxnIdsCount;
|
import static accord.primitives.KeyDeps.SerializerSupport.txnIdsToKeys;
|
||||||
|
import static accord.primitives.RangeDeps.SerializerSupport.ranges;
|
||||||
import static accord.primitives.RangeDeps.SerializerSupport.rangesToTxnIds;
|
import static accord.primitives.RangeDeps.SerializerSupport.rangesToTxnIds;
|
||||||
import static accord.primitives.RangeDeps.SerializerSupport.rangesToTxnIdsCount;
|
import static accord.primitives.RangeDeps.SerializerSupport.txnIdsToRanges;
|
||||||
import static org.apache.cassandra.db.TypeSizes.sizeofUnsignedVInt;
|
import static org.apache.cassandra.service.accord.serializers.SerializePacked.deserializePackedInts;
|
||||||
|
import static org.apache.cassandra.service.accord.serializers.SerializePacked.serializePackedInts;
|
||||||
|
import static org.apache.cassandra.service.accord.serializers.SerializePacked.serializedPackedIntsSize;
|
||||||
|
|
||||||
public class DepsSerializers
|
public class DepsSerializers
|
||||||
{
|
{
|
||||||
public static final UnversionedSerializer<Range> tokenRange;
|
public static final UnversionedSerializer<Range> tokenRange;
|
||||||
public static final DepsSerializer<Deps> deps;
|
public static final AbstractDepsSerializer<Deps> deps;
|
||||||
public static final UnversionedSerializer<Deps> nullableDeps;
|
public static final UnversionedSerializer<Deps> nullableDeps;
|
||||||
public static final DepsSerializer<PartialDeps> partialDeps;
|
public static final AbstractDepsSerializer<PartialDeps> partialDeps;
|
||||||
|
public static final AbstractDepsSerializer<PartialDeps> partialDepsById;
|
||||||
public static final UnversionedSerializer<PartialDeps> nullablePartialDeps;
|
public static final UnversionedSerializer<PartialDeps> nullablePartialDeps;
|
||||||
|
|
||||||
static
|
static
|
||||||
|
|
@ -58,14 +63,21 @@ public class DepsSerializers
|
||||||
deps = serializers.deps;
|
deps = serializers.deps;
|
||||||
nullableDeps = serializers.nullableDeps;
|
nullableDeps = serializers.nullableDeps;
|
||||||
partialDeps = serializers.partialDeps;
|
partialDeps = serializers.partialDeps;
|
||||||
|
partialDepsById = serializers.partialDepsById;
|
||||||
nullablePartialDeps = serializers.nullablePartialDeps;
|
nullablePartialDeps = serializers.nullablePartialDeps;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static abstract class DepsSerializer<D extends Deps> implements UnversionedSerializer<D>
|
public static abstract class AbstractDepsSerializer<D extends Deps> implements UnversionedSerializer<D>
|
||||||
{
|
{
|
||||||
protected UnversionedSerializer<Range> tokenRange;
|
static final int KEYS_BY_TXNID = 0x1;
|
||||||
public DepsSerializer(UnversionedSerializer<Range> tokenRange)
|
static final int RANGES_BY_TXNID = 0x2;
|
||||||
|
static final int FLAGS_SIZE = VIntCoding.sizeOfUnsignedVInt(KEYS_BY_TXNID | RANGES_BY_TXNID);
|
||||||
|
final boolean forceByTxnId;
|
||||||
|
|
||||||
|
protected final UnversionedSerializer<Range> tokenRange;
|
||||||
|
public AbstractDepsSerializer(boolean forceByTxnId, UnversionedSerializer<Range> tokenRange)
|
||||||
{
|
{
|
||||||
|
this.forceByTxnId = forceByTxnId;
|
||||||
this.tokenRange = tokenRange;
|
this.tokenRange = tokenRange;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -74,164 +86,225 @@ public class DepsSerializers
|
||||||
@Override
|
@Override
|
||||||
public void serialize(D deps, DataOutputPlus out) throws IOException
|
public void serialize(D deps, DataOutputPlus out) throws IOException
|
||||||
{
|
{
|
||||||
|
boolean keysByTxnId = forceByTxnId || deps.keyDeps.hasByTxnId();
|
||||||
|
boolean rangesByTxnId = forceByTxnId || deps.rangeDeps.hasByTxnId();
|
||||||
|
out.writeUnsignedVInt32((keysByTxnId ? KEYS_BY_TXNID : 0) | (rangesByTxnId ? RANGES_BY_TXNID : 0));
|
||||||
{
|
{
|
||||||
KeyDeps keyDeps = deps.keyDeps;
|
KeyDeps keyDeps = deps.keyDeps;
|
||||||
KeySerializers.routingKeys.serialize(keyDeps.keys(), out);
|
KeySerializers.routingKeys.serialize(keyDeps.keys(), out);
|
||||||
int txnIdCount = keyDeps.txnIdCount();
|
CommandSerializers.txnId.serializeArray(KeyDeps.SerializerSupport.txnIds(keyDeps), out);
|
||||||
out.writeUnsignedVInt32(txnIdCount);
|
if (keysByTxnId) serializePackedXtoY(txnIdsToKeys(keyDeps), keyDeps.txnIdCount(), keyDeps.keys().size(), out);
|
||||||
for (int i = 0; i < txnIdCount; i++)
|
else serializePackedXtoY(keysToTxnIds(keyDeps), keyDeps.keys().size(), keyDeps.txnIdCount(), out);
|
||||||
CommandSerializers.txnId.serialize(keyDeps.txnId(i), out);
|
|
||||||
|
|
||||||
int keysToTxnIdsCount = keysToTxnIdsCount(keyDeps);
|
|
||||||
out.writeUnsignedVInt32(keysToTxnIdsCount);
|
|
||||||
for (int i = 0; i < keysToTxnIdsCount; i++)
|
|
||||||
out.writeUnsignedVInt32(keysToTxnIds(keyDeps, i));
|
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
RangeDeps rangeDeps = deps.rangeDeps;
|
RangeDeps rangeDeps = deps.rangeDeps;
|
||||||
int rangeCount = rangeDeps.rangeCount();
|
KeySerializers.rangeArray.serialize(ranges(rangeDeps), out);
|
||||||
out.writeUnsignedVInt32(rangeCount);
|
CommandSerializers.txnId.serializeArray(RangeDeps.SerializerSupport.txnIds(rangeDeps), out);
|
||||||
for (int i = 0; i < rangeCount; i++)
|
if (rangesByTxnId) serializePackedXtoY(txnIdsToRanges(rangeDeps), rangeDeps.txnIdCount(), rangeDeps.rangeCount(), out);
|
||||||
tokenRange.serialize(rangeDeps.range(i), out);
|
else serializePackedXtoY(rangesToTxnIds(rangeDeps), rangeDeps.rangeCount(), rangeDeps.txnIdCount(), out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int txnIdCount = rangeDeps.txnIdCount();
|
private static void serializePackedXtoY(int[] xtoy, int xCount, int yCount, DataOutputPlus out) throws IOException
|
||||||
out.writeUnsignedVInt32(txnIdCount);
|
{
|
||||||
for (int i = 0; i < txnIdCount; i++)
|
out.writeUnsignedVInt32(xtoy.length);
|
||||||
CommandSerializers.txnId.serialize(rangeDeps.txnId(i), out);
|
|
||||||
|
|
||||||
int rangesToTxnIdsCount = rangesToTxnIdsCount(rangeDeps);
|
if ((xCount <= 1 || yCount <= 1) && (xtoy.length == xCount + yCount || xCount == 0 || yCount == 0))
|
||||||
out.writeUnsignedVInt32(rangesToTxnIdsCount);
|
{
|
||||||
for (int i = 0; i < rangesToTxnIdsCount; i++)
|
// no point serializing as can be directly inferred
|
||||||
out.writeUnsignedVInt32(rangesToTxnIds(rangeDeps, i));
|
if (Invariants.isParanoid())
|
||||||
|
{
|
||||||
|
if (xCount == 1)
|
||||||
|
{
|
||||||
|
Invariants.require(xtoy[0] == xtoy.length, "%d != %d", xtoy[0], xtoy.length);
|
||||||
|
for (int i = 0 ; i < yCount ; ++i) Invariants.require(xtoy[1 + i] == i, "%d != %d", xtoy[1 + i], i);
|
||||||
|
}
|
||||||
|
else if (yCount == 1)
|
||||||
|
{
|
||||||
|
for (int i = 0 ; i < xCount ; ++i) Invariants.require(xtoy[i] == xCount + i + 1, "%d != %d", xtoy[i], xCount + i + 1);
|
||||||
|
for (int i = xCount ; i < xtoy.length ; ++i) Invariants.require(xtoy[i] == 0, "%d != %d", xtoy[i], 0);
|
||||||
|
}
|
||||||
|
else if (yCount == 0)
|
||||||
|
{
|
||||||
|
for (int i = 0 ; i < xCount ; ++i) Invariants.require(xtoy[i] == xCount, "%d != %d", xtoy[i], xCount);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Invariants.require(xtoy.length == 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
serializePackedInts(xtoy, 0, xCount, xtoy.length, out);
|
||||||
|
serializePackedInts(xtoy, xCount, xtoy.length, yCount - 1, out);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public D deserialize(DataInputPlus in) throws IOException
|
public D deserialize(DataInputPlus in) throws IOException
|
||||||
{
|
{
|
||||||
|
int flags = in.readUnsignedVInt32();
|
||||||
KeyDeps keyDeps;
|
KeyDeps keyDeps;
|
||||||
{
|
{
|
||||||
RoutingKeys keys = KeySerializers.routingKeys.deserialize(in);
|
RoutingKeys keys = KeySerializers.routingKeys.deserialize(in);
|
||||||
int txnIdCount = in.readUnsignedVInt32();
|
TxnId[] txnIds = CommandSerializers.txnId.deserializeArray(in);
|
||||||
TxnId[] txnIds = new TxnId[txnIdCount];
|
int[] txnIdsToKeys = null, keysToTxnIds = null;
|
||||||
for (int i = 0; i < txnIdCount; i++)
|
if (0 != (flags & KEYS_BY_TXNID)) txnIdsToKeys = deserializePackedXtoY(txnIds.length, keys.size(), in);
|
||||||
txnIds[i] = CommandSerializers.txnId.deserialize(in);
|
else keysToTxnIds = deserializePackedXtoY(keys.size(), txnIds.length, in);
|
||||||
|
keyDeps = KeyDeps.SerializerSupport.create(keys, txnIds, keysToTxnIds, txnIdsToKeys);
|
||||||
int keysToTxnIdsCount = in.readUnsignedVInt32();
|
|
||||||
int[] keysToTxnIds = new int[keysToTxnIdsCount];
|
|
||||||
for (int i = 0; i < keysToTxnIdsCount; i++)
|
|
||||||
keysToTxnIds[i] = in.readUnsignedVInt32();
|
|
||||||
|
|
||||||
keyDeps = KeyDeps.SerializerSupport.create(keys, txnIds, keysToTxnIds);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RangeDeps rangeDeps;
|
RangeDeps rangeDeps;
|
||||||
{
|
{
|
||||||
int rangeCount = Ints.checkedCast(in.readUnsignedVInt32());
|
Range[] ranges = KeySerializers.rangeArray.deserialize(in);
|
||||||
Range[] ranges = new Range[rangeCount];
|
TxnId[] txnIds = CommandSerializers.txnId.deserializeArray(in);
|
||||||
for (int i = 0; i < rangeCount; i++)
|
int[] txnIdsToRanges = null, rangesToTxnIds = null;
|
||||||
ranges[i] = tokenRange.deserialize(in);
|
if (0 != (flags & RANGES_BY_TXNID)) txnIdsToRanges = deserializePackedXtoY(txnIds.length, ranges.length, in);
|
||||||
|
else rangesToTxnIds = deserializePackedXtoY(ranges.length, txnIds.length, in);
|
||||||
int txnIdCount = in.readUnsignedVInt32();
|
rangeDeps = RangeDeps.SerializerSupport.create(ranges, txnIds, rangesToTxnIds, txnIdsToRanges);
|
||||||
TxnId[] txnIds = new TxnId[txnIdCount];
|
|
||||||
for (int i = 0; i < txnIdCount; i++)
|
|
||||||
txnIds[i] = CommandSerializers.txnId.deserialize(in);
|
|
||||||
|
|
||||||
int rangesToTxnIdsCount = in.readUnsignedVInt32();
|
|
||||||
int[] rangesToTxnIds = new int[rangesToTxnIdsCount];
|
|
||||||
for (int i = 0; i < rangesToTxnIdsCount; i++)
|
|
||||||
rangesToTxnIds[i] = in.readUnsignedVInt32();
|
|
||||||
|
|
||||||
rangeDeps = RangeDeps.SerializerSupport.create(ranges, txnIds, rangesToTxnIds);
|
|
||||||
}
|
}
|
||||||
return deserialize(keyDeps, rangeDeps, in);
|
return deserialize(keyDeps, rangeDeps, in);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static int[] deserializePackedXtoY(int xCount, int yCount, DataInputPlus in) throws IOException
|
||||||
|
{
|
||||||
|
int length = in.readUnsignedVInt32();
|
||||||
|
int[] xtoy = new int[length];
|
||||||
|
|
||||||
|
if ((xCount <= 1 || yCount <= 1) && (xtoy.length == xCount + yCount || xCount == 0 || yCount == 0))
|
||||||
|
{
|
||||||
|
// no point serializing as can be directly inferred
|
||||||
|
if (xCount == 1)
|
||||||
|
{
|
||||||
|
xtoy[0] = xtoy.length;
|
||||||
|
for (int i = 0 ; i < yCount ; ++i)
|
||||||
|
xtoy[1 + i] = i;
|
||||||
|
}
|
||||||
|
else if (yCount == 1)
|
||||||
|
{
|
||||||
|
for (int i = 0 ; i < xCount ; ++i)
|
||||||
|
xtoy[i] = xCount + i + 1;
|
||||||
|
}
|
||||||
|
else if (yCount == 0)
|
||||||
|
{
|
||||||
|
for (int i = 0 ; i < xCount ; ++i)
|
||||||
|
xtoy[i] = xCount;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Invariants.require(length == 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
deserializePackedInts(xtoy, 0, xCount, xtoy.length, in);
|
||||||
|
deserializePackedInts(xtoy, xCount, xtoy.length, yCount - 1, in);
|
||||||
|
}
|
||||||
|
return xtoy;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long serializedSize(D deps)
|
public long serializedSize(D deps)
|
||||||
{
|
{
|
||||||
long size;
|
boolean keysByTxnId = forceByTxnId || deps.keyDeps.hasByTxnId();
|
||||||
|
boolean rangesByTxnId = forceByTxnId || deps.rangeDeps.hasByTxnId();
|
||||||
|
long size = FLAGS_SIZE;
|
||||||
{
|
{
|
||||||
KeyDeps keyDeps = deps.keyDeps;
|
KeyDeps keyDeps = deps.keyDeps;
|
||||||
size = KeySerializers.routingKeys.serializedSize(deps.keyDeps.keys());
|
size += KeySerializers.routingKeys.serializedSize(keyDeps.keys());
|
||||||
int txnIdCount = keyDeps.txnIdCount();
|
size += CommandSerializers.txnId.serializedArraySize(KeyDeps.SerializerSupport.txnIds(keyDeps));
|
||||||
size += sizeofUnsignedVInt(txnIdCount);
|
size += keysByTxnId ? serializedPackedXtoYSize(txnIdsToKeys(keyDeps), keyDeps.txnIdCount(), keyDeps.keys().size())
|
||||||
for (int i = 0; i < txnIdCount; i++)
|
: serializedPackedXtoYSize(keysToTxnIds(keyDeps), keyDeps.keys().size(), keyDeps.txnIdCount());
|
||||||
size += CommandSerializers.txnId.serializedSize(keyDeps.txnId(i));
|
|
||||||
|
|
||||||
int keysToTxnIdsCount = keysToTxnIdsCount(keyDeps);
|
|
||||||
size += sizeofUnsignedVInt(keysToTxnIdsCount);
|
|
||||||
for (int i = 0; i < keysToTxnIdsCount; i++)
|
|
||||||
size += sizeofUnsignedVInt(keysToTxnIds(keyDeps, i));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
RangeDeps rangeDeps = deps.rangeDeps;
|
RangeDeps rangeDeps = deps.rangeDeps;
|
||||||
int rangeCount = rangeDeps.rangeCount();
|
size += KeySerializers.rangeArray.serializedSize(ranges(rangeDeps));
|
||||||
size += sizeofUnsignedVInt(rangeCount);
|
size += CommandSerializers.txnId.serializedArraySize(RangeDeps.SerializerSupport.txnIds(rangeDeps));
|
||||||
for (int i = 0; i < rangeCount; ++i)
|
size += rangesByTxnId ? serializedPackedXtoYSize(txnIdsToRanges(rangeDeps), rangeDeps.txnIdCount(), rangeDeps.rangeCount())
|
||||||
size += tokenRange.serializedSize(rangeDeps.range(i));
|
: serializedPackedXtoYSize(rangesToTxnIds(rangeDeps), rangeDeps.rangeCount(), rangeDeps.txnIdCount());
|
||||||
|
|
||||||
int txnIdCount = rangeDeps.txnIdCount();
|
|
||||||
size += sizeofUnsignedVInt(txnIdCount);
|
|
||||||
for (int i = 0; i < txnIdCount; i++)
|
|
||||||
size += CommandSerializers.txnId.serializedSize(rangeDeps.txnId(i));
|
|
||||||
|
|
||||||
int rangesToTxnIdsCount = rangesToTxnIdsCount(rangeDeps);
|
|
||||||
size += sizeofUnsignedVInt(rangesToTxnIdsCount);
|
|
||||||
for (int i = 0; i < rangesToTxnIdsCount; i++)
|
|
||||||
size += sizeofUnsignedVInt(rangesToTxnIds(rangeDeps, i));
|
|
||||||
}
|
}
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static long serializedPackedXtoYSize(int[] xtoy, int xCount, int yCount)
|
||||||
|
{
|
||||||
|
long size = VIntCoding.sizeOfUnsignedVInt(xtoy.length);
|
||||||
|
if ((xCount <= 1 || yCount <= 1) && (xtoy.length == xCount + yCount || xCount == 0 || yCount == 0))
|
||||||
|
{
|
||||||
|
// no point serializing as can be directly inferred
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
size += serializedPackedIntsSize(xtoy, 0, xCount, xtoy.length);
|
||||||
|
size += serializedPackedIntsSize(xtoy, xCount, xtoy.length, yCount - 1);
|
||||||
|
}
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class PartialDepsSerializer extends AbstractDepsSerializer<PartialDeps>
|
||||||
|
{
|
||||||
|
public PartialDepsSerializer(boolean preferByTxnId, UnversionedSerializer<Range> tokenRange)
|
||||||
|
{
|
||||||
|
super(preferByTxnId, tokenRange);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
PartialDeps deserialize(KeyDeps keyDeps, RangeDeps rangeDeps, DataInputPlus in) throws IOException
|
||||||
|
{
|
||||||
|
Participants<?> covering = KeySerializers.participants.deserialize(in);
|
||||||
|
return new PartialDeps(covering, keyDeps, rangeDeps);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void serialize(PartialDeps partialDeps, DataOutputPlus out) throws IOException
|
||||||
|
{
|
||||||
|
super.serialize(partialDeps, out);
|
||||||
|
KeySerializers.participants.serialize(partialDeps.covering, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long serializedSize(PartialDeps partialDeps)
|
||||||
|
{
|
||||||
|
return super.serializedSize(partialDeps)
|
||||||
|
+ KeySerializers.participants.serializedSize(partialDeps.covering);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class DepsSerializer extends AbstractDepsSerializer<Deps>
|
||||||
|
{
|
||||||
|
public DepsSerializer(boolean preferByTxnId, UnversionedSerializer<Range> tokenRange)
|
||||||
|
{
|
||||||
|
super(preferByTxnId, tokenRange);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Deps deserialize(KeyDeps keyDeps, RangeDeps rangeDeps, DataInputPlus in) throws IOException
|
||||||
|
{
|
||||||
|
return new Deps(keyDeps, rangeDeps);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
public static class Impl
|
public static class Impl
|
||||||
{
|
{
|
||||||
final UnversionedSerializer<Range> tokenRange;
|
final UnversionedSerializer<Range> tokenRange;
|
||||||
final DepsSerializer<Deps> deps;
|
final DepsSerializer deps;
|
||||||
final UnversionedSerializer<Deps> nullableDeps;
|
final UnversionedSerializer<Deps> nullableDeps;
|
||||||
final DepsSerializer<PartialDeps> partialDeps;
|
final PartialDepsSerializer partialDeps;
|
||||||
|
final PartialDepsSerializer partialDepsById;
|
||||||
final UnversionedSerializer<PartialDeps> nullablePartialDeps;
|
final UnversionedSerializer<PartialDeps> nullablePartialDeps;
|
||||||
|
|
||||||
public Impl(UnversionedSerializer<Range> tokenRange)
|
public Impl(UnversionedSerializer<Range> tokenRange)
|
||||||
{
|
{
|
||||||
this.tokenRange = tokenRange;
|
this.tokenRange = tokenRange;
|
||||||
this.deps = new DepsSerializer<>(tokenRange)
|
this.deps = new DepsSerializer(false, tokenRange);
|
||||||
{
|
|
||||||
@Override
|
|
||||||
Deps deserialize(KeyDeps keyDeps, RangeDeps rangeDeps, DataInputPlus in)
|
|
||||||
{
|
|
||||||
return new Deps(keyDeps, rangeDeps);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
this.nullableDeps = NullableSerializer.wrap(deps);
|
this.nullableDeps = NullableSerializer.wrap(deps);
|
||||||
this.partialDeps = new DepsSerializer<>(tokenRange)
|
this.partialDeps = new PartialDepsSerializer(false, tokenRange);
|
||||||
{
|
this.partialDepsById = new PartialDepsSerializer(true, tokenRange);
|
||||||
@Override
|
|
||||||
PartialDeps deserialize(KeyDeps keyDeps, RangeDeps rangeDeps, DataInputPlus in) throws IOException
|
|
||||||
{
|
|
||||||
Participants<?> covering = KeySerializers.participants.deserialize(in);
|
|
||||||
return new PartialDeps(covering, keyDeps, rangeDeps);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void serialize(PartialDeps partialDeps, DataOutputPlus out) throws IOException
|
|
||||||
{
|
|
||||||
super.serialize(partialDeps, out);
|
|
||||||
KeySerializers.participants.serialize(partialDeps.covering, out);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long serializedSize(PartialDeps partialDeps)
|
|
||||||
{
|
|
||||||
return super.serializedSize(partialDeps)
|
|
||||||
+ KeySerializers.participants.serializedSize(partialDeps.covering);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
this.nullablePartialDeps = NullableSerializer.wrap(partialDeps);
|
this.nullablePartialDeps = NullableSerializer.wrap(partialDeps);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -73,16 +73,17 @@ public class KeySerializers
|
||||||
public static final AccordSearchableKeySerializer<RoutingKey> routingKey;
|
public static final AccordSearchableKeySerializer<RoutingKey> routingKey;
|
||||||
|
|
||||||
public static final UnversionedSerializer<RoutingKey> nullableRoutingKey;
|
public static final UnversionedSerializer<RoutingKey> nullableRoutingKey;
|
||||||
public static final AbstractSearchableRoutingKeysSerializer<RoutingKeys> routingKeys;
|
public static final AbstractKeyRoutablesSerializer<RoutingKeys> routingKeys;
|
||||||
public static final UnversionedSerializer<Keys> keys;
|
public static final UnversionedSerializer<Keys> keys;
|
||||||
|
|
||||||
public static final AbstractSearchableRoutingKeysSerializer<PartialKeyRoute> partialKeyRoute;
|
public static final AbstractKeyRoutablesSerializer<PartialKeyRoute> partialKeyRoute;
|
||||||
public static final AbstractSearchableRoutingKeysSerializer<FullKeyRoute> fullKeyRoute;
|
public static final AbstractKeyRoutablesSerializer<FullKeyRoute> fullKeyRoute;
|
||||||
|
|
||||||
public static final UnversionedSerializer<Range> range;
|
public static final UnversionedSerializer<Range> range;
|
||||||
public static final AbstractRangesSerializer<Ranges> ranges;
|
public static final AbstractRangesSerializer<Range[]> rangeArray;
|
||||||
public static final AbstractRangesSerializer<PartialRangeRoute> partialRangeRoute;
|
public static final AbstractRangeRoutablesSerializer<Ranges> ranges;
|
||||||
public static final AbstractRangesSerializer<FullRangeRoute> fullRangeRoute;
|
public static final AbstractRangeRoutablesSerializer<PartialRangeRoute> partialRangeRoute;
|
||||||
|
public static final AbstractRangeRoutablesSerializer<FullRangeRoute> fullRangeRoute;
|
||||||
|
|
||||||
public static final AbstractRoutablesSerializer<Route<?>> route;
|
public static final AbstractRoutablesSerializer<Route<?>> route;
|
||||||
public static final UnversionedSerializer<Route<?>> nullableRoute;
|
public static final UnversionedSerializer<Route<?>> nullableRoute;
|
||||||
|
|
@ -109,6 +110,7 @@ public class KeySerializers
|
||||||
fullKeyRoute = impl.fullKeyRoute;
|
fullKeyRoute = impl.fullKeyRoute;
|
||||||
|
|
||||||
range = impl.range;
|
range = impl.range;
|
||||||
|
rangeArray = impl.rangeArray;
|
||||||
ranges = impl.ranges;
|
ranges = impl.ranges;
|
||||||
partialRangeRoute = impl.partialRangeRoute;
|
partialRangeRoute = impl.partialRangeRoute;
|
||||||
fullRangeRoute = impl.fullRangeRoute;
|
fullRangeRoute = impl.fullRangeRoute;
|
||||||
|
|
@ -131,16 +133,17 @@ public class KeySerializers
|
||||||
final AccordSearchableKeySerializer<RoutingKey> routingKey;
|
final AccordSearchableKeySerializer<RoutingKey> routingKey;
|
||||||
|
|
||||||
final UnversionedSerializer<RoutingKey> nullableRoutingKey;
|
final UnversionedSerializer<RoutingKey> nullableRoutingKey;
|
||||||
final AbstractSearchableRoutingKeysSerializer<RoutingKeys> routingKeys;
|
final AbstractKeyRoutablesSerializer<RoutingKeys> routingKeys;
|
||||||
final UnversionedSerializer<Keys> keys;
|
final UnversionedSerializer<Keys> keys;
|
||||||
|
|
||||||
final AbstractSearchableRoutingKeysSerializer<PartialKeyRoute> partialKeyRoute;
|
final AbstractKeyRoutablesSerializer<PartialKeyRoute> partialKeyRoute;
|
||||||
final AbstractSearchableRoutingKeysSerializer<FullKeyRoute> fullKeyRoute;
|
final AbstractKeyRoutablesSerializer<FullKeyRoute> fullKeyRoute;
|
||||||
|
|
||||||
final UnversionedSerializer<Range> range;
|
final UnversionedSerializer<Range> range;
|
||||||
final AbstractRangesSerializer<Ranges> ranges;
|
final AbstractRangesSerializer<Range[]> rangeArray;
|
||||||
final AbstractRangesSerializer<PartialRangeRoute> partialRangeRoute;
|
final AbstractRangeRoutablesSerializer<Ranges> ranges;
|
||||||
final AbstractRangesSerializer<FullRangeRoute> fullRangeRoute;
|
final AbstractRangeRoutablesSerializer<PartialRangeRoute> partialRangeRoute;
|
||||||
|
final AbstractRangeRoutablesSerializer<FullRangeRoute> fullRangeRoute;
|
||||||
|
|
||||||
final AbstractRoutablesSerializer<Route<?>> route;
|
final AbstractRoutablesSerializer<Route<?>> route;
|
||||||
final UnversionedSerializer<Route<?>> nullableRoute;
|
final UnversionedSerializer<Route<?>> nullableRoute;
|
||||||
|
|
@ -169,7 +172,7 @@ public class KeySerializers
|
||||||
this.range = range;
|
this.range = range;
|
||||||
|
|
||||||
this.nullableRoutingKey = NullableSerializer.wrap(routingKey);
|
this.nullableRoutingKey = NullableSerializer.wrap(routingKey);
|
||||||
this.routingKeys = new AbstractSearchableRoutingKeysSerializer<>(routingKey)
|
this.routingKeys = new AbstractKeyRoutablesSerializer<>()
|
||||||
{
|
{
|
||||||
@Override RoutingKeys deserialize(DataInputPlus in, RoutingKey[] keys)
|
@Override RoutingKeys deserialize(DataInputPlus in, RoutingKey[] keys)
|
||||||
{
|
{
|
||||||
|
|
@ -185,7 +188,7 @@ public class KeySerializers
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.partialKeyRoute = new AbstractKeyRouteSerializer<>(routingKey)
|
this.partialKeyRoute = new AbstractKeyRouteSerializer<>()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
PartialKeyRoute construct(RoutingKey homeKey, RoutingKey[] keys)
|
PartialKeyRoute construct(RoutingKey homeKey, RoutingKey[] keys)
|
||||||
|
|
@ -194,7 +197,7 @@ public class KeySerializers
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.fullKeyRoute = new AbstractKeyRouteSerializer<>(routingKey)
|
this.fullKeyRoute = new AbstractKeyRouteSerializer<>()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
FullKeyRoute construct(RoutingKey homeKey, RoutingKey[] keys)
|
FullKeyRoute construct(RoutingKey homeKey, RoutingKey[] keys)
|
||||||
|
|
@ -203,7 +206,7 @@ public class KeySerializers
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.ranges = new AbstractRangesSerializer<>()
|
this.ranges = new AbstractRangeRoutablesSerializer<>()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public Ranges deserialize(DataInputPlus in, Range[] ranges)
|
public Ranges deserialize(DataInputPlus in, Range[] ranges)
|
||||||
|
|
@ -212,6 +215,12 @@ public class KeySerializers
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.rangeArray = new AbstractRangesSerializer<>()
|
||||||
|
{
|
||||||
|
@Override Range[] getArray(Range[] ranges) { return ranges; }
|
||||||
|
@Override public Range[] deserialize(DataInputPlus in, Range[] ranges) { return ranges; }
|
||||||
|
};
|
||||||
|
|
||||||
this.partialRangeRoute = new AbstractRangeRouteSerializer<>()
|
this.partialRangeRoute = new AbstractRangeRouteSerializer<>()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -250,20 +259,20 @@ public class KeySerializers
|
||||||
public static class AbstractRoutablesSerializer<RS extends Unseekables<?>> implements UnversionedSerializer<RS>
|
public static class AbstractRoutablesSerializer<RS extends Unseekables<?>> implements UnversionedSerializer<RS>
|
||||||
{
|
{
|
||||||
final TinyEnumSet<UnseekablesKind> permitted;
|
final TinyEnumSet<UnseekablesKind> permitted;
|
||||||
final AbstractSearchableRoutingKeysSerializer<RoutingKeys> routingKeys;
|
final AbstractKeyRoutablesSerializer<RoutingKeys> routingKeys;
|
||||||
final AbstractSearchableRoutingKeysSerializer<PartialKeyRoute> partialKeyRoute;
|
final AbstractKeyRoutablesSerializer<PartialKeyRoute> partialKeyRoute;
|
||||||
final AbstractSearchableRoutingKeysSerializer<FullKeyRoute> fullKeyRoute;
|
final AbstractKeyRoutablesSerializer<FullKeyRoute> fullKeyRoute;
|
||||||
final AbstractRangesSerializer<Ranges> ranges;
|
final AbstractRangeRoutablesSerializer<Ranges> ranges;
|
||||||
final AbstractRangesSerializer<PartialRangeRoute> partialRangeRoute;
|
final AbstractRangeRoutablesSerializer<PartialRangeRoute> partialRangeRoute;
|
||||||
final AbstractRangesSerializer<FullRangeRoute> fullRangeRoute;
|
final AbstractRangeRoutablesSerializer<FullRangeRoute> fullRangeRoute;
|
||||||
|
|
||||||
protected AbstractRoutablesSerializer(TinyEnumSet<UnseekablesKind> permitted,
|
protected AbstractRoutablesSerializer(TinyEnumSet<UnseekablesKind> permitted,
|
||||||
AbstractSearchableRoutingKeysSerializer<RoutingKeys> routingKeys,
|
AbstractKeyRoutablesSerializer<RoutingKeys> routingKeys,
|
||||||
AbstractSearchableRoutingKeysSerializer<PartialKeyRoute> partialKeyRoute,
|
AbstractKeyRoutablesSerializer<PartialKeyRoute> partialKeyRoute,
|
||||||
AbstractSearchableRoutingKeysSerializer<FullKeyRoute> fullKeyRoute,
|
AbstractKeyRoutablesSerializer<FullKeyRoute> fullKeyRoute,
|
||||||
AbstractRangesSerializer<Ranges> ranges,
|
AbstractRangeRoutablesSerializer<Ranges> ranges,
|
||||||
AbstractRangesSerializer<PartialRangeRoute> partialRangeRoute,
|
AbstractRangeRoutablesSerializer<PartialRangeRoute> partialRangeRoute,
|
||||||
AbstractRangesSerializer<FullRangeRoute> fullRangeRoute)
|
AbstractRangeRoutablesSerializer<FullRangeRoute> fullRangeRoute)
|
||||||
{
|
{
|
||||||
this.permitted = permitted;
|
this.permitted = permitted;
|
||||||
this.routingKeys = routingKeys;
|
this.routingKeys = routingKeys;
|
||||||
|
|
@ -527,6 +536,18 @@ public class KeySerializers
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void skip(DataInputPlus in) throws IOException
|
||||||
|
{
|
||||||
|
byte b = in.readByte();
|
||||||
|
switch (b)
|
||||||
|
{
|
||||||
|
default: throw new IOException("Corrupted input: expected byte 1 or 2, received " + b);
|
||||||
|
case 0: PartitionKey.serializer.skip(in); break;
|
||||||
|
case 1: TokenRange.serializer.skip(in); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long serializedSize(Seekable seekable)
|
public long serializedSize(Seekable seekable)
|
||||||
{
|
{
|
||||||
|
|
@ -544,9 +565,9 @@ public class KeySerializers
|
||||||
public static class AbstractSeekablesSerializer implements UnversionedSerializer<Seekables<?, ?>>
|
public static class AbstractSeekablesSerializer implements UnversionedSerializer<Seekables<?, ?>>
|
||||||
{
|
{
|
||||||
final UnversionedSerializer<Keys> keys;
|
final UnversionedSerializer<Keys> keys;
|
||||||
final AbstractRangesSerializer<Ranges> ranges;
|
final AbstractRangeRoutablesSerializer<Ranges> ranges;
|
||||||
|
|
||||||
public AbstractSeekablesSerializer(UnversionedSerializer<Keys> keys, AbstractRangesSerializer<Ranges> ranges)
|
public AbstractSeekablesSerializer(UnversionedSerializer<Keys> keys, AbstractRangeRoutablesSerializer<Ranges> ranges)
|
||||||
{
|
{
|
||||||
this.keys = keys;
|
this.keys = keys;
|
||||||
this.ranges = ranges;
|
this.ranges = ranges;
|
||||||
|
|
@ -646,7 +667,7 @@ public class KeySerializers
|
||||||
|
|
||||||
// this serializer is designed to permits using the collection in its serialized form with minimal in-memory state.
|
// this serializer is designed to permits using the collection in its serialized form with minimal in-memory state.
|
||||||
// it also saves some memory by avoiding duplicating prefixes (which happens to also assist faster lookups)
|
// it also saves some memory by avoiding duplicating prefixes (which happens to also assist faster lookups)
|
||||||
public abstract static class AbstractSearchableSerializer<K extends RoutableKey, R extends Routable, RS extends Routables<R>> extends IVersionedWithKeysSerializer.AbstractWithKeysSerializer implements UnversionedSerializer<RS>
|
public abstract static class AbstractSearchableSerializer<R extends Routable, RS> extends IVersionedWithKeysSerializer.AbstractWithKeysSerializer implements UnversionedSerializer<RS>
|
||||||
{
|
{
|
||||||
final IntFunction<R[]> allocate;
|
final IntFunction<R[]> allocate;
|
||||||
|
|
||||||
|
|
@ -675,29 +696,35 @@ public class KeySerializers
|
||||||
abstract int fixedKeyLengthForPrefix(Object prefix);
|
abstract int fixedKeyLengthForPrefix(Object prefix);
|
||||||
abstract int serializedSizeWithoutPrefix(R routable);
|
abstract int serializedSizeWithoutPrefix(R routable);
|
||||||
abstract void serializeWithoutPrefixOrLength(R routable, DataOutputPlus out) throws IOException;
|
abstract void serializeWithoutPrefixOrLength(R routable, DataOutputPlus out) throws IOException;
|
||||||
abstract void serializeOffsets(RS unseekables, int start, int end, DataOutputPlus out) throws IOException;
|
abstract void serializeOffsets(R[] keys, int start, int end, DataOutputPlus out) throws IOException;
|
||||||
|
|
||||||
abstract R deserializeWithPrefix(Object prefix, int length, DataInputPlus in) throws IOException;
|
abstract R deserializeWithPrefix(Object prefix, int length, DataInputPlus in) throws IOException;
|
||||||
abstract R deserializeWithPrefix(Object prefix, int lengthIndex, int[] lengths, DataInputPlus in) throws IOException;
|
abstract R deserializeWithPrefix(Object prefix, int lengthIndex, int[] lengths, DataInputPlus in) throws IOException;
|
||||||
|
|
||||||
|
abstract R[] getArray(RS routables);
|
||||||
abstract RS deserialize(DataInputPlus in, R[] keys) throws IOException;
|
abstract RS deserialize(DataInputPlus in, R[] keys) throws IOException;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long serializedSize(RS routables)
|
public long serializedSize(RS routables)
|
||||||
{
|
{
|
||||||
int count = routables.size();
|
return serializedArraySize(getArray(routables));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected long serializedArraySize(R[] rs)
|
||||||
|
{
|
||||||
|
int count = rs.length;
|
||||||
long size = TypeSizes.sizeofUnsignedVInt(count);
|
long size = TypeSizes.sizeofUnsignedVInt(count);
|
||||||
if (count == 0)
|
if (count == 0)
|
||||||
return size;
|
return size;
|
||||||
|
|
||||||
Object prefix = routables.get(0).prefix();
|
Object prefix = rs[0].prefix();
|
||||||
int prefixStart = 0;
|
int prefixStart = 0;
|
||||||
for (int i = 1 ; i <= count ; ++i)
|
for (int i = 1 ; i <= count ; ++i)
|
||||||
{
|
{
|
||||||
Object nextPrefix = null;
|
Object nextPrefix = null;
|
||||||
if (i < count)
|
if (i < count)
|
||||||
{
|
{
|
||||||
nextPrefix = routables.get(i).prefix();
|
nextPrefix = rs[i].prefix();
|
||||||
if (Objects.equals(prefix, nextPrefix))
|
if (Objects.equals(prefix, nextPrefix))
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -708,7 +735,7 @@ public class KeySerializers
|
||||||
if (fixedLength < 0)
|
if (fixedLength < 0)
|
||||||
{
|
{
|
||||||
size += 4L * recordCountToLengthCount(i - prefixStart);
|
size += 4L * recordCountToLengthCount(i - prefixStart);
|
||||||
size += serializedSizeOfKeysWithoutPrefix(routables, prefixStart, i);
|
size += serializedSizeOfKeysWithoutPrefix(rs, prefixStart, i);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -721,27 +748,27 @@ public class KeySerializers
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long serializedSubsetSize(RS keys, Routables<?> superset)
|
|
||||||
{
|
|
||||||
return serializedSubsetSizeInternal(keys, superset);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void serialize(RS keys, DataOutputPlus out) throws IOException
|
public void serialize(RS keys, DataOutputPlus out) throws IOException
|
||||||
{
|
{
|
||||||
int size = keys.size();
|
serializeArray(getArray(keys), out);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void serializeArray(R[] rs, DataOutputPlus out) throws IOException
|
||||||
|
{
|
||||||
|
int size = rs.length;
|
||||||
out.writeUnsignedVInt32(size);
|
out.writeUnsignedVInt32(size);
|
||||||
if (size == 0)
|
if (size == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Object prefix = keys.get(0).prefix();
|
Object prefix = rs[0].prefix();
|
||||||
int prefixStart = 0;
|
int prefixStart = 0;
|
||||||
for (int i = 1 ; i <= size ; ++i)
|
for (int i = 1 ; i <= size ; ++i)
|
||||||
{
|
{
|
||||||
Object nextPrefix = null;
|
Object nextPrefix = null;
|
||||||
if (i < size)
|
if (i < size)
|
||||||
{
|
{
|
||||||
nextPrefix = keys.get(i).prefix();
|
nextPrefix = rs[i].prefix();
|
||||||
if (Objects.equals(prefix, nextPrefix))
|
if (Objects.equals(prefix, nextPrefix))
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -750,30 +777,25 @@ public class KeySerializers
|
||||||
serializePrefix(prefix, out);
|
serializePrefix(prefix, out);
|
||||||
int fixedLength = fixedKeyLengthForPrefix(prefix);
|
int fixedLength = fixedKeyLengthForPrefix(prefix);
|
||||||
if (fixedLength < 0)
|
if (fixedLength < 0)
|
||||||
serializeOffsets(keys, prefixStart, i, out);
|
serializeOffsets(rs, prefixStart, i, out);
|
||||||
serializeKeysWithoutPrefix(keys, prefixStart, i, out);
|
serializeKeysWithoutPrefix(rs, prefixStart, i, out);
|
||||||
prefixStart = i;
|
prefixStart = i;
|
||||||
prefix = nextPrefix;
|
prefix = nextPrefix;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private long serializedSizeOfKeysWithoutPrefix(RS keys, int start, int end)
|
private long serializedSizeOfKeysWithoutPrefix(R[] keys, int start, int end)
|
||||||
{
|
{
|
||||||
long size = 0;
|
long size = 0;
|
||||||
for (int i = start; i < end; ++i)
|
for (int i = start; i < end; ++i)
|
||||||
size += serializedSizeWithoutPrefix(keys.get(i));
|
size += serializedSizeWithoutPrefix(keys[i]);
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void serializeKeysWithoutPrefix(RS keys, int start, int end, DataOutputPlus out) throws IOException
|
private void serializeKeysWithoutPrefix(R[] rs, int start, int end, DataOutputPlus out) throws IOException
|
||||||
{
|
{
|
||||||
for (int i = start; i < end; ++i)
|
for (int i = start; i < end; ++i)
|
||||||
serializeWithoutPrefixOrLength(keys.get(i), out);
|
serializeWithoutPrefixOrLength(rs[i], out);
|
||||||
}
|
|
||||||
|
|
||||||
public void serializeSubset(RS keys, Routables<?> superset, DataOutputPlus out) throws IOException
|
|
||||||
{
|
|
||||||
serializeSubsetInternal(keys, superset, out);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void skip(DataInputPlus in) throws IOException
|
public void skip(DataInputPlus in) throws IOException
|
||||||
|
|
@ -863,13 +885,19 @@ public class KeySerializers
|
||||||
|
|
||||||
// this serializer is designed to permits using the collection in its serialized form with minimal in-memory state.
|
// this serializer is designed to permits using the collection in its serialized form with minimal in-memory state.
|
||||||
// it also saves some memory by avoiding duplicating prefixes (which happens to also assist faster lookups)
|
// it also saves some memory by avoiding duplicating prefixes (which happens to also assist faster lookups)
|
||||||
public abstract static class AbstractSearchableRoutingKeysSerializer<KS extends AbstractUnseekableKeys> extends AbstractSearchableSerializer<RoutingKey, RoutingKey, KS> implements UnversionedSerializer<KS>
|
public abstract static class AbstractKeyRoutablesSerializer<KS extends AbstractUnseekableKeys> extends AbstractSearchableSerializer<RoutingKey, KS> implements UnversionedSerializer<KS>
|
||||||
{
|
{
|
||||||
public AbstractSearchableRoutingKeysSerializer(AccordSearchableKeySerializer<RoutingKey> serializer)
|
public AbstractKeyRoutablesSerializer()
|
||||||
{
|
{
|
||||||
super(RoutingKey[]::new);
|
super(RoutingKey[]::new);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
RoutingKey[] getArray(KS keys)
|
||||||
|
{
|
||||||
|
return keys.unsafeKeys();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
final int fixedKeyLengthForPrefix(Object prefix)
|
final int fixedKeyLengthForPrefix(Object prefix)
|
||||||
{
|
{
|
||||||
|
|
@ -895,12 +923,12 @@ public class KeySerializers
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
final void serializeOffsets(KS keys, int startIndex, int endIndex, DataOutputPlus out) throws IOException
|
final void serializeOffsets(RoutingKey[] keys, int startIndex, int endIndex, DataOutputPlus out) throws IOException
|
||||||
{
|
{
|
||||||
int endOffset = 0;
|
int endOffset = 0;
|
||||||
for (int i = startIndex; i < endIndex; ++i)
|
for (int i = startIndex; i < endIndex; ++i)
|
||||||
{
|
{
|
||||||
endOffset += serializedSizeWithoutPrefix(keys.get(i));
|
endOffset += serializedSizeWithoutPrefix(keys[i]);
|
||||||
out.writeInt(endOffset);
|
out.writeInt(endOffset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -922,13 +950,23 @@ public class KeySerializers
|
||||||
RoutingKey[] keys = deserializeSubset(superset, in, (ks, s) -> ks == null ? s.unsafeKeys() : ks, RoutingKey[]::new);
|
RoutingKey[] keys = deserializeSubset(superset, in, (ks, s) -> ks == null ? s.unsafeKeys() : ks, RoutingKey[]::new);
|
||||||
return deserialize(in, keys);
|
return deserialize(in, keys);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public long serializedSubsetSize(KS keys, Routables<?> superset)
|
||||||
|
{
|
||||||
|
return serializedSubsetSizeInternal(keys, superset);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void serializeSubset(KS keys, Routables<?> superset, DataOutputPlus out) throws IOException
|
||||||
|
{
|
||||||
|
serializeSubsetInternal(keys, superset, out);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract static class AbstractKeyRouteSerializer<KS extends KeyRoute> extends AbstractSearchableRoutingKeysSerializer<KS>
|
public abstract static class AbstractKeyRouteSerializer<KS extends KeyRoute> extends AbstractKeyRoutablesSerializer<KS>
|
||||||
{
|
{
|
||||||
public AbstractKeyRouteSerializer(AccordSearchableKeySerializer<RoutingKey> serializer)
|
public AbstractKeyRouteSerializer()
|
||||||
{
|
{
|
||||||
super(serializer);
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract KS construct(RoutingKey homeKey, RoutingKey[] keys);
|
abstract KS construct(RoutingKey homeKey, RoutingKey[] keys);
|
||||||
|
|
@ -1006,7 +1044,7 @@ public class KeySerializers
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract static class AbstractRangesSerializer<RS extends AbstractRanges> extends AbstractSearchableSerializer<RoutingKey, Range, RS> implements UnversionedSerializer<RS>
|
public abstract static class AbstractRangesSerializer<RS> extends AbstractSearchableSerializer<Range, RS> implements UnversionedSerializer<RS>
|
||||||
{
|
{
|
||||||
public AbstractRangesSerializer()
|
public AbstractRangesSerializer()
|
||||||
{
|
{
|
||||||
|
|
@ -1040,12 +1078,12 @@ public class KeySerializers
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
final void serializeOffsets(RS ranges, int startIndex, int endIndex, DataOutputPlus out) throws IOException
|
final void serializeOffsets(Range[] ranges, int startIndex, int endIndex, DataOutputPlus out) throws IOException
|
||||||
{
|
{
|
||||||
int endOffset = 0;
|
int endOffset = 0;
|
||||||
for (int i = startIndex; i < endIndex; ++i)
|
for (int i = startIndex; i < endIndex; ++i)
|
||||||
{
|
{
|
||||||
Range r = ranges.get(i);
|
Range r = ranges[i];
|
||||||
endOffset += routingKey.serializedSizeWithoutPrefix(r.start());
|
endOffset += routingKey.serializedSizeWithoutPrefix(r.start());
|
||||||
out.writeInt(endOffset);
|
out.writeInt(endOffset);
|
||||||
endOffset += routingKey.serializedSizeWithoutPrefix(r.end());
|
endOffset += routingKey.serializedSizeWithoutPrefix(r.end());
|
||||||
|
|
@ -1068,6 +1106,25 @@ public class KeySerializers
|
||||||
RoutingKey end = routingKey.deserializeWithPrefix(prefix, lengths[lengthIndex * 2 + 1], in);
|
RoutingKey end = routingKey.deserializeWithPrefix(prefix, lengths[lengthIndex * 2 + 1], in);
|
||||||
return start.rangeFactory().newRange(start, end);
|
return start.rangeFactory().newRange(start, end);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract static class AbstractRangeRoutablesSerializer<RS extends AbstractRanges> extends AbstractRangesSerializer<RS> implements UnversionedSerializer<RS>
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
Range[] getArray(RS ranges)
|
||||||
|
{
|
||||||
|
return ranges.unsafeRanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
public long serializedSubsetSize(RS ranges, Routables<?> superset)
|
||||||
|
{
|
||||||
|
return serializedSubsetSizeInternal(ranges, superset);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void serializeSubset(RS ranges, Routables<?> superset, DataOutputPlus out) throws IOException
|
||||||
|
{
|
||||||
|
serializeSubsetInternal(ranges, superset, out);
|
||||||
|
}
|
||||||
|
|
||||||
public RS deserializeSubset(AbstractRanges superset, DataInputPlus in) throws IOException
|
public RS deserializeSubset(AbstractRanges superset, DataInputPlus in) throws IOException
|
||||||
{
|
{
|
||||||
|
|
@ -1076,7 +1133,7 @@ public class KeySerializers
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract static class AbstractRangeRouteSerializer<RS extends RangeRoute> extends AbstractRangesSerializer<RS>
|
public abstract static class AbstractRangeRouteSerializer<RS extends RangeRoute> extends AbstractRangeRoutablesSerializer<RS>
|
||||||
{
|
{
|
||||||
public AbstractRangeRouteSerializer()
|
public AbstractRangeRouteSerializer()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -46,23 +46,31 @@ import org.apache.cassandra.io.util.DataInputPlus;
|
||||||
import org.apache.cassandra.io.util.DataOutputPlus;
|
import org.apache.cassandra.io.util.DataOutputPlus;
|
||||||
import org.apache.cassandra.service.accord.serializers.CommandSerializers.ExecuteAtSerializer;
|
import org.apache.cassandra.service.accord.serializers.CommandSerializers.ExecuteAtSerializer;
|
||||||
import org.apache.cassandra.service.accord.serializers.TxnRequestSerializer.WithUnsyncedSerializer;
|
import org.apache.cassandra.service.accord.serializers.TxnRequestSerializer.WithUnsyncedSerializer;
|
||||||
|
import org.apache.cassandra.utils.vint.VIntCoding;
|
||||||
|
|
||||||
import static accord.messages.BeginRecovery.RecoverReply.Kind.Ok;
|
import static accord.messages.BeginRecovery.RecoverReply.Kind.Ok;
|
||||||
import static org.apache.cassandra.utils.NullableSerializer.deserializeNullable;
|
|
||||||
import static org.apache.cassandra.utils.NullableSerializer.serializeNullable;
|
|
||||||
import static org.apache.cassandra.utils.NullableSerializer.serializedNullableSize;
|
|
||||||
|
|
||||||
public class RecoverySerializers
|
public class RecoverySerializers
|
||||||
{
|
{
|
||||||
|
static final int HAS_ROUTE = 0x1;
|
||||||
|
static final int HAS_EXECUTE_AT_EPOCH = 0x2;
|
||||||
|
static final int IS_FAST_PATH_DECIDED = 0x4;
|
||||||
|
static final int SIZE_OF_FLAGS = VIntCoding.computeUnsignedVIntSize(HAS_ROUTE | HAS_EXECUTE_AT_EPOCH | IS_FAST_PATH_DECIDED);
|
||||||
public static final IVersionedSerializer<BeginRecovery> request = new WithUnsyncedSerializer<BeginRecovery>()
|
public static final IVersionedSerializer<BeginRecovery> request = new WithUnsyncedSerializer<BeginRecovery>()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void serializeBody(BeginRecovery recover, DataOutputPlus out, Version version) throws IOException
|
public void serializeBody(BeginRecovery recover, DataOutputPlus out, Version version) throws IOException
|
||||||
{
|
{
|
||||||
CommandSerializers.partialTxn.serialize(recover.partialTxn, out, version);
|
CommandSerializers.partialTxn.serialize(recover.partialTxn, out, version);
|
||||||
|
int flags = (recover.route != null ? HAS_ROUTE : 0)
|
||||||
|
| (recover.executeAtOrTxnIdEpoch != recover.txnId.epoch() ? HAS_EXECUTE_AT_EPOCH : 0)
|
||||||
|
| (recover.isFastPathDecided ? IS_FAST_PATH_DECIDED : 0);
|
||||||
CommandSerializers.ballot.serialize(recover.ballot, out);
|
CommandSerializers.ballot.serialize(recover.ballot, out);
|
||||||
serializeNullable(recover.route, out, KeySerializers.fullRoute);
|
out.writeUnsignedVInt32(flags);
|
||||||
out.writeUnsignedVInt(recover.executeAtOrTxnIdEpoch - recover.txnId.epoch());
|
if (recover.route != null)
|
||||||
|
KeySerializers.fullRoute.serialize(recover.route, out);
|
||||||
|
if (0 != (flags & HAS_EXECUTE_AT_EPOCH))
|
||||||
|
out.writeUnsignedVInt(recover.executeAtOrTxnIdEpoch - recover.txnId.epoch());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -70,9 +78,15 @@ public class RecoverySerializers
|
||||||
{
|
{
|
||||||
PartialTxn partialTxn = CommandSerializers.partialTxn.deserialize(in, version);
|
PartialTxn partialTxn = CommandSerializers.partialTxn.deserialize(in, version);
|
||||||
Ballot ballot = CommandSerializers.ballot.deserialize(in);
|
Ballot ballot = CommandSerializers.ballot.deserialize(in);
|
||||||
@Nullable FullRoute<?> route = deserializeNullable(in, KeySerializers.fullRoute);
|
int flags = in.readUnsignedVInt32();
|
||||||
long executeAtOrTxnIdEpoch = in.readUnsignedVInt32() + txnId.epoch();
|
FullRoute<?> route = null;
|
||||||
return BeginRecovery.SerializationSupport.create(txnId, scope, waitForEpoch, minEpoch, partialTxn, ballot, route, executeAtOrTxnIdEpoch);
|
if (0 != (flags & HAS_ROUTE))
|
||||||
|
route = KeySerializers.fullRoute.deserialize(in);
|
||||||
|
long executeAtOrTxnIdEpoch = txnId.epoch();
|
||||||
|
if (0 != (flags & HAS_EXECUTE_AT_EPOCH))
|
||||||
|
executeAtOrTxnIdEpoch += in.readUnsignedVInt32();
|
||||||
|
boolean isFastPathDecided = 0 != (flags & IS_FAST_PATH_DECIDED);
|
||||||
|
return BeginRecovery.SerializationSupport.create(txnId, scope, waitForEpoch, minEpoch, partialTxn, ballot, route, executeAtOrTxnIdEpoch, isFastPathDecided);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -80,8 +94,9 @@ public class RecoverySerializers
|
||||||
{
|
{
|
||||||
return CommandSerializers.partialTxn.serializedSize(recover.partialTxn, version)
|
return CommandSerializers.partialTxn.serializedSize(recover.partialTxn, version)
|
||||||
+ CommandSerializers.ballot.serializedSize(recover.ballot)
|
+ CommandSerializers.ballot.serializedSize(recover.ballot)
|
||||||
+ serializedNullableSize(recover.route, KeySerializers.fullRoute)
|
+ SIZE_OF_FLAGS
|
||||||
+ TypeSizes.sizeofUnsignedVInt(recover.executeAtOrTxnIdEpoch - recover.txnId.epoch());
|
+ (recover.route == null ? 0 : KeySerializers.fullRoute.serializedSize(recover.route))
|
||||||
|
+ (recover.executeAtOrTxnIdEpoch == recover.txnId.epoch() ? 0 : TypeSizes.sizeofUnsignedVInt(recover.executeAtOrTxnIdEpoch - recover.txnId.epoch()));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,145 @@
|
||||||
|
/*
|
||||||
|
* 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.service.accord.serializers;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import accord.utils.BitUtils;
|
||||||
|
import accord.utils.Invariants;
|
||||||
|
import net.nicoulaj.compilecommand.annotations.Inline;
|
||||||
|
import org.apache.cassandra.io.util.DataInputPlus;
|
||||||
|
import org.apache.cassandra.io.util.DataOutputPlus;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A set of simple utilities to quickly serialize/deserialize arrays/lists of values that each require <= 64 bits to represent.
|
||||||
|
* These are packed into an "array" of fixed bit width, so that the total size consumed is ceil((bits*elements)/8).
|
||||||
|
* This can (in future) be read directly without deserialization, by indexing into the byte stream directly.
|
||||||
|
*/
|
||||||
|
public class SerializePacked
|
||||||
|
{
|
||||||
|
public static void serializePackedInts(int[] vs, int from, int to, int max, DataOutputPlus out) throws IOException
|
||||||
|
{
|
||||||
|
serializePacked((in, i) -> in[i], vs, from, to, max, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void deserializePackedInts(int[] vs, int from, int to, int max, DataInputPlus in) throws IOException
|
||||||
|
{
|
||||||
|
deserializePacked((out, i, v) -> out[i] = (int)v, vs, from, to, max, in);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long serializedPackedIntsSize(int[] vs, int from, int to, int max)
|
||||||
|
{
|
||||||
|
return serializedPackedSize(to - from, max);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface SerializeAdapter<In>
|
||||||
|
{
|
||||||
|
long get(In in, int i);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Inline
|
||||||
|
public static <In> void serializePacked(SerializeAdapter<In> adapter, In in, int from, int to, long max, DataOutputPlus out) throws IOException
|
||||||
|
{
|
||||||
|
int bitsPerEntry = BitUtils.numberOfBitsToRepresent(max);
|
||||||
|
if (bitsPerEntry == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
long buffer = 0L;
|
||||||
|
int bufferCount = 0;
|
||||||
|
for (int i = from; i < to; i++)
|
||||||
|
{
|
||||||
|
long v = adapter.get(in, i);
|
||||||
|
Invariants.require(v <= max);
|
||||||
|
buffer |= v << bufferCount;
|
||||||
|
bufferCount = bufferCount + bitsPerEntry;
|
||||||
|
if (bufferCount >= 64)
|
||||||
|
{
|
||||||
|
out.writeLong(buffer);
|
||||||
|
bufferCount -= 64;
|
||||||
|
buffer = v >>> (bitsPerEntry - bufferCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (bufferCount > 0)
|
||||||
|
out.writeLeastSignificantBytes(buffer, (bufferCount + 7) / 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface DeserializeAdapter<Out>
|
||||||
|
{
|
||||||
|
void accept(Out out, int i, long v);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Inline
|
||||||
|
public static <Out> void deserializePacked(DeserializeAdapter<Out> consumer, Out out, int from, int to, long max, DataInputPlus in) throws IOException
|
||||||
|
{
|
||||||
|
int bitsPerEntry = BitUtils.numberOfBitsToRepresent(max);
|
||||||
|
if (bitsPerEntry == 0)
|
||||||
|
{
|
||||||
|
for (int i = from; i < to ; ++i)
|
||||||
|
consumer.accept(out, i, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
long mask = -1L >>> (64 - bitsPerEntry);
|
||||||
|
int remainingBytes = (bitsPerEntry * (to - from) + 7) / 8;
|
||||||
|
long buffer = 0L;
|
||||||
|
int bufferCount = 0;
|
||||||
|
for (int i = from; i < to; i++)
|
||||||
|
{
|
||||||
|
long v = buffer & mask;
|
||||||
|
if (bufferCount >= bitsPerEntry)
|
||||||
|
{
|
||||||
|
bufferCount -= bitsPerEntry;
|
||||||
|
buffer >>>= bitsPerEntry;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int newBufferCount;
|
||||||
|
if (remainingBytes >= 8)
|
||||||
|
{
|
||||||
|
buffer = in.readLong();
|
||||||
|
newBufferCount = 64;
|
||||||
|
remainingBytes -= 8;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Invariants.require(remainingBytes > 0);
|
||||||
|
newBufferCount = remainingBytes * 8;
|
||||||
|
buffer = in.readLeastSignificantBytes(remainingBytes);
|
||||||
|
remainingBytes = 0;
|
||||||
|
}
|
||||||
|
int readExtra = bitsPerEntry - bufferCount;
|
||||||
|
long extraBits = buffer & (mask >>> bufferCount);
|
||||||
|
v |= extraBits << bufferCount;
|
||||||
|
bufferCount = newBufferCount - readExtra;
|
||||||
|
buffer >>>= readExtra;
|
||||||
|
}
|
||||||
|
Invariants.require(v <= max);
|
||||||
|
consumer.accept(out, i, v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long serializedPackedSize(int count, long max)
|
||||||
|
{
|
||||||
|
return serializedPackedBitsSize(count, BitUtils.numberOfBitsToRepresent(max));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long serializedPackedBitsSize(int count, int bitsPerEntry)
|
||||||
|
{
|
||||||
|
return ((long)bitsPerEntry * count + 7)/8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -134,6 +134,11 @@ public class TableMetadatasAndKeys extends IVersionedWithKeysSerializer.Abstract
|
||||||
return (Keys)deserializeSubsetInternal(this.keys, in);
|
return (Keys)deserializeSubsetInternal(this.keys, in);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void skipKeys(DataInputPlus in) throws IOException
|
||||||
|
{
|
||||||
|
skipSubsetInternal(this.keys.size(), in);
|
||||||
|
}
|
||||||
|
|
||||||
public void serializeSeekable(Seekable seekable, DataOutputPlus out) throws IOException
|
public void serializeSeekable(Seekable seekable, DataOutputPlus out) throws IOException
|
||||||
{
|
{
|
||||||
int index = keys.indexOf(seekable);
|
int index = keys.indexOf(seekable);
|
||||||
|
|
@ -162,6 +167,12 @@ public class TableMetadatasAndKeys extends IVersionedWithKeysSerializer.Abstract
|
||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void skipSeekable(DataInputPlus in) throws IOException
|
||||||
|
{
|
||||||
|
int offset = in.readUnsignedVInt32();
|
||||||
|
if (offset <= 0) KeySerializers.seekable.skip(in);
|
||||||
|
}
|
||||||
|
|
||||||
public PartitionKey deserializeKey(DataInputPlus in) throws IOException
|
public PartitionKey deserializeKey(DataInputPlus in) throws IOException
|
||||||
{
|
{
|
||||||
int offset = in.readUnsignedVInt32();
|
int offset = in.readUnsignedVInt32();
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ package org.apache.cassandra.service.accord.serializers;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import accord.impl.CommandChange.WaitingOnProvider;
|
import accord.impl.CommandChange.WaitingOnBitSets;
|
||||||
import accord.local.Command;
|
import accord.local.Command;
|
||||||
import accord.local.Command.WaitingOn;
|
import accord.local.Command.WaitingOn;
|
||||||
import accord.primitives.PartialDeps;
|
import accord.primitives.PartialDeps;
|
||||||
|
|
@ -57,21 +57,18 @@ public class WaitingOnSerializer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final class Provider implements WaitingOnProvider
|
public static final class WaitingOnBitSetsAndLength extends WaitingOnBitSets
|
||||||
{
|
{
|
||||||
final ImmutableBitSet waitingOn, appliedOrInvalidated;
|
|
||||||
final int waitingOnLength, appliedOrInvalidatedLength;
|
final int waitingOnLength, appliedOrInvalidatedLength;
|
||||||
|
|
||||||
public Provider(ImmutableBitSet waitingOn, ImmutableBitSet appliedOrInvalidated, int waitingOnLength, int appliedOrInvalidatedLength)
|
public WaitingOnBitSetsAndLength(ImmutableBitSet waitingOn, ImmutableBitSet appliedOrInvalidated, int waitingOnLength, int appliedOrInvalidatedLength)
|
||||||
{
|
{
|
||||||
this.waitingOn = waitingOn;
|
super(waitingOn, appliedOrInvalidated);
|
||||||
this.appliedOrInvalidated = appliedOrInvalidated;
|
|
||||||
this.waitingOnLength = waitingOnLength;
|
this.waitingOnLength = waitingOnLength;
|
||||||
this.appliedOrInvalidatedLength = appliedOrInvalidatedLength;
|
this.appliedOrInvalidatedLength = appliedOrInvalidatedLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public WaitingOn construct(PartialDeps deps, Timestamp executeAtLeast, long uniqueHlc)
|
||||||
public WaitingOn provide(TxnId txnId, PartialDeps deps, Timestamp executeAtLeast, long uniqueHlc)
|
|
||||||
{
|
{
|
||||||
Invariants.nonNull(deps);
|
Invariants.nonNull(deps);
|
||||||
RoutingKeys keys = deps.keyDeps.keys();
|
RoutingKeys keys = deps.keyDeps.keys();
|
||||||
|
|
@ -98,7 +95,7 @@ public class WaitingOnSerializer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static WaitingOnProvider deserializeProvider(TxnId txnId, DataInputPlus in) throws IOException
|
public static WaitingOnBitSets deserializeBitSets(TxnId txnId, DataInputPlus in) throws IOException
|
||||||
{
|
{
|
||||||
ImmutableBitSet waitingOn, appliedOrInvalidated = null;
|
ImmutableBitSet waitingOn, appliedOrInvalidated = null;
|
||||||
int waitingOnLength, appliedOrInvalidatedLength = 0;
|
int waitingOnLength, appliedOrInvalidatedLength = 0;
|
||||||
|
|
@ -110,7 +107,7 @@ public class WaitingOnSerializer
|
||||||
appliedOrInvalidated = deserialize(appliedOrInvalidatedLength, in);
|
appliedOrInvalidated = deserialize(appliedOrInvalidatedLength, in);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Provider(waitingOn, appliedOrInvalidated, waitingOnLength, appliedOrInvalidatedLength);
|
return new WaitingOnBitSetsAndLength(waitingOn, appliedOrInvalidated, waitingOnLength, appliedOrInvalidatedLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void skip(TxnId txnId, DataInputPlus in) throws IOException
|
public static void skip(TxnId txnId, DataInputPlus in) throws IOException
|
||||||
|
|
|
||||||
|
|
@ -121,6 +121,13 @@ public abstract class AccordUpdate implements Update
|
||||||
return (AccordUpdate) serializerFor(kind).deserialize(tablesAndKeys, in, version);
|
return (AccordUpdate) serializerFor(kind).deserialize(tablesAndKeys, in, version);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void skip(TableMetadatasAndKeys tablesAndKeys, DataInputPlus in, Version version) throws IOException
|
||||||
|
{
|
||||||
|
Kind kind = Kind.valueOf(in.readByte());
|
||||||
|
serializerFor(kind).skip(tablesAndKeys, in, version);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long serializedSize(AccordUpdate update, TableMetadatasAndKeys tablesAndKeys, Version version)
|
public long serializedSize(AccordUpdate update, TableMetadatasAndKeys tablesAndKeys, Version version)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -73,6 +73,7 @@ import static com.google.common.base.Preconditions.checkState;
|
||||||
import static org.apache.cassandra.io.util.DataOutputBuffer.scratchBuffer;
|
import static org.apache.cassandra.io.util.DataOutputBuffer.scratchBuffer;
|
||||||
import static org.apache.cassandra.utils.ByteBufferUtil.readWithVIntLength;
|
import static org.apache.cassandra.utils.ByteBufferUtil.readWithVIntLength;
|
||||||
import static org.apache.cassandra.utils.ByteBufferUtil.serializedSizeWithVIntLength;
|
import static org.apache.cassandra.utils.ByteBufferUtil.serializedSizeWithVIntLength;
|
||||||
|
import static org.apache.cassandra.utils.ByteBufferUtil.skipWithVIntLength;
|
||||||
import static org.apache.cassandra.utils.ByteBufferUtil.writeWithVIntLength;
|
import static org.apache.cassandra.utils.ByteBufferUtil.writeWithVIntLength;
|
||||||
|
|
||||||
public class TxnNamedRead extends AbstractParameterisedVersionedSerialized<ReadCommand, TableMetadatas>
|
public class TxnNamedRead extends AbstractParameterisedVersionedSerialized<ReadCommand, TableMetadatas>
|
||||||
|
|
@ -431,6 +432,14 @@ public class TxnNamedRead extends AbstractParameterisedVersionedSerialized<ReadC
|
||||||
return new TxnNamedRead(name, key, bytes);
|
return new TxnNamedRead(name, key, bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void skip(TableMetadatasAndKeys tablesAndKeys, DataInputPlus in, Version version) throws IOException
|
||||||
|
{
|
||||||
|
in.readInt();
|
||||||
|
tablesAndKeys.skipSeekable(in);
|
||||||
|
if (in.readByte() != 1) skipWithVIntLength(in);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long serializedSize(TxnNamedRead read, TableMetadatasAndKeys tablesAndKeys, Version version)
|
public long serializedSize(TxnNamedRead read, TableMetadatasAndKeys tablesAndKeys, Version version)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -71,6 +71,7 @@ import static org.apache.cassandra.service.accord.txn.TxnData.txnDataName;
|
||||||
import static org.apache.cassandra.utils.ArraySerializers.deserializeArray;
|
import static org.apache.cassandra.utils.ArraySerializers.deserializeArray;
|
||||||
import static org.apache.cassandra.utils.ArraySerializers.serializeArray;
|
import static org.apache.cassandra.utils.ArraySerializers.serializeArray;
|
||||||
import static org.apache.cassandra.utils.ArraySerializers.serializedArraySize;
|
import static org.apache.cassandra.utils.ArraySerializers.serializedArraySize;
|
||||||
|
import static org.apache.cassandra.utils.ArraySerializers.skipArray;
|
||||||
import static org.apache.cassandra.utils.NullableSerializer.deserializeNullable;
|
import static org.apache.cassandra.utils.NullableSerializer.deserializeNullable;
|
||||||
import static org.apache.cassandra.utils.NullableSerializer.serializeNullable;
|
import static org.apache.cassandra.utils.NullableSerializer.serializeNullable;
|
||||||
import static org.apache.cassandra.utils.NullableSerializer.serializedNullableSize;
|
import static org.apache.cassandra.utils.NullableSerializer.serializedNullableSize;
|
||||||
|
|
@ -420,6 +421,23 @@ public class TxnRead extends AbstractKeySorted<TxnNamedRead> implements Read
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void skip(TableMetadatasAndKeys tablesAndKeys, DataInputPlus in, Version version) throws IOException
|
||||||
|
{
|
||||||
|
byte type = in.readByte();
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
throw new IllegalStateException("Unhandled type " + type);
|
||||||
|
case TYPE_EMPTY_KEY:
|
||||||
|
case TYPE_EMPTY_RANGE:
|
||||||
|
return;
|
||||||
|
case TYPE_NOT_EMPTY:
|
||||||
|
skipArray(tablesAndKeys, in, version, TxnNamedRead.serializer);
|
||||||
|
deserializeNullable(in, consistencyLevelSerializer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TxnRead deserialize(TableMetadatasAndKeys tablesAndKeys, DataInputPlus in, Version version) throws IOException
|
public TxnRead deserialize(TableMetadatasAndKeys tablesAndKeys, DataInputPlus in, Version version) throws IOException
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -66,8 +66,10 @@ import static org.apache.cassandra.service.accord.AccordSerializers.consistencyL
|
||||||
import static org.apache.cassandra.utils.ArraySerializers.deserializeArray;
|
import static org.apache.cassandra.utils.ArraySerializers.deserializeArray;
|
||||||
import static org.apache.cassandra.utils.ArraySerializers.serializeArray;
|
import static org.apache.cassandra.utils.ArraySerializers.serializeArray;
|
||||||
import static org.apache.cassandra.utils.ArraySerializers.serializedArraySize;
|
import static org.apache.cassandra.utils.ArraySerializers.serializedArraySize;
|
||||||
|
import static org.apache.cassandra.utils.ArraySerializers.skipArray;
|
||||||
import static org.apache.cassandra.utils.ByteBufferUtil.readWithVIntLength;
|
import static org.apache.cassandra.utils.ByteBufferUtil.readWithVIntLength;
|
||||||
import static org.apache.cassandra.utils.ByteBufferUtil.serializedSizeWithVIntLength;
|
import static org.apache.cassandra.utils.ByteBufferUtil.serializedSizeWithVIntLength;
|
||||||
|
import static org.apache.cassandra.utils.ByteBufferUtil.skipWithVIntLength;
|
||||||
import static org.apache.cassandra.utils.ByteBufferUtil.writeWithVIntLength;
|
import static org.apache.cassandra.utils.ByteBufferUtil.writeWithVIntLength;
|
||||||
import static org.apache.cassandra.utils.NullableSerializer.deserializeNullable;
|
import static org.apache.cassandra.utils.NullableSerializer.deserializeNullable;
|
||||||
import static org.apache.cassandra.utils.NullableSerializer.serializeNullable;
|
import static org.apache.cassandra.utils.NullableSerializer.serializeNullable;
|
||||||
|
|
@ -292,6 +294,16 @@ public class TxnUpdate extends AccordUpdate
|
||||||
return new TxnUpdate(tablesAndKeys.tables, keys, fragments, new SerializedTxnCondition(condition), consistencyLevel, preserveTimestamps ? PreserveTimestamp.yes : PreserveTimestamp.no);
|
return new TxnUpdate(tablesAndKeys.tables, keys, fragments, new SerializedTxnCondition(condition), consistencyLevel, preserveTimestamps ? PreserveTimestamp.yes : PreserveTimestamp.no);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void skip(TableMetadatasAndKeys tablesAndKeys, DataInputPlus in, Version version) throws IOException
|
||||||
|
{
|
||||||
|
in.readByte();
|
||||||
|
tablesAndKeys.skipKeys(in);
|
||||||
|
skipWithVIntLength(in);
|
||||||
|
skipArray(in, ByteBufferUtil.byteBufferSerializer);
|
||||||
|
deserializeNullable(in, consistencyLevelSerializer);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long serializedSize(TxnUpdate update, TableMetadatasAndKeys tablesAndKeys, Version version)
|
public long serializedSize(TxnUpdate update, TableMetadatasAndKeys tablesAndKeys, Version version)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -77,6 +77,7 @@ import static org.apache.cassandra.db.rows.DeserializationHelper.Flag.FROM_REMOT
|
||||||
import static org.apache.cassandra.utils.ArraySerializers.deserializeArray;
|
import static org.apache.cassandra.utils.ArraySerializers.deserializeArray;
|
||||||
import static org.apache.cassandra.utils.ArraySerializers.serializeArray;
|
import static org.apache.cassandra.utils.ArraySerializers.serializeArray;
|
||||||
import static org.apache.cassandra.utils.ArraySerializers.serializedArraySize;
|
import static org.apache.cassandra.utils.ArraySerializers.serializedArraySize;
|
||||||
|
import static org.apache.cassandra.utils.ArraySerializers.skipArray;
|
||||||
|
|
||||||
public class TxnWrite extends AbstractKeySorted<TxnWrite.Update> implements Write
|
public class TxnWrite extends AbstractKeySorted<TxnWrite.Update> implements Write
|
||||||
{
|
{
|
||||||
|
|
@ -215,6 +216,14 @@ public class TxnWrite extends AbstractKeySorted<TxnWrite.Update> implements Writ
|
||||||
return new Update(key, index, bytes);
|
return new Update(key, index, bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void skip(TableMetadatasAndKeys tablesAndKeys, DataInputPlus in, Version version) throws IOException
|
||||||
|
{
|
||||||
|
PartitionKey key = tablesAndKeys.deserializeKey(in);
|
||||||
|
int index = in.readInt();
|
||||||
|
ByteBufferUtil.skipWithVIntLength(in);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long serializedSize(Update write, TableMetadatasAndKeys tablesAndKeys, Version version)
|
public long serializedSize(Update write, TableMetadatasAndKeys tablesAndKeys, Version version)
|
||||||
{
|
{
|
||||||
|
|
@ -493,6 +502,14 @@ public class TxnWrite extends AbstractKeySorted<TxnWrite.Update> implements Writ
|
||||||
return new TxnWrite(tables, deserializeArray(new TableMetadatasAndKeys(tables, keys), in, version, Update.serializer, Update[]::new), isConditionMet);
|
return new TxnWrite(tables, deserializeArray(new TableMetadatasAndKeys(tables, keys), in, version, Update.serializer, Update[]::new), isConditionMet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void skip(Seekables keys, DataInputPlus in, Version version) throws IOException
|
||||||
|
{
|
||||||
|
TableMetadatas tables = TableMetadatas.deserializeSelf(in);
|
||||||
|
BooleanSerializer.serializer.deserialize(in);
|
||||||
|
skipArray(new TableMetadatasAndKeys(tables, keys), in, version, Update.serializer);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long serializedSize(TxnWrite write, Seekables keys, Version version)
|
public long serializedSize(TxnWrite write, Seekables keys, Version version)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -69,6 +69,13 @@ public class ArraySerializers
|
||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static <T, P, Version> void skipArray(DataInputPlus in, UnversionedSerializer<T> serializer) throws IOException
|
||||||
|
{
|
||||||
|
int size = in.readUnsignedVInt32();
|
||||||
|
for (int i = 0; i < size; i++)
|
||||||
|
serializer.skip(in);
|
||||||
|
}
|
||||||
|
|
||||||
public static <T> T[] deserializeArray(DataInputPlus in, int version, IVersionedSerializer<T> serializer, IntFunction<T[]> arrayFactory) throws IOException
|
public static <T> T[] deserializeArray(DataInputPlus in, int version, IVersionedSerializer<T> serializer, IntFunction<T[]> arrayFactory) throws IOException
|
||||||
{
|
{
|
||||||
int size = in.readUnsignedVInt32();
|
int size = in.readUnsignedVInt32();
|
||||||
|
|
@ -87,6 +94,14 @@ public class ArraySerializers
|
||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static <T, Version> void skipArray(DataInputPlus in, Version version, AsymmetricVersionedSerializer<T, ?, Version> serializer) throws IOException
|
||||||
|
{
|
||||||
|
int size = in.readUnsignedVInt32();
|
||||||
|
for (int i = 0; i < size; i++)
|
||||||
|
serializer.skip(in, version);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public static <T, P, Version> T[] deserializeArray(P p, DataInputPlus in, Version version, ParameterisedVersionedSerializer<T, P, Version> serializer, IntFunction<T[]> arrayFactory) throws IOException
|
public static <T, P, Version> T[] deserializeArray(P p, DataInputPlus in, Version version, ParameterisedVersionedSerializer<T, P, Version> serializer, IntFunction<T[]> arrayFactory) throws IOException
|
||||||
{
|
{
|
||||||
int size = in.readUnsignedVInt32();
|
int size = in.readUnsignedVInt32();
|
||||||
|
|
@ -96,6 +111,13 @@ public class ArraySerializers
|
||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static <T, P, Version> void skipArray(P p, DataInputPlus in, Version version, ParameterisedVersionedSerializer<T, P, Version> serializer) throws IOException
|
||||||
|
{
|
||||||
|
int size = in.readUnsignedVInt32();
|
||||||
|
for (int i = 0; i < size; i++)
|
||||||
|
serializer.skip(p, in, version);
|
||||||
|
}
|
||||||
|
|
||||||
public static <T> long serializedArraySize(T[] array, UnversionedSerializer<T> serializer)
|
public static <T> long serializedArraySize(T[] array, UnversionedSerializer<T> serializer)
|
||||||
{
|
{
|
||||||
long size = sizeofUnsignedVInt(array.length);
|
long size = sizeofUnsignedVInt(array.length);
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ package org.apache.cassandra.index.accord;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
import accord.local.MaxDecidedRX;
|
||||||
import accord.primitives.TxnId;
|
import accord.primitives.TxnId;
|
||||||
import org.apache.cassandra.service.accord.TokenRange;
|
import org.apache.cassandra.service.accord.TokenRange;
|
||||||
import org.apache.cassandra.service.accord.api.TokenKey;
|
import org.apache.cassandra.service.accord.api.TokenKey;
|
||||||
|
|
@ -40,4 +41,9 @@ public class AccordIndexUtil
|
||||||
{
|
{
|
||||||
return "T:" + (txnId == null ? "null" : Long.toString(txnId.hlc()));
|
return "T:" + (txnId == null ? "null" : Long.toString(txnId.hlc()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String normalize(@Nullable MaxDecidedRX.DecidedRX decidedRX)
|
||||||
|
{
|
||||||
|
return "T:" + (decidedRX == null ? "null" : Long.toString(decidedRX.any.hlc()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ import org.junit.Test;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import accord.local.MaxDecidedRX.DecidedRX;
|
||||||
import accord.local.Node;
|
import accord.local.Node;
|
||||||
import accord.primitives.Routable;
|
import accord.primitives.Routable;
|
||||||
import accord.primitives.Txn;
|
import accord.primitives.Txn;
|
||||||
|
|
@ -113,13 +114,14 @@ public class RangeMemoryIndexTest
|
||||||
return TxnRange.next(rs, minKnown, maxKnown, RangeMemoryIndexTest::idFor);
|
return TxnRange.next(rs, minKnown, maxKnown, RangeMemoryIndexTest::idFor);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static @Nullable TxnId nextMinDecidedId(RandomSource rs, State state)
|
private static @Nullable DecidedRX nextDecidedRX(RandomSource rs, State state)
|
||||||
{
|
{
|
||||||
if (rs.decide(state.minDecidedIdNull)) return null;
|
if (rs.decide(state.minDecidedIdNull)) return null;
|
||||||
long maxKnown = state.operations;
|
long maxKnown = state.operations;
|
||||||
long minKnown = state.model.isEmpty() ? maxKnown : state.model.minTime();
|
long minKnown = state.model.isEmpty() ? maxKnown : state.model.minTime();
|
||||||
if (minKnown == maxKnown) return idFor(maxKnown);
|
TxnId txnId = minKnown == maxKnown ? idFor(maxKnown)
|
||||||
return idFor(rs.nextLong(minKnown, maxKnown));
|
: idFor(rs.nextLong(minKnown, maxKnown));
|
||||||
|
return new DecidedRX(txnId, txnId);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static DecoratedKey pk(TxnId txnId)
|
private static DecoratedKey pk(TxnId txnId)
|
||||||
|
|
@ -159,11 +161,11 @@ public class RangeMemoryIndexTest
|
||||||
var txnRange = nextTxnRange(rs, state);
|
var txnRange = nextTxnRange(rs, state);
|
||||||
byte[] start = OrderedRouteSerializer.serializeTokenOnly(range.start());
|
byte[] start = OrderedRouteSerializer.serializeTokenOnly(range.start());
|
||||||
byte[] end = OrderedRouteSerializer.serializeTokenOnly(range.end());
|
byte[] end = OrderedRouteSerializer.serializeTokenOnly(range.end());
|
||||||
@Nullable TxnId minDecidedId = nextMinDecidedId(rs, state);
|
@Nullable DecidedRX decidedRX = nextDecidedRX(rs, state);
|
||||||
return new Property.SimpleCommand<>("search(" + normalize(range) + ", " + txnRange + ", " + normalize(minDecidedId) + ')', s2 -> {
|
return new Property.SimpleCommand<>("search(" + normalize(range) + ", " + txnRange + ", " + decidedRX + ')', s2 -> {
|
||||||
TreeSet<TxnId> actual = new TreeSet<>();
|
TreeSet<TxnId> actual = new TreeSet<>();
|
||||||
state.index.search(STORE, TABLE_ID, start, end, txnRange.minTxnId, txnRange.maxTxnId, minDecidedId, bb -> actual.add(AccordKeyspace.JournalColumns.getJournalKey(bb).id));
|
state.index.search(STORE, TABLE_ID, start, end, txnRange.minTxnId, txnRange.maxTxnId, decidedRX, bb -> actual.add(AccordKeyspace.JournalColumns.getJournalKey(bb).id));
|
||||||
var expected = state.model.search(range, txnRange.minTxnId, txnRange.maxTxnId, minDecidedId);
|
var expected = state.model.search(range, txnRange.minTxnId, txnRange.maxTxnId, decidedRX);
|
||||||
Assertions.assertThat(actual).isEqualTo(expected);
|
Assertions.assertThat(actual).isEqualTo(expected);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -173,11 +175,11 @@ public class RangeMemoryIndexTest
|
||||||
TokenKey key = tokenKey(rs.nextLong(MIN_TOKEN, MAX_TOKEN + 1));
|
TokenKey key = tokenKey(rs.nextLong(MIN_TOKEN, MAX_TOKEN + 1));
|
||||||
var txnRange = nextTxnRange(rs, state);
|
var txnRange = nextTxnRange(rs, state);
|
||||||
var start = OrderedRouteSerializer.serializeTokenOnly(key);
|
var start = OrderedRouteSerializer.serializeTokenOnly(key);
|
||||||
@Nullable TxnId minDecidedId = nextMinDecidedId(rs, state);
|
@Nullable DecidedRX decidedRX = nextDecidedRX(rs, state);
|
||||||
return new Property.SimpleCommand<>("search(" + normalize(key) + ", " + txnRange + ", " + normalize(minDecidedId) + ')', s2 -> {
|
return new Property.SimpleCommand<>("search(" + normalize(key) + ", " + txnRange + ", " + decidedRX + ')', s2 -> {
|
||||||
TreeSet<TxnId> actual = new TreeSet<>();
|
TreeSet<TxnId> actual = new TreeSet<>();
|
||||||
state.index.search(STORE, TABLE_ID, start, txnRange.minTxnId, txnRange.maxTxnId, minDecidedId, bb -> actual.add(AccordKeyspace.JournalColumns.getJournalKey(bb).id));
|
state.index.search(STORE, TABLE_ID, start, txnRange.minTxnId, txnRange.maxTxnId, decidedRX, bb -> actual.add(AccordKeyspace.JournalColumns.getJournalKey(bb).id));
|
||||||
var expected = state.model.search(key, txnRange.minTxnId, txnRange.maxTxnId, minDecidedId);
|
var expected = state.model.search(key, txnRange.minTxnId, txnRange.maxTxnId, decidedRX);
|
||||||
Assertions.assertThat(actual).isEqualTo(expected);
|
Assertions.assertThat(actual).isEqualTo(expected);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -219,23 +221,23 @@ public class RangeMemoryIndexTest
|
||||||
return values.isEmpty();
|
return values.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
public NavigableSet<TxnId> search(TokenRange range, TxnId minTxnId, TxnId maxTxnId, @Nullable TxnId minDecidedId)
|
public NavigableSet<TxnId> search(TokenRange range, TxnId minTxnId, TxnId maxTxnId, @Nullable DecidedRX decidedRX)
|
||||||
{
|
{
|
||||||
return search(r -> r.compareIntersecting(range) == 0, minTxnId, maxTxnId, minDecidedId);
|
return search(r -> r.compareIntersecting(range) == 0, minTxnId, maxTxnId, decidedRX);
|
||||||
}
|
}
|
||||||
|
|
||||||
public NavigableSet<TxnId> search(TokenKey key, TxnId minTxnId, TxnId maxTxnId, @Nullable TxnId minDecidedId)
|
public NavigableSet<TxnId> search(TokenKey key, TxnId minTxnId, TxnId maxTxnId, @Nullable DecidedRX decidedRX)
|
||||||
{
|
{
|
||||||
return search(r -> r.contains(key), minTxnId, maxTxnId, minDecidedId);
|
return search(r -> r.contains(key), minTxnId, maxTxnId, decidedRX);
|
||||||
}
|
}
|
||||||
|
|
||||||
public NavigableSet<TxnId> search(Predicate<TokenRange> test, TxnId minTxnId, TxnId maxTxnId, @Nullable TxnId minDecidedId)
|
public NavigableSet<TxnId> search(Predicate<TokenRange> test, TxnId minTxnId, TxnId maxTxnId, @Nullable DecidedRX decidedRX)
|
||||||
{
|
{
|
||||||
NavigableSet<TxnId> result = new TreeSet<>();
|
NavigableSet<TxnId> result = new TreeSet<>();
|
||||||
for (var value : values)
|
for (var value : values)
|
||||||
{
|
{
|
||||||
if (value.txnId.compareTo(minTxnId) < 0 || value.txnId.compareTo(maxTxnId) > 0) continue;
|
if (value.txnId.compareTo(minTxnId) < 0 || value.txnId.compareTo(maxTxnId) > 0) continue;
|
||||||
if (minDecidedId != null && minDecidedId.compareTo(maxRXId) > 0) continue;
|
if (decidedRX != null && decidedRX.excludeDecided(maxRXId)) continue;
|
||||||
if (test.test(value.range))
|
if (test.test(value.range))
|
||||||
result.add(value.txnId);
|
result.add(value.txnId);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@ import accord.api.RoutingKey;
|
||||||
import accord.local.CommandStores;
|
import accord.local.CommandStores;
|
||||||
import accord.local.CommandStores.RangesForEpoch;
|
import accord.local.CommandStores.RangesForEpoch;
|
||||||
import accord.local.DurableBefore;
|
import accord.local.DurableBefore;
|
||||||
|
import accord.local.MaxDecidedRX.DecidedRX;
|
||||||
import accord.local.Node;
|
import accord.local.Node;
|
||||||
import accord.local.RedundantBefore;
|
import accord.local.RedundantBefore;
|
||||||
import accord.local.StoreParticipants;
|
import accord.local.StoreParticipants;
|
||||||
|
|
@ -101,7 +102,6 @@ import static accord.local.RedundantStatus.SomeStatus.NONE;
|
||||||
import static accord.utils.Property.commands;
|
import static accord.utils.Property.commands;
|
||||||
import static accord.utils.Property.stateful;
|
import static accord.utils.Property.stateful;
|
||||||
import static org.apache.cassandra.config.DatabaseDescriptor.getPartitioner;
|
import static org.apache.cassandra.config.DatabaseDescriptor.getPartitioner;
|
||||||
import static org.apache.cassandra.index.accord.AccordIndexUtil.normalize;
|
|
||||||
import static org.apache.cassandra.schema.SchemaConstants.ACCORD_KEYSPACE_NAME;
|
import static org.apache.cassandra.schema.SchemaConstants.ACCORD_KEYSPACE_NAME;
|
||||||
|
|
||||||
public class RouteIndexTest extends CQLTester
|
public class RouteIndexTest extends CQLTester
|
||||||
|
|
@ -179,8 +179,8 @@ public class RouteIndexTest extends CQLTester
|
||||||
long start = range.start().isMin() ? Long.MIN_VALUE : ((LongToken) range.start().token()).token;
|
long start = range.start().isMin() ? Long.MIN_VALUE : ((LongToken) range.start().token()).token;
|
||||||
long end = range.end().isMax() ? Long.MAX_VALUE : ((LongToken) range.end().token()).token;
|
long end = range.end().isMax() ? Long.MAX_VALUE : ((LongToken) range.end().token()).token;
|
||||||
long token = 1 + rs.nextLong(start, end);
|
long token = 1 + rs.nextLong(start, end);
|
||||||
@Nullable TxnId minDecidedId = state.nextMinDecidedId(rs);
|
@Nullable DecidedRX decidedRX = state.nextDecidedRX(rs);
|
||||||
return new KeySearch(storeId, new TokenKey(tableId, new LongToken(token)), state.nextTxnRange(rs), minDecidedId);
|
return new KeySearch(storeId, new TokenKey(tableId, new LongToken(token)), state.nextTxnRange(rs), decidedRX);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static RangeSearch rangeSearchExisting(RandomSource rs, State state)
|
private static RangeSearch rangeSearchExisting(RandomSource rs, State state)
|
||||||
|
|
@ -189,20 +189,20 @@ public class RouteIndexTest extends CQLTester
|
||||||
var tables = state.storeToTableToRangesToTxns.get(storeId);
|
var tables = state.storeToTableToRangesToTxns.get(storeId);
|
||||||
TableId tableId = rs.pickUnorderedSet(tables.keySet());
|
TableId tableId = rs.pickUnorderedSet(tables.keySet());
|
||||||
var ranges = tables.get(tableId);
|
var ranges = tables.get(tableId);
|
||||||
@Nullable TxnId minDecidedId = state.nextMinDecidedId(rs);
|
@Nullable DecidedRX decidedRX = state.nextDecidedRX(rs);
|
||||||
return new RangeSearch(storeId, selectExistingRange(rs, ranges), state.nextTxnRange(rs), minDecidedId);
|
return new RangeSearch(storeId, selectExistingRange(rs, ranges), state.nextTxnRange(rs), decidedRX);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Command<State, Sut, ?> rangeSearch(RandomSource rs, State state)
|
private static Command<State, Sut, ?> rangeSearch(RandomSource rs, State state)
|
||||||
{
|
{
|
||||||
@Nullable TxnId minDecidedId = state.nextMinDecidedId(rs);
|
@Nullable DecidedRX decidedRX = state.nextDecidedRX(rs);
|
||||||
return new RangeSearch(rs.nextInt(0, state.numStores), state.rangeGen.next(rs), state.nextTxnRange(rs), minDecidedId);
|
return new RangeSearch(rs.nextInt(0, state.numStores), state.rangeGen.next(rs), state.nextTxnRange(rs), decidedRX);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Command<State, Sut, ?> keySearch(RandomSource rs, State state)
|
private static Command<State, Sut, ?> keySearch(RandomSource rs, State state)
|
||||||
{
|
{
|
||||||
@Nullable TxnId minDecidedId = state.nextMinDecidedId(rs);
|
@Nullable DecidedRX decidedRX = state.nextDecidedRX(rs);
|
||||||
return new KeySearch(rs.nextInt(0, state.numStores), new TokenKey(rs.pick(state.tables), new LongToken(state.tokenGen.nextInt(rs))), state.nextTxnRange(rs), minDecidedId);
|
return new KeySearch(rs.nextInt(0, state.numStores), new TokenKey(rs.pick(state.tables), new LongToken(state.tokenGen.nextInt(rs))), state.nextTxnRange(rs), decidedRX);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -270,14 +270,14 @@ public class RouteIndexTest extends CQLTester
|
||||||
private final int storeId;
|
private final int storeId;
|
||||||
private final TokenKey key;
|
private final TokenKey key;
|
||||||
private final TxnRange txnRange;
|
private final TxnRange txnRange;
|
||||||
private final @Nullable TxnId minDecidedId;
|
private final @Nullable DecidedRX decidedRX;
|
||||||
|
|
||||||
private KeySearch(int storeId, TokenKey key, TxnRange txnRange, @Nullable TxnId minDecidedId)
|
private KeySearch(int storeId, TokenKey key, TxnRange txnRange, @Nullable DecidedRX decidedRX)
|
||||||
{
|
{
|
||||||
this.storeId = storeId;
|
this.storeId = storeId;
|
||||||
this.key = key;
|
this.key = key;
|
||||||
this.txnRange = txnRange;
|
this.txnRange = txnRange;
|
||||||
this.minDecidedId = minDecidedId;
|
this.decidedRX = decidedRX;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -290,7 +290,7 @@ public class RouteIndexTest extends CQLTester
|
||||||
Set<TxnId> matches = new HashSet<>();
|
Set<TxnId> matches = new HashSet<>();
|
||||||
ranges.searchToken(key, e -> {
|
ranges.searchToken(key, e -> {
|
||||||
TxnId txnId = e.getValue();
|
TxnId txnId = e.getValue();
|
||||||
if (minDecidedId != null && txnId.is(Txn.Kind.ExclusiveSyncPoint) && minDecidedId.compareTo(txnId) > 0)
|
if (decidedRX != null && txnId.is(Txn.Kind.ExclusiveSyncPoint) && decidedRX.excludeDecided(txnId))
|
||||||
return;
|
return;
|
||||||
if (txnRange.includes(txnId))
|
if (txnRange.includes(txnId))
|
||||||
matches.add(txnId);
|
matches.add(txnId);
|
||||||
|
|
@ -302,7 +302,7 @@ public class RouteIndexTest extends CQLTester
|
||||||
public Set<TxnId> run(Sut sut) throws Throwable
|
public Set<TxnId> run(Sut sut) throws Throwable
|
||||||
{
|
{
|
||||||
Set<TxnId> result = new ObjectHashSet<>();
|
Set<TxnId> result = new ObjectHashSet<>();
|
||||||
sut.journal.get().rangeSearcher().search(storeId, key, txnRange.minTxnId, txnRange.maxTxnId, minDecidedId).consume(result::add);
|
sut.journal.get().rangeSearcher().search(storeId, key, txnRange.minTxnId, txnRange.maxTxnId, decidedRX).consume(result::add);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -319,7 +319,7 @@ public class RouteIndexTest extends CQLTester
|
||||||
return "KeySearch{" +
|
return "KeySearch{" +
|
||||||
"storeId=" + storeId +
|
"storeId=" + storeId +
|
||||||
", key=" + key +
|
", key=" + key +
|
||||||
", minDecidedId=" + normalize(minDecidedId) +
|
", decidedRX=" + decidedRX +
|
||||||
'}';
|
'}';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -329,14 +329,14 @@ public class RouteIndexTest extends CQLTester
|
||||||
private final int storeId;
|
private final int storeId;
|
||||||
private final TokenRange range;
|
private final TokenRange range;
|
||||||
private final TxnRange txnRange;
|
private final TxnRange txnRange;
|
||||||
private final TxnId minDecidedId;
|
private final DecidedRX decidedRX;
|
||||||
|
|
||||||
private RangeSearch(int storeId, TokenRange range, TxnRange txnRange, @Nullable TxnId minDecidedId)
|
private RangeSearch(int storeId, TokenRange range, TxnRange txnRange, DecidedRX decidedRX)
|
||||||
{
|
{
|
||||||
this.storeId = storeId;
|
this.storeId = storeId;
|
||||||
this.range = range;
|
this.range = range;
|
||||||
this.txnRange = txnRange;
|
this.txnRange = txnRange;
|
||||||
this.minDecidedId = minDecidedId;
|
this.decidedRX = decidedRX;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -349,7 +349,7 @@ public class RouteIndexTest extends CQLTester
|
||||||
Set<TxnId> matches = new HashSet<>();
|
Set<TxnId> matches = new HashSet<>();
|
||||||
ranges.search(range, e -> {
|
ranges.search(range, e -> {
|
||||||
TxnId txnId = e.getValue();
|
TxnId txnId = e.getValue();
|
||||||
if (minDecidedId != null && txnId.is(Txn.Kind.ExclusiveSyncPoint) && minDecidedId.compareTo(txnId) > 0) return;
|
if (decidedRX != null && txnId.is(Txn.Kind.ExclusiveSyncPoint) && decidedRX.excludeDecided(txnId)) return;
|
||||||
if (txnRange.includes(txnId))
|
if (txnRange.includes(txnId))
|
||||||
matches.add(txnId);
|
matches.add(txnId);
|
||||||
});
|
});
|
||||||
|
|
@ -360,7 +360,7 @@ public class RouteIndexTest extends CQLTester
|
||||||
public Set<TxnId> run(Sut sut) throws Throwable
|
public Set<TxnId> run(Sut sut) throws Throwable
|
||||||
{
|
{
|
||||||
Set<TxnId> result = new ObjectHashSet<>();
|
Set<TxnId> result = new ObjectHashSet<>();
|
||||||
sut.journal.get().rangeSearcher().search(storeId, range, txnRange.minTxnId, txnRange.maxTxnId, minDecidedId).consume(result::add);
|
sut.journal.get().rangeSearcher().search(storeId, range, txnRange.minTxnId, txnRange.maxTxnId, decidedRX).consume(result::add);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -377,7 +377,7 @@ public class RouteIndexTest extends CQLTester
|
||||||
return "RangeSearch{" +
|
return "RangeSearch{" +
|
||||||
"storeId=" + storeId +
|
"storeId=" + storeId +
|
||||||
", range=" + range +
|
", range=" + range +
|
||||||
", minDecidedId=" + normalize(minDecidedId) +
|
", minDecidedId=" + decidedRX +
|
||||||
'}';
|
'}';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -540,13 +540,14 @@ public class RouteIndexTest extends CQLTester
|
||||||
return TxnRange.next(rs, minKnown, maxKnown, hlc -> idFor(Domain.Key, hlc));
|
return TxnRange.next(rs, minKnown, maxKnown, hlc -> idFor(Domain.Key, hlc));
|
||||||
}
|
}
|
||||||
|
|
||||||
private @Nullable TxnId nextMinDecidedId(RandomSource rs)
|
private @Nullable DecidedRX nextDecidedRX(RandomSource rs)
|
||||||
{
|
{
|
||||||
if (rs.decide(minDecidedIdNull)) return null;
|
if (rs.decide(minDecidedIdNull)) return null;
|
||||||
long maxKnown = hlc;
|
long maxKnown = hlc;
|
||||||
long minKnown = MIN_TIMESTAMP;
|
long minKnown = MIN_TIMESTAMP;
|
||||||
if (minKnown == maxKnown) return idFor(Domain.Range, maxKnown);
|
TxnId txnId = minKnown == maxKnown ? idFor(Domain.Range, maxKnown)
|
||||||
return idFor(Domain.Range, rs.nextLong(minKnown, maxKnown));
|
: idFor(Domain.Range, rs.nextLong(minKnown, maxKnown));
|
||||||
|
return new DecidedRX(txnId, txnId);
|
||||||
}
|
}
|
||||||
|
|
||||||
void insertTxn(int storeId, TxnId txnId, Route<?> route)
|
void insertTxn(int storeId, TxnId txnId, Route<?> route)
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ import org.junit.Test;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import accord.local.MaxDecidedRX.DecidedRX;
|
||||||
import accord.local.Node;
|
import accord.local.Node;
|
||||||
import accord.primitives.FullRangeRoute;
|
import accord.primitives.FullRangeRoute;
|
||||||
import accord.primitives.Range;
|
import accord.primitives.Range;
|
||||||
|
|
@ -119,13 +120,14 @@ public class RouteInMemoryIndexTest
|
||||||
return TxnRange.next(rs, minKnown, maxKnown, RouteInMemoryIndexTest::idFor);
|
return TxnRange.next(rs, minKnown, maxKnown, RouteInMemoryIndexTest::idFor);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static @Nullable TxnId nextMinDecidedId(RandomSource rs, State state)
|
private static @Nullable DecidedRX nextDecidedRX(RandomSource rs, State state)
|
||||||
{
|
{
|
||||||
if (rs.decide(state.minDecidedIdNull)) return null;
|
if (rs.decide(state.minDecidedIdNull)) return null;
|
||||||
long maxKnown = state.operations;
|
long maxKnown = state.operations;
|
||||||
long minKnown = state.model.isEmpty() ? maxKnown : state.model.minTime();
|
long minKnown = state.model.isEmpty() ? maxKnown : state.model.minTime();
|
||||||
if (minKnown == maxKnown) return idFor(maxKnown);
|
TxnId txnId = minKnown == maxKnown ? idFor(maxKnown)
|
||||||
return idFor(rs.nextLong(minKnown, maxKnown));
|
: idFor(rs.nextLong(minKnown, maxKnown));
|
||||||
|
return new DecidedRX(txnId, txnId);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class State
|
private static class State
|
||||||
|
|
@ -197,33 +199,33 @@ public class RouteInMemoryIndexTest
|
||||||
{
|
{
|
||||||
var range = nextRange(rs);
|
var range = nextRange(rs);
|
||||||
var txnRange = nextTxnRange(rs, state);
|
var txnRange = nextTxnRange(rs, state);
|
||||||
@Nullable TxnId minDecidedId = nextMinDecidedId(rs, state);
|
@Nullable DecidedRX decidedRX = nextDecidedRX(rs, state);
|
||||||
return new Property.SimpleCommand<>("Search " + normalize(range) + ", txn_id range " + txnRange + ", minDecidedId " + normalize(minDecidedId), s2 -> s2.assertSearchMatch(range, txnRange.minTxnId, txnRange.maxTxnId, minDecidedId));
|
return new Property.SimpleCommand<>("Search " + normalize(range) + ", txn_id range " + txnRange + ", minDecidedId " + normalize(decidedRX), s2 -> s2.assertSearchMatch(range, txnRange.minTxnId, txnRange.maxTxnId, decidedRX));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Property.Command<State, Void, ?> keySearch(RandomSource rs, State state)
|
public static Property.Command<State, Void, ?> keySearch(RandomSource rs, State state)
|
||||||
{
|
{
|
||||||
TokenKey key = tokenKey(rs.nextLong(MIN_TOKEN, MAX_TOKEN + 1));
|
TokenKey key = tokenKey(rs.nextLong(MIN_TOKEN, MAX_TOKEN + 1));
|
||||||
var txnRange = nextTxnRange(rs, state);
|
var txnRange = nextTxnRange(rs, state);
|
||||||
@Nullable TxnId minDecidedId = nextMinDecidedId(rs, state);
|
@Nullable DecidedRX decidedRX = nextDecidedRX(rs, state);
|
||||||
return new Property.SimpleCommand<>("Search " + normalize(key) + ", txn_id range " + txnRange + ", minDecidedId " + normalize(minDecidedId), s2 -> s2.assertSearchMatch(key, txnRange.minTxnId, txnRange.maxTxnId, minDecidedId));
|
return new Property.SimpleCommand<>("Search " + normalize(key) + ", txn_id range " + txnRange + ", decidedRX " + normalize(decidedRX), s2 -> s2.assertSearchMatch(key, txnRange.minTxnId, txnRange.maxTxnId, decidedRX));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertSearchMatch(TokenRange range, TxnId minTxnId, TxnId maxTxnId, @Nullable TxnId minDecidedId)
|
private void assertSearchMatch(TokenRange range, TxnId minTxnId, TxnId maxTxnId, @Nullable DecidedRX decidedRX)
|
||||||
{
|
{
|
||||||
List<TxnId> actual = new ArrayList<>();
|
List<TxnId> actual = new ArrayList<>();
|
||||||
index.search(0, range, minTxnId, maxTxnId, minDecidedId).consume(actual::add);
|
index.search(0, range, minTxnId, maxTxnId, decidedRX).consume(actual::add);
|
||||||
List<TxnId> expected = new ArrayList<>();
|
List<TxnId> expected = new ArrayList<>();
|
||||||
model.search(range, minTxnId, maxTxnId, minDecidedId).consume(expected::add);
|
model.search(range, minTxnId, maxTxnId, decidedRX).consume(expected::add);
|
||||||
Assertions.assertThat(actual).isEqualTo(expected);
|
Assertions.assertThat(actual).isEqualTo(expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertSearchMatch(TokenKey key, TxnId minTxnId, TxnId maxTxnId, @Nullable TxnId minDecidedId)
|
private void assertSearchMatch(TokenKey key, TxnId minTxnId, TxnId maxTxnId, @Nullable DecidedRX decidedRX)
|
||||||
{
|
{
|
||||||
List<TxnId> actual = new ArrayList<>();
|
List<TxnId> actual = new ArrayList<>();
|
||||||
index.search(0, key, minTxnId, maxTxnId, minDecidedId).consume(actual::add);
|
index.search(0, key, minTxnId, maxTxnId, decidedRX).consume(actual::add);
|
||||||
List<TxnId> expected = new ArrayList<>();
|
List<TxnId> expected = new ArrayList<>();
|
||||||
model.search(key, minTxnId, maxTxnId, minDecidedId).consume(expected::add);
|
model.search(key, minTxnId, maxTxnId, decidedRX).consume(expected::add);
|
||||||
Assertions.assertThat(actual).isEqualTo(expected);
|
Assertions.assertThat(actual).isEqualTo(expected);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -276,22 +278,22 @@ public class RouteInMemoryIndexTest
|
||||||
segments.computeIfAbsent(segment, i -> new Segment()).add(range, txnId);
|
segments.computeIfAbsent(segment, i -> new Segment()).add(range, txnId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public RangeSearcher.Result search(TokenRange range, TxnId minTxnId, TxnId maxTxnId, @Nullable TxnId minDecidedId)
|
public RangeSearcher.Result search(TokenRange range, TxnId minTxnId, TxnId maxTxnId, @Nullable DecidedRX decidedRX)
|
||||||
{
|
{
|
||||||
return search(vrange -> range.compareIntersecting(vrange) == 0, minTxnId, maxTxnId, minDecidedId);
|
return search(vrange -> range.compareIntersecting(vrange) == 0, minTxnId, maxTxnId, decidedRX);
|
||||||
}
|
}
|
||||||
|
|
||||||
public RangeSearcher.Result search(TokenKey key, TxnId minTxnId, TxnId maxTxnId, @Nullable TxnId minDecidedId)
|
public RangeSearcher.Result search(TokenKey key, TxnId minTxnId, TxnId maxTxnId, @Nullable DecidedRX decidedRX)
|
||||||
{
|
{
|
||||||
return search(range -> range.contains(key), minTxnId, maxTxnId, minDecidedId);
|
return search(range -> range.contains(key), minTxnId, maxTxnId, decidedRX);
|
||||||
}
|
}
|
||||||
|
|
||||||
public RangeSearcher.Result search(Predicate<TokenRange> test, TxnId minTxnId, TxnId maxTxnId, @Nullable TxnId minDecidedId)
|
public RangeSearcher.Result search(Predicate<TokenRange> test, TxnId minTxnId, TxnId maxTxnId, @Nullable DecidedRX decidedRX)
|
||||||
{
|
{
|
||||||
TreeSet<TxnId> result = new TreeSet<>();
|
TreeSet<TxnId> result = new TreeSet<>();
|
||||||
for (var segment: segments.values())
|
for (var segment: segments.values())
|
||||||
{
|
{
|
||||||
if (!RouteIndexFormat.includeByMinDecidedId(minDecidedId, segment.maxRXId)) continue;
|
if (!RouteIndexFormat.includeByDecidedRX(decidedRX, segment.maxRXId)) continue;
|
||||||
for (var value : segment.values)
|
for (var value : segment.values)
|
||||||
{
|
{
|
||||||
if (value.txnId.compareTo(minTxnId) < 0 || value.txnId.compareTo(maxTxnId) > 0) continue;
|
if (value.txnId.compareTo(minTxnId) < 0 || value.txnId.compareTo(maxTxnId) > 0) continue;
|
||||||
|
|
@ -299,7 +301,7 @@ public class RouteInMemoryIndexTest
|
||||||
result.add(value.txnId);
|
result.add(value.txnId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return new RangeSearcher.DefaultResult(minTxnId, maxTxnId, minDecidedId, CloseableIterator.wrap(result.iterator()));
|
return new RangeSearcher.DefaultResult(minTxnId, maxTxnId, decidedRX, CloseableIterator.wrap(result.iterator()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void remove(long segment)
|
void remove(long segment)
|
||||||
|
|
|
||||||
|
|
@ -459,7 +459,7 @@ public class SimulatedAccordCommandStore implements AutoCloseable
|
||||||
{
|
{
|
||||||
TxnId txnId = nextTxnId(txn.kind(), txn.keys().domain());
|
TxnId txnId = nextTxnId(txn.kind(), txn.keys().domain());
|
||||||
Ballot ballot = Ballot.fromValues(storeService.epoch(), storeService.now(), nodeId);
|
Ballot ballot = Ballot.fromValues(storeService.epoch(), storeService.now(), nodeId);
|
||||||
BeginRecovery br = new BeginRecovery(nodeId, topologies, txnId, null, txn, route, ballot);
|
BeginRecovery br = new BeginRecovery(nodeId, topologies, txnId, null, false, txn, route, ballot);
|
||||||
|
|
||||||
return Pair.create(txnId, processAsync(br, safe -> {
|
return Pair.create(txnId, processAsync(br, safe -> {
|
||||||
var reply = br.apply(safe);
|
var reply = br.apply(safe);
|
||||||
|
|
|
||||||
|
|
@ -287,7 +287,7 @@ public abstract class SimulatedAccordCommandStoreTestBase extends CQLTester
|
||||||
});
|
});
|
||||||
var delay = preAcceptAsync.flatMap(ignore -> AsyncChains.ofCallable(instance.unorderedScheduled, () -> {
|
var delay = preAcceptAsync.flatMap(ignore -> AsyncChains.ofCallable(instance.unorderedScheduled, () -> {
|
||||||
Ballot ballot = Ballot.fromValues(instance.storeService.epoch(), instance.storeService.now(), nodeId);
|
Ballot ballot = Ballot.fromValues(instance.storeService.epoch(), instance.storeService.now(), nodeId);
|
||||||
return new BeginRecovery(nodeId, new Topologies.Single(SizeOfIntersectionSorter.SUPPLIER, instance.topology), txnId, null, txn, route, ballot);
|
return new BeginRecovery(nodeId, new Topologies.Single(SizeOfIntersectionSorter.SUPPLIER, instance.topology), txnId, null, false, txn, route, ballot);
|
||||||
}));
|
}));
|
||||||
var recoverAsync = delay.flatMap(br -> instance.processAsync(br, safe -> {
|
var recoverAsync = delay.flatMap(br -> instance.processAsync(br, safe -> {
|
||||||
var reply = br.apply(safe);
|
var reply = br.apply(safe);
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ import java.nio.ByteBuffer;
|
||||||
import org.junit.BeforeClass;
|
import org.junit.BeforeClass;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import accord.impl.CommandChange;
|
||||||
import accord.local.Command;
|
import accord.local.Command;
|
||||||
import accord.primitives.Deps;
|
import accord.primitives.Deps;
|
||||||
import accord.primitives.KeyDeps;
|
import accord.primitives.KeyDeps;
|
||||||
|
|
@ -68,7 +69,8 @@ public class WaitingOnSerializerTest
|
||||||
try (DataInputBuffer buf = new DataInputBuffer(bb, true))
|
try (DataInputBuffer buf = new DataInputBuffer(bb, true))
|
||||||
{
|
{
|
||||||
PartialDeps deps = new PartialDeps(RoutingKeys.EMPTY, KeyDeps.none(waitingOn.keys), waitingOn.directRangeDeps);
|
PartialDeps deps = new PartialDeps(RoutingKeys.EMPTY, KeyDeps.none(waitingOn.keys), waitingOn.directRangeDeps);
|
||||||
Command.WaitingOn read = WaitingOnSerializer.deserializeProvider(txnId, buf).provide(txnId, deps, null, 0);
|
CommandChange.WaitingOnBitSets bitSets = WaitingOnSerializer.deserializeBitSets(txnId, buf);
|
||||||
|
Command.WaitingOn read = new Command.WaitingOn(deps.keyDeps.keys(), deps.rangeDeps, bitSets.waitingOn, bitSets.appliedOrInvalidated);
|
||||||
Assertions.assertThat(read).isEqualTo(waitingOn);
|
Assertions.assertThat(read).isEqualTo(waitingOn);
|
||||||
Assertions.assertThat(buf.available()).isEqualTo(0);
|
Assertions.assertThat(buf.available()).isEqualTo(0);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue