Repository navigation
Separate background storage and platform work #1629
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
6e459bb
1d778fa
535eff5
8c8d118
dcd9a8e
0a509b5
2d047c7
a03ee66
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,6 +17,7 @@ | |
| import java.util.concurrent.RejectedExecutionException; | ||
| import java.util.concurrent.ScheduledExecutorService; | ||
| import java.util.concurrent.TimeUnit; | ||
| import java.util.concurrent.atomic.AtomicBoolean; | ||
| import java.util.concurrent.atomic.AtomicReference; | ||
| import java.util.zip.ZipEntry; | ||
| import java.util.zip.ZipInputStream; | ||
|
|
@@ -101,8 +102,9 @@ | |
| import com.bencodez.votingplugin.placeholders.VotingPluginExpansion; | ||
| import com.bencodez.votingplugin.presets.VoteSitePresetSetupHandler; | ||
| import com.bencodez.votingplugin.proxy.control.HostedControlManager; | ||
| import com.bencodez.votingplugin.util.ControlCredentialFile.PendingAutoEnrollment; | ||
| import com.bencodez.votingplugin.util.BoundedScheduledExecutor; | ||
| import com.bencodez.votingplugin.util.BukkitCompletionScheduler; | ||
| import com.bencodez.votingplugin.util.ControlCredentialFile.PendingAutoEnrollment; | ||
| import com.bencodez.votingplugin.rewards.VotingPluginRewardRegistrar; | ||
| import com.bencodez.votingplugin.servicesites.ServiceSiteHandler; | ||
| import com.bencodez.votingplugin.signs.Signs; | ||
|
|
@@ -307,7 +309,13 @@ public File getLoadedPluginJarFile() { | |
| @Getter | ||
| private DiscordHandler discordHandler; | ||
|
|
||
| private VotingPluginBackgroundTask backgroundTask; | ||
| private volatile VotingPluginBackgroundTask backgroundTask; | ||
| private volatile AtomicBoolean basicBungeeUpdateRunning; | ||
|
|
||
| private synchronized AtomicBoolean basicBungeeUpdateAdmission() { | ||
| if (basicBungeeUpdateRunning == null) basicBungeeUpdateRunning = new AtomicBoolean(); | ||
| return basicBungeeUpdateRunning; | ||
| } | ||
| private VotingPluginVersionInfo versionInfo; | ||
| private VotingPluginConfigHealth configHealth; | ||
| private VotifierIntegration votifierIntegration; | ||
|
|
@@ -321,10 +329,12 @@ public boolean isUpdate() { | |
| } | ||
|
|
||
| public void setUpdate(boolean update) { | ||
| if (backgroundTask == null) { | ||
| backgroundTask = new VotingPluginBackgroundTask(this); | ||
| } | ||
| backgroundTask.setRequested(update); | ||
| backgroundTask().setRequested(update); | ||
| } | ||
|
|
||
| private synchronized VotingPluginBackgroundTask backgroundTask() { | ||
| if (backgroundTask == null) backgroundTask = new VotingPluginBackgroundTask(this); | ||
| return backgroundTask; | ||
| } | ||
|
|
||
| public boolean isUpdateStarted() { | ||
|
|
@@ -340,15 +350,70 @@ public void addDirectlyDefinedRewards(DirectlyDefinedReward directlyDefinedRewar | |
| } | ||
|
|
||
|
|
||
| public void basicBungeeUpdate() { | ||
| for (Player player : Bukkit.getOnlinePlayers()) { | ||
| VotingPluginUser user = getVotingPluginUserManager().getVotingPluginUser(player); | ||
| user.cache(); | ||
| user.offVote(); | ||
| user.checkOfflineRewards(); | ||
| /** | ||
| * Capture live player state without crossing Folia ownership boundaries. | ||
| * Completion runs after every player owner callback has finished. | ||
| */ | ||
| public void captureOnlineTopVoterIgnore(java.util.function.Consumer<java.util.Map<UUID, Boolean>> completion) { | ||
| captureOnlineTopVoterIgnore(completion, () -> completion.accept(java.util.Map.of())); | ||
| } | ||
|
|
||
| public void captureOnlineTopVoterIgnore(java.util.function.Consumer<java.util.Map<UUID, Boolean>> completion, | ||
| Runnable failureCompletion) { | ||
| java.util.Objects.requireNonNull(completion, "completion"); | ||
| java.util.Objects.requireNonNull(failureCompletion, "failureCompletion"); | ||
| try { | ||
| getBukkitScheduler().runTask(this, () -> { | ||
| java.util.List<Player> players = new java.util.ArrayList<>(Bukkit.getOnlinePlayers()); | ||
| if (players.isEmpty()) { | ||
| completion.accept(java.util.Map.of()); | ||
| return; | ||
| } | ||
| java.util.concurrent.ConcurrentHashMap<UUID, Boolean> captured = new java.util.concurrent.ConcurrentHashMap<>(); | ||
| java.util.concurrent.atomic.AtomicInteger remaining = new java.util.concurrent.atomic.AtomicInteger(players.size()); | ||
| Runnable maybeComplete = () -> { | ||
| if (remaining.decrementAndGet() == 0) completion.accept(java.util.Map.copyOf(captured)); | ||
| }; | ||
| for (Player player : players) { | ||
| BukkitCompletionScheduler.run(this, player, () -> { | ||
| try { | ||
| if (!player.isOnline()) return; | ||
| UUID storageUuid = getPlaceholderPlayerPresence().storageUuid(player); | ||
| if (storageUuid == null) storageUuid = placeholderStorageUuid(player, getOptions().isOnlineMode()); | ||
| if (storageUuid != null) captured.put(storageUuid, | ||
| player.hasPermission("VotingPlugin.TopVoter.Ignore")); | ||
| } finally { maybeComplete.run(); } | ||
| }, maybeComplete, maybeComplete); | ||
| } | ||
| }); | ||
| } catch (RuntimeException failure) { | ||
| debug(failure); | ||
| failureCompletion.run(); | ||
| } | ||
| } | ||
|
|
||
| public void basicBungeeUpdate() { | ||
| AtomicBoolean admission = basicBungeeUpdateAdmission(); | ||
| if (!admission.compareAndSet(false, true)) return; | ||
| captureOnlineTopVoterIgnore(online -> { | ||
| try { | ||
| getUserManager().getDataManager().getTimer().execute(() -> { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When Useful? React with 👍 / 👎. |
||
| try { | ||
| for (java.util.Map.Entry<UUID, Boolean> entry : online.entrySet()) { | ||
| VotingPluginUser user = getVotingPluginUserManager().getVotingPluginUser(entry.getKey(), false); | ||
| if (user == null) continue; | ||
| user.cache(); | ||
| user.offVoteWithCapturedTopVoterIgnore(entry.getValue().booleanValue()); | ||
| user.checkOfflineRewards(); | ||
| } | ||
| } finally { admission.set(false); } | ||
| }); | ||
| } catch (RuntimeException failure) { | ||
| admission.set(false); | ||
| debug(failure); | ||
| } | ||
| }, () -> admission.set(false)); | ||
| } | ||
|
|
||
|
|
||
| /** | ||
|
|
@@ -2067,11 +2132,8 @@ private void setupFiles() { | |
| plugin.debug("Loaded Files"); | ||
| } | ||
|
|
||
| public synchronized void update() { | ||
| if (backgroundTask == null) { | ||
| backgroundTask = new VotingPluginBackgroundTask(this); | ||
| } | ||
| backgroundTask.run(); | ||
| public void update() { | ||
| backgroundTask().run(); | ||
| } | ||
|
|
||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
🔎 Supported by static analysis
🏁 Script executed:
Repository: BenCodez/VotingPlugin
Length of output: 11709
🏁 Script executed:
Repository: BenCodez/VotingPlugin
Length of output: 26832
Avoid taking the plugin monitor during task lookup.
runRefreshholdssynchronized (plugin)while iterating users. A server-thread reload or command call tosetUpdate(true)can therefore block until the refresh finishes, which can stall a tick for a large user table.The old
update()method was already synchronized, so this change newly adds the same contention tosetUpdate(). Use a dedicated initialization lock and retain the volatile fast path.🐛 Suggested fix
🤖 Prompt for AI Agents