From 32452c8c8df3ae10c1a2f1a03235c0224899c084 Mon Sep 17 00:00:00 2001 From: XxFran10xX <318299142+XxFran10xX@users.noreply.github.com> Date: Thu, 5 Nov 2026 11:16:24 +0100 Subject: [PATCH] perf: move the hourly estate reconcile 45 minutes off the shared hourly tick CharacterQueueEstate.reconcile (about 90 ms on Main, mostly fsync) ran at enable + 1 h and hourly after, the tick most plugins' hourly estate passes share (RPCharacters' coordinator runs the tick before). The boot pass still runs on the first tick; the hourly passes now start at 1 h 45 min and repeat every hour, the same pattern Archaeo uses with a 15 minute offset. Co-Authored-By: Claude Opus 5.5 --- .../recycler/ownership/CharacterQueueEstate.java | 8 +++++++- .../tfminecraft/recycler/ownership/QueueEstateTest.java | 6 +++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/tfminecraft/recycler/ownership/CharacterQueueEstate.java b/src/main/java/net/tfminecraft/recycler/ownership/CharacterQueueEstate.java index c7b2bc0..7963ddf 100644 --- a/src/main/java/net/tfminecraft/recycler/ownership/CharacterQueueEstate.java +++ b/src/main/java/net/tfminecraft/recycler/ownership/CharacterQueueEstate.java @@ -17,11 +17,17 @@ public final class CharacterQueueEstate implements EstateParticipant { private final Recycler plugin; private final Map> reported = new java.util.HashMap<>(); + static final long HOUR_TICKS = 72_000L; + /** 45 minutes: keeps the hourly pass off the tick other plugins' hourly jobs share (enable + 1 h). */ + static final long HOURLY_OFFSET_TICKS = 54_000L; public CharacterQueueEstate(Recycler plugin) { this.plugin = plugin; } public static void start(Recycler plugin) { CharacterQueueEstate participant = new CharacterQueueEstate(plugin); plugin.getServer().getServicesManager().register(EstateParticipant.class, participant, plugin, ServicePriority.Normal); - plugin.getServer().getScheduler().runTaskTimer(plugin, participant::reconcile, 1L, 72_000L); + var scheduler = plugin.getServer().getScheduler(); + // Boot pass on the first tick as before; the hourly passes then run 45 minutes later than they did. + scheduler.runTaskLater(plugin, participant::reconcile, 1L); + scheduler.runTaskTimer(plugin, participant::reconcile, 1L + HOUR_TICKS + HOURLY_OFFSET_TICKS, HOUR_TICKS); } public String id() { return "recycler"; } public Outcome settle(EstateDecision decision) { diff --git a/src/test/java/net/tfminecraft/recycler/ownership/QueueEstateTest.java b/src/test/java/net/tfminecraft/recycler/ownership/QueueEstateTest.java index 77573b4..5296b98 100644 --- a/src/test/java/net/tfminecraft/recycler/ownership/QueueEstateTest.java +++ b/src/test/java/net/tfminecraft/recycler/ownership/QueueEstateTest.java @@ -61,6 +61,10 @@ class QueueEstateTest { } var server=mock(org.bukkit.Server.class);var services=mock(org.bukkit.plugin.ServicesManager.class);var scheduler=mock(org.bukkit.scheduler.BukkitScheduler.class); when(plugin.getServer()).thenReturn(server);when(server.getServicesManager()).thenReturn(services);when(server.getScheduler()).thenReturn(scheduler); - QueueEstate.start(plugin);verify(services).register(eq(EstateParticipant.class),any(CharacterQueueEstate.class),eq(plugin),eq(org.bukkit.plugin.ServicePriority.Normal));verify(scheduler).runTaskTimer(eq(plugin),any(Runnable.class),eq(1L),eq(72_000L)); + QueueEstate.start(plugin);verify(services).register(eq(EstateParticipant.class),any(CharacterQueueEstate.class),eq(plugin),eq(org.bukkit.plugin.ServicePriority.Normal)); + // Boot pass on the first tick; hourly passes 45 minutes off the enable + 1 h tick other plugins use. + var boot=org.mockito.ArgumentCaptor.forClass(Runnable.class);var hourly=org.mockito.ArgumentCaptor.forClass(Runnable.class); + verify(scheduler).runTaskLater(eq(plugin),boot.capture(),eq(1L));verify(scheduler).runTaskTimer(eq(plugin),hourly.capture(),eq(1L+72_000L+54_000L),eq(72_000L));verifyNoMoreInteractions(scheduler); + try(var estates=mockStatic(Estates.class)){boot.getValue().run();hourly.getValue().run();estates.verify(Estates::decided,times(2));} } }