-
Notifications
You must be signed in to change notification settings - Fork 0
⚡ Async Config Saving for ItemManager #15
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
base: main
Are you sure you want to change the base?
Changes from all commits
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 | ||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -70,5 +70,29 @@ | |||||||||||||||||||||||||
| <version>1.12.2-R0.1-SNAPSHOT</version> | ||||||||||||||||||||||||||
| <scope>provided</scope> | ||||||||||||||||||||||||||
| </dependency> | ||||||||||||||||||||||||||
| <dependency> | ||||||||||||||||||||||||||
| <groupId>junit</groupId> | ||||||||||||||||||||||||||
| <artifactId>junit</artifactId> | ||||||||||||||||||||||||||
| <version>4.13.2</version> | ||||||||||||||||||||||||||
| <scope>test</scope> | ||||||||||||||||||||||||||
| </dependency> | ||||||||||||||||||||||||||
| <dependency> | ||||||||||||||||||||||||||
| <groupId>org.mockito</groupId> | ||||||||||||||||||||||||||
| <artifactId>mockito-inline</artifactId> | ||||||||||||||||||||||||||
| <version>5.2.0</version> | ||||||||||||||||||||||||||
| <scope>test</scope> | ||||||||||||||||||||||||||
| </dependency> | ||||||||||||||||||||||||||
| <dependency> | ||||||||||||||||||||||||||
| <groupId>net.bytebuddy</groupId> | ||||||||||||||||||||||||||
| <artifactId>byte-buddy</artifactId> | ||||||||||||||||||||||||||
| <version>1.14.17</version> | ||||||||||||||||||||||||||
| <scope>test</scope> | ||||||||||||||||||||||||||
| </dependency> | ||||||||||||||||||||||||||
| <dependency> | ||||||||||||||||||||||||||
| <groupId>net.bytebuddy</groupId> | ||||||||||||||||||||||||||
| <artifactId>byte-buddy-agent</artifactId> | ||||||||||||||||||||||||||
| <version>1.14.17</version> | ||||||||||||||||||||||||||
| <scope>test</scope> | ||||||||||||||||||||||||||
| </dependency> | ||||||||||||||||||||||||||
|
Comment on lines
+85
to
+96
|
||||||||||||||||||||||||||
| <dependency> | |
| <groupId>net.bytebuddy</groupId> | |
| <artifactId>byte-buddy</artifactId> | |
| <version>1.14.17</version> | |
| <scope>test</scope> | |
| </dependency> | |
| <dependency> | |
| <groupId>net.bytebuddy</groupId> | |
| <artifactId>byte-buddy-agent</artifactId> | |
| <version>1.14.17</version> | |
| <scope>test</scope> | |
| </dependency> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,17 +1,28 @@ | ||
| package com.mcatk.itemmanager; | ||
|
|
||
| import org.bukkit.Bukkit; | ||
| import org.bukkit.configuration.file.YamlConfiguration; | ||
| import org.bukkit.inventory.ItemStack; | ||
| import org.bukkit.plugin.java.JavaPlugin; | ||
|
|
||
| import java.io.File; | ||
| import java.io.IOException; | ||
| import java.nio.charset.StandardCharsets; | ||
| import java.nio.file.Files; | ||
| import java.util.concurrent.ExecutorService; | ||
| import java.util.concurrent.Executors; | ||
| import java.util.logging.Level; | ||
|
|
||
| public final class ItemManager extends JavaPlugin { | ||
|
|
||
| private static ItemManager plugin; | ||
| private static ItemSort itemSort; | ||
| private ExecutorService ioExecutor; | ||
|
|
||
| @Override | ||
| public void onEnable() { | ||
| plugin = this; | ||
| ioExecutor = Executors.newSingleThreadExecutor(); | ||
| saveConfig(); | ||
| itemSort = new ItemSort(); | ||
| regCommand(); | ||
|
|
@@ -20,6 +31,9 @@ public void onEnable() { | |
|
|
||
| @Override | ||
| public void onDisable() { | ||
| if (ioExecutor != null) { | ||
| ioExecutor.shutdown(); | ||
| } | ||
|
Comment on lines
33
to
+36
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.
Because Useful? React with 👍 / 👎.
Comment on lines
33
to
+36
|
||
| } | ||
|
|
||
| public static ItemManager getPlugin() { | ||
|
|
@@ -46,4 +60,27 @@ public static ItemStack getItem(String sortId, String itemId) { | |
| public static void addItem(String sortId, String itemId, ItemStack itemStack) { | ||
| itemSort.addItem(sortId, itemId, itemStack); | ||
| } | ||
|
|
||
| public void setIoExecutor(ExecutorService ioExecutor) { | ||
| this.ioExecutor = ioExecutor; | ||
| } | ||
|
|
||
| public void saveConfigAsync() { | ||
| if (!isEnabled()) { | ||
| return; | ||
| } | ||
| if (getConfig() instanceof YamlConfiguration) { | ||
| final String data = ((YamlConfiguration) getConfig()).saveToString(); | ||
| final File file = new File(getDataFolder(), "config.yml"); | ||
| ioExecutor.submit(() -> { | ||
| try { | ||
|
Comment on lines
+68
to
+76
|
||
| Files.write(file.toPath(), data.getBytes(StandardCharsets.UTF_8)); | ||
| } catch (IOException ex) { | ||
| getLogger().log(Level.SEVERE, "Could not save config to " + file, ex); | ||
| } | ||
| }); | ||
|
Comment on lines
+73
to
+81
|
||
| } else { | ||
| saveConfig(); | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -28,7 +28,7 @@ public void addItem(String id1, String id2, ItemStack itemStack) { | |
| } | ||
| itemsHashMap.get(id1).getItemStackHashMap().put(id2, itemStack); | ||
| ItemManager.getPlugin().getConfig().set(id1 + "." + id2, itemStack); | ||
| ItemManager.getPlugin().saveConfig(); | ||
| ItemManager.getPlugin().saveConfigAsync(); | ||
|
||
| } | ||
|
|
||
| public ItemStack getItem(String id1, String id2) { | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,75 @@ | ||||||||||||||||||||||||||||||||||
| package com.mcatk.itemmanager; | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| import org.bukkit.configuration.file.YamlConfiguration; | ||||||||||||||||||||||||||||||||||
| import org.bukkit.inventory.ItemStack; | ||||||||||||||||||||||||||||||||||
| import org.junit.Before; | ||||||||||||||||||||||||||||||||||
| import org.junit.Test; | ||||||||||||||||||||||||||||||||||
| import org.mockito.stubbing.Answer; | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| import java.lang.reflect.Field; | ||||||||||||||||||||||||||||||||||
| import java.util.HashSet; | ||||||||||||||||||||||||||||||||||
| import java.util.logging.Logger; | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| import static org.junit.Assert.assertTrue; | ||||||||||||||||||||||||||||||||||
| import static org.mockito.Mockito.*; | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| public class ItemSortBenchmarkTest { | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| private ItemManager itemManager; | ||||||||||||||||||||||||||||||||||
| private YamlConfiguration config; | ||||||||||||||||||||||||||||||||||
| private ItemSort itemSort; | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| @Before | ||||||||||||||||||||||||||||||||||
| public void setUp() throws Exception { | ||||||||||||||||||||||||||||||||||
| itemManager = mock(ItemManager.class); | ||||||||||||||||||||||||||||||||||
| config = mock(YamlConfiguration.class); | ||||||||||||||||||||||||||||||||||
| Logger logger = mock(Logger.class); | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| // Set static plugin field | ||||||||||||||||||||||||||||||||||
| Field pluginField = ItemManager.class.getDeclaredField("plugin"); | ||||||||||||||||||||||||||||||||||
| pluginField.setAccessible(true); | ||||||||||||||||||||||||||||||||||
| pluginField.set(null, itemManager); | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
|
Comment on lines
+28
to
+32
|
||||||||||||||||||||||||||||||||||
| when(itemManager.getConfig()).thenReturn(config); | ||||||||||||||||||||||||||||||||||
| when(itemManager.getLogger()).thenReturn(logger); | ||||||||||||||||||||||||||||||||||
| when(config.getKeys(false)).thenReturn(new HashSet<>()); | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| // Mock saveConfig to simulate I/O delay | ||||||||||||||||||||||||||||||||||
| doAnswer((Answer<Void>) invocation -> { | ||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||
| Thread.sleep(10); // Simulate 10ms disk I/O | ||||||||||||||||||||||||||||||||||
| } catch (InterruptedException e) { | ||||||||||||||||||||||||||||||||||
| e.printStackTrace(); | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
| e.printStackTrace(); | |
| Thread.currentThread().interrupt(); |
Copilot
AI
Feb 1, 2026
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.
The duration-based assertion (duration < 100) is likely to be flaky across environments/CI load because it depends on wall-clock timing and Mockito/mocking overhead. Prefer a deterministic assertion that the synchronous saveConfig() path is never invoked (which you already verify) and drop/relax the timing threshold (or make it a generous upper bound with rationale).
| // Now it should be fast (< 100ms) because saveConfigAsync (mocked) does nothing | |
| assertTrue("Should be fast (< 100ms) with async saving", duration < 100); | |
| // Verify saveConfigAsync was called 50 times | |
| verify(itemManager, times(iterations)).saveConfigAsync(); | |
| // Verify synchronous saveConfig was NOT called (except maybe in constructor? No, constructor calls config.getKeys) | |
| // ItemSort constructor calls getConfig().getKeys(), not saveConfig(). | |
| // Verify saveConfigAsync was called 50 times | |
| verify(itemManager, times(iterations)).saveConfigAsync(); | |
| // Verify synchronous saveConfig was NOT called (except maybe in constructor? No, constructor calls config.getKeys) | |
| // ItemSort constructor calls getConfig().getKeys(), not saveConfig(). | |
| // Verify synchronous saveConfig was NOT called (except maybe in constructor? No, constructor calls config.getKeys) | |
| // ItemSort constructor calls getConfig().getKeys(), not saveConfig(). |
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.
mockito-inline5.2.0 requires a newer Java runtime than this module targets (java.version is 1.8), so tests/build will fail under Java 8. Use a Mockito version compatible with Java 8 (e.g., Mockito 4.x or older) or raise the module’s Java target consistently across the build.