From ece2091b532f047d01253cee37862c5d854a9551 Mon Sep 17 00:00:00 2001 From: Dai Ha Date: Fri, 4 Sep 2026 11:32:56 +0700 Subject: [PATCH] #280: states is now touched by two scheduler tasks, so make it concurrent MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The delayed re-check reads `states` from its own scheduled task, while `tick` writes and prunes it. Both run on the single-threaded scheduler Fleetd passes in today, so they are serialised — but nothing in the class enforces that, and an unsynchronised HashMap read racing a resize can spin a CPU forever rather than fail visibly. `priors` and `orphanStreaks` stay plain maps: `tick` is still their only toucher. The comment says which is which, so the next person does not have to re-derive it. --- .../dev/ltms/fleet/health/FleetHealthMonitor.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/fleetd/src/main/java/dev/ltms/fleet/health/FleetHealthMonitor.java b/fleetd/src/main/java/dev/ltms/fleet/health/FleetHealthMonitor.java index 6ec2e49..cfa11b3 100644 --- a/fleetd/src/main/java/dev/ltms/fleet/health/FleetHealthMonitor.java +++ b/fleetd/src/main/java/dev/ltms/fleet/health/FleetHealthMonitor.java @@ -14,6 +14,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.function.BiConsumer; @@ -46,7 +47,16 @@ public final class FleetHealthMonitor { private final long workingSuspectAfterNanos; private final BiConsumer failTarget; private final Map priors = new HashMap<>(); - private final Map states = new HashMap<>(); + /** + * The live classification per member, and the only one of this class's three maps that more + * than one scheduler task touches. {@code tick} writes it (and prunes it to the roster); + * fleetd #280's delayed {@link #recheckTerminalTarget} reads it from its own separate scheduled + * task. Both run on the single-threaded scheduler {@code Fleetd} passes in today, so they are + * serialised — but nothing in this class enforces that, and an unsynchronised {@link HashMap} + * read racing a resize can spin a CPU forever rather than fail visibly. {@code priors} and + * {@code orphanStreaks} stay plain maps because {@code tick} is still their only toucher. + */ + private final Map states = new ConcurrentHashMap<>(); /** * CB-643: consecutive ticks on which a target looked like an orphaned delegation. The fact * {@link MessageService#hasOrphanedDelegation} reports is a true snapshot, but it can read true