From 5bec68c27e45fc84b09e14cbf20b15bd92300250 Mon Sep 17 00:00:00 2001 From: Ben Date: Mon, 28 Sep 2026 20:25:45 -0600 Subject: [PATCH 1/4] Reject proxy votes at pending admission limit --- .../proxy/bungee/VotingPluginBungee.java | 21 ++++--------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/VotingPlugin/src/main/java/com/bencodez/votingplugin/proxy/bungee/VotingPluginBungee.java b/VotingPlugin/src/main/java/com/bencodez/votingplugin/proxy/bungee/VotingPluginBungee.java index 321cbe310..b54b409e1 100644 --- a/VotingPlugin/src/main/java/com/bencodez/votingplugin/proxy/bungee/VotingPluginBungee.java +++ b/VotingPlugin/src/main/java/com/bencodez/votingplugin/proxy/bungee/VotingPluginBungee.java @@ -615,24 +615,11 @@ public void acceptIncomingVote(String player, String service) { if (!pendingIncomingVotes.isAccepting()) { getLogger().severe("Vote received after VotingPlugin proxy shutdown began; vote was not accepted for " + MinecraftUsernameValidator.sanitizeForLog(player)); - return; - } - synchronized (reloadLock) { - if (!pendingIncomingVotes.isAccepting()) { - getLogger().severe("Vote received after VotingPlugin proxy shutdown began; vote was not accepted for " - + MinecraftUsernameValidator.sanitizeForLog(player)); - return; - } - pending = new PendingIncomingVote(UUID.randomUUID(), player, service, System.currentTimeMillis()); - if (votingPluginProxy == null || !votingPluginProxy.retainIncomingVoteForRestart(pending)) { - getLogger().severe("Pending vote admission is full and durable overflow failed; vote was not accepted for " - + MinecraftUsernameValidator.sanitizeForLog(player)); - return; - } - votingPluginProxy.scheduleQueuedVoteReplay(); - getLogger().warning("Pending vote admission is full; accepted vote was handed directly to durable recovery"); - return; + } else { + getLogger().severe("Pending vote admission is full; vote was rejected instead of expanding durable recovery for " + + MinecraftUsernameValidator.sanitizeForLog(player)); } + return; } retryPendingIncomingVotes(); } From 593407d70297784d32701241898439c6d5a7cfb1 Mon Sep 17 00:00:00 2001 From: Ben Date: Mon, 28 Sep 2026 20:25:47 -0600 Subject: [PATCH 2/4] Reject proxy votes at pending admission limit --- .../proxy/velocity/VotingPluginVelocity.java | 21 ++++--------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/VotingPlugin/src/main/java/com/bencodez/votingplugin/proxy/velocity/VotingPluginVelocity.java b/VotingPlugin/src/main/java/com/bencodez/votingplugin/proxy/velocity/VotingPluginVelocity.java index 8e5f98818..52e48aab5 100644 --- a/VotingPlugin/src/main/java/com/bencodez/votingplugin/proxy/velocity/VotingPluginVelocity.java +++ b/VotingPlugin/src/main/java/com/bencodez/votingplugin/proxy/velocity/VotingPluginVelocity.java @@ -477,24 +477,11 @@ public void acceptIncomingVote(String player, String service) { if (!pendingIncomingVotes.isAccepting()) { logger.error("Vote received after VotingPlugin proxy shutdown began; vote was not accepted for {}", MinecraftUsernameValidator.sanitizeForLog(player)); - return; - } - synchronized (reloadLock) { - if (!pendingIncomingVotes.isAccepting()) { - logger.error("Vote received after VotingPlugin proxy shutdown began; vote was not accepted for {}", - MinecraftUsernameValidator.sanitizeForLog(player)); - return; - } - pending = new PendingIncomingVote(UUID.randomUUID(), player, service, System.currentTimeMillis()); - if (votingPluginProxy == null || !votingPluginProxy.retainIncomingVoteForRestart(pending)) { - logger.error("Pending vote admission is full and durable overflow failed; vote was not accepted for {}", - MinecraftUsernameValidator.sanitizeForLog(player)); - return; - } - votingPluginProxy.scheduleQueuedVoteReplay(); - logger.warn("Pending vote admission is full; accepted vote was handed directly to durable recovery"); - return; + } else { + logger.error("Pending vote admission is full; vote was rejected instead of expanding durable recovery for {}", + MinecraftUsernameValidator.sanitizeForLog(player)); } + return; } retryPendingIncomingVotes(); } From 792477dcc341a1df0454dfe1db06adf33f61ca56 Mon Sep 17 00:00:00 2001 From: Ben Date: Mon, 28 Sep 2026 20:25:49 -0600 Subject: [PATCH 3/4] Test Bungee pending vote hard limit --- .../VotingPluginBungeeInitializationTest.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/VotingPlugin/src/test/java/com/bencodez/votingplugin/proxy/bungee/VotingPluginBungeeInitializationTest.java b/VotingPlugin/src/test/java/com/bencodez/votingplugin/proxy/bungee/VotingPluginBungeeInitializationTest.java index 51d8d645c..c197e8a09 100644 --- a/VotingPlugin/src/test/java/com/bencodez/votingplugin/proxy/bungee/VotingPluginBungeeInitializationTest.java +++ b/VotingPlugin/src/test/java/com/bencodez/votingplugin/proxy/bungee/VotingPluginBungeeInitializationTest.java @@ -173,6 +173,23 @@ void rejectedSchedulerAdmissionLeavesVoteOwnedUntilDurableHandoff() throws Excep assertEquals(0, pending.size()); } + @Test + void fullPendingAdmissionRejectsInsteadOfSpillingToDurableRecovery() throws Exception { + VotingPluginBungee plugin = mock(VotingPluginBungee.class, CALLS_REAL_METHODS); + VotingPluginProxy runtime = mock(VotingPluginProxy.class); + PendingIncomingVoteQueue queue = new PendingIncomingVoteQueue(); + for (int i = 0; i < 4096; i++) assertTrue(queue.admit("Player" + i, "Service") != null); + setField(plugin, "pendingIncomingVotes", queue); + setField(plugin, "votingPluginProxy", runtime); + when(plugin.getLogger()).thenReturn(java.util.logging.Logger.getLogger("VotingPluginBungeeInitializationTest")); + + plugin.acceptIncomingVote("Overflow", "Service"); + + assertEquals(4096, queue.size()); + verify(runtime, never()).retainIncomingVoteForRestart(any(PendingIncomingVote.class)); + verify(runtime, never()).scheduleQueuedVoteReplay(); + } + @Test void failedDurableHandoffSchedulesAnotherAttempt() throws Exception { VotingPluginBungee plugin = mock(VotingPluginBungee.class, CALLS_REAL_METHODS); From ba8e49a87ce460af1f166fc5ac3b829a53aabe10 Mon Sep 17 00:00:00 2001 From: Ben Date: Mon, 28 Sep 2026 20:25:51 -0600 Subject: [PATCH 4/4] Test Velocity pending vote hard limit --- .../VotingPluginVelocityInitializationTest.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/VotingPlugin/src/test/java/com/bencodez/votingplugin/proxy/velocity/VotingPluginVelocityInitializationTest.java b/VotingPlugin/src/test/java/com/bencodez/votingplugin/proxy/velocity/VotingPluginVelocityInitializationTest.java index 1bcb7c463..0740d6ff2 100644 --- a/VotingPlugin/src/test/java/com/bencodez/votingplugin/proxy/velocity/VotingPluginVelocityInitializationTest.java +++ b/VotingPlugin/src/test/java/com/bencodez/votingplugin/proxy/velocity/VotingPluginVelocityInitializationTest.java @@ -256,6 +256,23 @@ void rejectedExecutorAdmissionLeavesVoteOwnedUntilDurableHandoff(@TempDir Path d assertEquals(0, pending.size()); } + @Test + void fullPendingAdmissionRejectsInsteadOfSpillingToDurableRecovery(@TempDir Path dataDirectory) throws Exception { + VotingPluginVelocity plugin = new VotingPluginVelocity(mock(ProxyServer.class), mock(Logger.class), + mock(Metrics.Factory.class), dataDirectory); + VotingPluginProxy runtime = mock(VotingPluginProxy.class); + PendingIncomingVoteQueue queue = (PendingIncomingVoteQueue) getField(plugin, "pendingIncomingVotes"); + for (int i = 0; i < 4096; i++) assertTrue(queue.admit("Player" + i, "Service") != null); + setField(plugin, "votingPluginProxy", runtime); + + plugin.acceptIncomingVote("Overflow", "Service"); + + assertEquals(4096, queue.size()); + verify(runtime, never()).retainIncomingVoteForRestart(any(PendingIncomingVote.class)); + verify(runtime, never()).scheduleQueuedVoteReplay(); + plugin.getTimer().shutdownNow(); + } + @Test void failedDurableHandoffSchedulesAnotherAttempt(@TempDir Path dataDirectory) throws Exception { VotingPluginVelocity plugin = new VotingPluginVelocity(mock(ProxyServer.class), mock(Logger.class),