CEP-15 Accord: NotWitnessed commands can receive an invalidate promise but would return Zero instead
patch by David Capwell; reviewed by Benedict Elliott Smith for CASSANDRA-18471
diff --git a/accord-core/src/main/java/accord/local/Command.java b/accord-core/src/main/java/accord/local/Command.java
index f8f6d00..54aec10 100644
--- a/accord-core/src/main/java/accord/local/Command.java
+++ b/accord-core/src/main/java/accord/local/Command.java
@@ -514,12 +514,6 @@
}
@Override
- public Ballot promised()
- {
- return Ballot.ZERO;
- }
-
- @Override
public Ballot accepted()
{
return Ballot.ZERO;
diff --git a/accord-core/src/main/java/accord/messages/BeginInvalidation.java b/accord-core/src/main/java/accord/messages/BeginInvalidation.java
index 1bb3ba2..d0d1c79 100644
--- a/accord-core/src/main/java/accord/messages/BeginInvalidation.java
+++ b/accord-core/src/main/java/accord/messages/BeginInvalidation.java
@@ -30,6 +30,7 @@
import javax.annotation.Nullable;
import java.util.Collections;
import java.util.List;
+import java.util.Objects;
import static accord.primitives.Route.castToFullRoute;
import static accord.primitives.Route.isFullRoute;
@@ -145,6 +146,21 @@
}
@Override
+ public boolean equals(Object o)
+ {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ InvalidateReply that = (InvalidateReply) o;
+ return acceptedFastPath == that.acceptedFastPath && Objects.equals(supersededBy, that.supersededBy) && Objects.equals(accepted, that.accepted) && status == that.status && Objects.equals(route, that.route) && Objects.equals(homeKey, that.homeKey);
+ }
+
+ @Override
+ public int hashCode()
+ {
+ return Objects.hash(supersededBy, accepted, status, acceptedFastPath, route, homeKey);
+ }
+
+ @Override
public String toString()
{
return "Invalidate" + (isPromised() ? "Promised{" : "NotPromised{" + supersededBy + ",") + status + ',' + (route != null ? route: homeKey) + '}';
diff --git a/accord-core/src/test/java/accord/messages/PreAcceptTest.java b/accord-core/src/test/java/accord/messages/PreAcceptTest.java
index 29be803..c32c563 100644
--- a/accord-core/src/test/java/accord/messages/PreAcceptTest.java
+++ b/accord-core/src/test/java/accord/messages/PreAcceptTest.java
@@ -26,14 +26,9 @@
import accord.impl.mock.*;
import accord.local.Node;
import accord.local.Node.Id;
-import accord.api.MessageSink;
-import accord.api.Scheduler;
import accord.impl.mock.MockCluster.Clock;
import accord.primitives.*;
import accord.topology.Topology;
-import accord.utils.DefaultRandom;
-import accord.utils.EpochFunction;
-import accord.utils.ThreadPoolScheduler;
import accord.local.*;
import org.junit.jupiter.api.Assertions;
@@ -51,6 +46,7 @@
import static accord.primitives.Routable.Domain.Key;
import static accord.primitives.Txn.Kind.Write;
import static accord.utils.Utils.listOf;
+import static org.assertj.core.api.Assertions.assertThat;
public class PreAcceptTest
{
@@ -137,6 +133,41 @@
}
@Test
+ void invalidatedTest()
+ {
+ RecordingMessageSink messageSink = new RecordingMessageSink(ID1, Network.BLACK_HOLE);
+ Clock clock = new Clock(100);
+ Node node = createNode(ID1, TOPOLOGY, messageSink, clock);
+ try
+ {
+ Raw key = IntKey.key(10);
+ CommandStore commandStore = node.unsafeForKey(key);
+ Assertions.assertFalse(inMemory(commandStore).hasCommandsForKey(key));
+
+ TxnId txnId = clock.idForNode(1, ID2);
+ Txn txn = writeTxn(Keys.of(key));
+
+ Unseekables<?, ?> invalidateWith = txn.keys().toUnseekables();
+ BeginInvalidation invalidate = new BeginInvalidation(ID1, node.topology().forEpoch(invalidateWith, txnId.epoch()), txnId, invalidateWith, Ballot.fromValues(txnId.epoch(), txnId.hlc(), txnId.node));
+ invalidate.process(node, ID2, REPLY_CONTEXT);
+
+ messageSink.assertHistorySizes(0, 1);
+ assertThat(messageSink.responses.get(0).payload).isEqualTo(new BeginInvalidation.InvalidateReply(null, Ballot.ZERO, Status.NotWitnessed, false, null, null));
+ messageSink.clearHistory();
+
+ PreAccept preAccept = preAccept(txnId, txn, key.toUnseekable());
+ preAccept.process(node, ID2, REPLY_CONTEXT);
+
+ messageSink.assertHistorySizes(0, 1);
+ assertThat(messageSink.responses.get(0).payload).isEqualTo(PreAccept.PreAcceptNack.INSTANCE);
+ }
+ finally
+ {
+ node.shutdown();
+ }
+ }
+
+ @Test
void singleKeyTimestampUpdate()
{
}