This is an automated email from the ASF dual-hosted git repository.

benedict pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/cassandra-accord.git


The following commit(s) were added to refs/heads/trunk by this push:
     new 3aba47c2 Accord Fixes:  - Not updating CommandsForKey in all cases on 
restart Also Improve:  - Tracing coverage of FetchRoute  - LatestDeps.equals  - 
Route toString methods
3aba47c2 is described below

commit 3aba47c29a1cb13dba05814fd6b007165193fd7e
Author: Benedict Elliott Smith <[email protected]>
AuthorDate: Sat Jul 12 10:27:58 2025 +0100

    Accord Fixes:
     - Not updating CommandsForKey in all cases on restart
    Also Improve:
     - Tracing coverage of FetchRoute
     - LatestDeps.equals
     - Route toString methods
    
    patch by Benedict; reviewed by Alex Petrov for CASSANDRA-20763
---
 .../main/java/accord/coordinate/FetchRoute.java    | 32 ++++++++++++-----
 .../src/main/java/accord/coordinate/Infer.java     | 21 +++++++----
 .../main/java/accord/coordinate/MaybeRecover.java  |  2 +-
 .../src/main/java/accord/coordinate/Recover.java   |  2 +-
 .../accord/impl/AbstractConfigurationService.java  |  4 +--
 .../src/main/java/accord/impl/AbstractLoader.java  |  9 ++---
 .../java/accord/impl/InMemoryCommandStore.java     |  3 +-
 .../src/main/java/accord/local/SafeCommand.java    |  2 +-
 .../main/java/accord/primitives/AbstractKeys.java  | 41 +++++++++++++++++++++-
 .../java/accord/primitives/AbstractRanges.java     | 28 ++++++++++-----
 .../main/java/accord/primitives/FullKeyRoute.java  |  6 ----
 .../java/accord/primitives/FullRangeRoute.java     |  7 ----
 .../src/main/java/accord/primitives/KeyRoute.java  |  9 +++++
 .../main/java/accord/primitives/LatestDeps.java    | 14 ++++++++
 .../src/main/java/accord/primitives/Range.java     |  1 -
 .../main/java/accord/primitives/RangeRoute.java    |  5 +++
 16 files changed, 132 insertions(+), 54 deletions(-)

diff --git a/accord-core/src/main/java/accord/coordinate/FetchRoute.java 
b/accord-core/src/main/java/accord/coordinate/FetchRoute.java
index b60ecffa..e7bd8daa 100644
--- a/accord-core/src/main/java/accord/coordinate/FetchRoute.java
+++ b/accord-core/src/main/java/accord/coordinate/FetchRoute.java
@@ -20,6 +20,9 @@ package accord.coordinate;
 
 import java.util.function.BiConsumer;
 
+import javax.annotation.Nullable;
+
+import accord.api.Tracing;
 import accord.local.Commands;
 import accord.local.Node;
 import accord.local.SafeCommand;
@@ -33,6 +36,7 @@ import accord.primitives.Route;
 import accord.primitives.TxnId;
 import accord.utils.MapReduceConsume;
 
+import static accord.api.TraceEventType.FETCH;
 import static accord.coordinate.Infer.InvalidIf.NotKnownToBeInvalid;
 import static 
accord.coordinate.Infer.InvalidateAndCallback.locallyInvalidateAndCallback;
 import static accord.local.CommandStores.*;
@@ -46,28 +50,31 @@ public class FetchRoute extends CheckShards<Participants<?>>
 {
     final LatentStoreSelector reportTo;
     final BiConsumer<Route<?>, Throwable> callback;
-    FetchRoute(Node node, TxnId txnId, Infer.InvalidIf invalidIf, 
Participants<?> contactable, LatentStoreSelector reportTo, BiConsumer<Route<?>, 
Throwable> callback)
+
+    FetchRoute(Node node, TxnId txnId, Infer.InvalidIf invalidIf, 
Participants<?> contactable, LatentStoreSelector reportTo, BiConsumer<Route<?>, 
Throwable> callback, @Nullable Tracing tracing)
     {
-        super(node, node.someSequentialExecutor(), txnId, contactable, 
IncludeInfo.Route, null, invalidIf);
+        super(node, node.someSequentialExecutor(), txnId, contactable, 
txnId.epoch(), IncludeInfo.Route, null, invalidIf, tracing);
         this.reportTo = reportTo;
         this.callback = callback;
     }
 
-    public static void fetchRoute(Node node, TxnId txnId, Infer.InvalidIf 
invalidIf, Participants<?> unseekables, LatentStoreSelector reportTo, 
BiConsumer<Route<?>, Throwable> callback)
+    public static void fetchRoute(Node node, TxnId txnId, Infer.InvalidIf 
invalidIf, Participants<?> unseekables, LatentStoreSelector reportTo, 
BiConsumer<Route<?>, Throwable> callback, Tracing tracing)
     {
         if (!node.topology().hasEpoch(txnId.epoch()))
         {
-            node.withEpochAtLeast(txnId.epoch(), null, callback, () -> 
fetchRoute(node, txnId, invalidIf, unseekables, reportTo, callback));
+            if (tracing != null)
+                tracing.trace(null, "Waiting for epoch %d", txnId.epoch());
+            node.withEpochAtLeast(txnId.epoch(), null, callback, () -> 
fetchRoute(node, txnId, invalidIf, unseekables, reportTo, callback, tracing));
             return;
         }
 
-        FetchRoute fetchRoute = new FetchRoute(node, txnId, invalidIf, 
unseekables, reportTo, callback);
+        FetchRoute fetchRoute = new FetchRoute(node, txnId, invalidIf, 
unseekables, reportTo, callback, tracing);
         fetchRoute.start();
     }
 
     public static void fetchRoute(Node node, TxnId txnId, Participants<?> 
contactable, LatentStoreSelector reportTo, BiConsumer<Route<?>, Throwable> 
callback)
     {
-        fetchRoute(node, txnId, NotKnownToBeInvalid, contactable, reportTo, 
callback);
+        fetchRoute(node, txnId, NotKnownToBeInvalid, contactable, reportTo, 
callback, node.agent().trace(txnId, FETCH));
     }
 
     @Override
@@ -88,7 +95,7 @@ public class FetchRoute extends CheckShards<Participants<?>>
                 Known known = Nothing;
                 if (merged != null)
                     known = merged.finish(query, query, query, 
success.withQuorum, previouslyKnownToBeInvalidIf).knownFor(txnId, query, query);
-                reportRouteNotFound(node, success.withQuorum, known, txnId, 
query, reportTo, callback);
+                reportRouteNotFound(node, success.withQuorum, known, txnId, 
query, reportTo, callback, tracing);
             }
             else
             {
@@ -120,13 +127,16 @@ public class FetchRoute extends 
CheckShards<Participants<?>>
         }
     }
 
-    private static void reportRouteNotFound(Node node, WithQuorum withQuorum, 
Known found, TxnId txnId, Participants<?> participants, LatentStoreSelector 
reportTo, BiConsumer<Route<?>, Throwable> callback)
+    private static void reportRouteNotFound(Node node, WithQuorum withQuorum, 
Known found, TxnId txnId, Participants<?> participants, LatentStoreSelector 
reportTo, BiConsumer<Route<?>, Throwable> callback, @Nullable Tracing tracing)
     {
         switch (found.outcome())
         {
             default: throw new AssertionError("Unknown outcome: " + 
found.outcome());
             case Abort:
-                locallyInvalidateAndCallback(node, txnId, 
reportTo.refine(txnId, null, participants), participants, null, callback);
+                if (tracing != null)
+                    tracing.trace(null, "No Route. Found %s; invalidating", 
found);
+
+                locallyInvalidateAndCallback(node, txnId, 
reportTo.refine(txnId, null, participants), participants, null, callback, 
tracing);
                 break;
 
             case Unknown:
@@ -135,6 +145,10 @@ public class FetchRoute extends 
CheckShards<Participants<?>>
                     Invalidate.invalidate(node, txnId, participants, false, 
reportTo, (outcome, throwable) -> callback.accept(null, throwable));
                     break;
                 }
+                else if (tracing != null)
+                {
+                    tracing.trace(null, "No Route. Found %s; cannot invalidate 
(%s, %s)", found, withQuorum, found.canProposeInvalidation());
+                }
             case Erased:
             case WasApply:
             case Apply:
diff --git a/accord-core/src/main/java/accord/coordinate/Infer.java 
b/accord-core/src/main/java/accord/coordinate/Infer.java
index e5e4c6b3..71ee91fb 100644
--- a/accord-core/src/main/java/accord/coordinate/Infer.java
+++ b/accord-core/src/main/java/accord/coordinate/Infer.java
@@ -20,6 +20,9 @@ package accord.coordinate;
 
 import java.util.function.BiConsumer;
 
+import javax.annotation.Nullable;
+
+import accord.api.Tracing;
 import accord.local.Command;
 import accord.local.CommandStores.StoreFinder;
 import accord.local.CommandStores.StoreSelector;
@@ -111,8 +114,9 @@ public class Infer
         final Participants<?> participants;
         final T param;
         final BiConsumer<T, Throwable> callback;
+        final Tracing tracing;
 
-        private CleanupAndCallback(Node node, TxnId txnId, StoreSelector 
reportTo, Participants<?> participants, T param, BiConsumer<T, Throwable> 
callback)
+        private CleanupAndCallback(Node node, TxnId txnId, StoreSelector 
reportTo, Participants<?> participants, T param, BiConsumer<T, Throwable> 
callback, Tracing tracing)
         {
             this.node = node;
             this.txnId = txnId;
@@ -120,6 +124,7 @@ public class Infer
             this.participants = participants;
             this.param = param;
             this.callback = callback;
+            this.tracing = tracing;
         }
 
         void start()
@@ -152,19 +157,19 @@ public class Infer
 
     static class InvalidateAndCallback<T> extends CleanupAndCallback<T>
     {
-        private InvalidateAndCallback(Node node, TxnId txnId, StoreSelector 
selector, Participants<?> participants, T param, BiConsumer<T, Throwable> 
callback)
+        private InvalidateAndCallback(Node node, TxnId txnId, StoreSelector 
selector, Participants<?> participants, T param, BiConsumer<T, Throwable> 
callback, @Nullable Tracing tracing)
         {
-            super(node, txnId, selector, participants, param, callback);
+            super(node, txnId, selector, participants, param, callback, 
tracing);
         }
 
-        public static <T> void locallyInvalidateAndCallback(Node node, TxnId 
txnId, long lowEpoch, long highEpoch, Participants<?> participants, T param, 
BiConsumer<T, Throwable> callback)
+        public static <T> void locallyInvalidateAndCallback(Node node, TxnId 
txnId, long lowEpoch, long highEpoch, Participants<?> participants, T param, 
BiConsumer<T, Throwable> callback, @Nullable Tracing tracing)
         {
-            new InvalidateAndCallback<>(node, txnId, 
StoreFinder.selector(participants, lowEpoch, highEpoch), participants, param, 
callback).start();
+            new InvalidateAndCallback<>(node, txnId, 
StoreFinder.selector(participants, lowEpoch, highEpoch), participants, param, 
callback, tracing).start();
         }
 
-        public static <T> void locallyInvalidateAndCallback(Node node, TxnId 
txnId, StoreSelector selector, Participants<?> participants, T param, 
BiConsumer<T, Throwable> callback)
+        public static <T> void locallyInvalidateAndCallback(Node node, TxnId 
txnId, StoreSelector selector, Participants<?> participants, T param, 
BiConsumer<T, Throwable> callback, @Nullable Tracing tracing)
         {
-            new InvalidateAndCallback<>(node, txnId, selector, participants, 
param, callback).start();
+            new InvalidateAndCallback<>(node, txnId, selector, participants, 
param, callback, tracing).start();
         }
 
         @Override
@@ -172,6 +177,8 @@ public class Infer
         {
             // we're applying an invalidation, so the record will not be 
cleaned up until the whole range is truncated
             Command command = safeCommand.current();
+            if (tracing != null)
+                tracing.trace(safeStore.commandStore(), "Invalidating (from 
%s)", command.saveStatus());
             Invariants.require(!command.hasBeen(PreCommitted) || 
command.hasBeen(Status.Truncated), "Unexpected status for %s", command);
             Commands.commitInvalidate(safeStore, safeCommand, participants);
             return null;
diff --git a/accord-core/src/main/java/accord/coordinate/MaybeRecover.java 
b/accord-core/src/main/java/accord/coordinate/MaybeRecover.java
index 160c30c7..922c671b 100644
--- a/accord-core/src/main/java/accord/coordinate/MaybeRecover.java
+++ b/accord-core/src/main/java/accord/coordinate/MaybeRecover.java
@@ -135,7 +135,7 @@ public class MaybeRecover extends CheckShards<Route<?>>
 
                 case Abort:
                     commitInvalidate(node, txnId, Route.merge(full.route, 
(Route) query), txnId.epoch());
-                    locallyInvalidateAndCallback(node, txnId, txnId.epoch(), 
txnId.epoch(), someRoute, full.toProgressToken(), callback);
+                    locallyInvalidateAndCallback(node, txnId, txnId.epoch(), 
txnId.epoch(), someRoute, full.toProgressToken(), callback, null);
                     break;
             }
         }
diff --git a/accord-core/src/main/java/accord/coordinate/Recover.java 
b/accord-core/src/main/java/accord/coordinate/Recover.java
index 20bd8cbb..398b5080 100644
--- a/accord-core/src/main/java/accord/coordinate/Recover.java
+++ b/accord-core/src/main/java/accord/coordinate/Recover.java
@@ -530,7 +530,7 @@ public class Recover implements Callback<RecoverReply>, 
BiConsumer<Result, Throw
             Commit.Invalidate.commitInvalidate(node, txnId, route, 
invalidateUntil);
         });
         isDone = true;
-        locallyInvalidateAndCallback(node, txnId, reportTo.refine(txnId, null, 
route), route, ProgressToken.INVALIDATED, callback);
+        locallyInvalidateAndCallback(node, txnId, reportTo.refine(txnId, null, 
route), route, ProgressToken.INVALIDATED, callback, null);
     }
 
     private void propose(Accept.Kind kind, Timestamp executeAt, 
List<RecoverOk> recoverOkList)
diff --git 
a/accord-core/src/main/java/accord/impl/AbstractConfigurationService.java 
b/accord-core/src/main/java/accord/impl/AbstractConfigurationService.java
index 0c4d0e56..bf579e79 100644
--- a/accord-core/src/main/java/accord/impl/AbstractConfigurationService.java
+++ b/accord-core/src/main/java/accord/impl/AbstractConfigurationService.java
@@ -376,9 +376,9 @@ public abstract class 
AbstractConfigurationService<EpochState extends AbstractCo
         long lastAcked = epochs.lastAcknowledged();
         if (lastAcked == 0 && lastReceived > 0)
         {
-            logger.debug("Epoch {} received; waiting for {} to ack before 
reporting", topology.epoch(), epochs.minEpoch(), executor());
+            logger.debug("Epoch {} received; waiting for {} to ack before 
reporting", topology.epoch(), epochs.minEpoch());
             epochs.acknowledgeFuture(epochs.minEpoch())
-                  .invokeIfSuccess(() -> reportTopology(topology, isLoad, 
startSync))
+                  .invokeIfSuccess(() -> reportTopology(topology, isLoad, 
startSync), executor())
                   .begin(agent);
             return;
         }
diff --git a/accord-core/src/main/java/accord/impl/AbstractLoader.java 
b/accord-core/src/main/java/accord/impl/AbstractLoader.java
index 152f67c8..c0dba44d 100644
--- a/accord-core/src/main/java/accord/impl/AbstractLoader.java
+++ b/accord-core/src/main/java/accord/impl/AbstractLoader.java
@@ -31,23 +31,19 @@ import accord.utils.Invariants;
 
 import static accord.primitives.SaveStatus.Applying;
 import static accord.primitives.SaveStatus.PreApplied;
-import static accord.primitives.SaveStatus.TruncatedApply;
 import static accord.primitives.SaveStatus.TruncatedApplyWithOutcome;
 import static accord.primitives.Status.Applied;
-import static accord.primitives.Status.Stable;
-import static accord.primitives.Status.Truncated;
 import static accord.primitives.Txn.Kind.Write;
 
 public abstract class AbstractLoader implements Journal.Loader
 {
-    protected void maybeApplyWrites(SafeCommandStore safeStore, TxnId txnId)
+    protected void initialiseState(SafeCommandStore safeStore, TxnId txnId)
     {
         SafeCommand safeCommand = safeStore.unsafeGet(txnId);
         Command command = safeCommand.current();
         if (command.saveStatus().compareTo(SaveStatus.Stable) >= 0 && 
command.saveStatus().compareTo(PreApplied) <= 0)
         {
-            if (Commands.maybeExecute(safeStore, safeCommand, command, true, 
true))
-                return;
+            Commands.maybeExecute(safeStore, safeCommand, command, false, 
true);
         }
         else if (command.saveStatus().compareTo(Applying) >= 0 && 
command.saveStatus().compareTo(TruncatedApplyWithOutcome) <= 0)
         {
@@ -64,7 +60,6 @@ public abstract class AbstractLoader implements Journal.Loader
                                Commands.postApply(ss, txnId, -1, true);
                            }))
                            .begin(safeStore.agent());
-                    return;
                 }
             }
             else Invariants.expect(command.hasBeen(Applied));
diff --git a/accord-core/src/main/java/accord/impl/InMemoryCommandStore.java 
b/accord-core/src/main/java/accord/impl/InMemoryCommandStore.java
index 08d59d83..857c98f5 100644
--- a/accord-core/src/main/java/accord/impl/InMemoryCommandStore.java
+++ b/accord-core/src/main/java/accord/impl/InMemoryCommandStore.java
@@ -90,7 +90,6 @@ import org.agrona.collections.ObjectHashSet;
 import static accord.local.Cleanup.Input.FULL;
 import static accord.local.KeyHistory.ASYNC;
 import static accord.local.KeyHistory.NONE;
-import static accord.local.KeyHistory.SYNC;
 import static accord.local.RedundantStatus.Coverage.ALL;
 import static accord.local.StoreParticipants.Filter.LOAD;
 import static accord.primitives.Known.KnownRoute.MaybeRoute;
@@ -1191,7 +1190,7 @@ public abstract class InMemoryCommandStore extends 
CommandStore
             return 
AsyncChains.success(commandStore.executeInContext(commandStore,
                                                                      txnId,
                                                                      
(SafeCommandStore safeStore) -> {
-                                                                         
maybeApplyWrites(safeStore, txnId);
+                                                                         
initialiseState(safeStore, txnId);
                                                                          
return null;
                                                                      }));
         }
diff --git a/accord-core/src/main/java/accord/local/SafeCommand.java 
b/accord-core/src/main/java/accord/local/SafeCommand.java
index dcccb4c3..943c4483 100644
--- a/accord-core/src/main/java/accord/local/SafeCommand.java
+++ b/accord-core/src/main/java/accord/local/SafeCommand.java
@@ -67,7 +67,7 @@ public abstract class SafeCommand
     public <C extends Command> C update(SafeCommandStore safeStore, C update, 
boolean force)
     {
         Command prev = current();
-        if (prev == update)
+        if (prev == update && !force)
             return update;
 
         set(update);
diff --git a/accord-core/src/main/java/accord/primitives/AbstractKeys.java 
b/accord-core/src/main/java/accord/primitives/AbstractKeys.java
index 2dee5b85..74d1289d 100644
--- a/accord-core/src/main/java/accord/primitives/AbstractKeys.java
+++ b/accord-core/src/main/java/accord/primitives/AbstractKeys.java
@@ -20,6 +20,7 @@ package accord.primitives;
 
 import java.util.Arrays;
 import java.util.Iterator;
+import java.util.Objects;
 import java.util.function.Consumer;
 import java.util.function.Function;
 import java.util.function.IntFunction;
@@ -27,6 +28,8 @@ import java.util.function.Predicate;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
+import javax.annotation.Nullable;
+
 import accord.api.RoutingKey;
 import accord.utils.ArrayBuffers.ObjectBuffers;
 import accord.utils.AsymmetricComparator;
@@ -172,9 +175,45 @@ public abstract class AbstractKeys<K extends RoutableKey> 
implements Iterable<K>
     @Override
     public String toString()
     {
-        return stream().map(Object::toString).collect(Collectors.joining(",", 
"[", "]"));
+        return toString("", null);
     }
 
+    protected String toString(String suffix, @Nullable Function<? super K, 
String> keySuffixes)
+    {
+        if (isEmpty() && suffix.isEmpty())
+            return "[]";
+
+        StringBuilder sb = new StringBuilder();
+        sb.append('[');
+        int i = 0;
+        while (i < keys.length)
+        {
+            if (i > 0) sb.append(", ");
+            Object prefix = keys[i].prefix();
+            int j = i + 1;
+            while (j < keys.length && Objects.equals(prefix, keys[j].prefix()))
+                ++j;
+            if (prefix != null)
+            {
+                sb.append(prefix);
+                sb.append(':');
+                sb.append('[');
+            }
+            while (i < j)
+            {
+                K key = keys[i++];
+                sb.append(key.printableSuffix());
+                if (keySuffixes != null)
+                    sb.append(keySuffixes.apply(key));
+                if (i < j) sb.append(',');
+            }
+            if (prefix != null)
+                sb.append(']');
+        }
+        sb.append(suffix);
+        sb.append(']');
+        return sb.toString();
+    }
 
     // TODO (expected, efficiency): accept cached buffers
     protected K[] slice(AbstractRanges ranges, IntFunction<K[]> factory)
diff --git a/accord-core/src/main/java/accord/primitives/AbstractRanges.java 
b/accord-core/src/main/java/accord/primitives/AbstractRanges.java
index 266b434d..62f9e8d6 100644
--- a/accord-core/src/main/java/accord/primitives/AbstractRanges.java
+++ b/accord-core/src/main/java/accord/primitives/AbstractRanges.java
@@ -20,6 +20,7 @@ package accord.primitives;
 
 import java.util.Arrays;
 import java.util.Iterator;
+import java.util.Objects;
 import java.util.function.Function;
 import javax.annotation.Nonnull;
 
@@ -623,9 +624,13 @@ public abstract class AbstractRanges implements 
Iterable<Range>, Routables<Range
     @Override
     public String toString()
     {
-        if (isEmpty()) return "[]";
-        if (ranges[0].start().prefix() == null)
-            return Arrays.toString(ranges);
+        return toString("");
+    }
+
+    protected String toString(String suffix)
+    {
+        if (isEmpty() && suffix.isEmpty())
+            return "[]";
 
         StringBuilder sb = new StringBuilder();
         sb.append('[');
@@ -635,18 +640,23 @@ public abstract class AbstractRanges implements 
Iterable<Range>, Routables<Range
             if (i > 0) sb.append(", ");
             Object prefix = ranges[i].start().prefix();
             int j = i + 1;
-            while (j < ranges.length && 
prefix.equals(ranges[j].end().prefix()))
+            while (j < ranges.length && Objects.equals(prefix, 
ranges[j].end().prefix()))
                 ++j;
-            sb.append(prefix);
-            sb.append(':');
-            sb.append('[');
+            if (prefix != null)
+            {
+                sb.append(prefix);
+                sb.append(':');
+                sb.append('[');
+            }
             while (i < j)
             {
                 sb.append(ranges[i++].toSuffixString());
-                if (i < j) sb.append(", ");
+                if (i < j) sb.append(',');
             }
-            sb.append(']');
+            if (prefix != null)
+                sb.append(']');
         }
+        sb.append(suffix);
         sb.append(']');
         return sb.toString();
     }
diff --git a/accord-core/src/main/java/accord/primitives/FullKeyRoute.java 
b/accord-core/src/main/java/accord/primitives/FullKeyRoute.java
index 4f6396a6..a910e47a 100644
--- a/accord-core/src/main/java/accord/primitives/FullKeyRoute.java
+++ b/accord-core/src/main/java/accord/primitives/FullKeyRoute.java
@@ -70,10 +70,4 @@ public class FullKeyRoute extends KeyRoute implements 
FullRoute<RoutingKey>
     {
         return true;
     }
-
-    @Override
-    public String toString()
-    {
-        return "{homeKey:" + homeKey + ',' + super.toString() + '}';
-    }
 }
diff --git a/accord-core/src/main/java/accord/primitives/FullRangeRoute.java 
b/accord-core/src/main/java/accord/primitives/FullRangeRoute.java
index 327f95f0..8f2d78f0 100644
--- a/accord-core/src/main/java/accord/primitives/FullRangeRoute.java
+++ b/accord-core/src/main/java/accord/primitives/FullRangeRoute.java
@@ -62,11 +62,4 @@ public class FullRangeRoute extends RangeRoute implements 
FullRoute<Range>
     {
         return this;
     }
-
-    @Override
-    public String toString()
-    {
-        return "{homeKey:" + homeKey + ',' + super.toString() + '}';
-    }
-
 }
diff --git a/accord-core/src/main/java/accord/primitives/KeyRoute.java 
b/accord-core/src/main/java/accord/primitives/KeyRoute.java
index f2ab3e5c..a1beeccf 100644
--- a/accord-core/src/main/java/accord/primitives/KeyRoute.java
+++ b/accord-core/src/main/java/accord/primitives/KeyRoute.java
@@ -18,6 +18,8 @@
 
 package accord.primitives;
 
+import java.util.Objects;
+
 import accord.utils.Invariants;
 
 import accord.api.RoutingKey;
@@ -164,4 +166,11 @@ public abstract class KeyRoute extends 
AbstractUnseekableKeys implements Route<R
     {
         return newKeys == keys ? this : new PartialKeyRoute(homeKey, newKeys);
     }
+
+    @Override
+    public String toString()
+    {
+        boolean containsHomeKey = containsHomeKey();
+        return toString(containsHomeKey ? "" : "(homeKey:" + homeKey + ')', 
containsHomeKey ? test -> test.equals(homeKey) ? "*" : "" : null);
+    }
 }
diff --git a/accord-core/src/main/java/accord/primitives/LatestDeps.java 
b/accord-core/src/main/java/accord/primitives/LatestDeps.java
index 8f856c29..605674b3 100644
--- a/accord-core/src/main/java/accord/primitives/LatestDeps.java
+++ b/accord-core/src/main/java/accord/primitives/LatestDeps.java
@@ -20,6 +20,7 @@ package accord.primitives;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Objects;
 import java.util.function.BiConsumer;
 import java.util.function.BiFunction;
 import java.util.function.Consumer;
@@ -270,6 +271,19 @@ public class LatestDeps extends 
ReducingRangeMap<LatestDeps.LatestEntry>
                    + (localDeps == null ? "" : ",local:" + 
localDeps.keyDeps.toBriefString() + "/" + localDeps.rangeDeps.toBriefString())
                    + (coordinatedDeps == null ? "" : ",coordinated:" + 
coordinatedDeps.keyDeps.toBriefString() + "/" + 
coordinatedDeps.rangeDeps.toBriefString());
         }
+
+        @Override
+        public boolean equals(Object obj)
+        {
+            if (!(obj instanceof LatestEntry))
+                return false;
+
+            LatestEntry that = (LatestEntry) obj;
+            return this.known == that.known
+                   && Objects.equals(this.ballot, that.ballot)
+                   && Objects.equals(this.localDeps, that.localDeps)
+                   && Objects.equals(this.coordinatedDeps, 
that.coordinatedDeps);
+        }
     }
 
     private LatestDeps()
diff --git a/accord-core/src/main/java/accord/primitives/Range.java 
b/accord-core/src/main/java/accord/primitives/Range.java
index 853e9c62..69d0c087 100644
--- a/accord-core/src/main/java/accord/primitives/Range.java
+++ b/accord-core/src/main/java/accord/primitives/Range.java
@@ -489,5 +489,4 @@ public abstract class Range implements 
Comparable<RoutableKey>, Unseekable, Seek
     {
         return (startInclusive() ? "[" : "(") + start().printableSuffix() + 
"," + end().printableSuffix() + (endInclusive() ? ']' : ')');
     }
-
 }
diff --git a/accord-core/src/main/java/accord/primitives/RangeRoute.java 
b/accord-core/src/main/java/accord/primitives/RangeRoute.java
index 2bc692ee..a0b4ea51 100644
--- a/accord-core/src/main/java/accord/primitives/RangeRoute.java
+++ b/accord-core/src/main/java/accord/primitives/RangeRoute.java
@@ -185,4 +185,9 @@ public abstract class RangeRoute extends AbstractRanges 
implements Route<Range>,
         return super.equals(that) && 
homeKey.equals(((RangeRoute)that).homeKey);
     }
 
+    @Override
+    public String toString()
+    {
+        return "{homeKey:" + homeKey + ',' + super.toString() + '}';
+    }
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to