Skip to content

Commit 331885b

Browse files
authored
Merge pull request #219 from sladkoff/refactor/tps
refactor(tick durations): composition and strategies
2 parents 247fc23 + 797e4b6 commit 331885b

11 files changed

+127
-88
lines changed

.gitignore

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@
44
.settings
55
.classpath
66
.project
7-
.vscode
7+
.vscode
8+
/docker/data

docker-compose.yml

+2-4
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ version: "3.8"
22

33
services:
44
mc:
5-
image: itzg/minecraft-server:2024.2.0
5+
image: itzg/minecraft-server:2024.1.0
66
environment:
77
EULA: "true"
88
TYPE: "PAPER"
@@ -14,9 +14,7 @@ services:
1414
- "25565:25565"
1515
- "9225:9225"
1616
volumes:
17-
- data:/data
17+
- ./docker/data:/data
1818
stdin_open: true
1919
tty: true
2020
restart: unless-stopped
21-
volumes:
22-
data: {}

src/main/java/de/sldk/mc/config/PrometheusExporterConfig.java

+4
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
import de.sldk.mc.MetricRegistry;
44
import de.sldk.mc.PrometheusExporter;
55
import de.sldk.mc.metrics.*;
6+
import de.sldk.mc.metrics.TickDurationAverageCollector;
7+
import de.sldk.mc.metrics.TickDurationMaxCollector;
8+
import de.sldk.mc.metrics.TickDurationMedianCollector;
9+
import de.sldk.mc.metrics.TickDurationMinCollector;
610
import org.bukkit.configuration.file.FileConfiguration;
711
import org.bukkit.plugin.Plugin;
812

src/main/java/de/sldk/mc/metrics/TickDurationAverageCollector.java

+7-6
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,25 @@
11
package de.sldk.mc.metrics;
22

3-
import org.bukkit.plugin.Plugin;
4-
3+
import de.sldk.mc.metrics.tick_duration.TickDurationCollector;
54
import io.prometheus.client.Gauge;
5+
import org.bukkit.plugin.Plugin;
66

7-
public class TickDurationAverageCollector extends TickDurationCollector {
7+
public class TickDurationAverageCollector extends Metric {
88
private static final String NAME = "tick_duration_average";
9+
private final TickDurationCollector collector = TickDurationCollector.forServerImplementation(this.getPlugin());
910

1011
private static final Gauge TD = Gauge.build()
11-
.name(prefix(NAME))
12+
.name(Metric.prefix(NAME))
1213
.help("Average duration of server tick (nanoseconds)")
1314
.create();
1415

1516
public TickDurationAverageCollector(Plugin plugin) {
16-
super(plugin, TD, NAME);
17+
super(plugin, TD);
1718
}
1819

1920
private long getTickDurationAverage() {
2021
long sum = 0;
21-
long[] durations = getTickDurations();
22+
long[] durations = collector.getTickDurations();
2223
for (Long val : durations) {
2324
sum += val;
2425
}

src/main/java/de/sldk/mc/metrics/TickDurationMaxCollector.java

+6-5
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,25 @@
11
package de.sldk.mc.metrics;
22

3-
import org.bukkit.plugin.Plugin;
4-
3+
import de.sldk.mc.metrics.tick_duration.TickDurationCollector;
54
import io.prometheus.client.Gauge;
5+
import org.bukkit.plugin.Plugin;
66

7-
public class TickDurationMaxCollector extends TickDurationCollector {
7+
public class TickDurationMaxCollector extends Metric {
88
private static final String NAME = "tick_duration_max";
9+
private final TickDurationCollector collector = TickDurationCollector.forServerImplementation(this.getPlugin());
910

1011
private static final Gauge TD = Gauge.build()
1112
.name(prefix(NAME))
1213
.help("Max duration of server tick (nanoseconds)")
1314
.create();
1415

1516
public TickDurationMaxCollector(Plugin plugin) {
16-
super(plugin, TD, NAME);
17+
super(plugin, TD);
1718
}
1819

1920
private long getTickDurationMax() {
2021
long max = Long.MIN_VALUE;
21-
for (Long val : getTickDurations()) {
22+
for (Long val : collector.getTickDurations()) {
2223
if (val > max) {
2324
max = val;
2425
}

src/main/java/de/sldk/mc/metrics/TickDurationMedianCollector.java

+7-7
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,26 @@
11
package de.sldk.mc.metrics;
22

3-
import java.util.Arrays;
4-
3+
import de.sldk.mc.metrics.tick_duration.TickDurationCollector;
4+
import io.prometheus.client.Gauge;
55
import org.bukkit.plugin.Plugin;
66

7-
import io.prometheus.client.Gauge;
7+
import java.util.Arrays;
88

9-
public class TickDurationMedianCollector extends TickDurationCollector {
9+
public class TickDurationMedianCollector extends Metric {
1010
private static final String NAME = "tick_duration_median";
11+
private final TickDurationCollector collector = TickDurationCollector.forServerImplementation(this.getPlugin());
1112

1213
private static final Gauge TD = Gauge.build()
1314
.name(prefix(NAME))
1415
.help("Median duration of server tick (nanoseconds)")
1516
.create();
1617

1718
public TickDurationMedianCollector(Plugin plugin) {
18-
super(plugin, TD, NAME);
19+
super(plugin, TD);
1920
}
2021

2122
private long getTickDurationMedian() {
22-
/* Copy the original array - don't want to sort it! */
23-
long[] tickTimes = getTickDurations().clone();
23+
long[] tickTimes = collector.getTickDurations();
2424
Arrays.sort(tickTimes);
2525
return tickTimes[tickTimes.length / 2];
2626
}

src/main/java/de/sldk/mc/metrics/TickDurationMinCollector.java

+6-5
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,25 @@
11
package de.sldk.mc.metrics;
22

3-
import org.bukkit.plugin.Plugin;
4-
3+
import de.sldk.mc.metrics.tick_duration.TickDurationCollector;
54
import io.prometheus.client.Gauge;
5+
import org.bukkit.plugin.Plugin;
66

7-
public class TickDurationMinCollector extends TickDurationCollector {
7+
public class TickDurationMinCollector extends Metric {
88
private static final String NAME = "tick_duration_min";
9+
private final TickDurationCollector collector = TickDurationCollector.forServerImplementation(this.getPlugin());
910

1011
private static final Gauge TD = Gauge.build()
1112
.name(prefix(NAME))
1213
.help("Min duration of server tick (nanoseconds)")
1314
.create();
1415

1516
public TickDurationMinCollector(Plugin plugin) {
16-
super(plugin, TD, NAME);
17+
super(plugin, TD);
1718
}
1819

1920
private long getTickDurationMin() {
2021
long min = Long.MAX_VALUE;
21-
for (Long val : getTickDurations()) {
22+
for (Long val : collector.getTickDurations()) {
2223
if (val < min) {
2324
min = val;
2425
}
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,31 @@
1-
package de.sldk.mc.metrics;
2-
3-
import java.lang.reflect.Field;
4-
import java.lang.reflect.Method;
5-
import java.util.logging.Level;
1+
package de.sldk.mc.metrics.tick_duration;
62

73
import org.bukkit.Bukkit;
84
import org.bukkit.Server;
9-
import org.bukkit.plugin.Plugin;
105

11-
import io.prometheus.client.Gauge;
6+
import java.lang.reflect.Field;
7+
import java.lang.reflect.Method;
8+
import java.util.logging.Level;
9+
import java.util.logging.Logger;
1210

13-
public abstract class TickDurationCollector extends Metric {
11+
public class DefaultTickDurationStrategy implements TickDurationStrategy {
1412
/*
1513
* If reflection is successful, this will hold a reference directly to the
1614
* MinecraftServer internal tick duration tracker
1715
*/
1816
private static long[] tickDurationReference = null;
1917

20-
/*
21-
* If this server is Paper and has a shorthand method
22-
* this will be set to true to use the method instead
23-
*/
24-
private static boolean usePaperMethod = false;
25-
26-
public TickDurationCollector(Plugin plugin, Gauge gauge, String name) {
27-
super(plugin, gauge);
28-
18+
public DefaultTickDurationStrategy(Logger logger) {
2919
/*
3020
* If there is not yet a handle to the internal tick duration buffer, try
3121
* to acquire one using reflection.
3222
*
3323
* This searches for any long[] array in the MinecraftServer class. It should
3424
* work across many versions of Spigot/Paper and various obfuscation mappings
3525
*/
36-
if (tickDurationReference == null && !usePaperMethod) {
37-
38-
/* Check if this server is Paper and has a handy method */
39-
if (getPaperTickTimes() != null) {
40-
usePaperMethod = true;
41-
plugin.getLogger().log(Level.FINE, "Managed to get Paper tick times method.");
42-
return;
43-
}
26+
if (tickDurationReference == null) {
4427

45-
plugin.getLogger().log(Level.FINE, "Could not get Paper tick times method.");
28+
logger.log(Level.FINE, "Could not get Paper tick times method.");
4629

4730
long[] longestArray = null;
4831

@@ -63,7 +46,7 @@ public TickDurationCollector(Plugin plugin, Gauge gauge, String name) {
6346
}
6447
}
6548
} catch (Exception e) {
66-
plugin.getLogger().log(Level.FINE, "Caught exception looking for tick times array: ", e);
49+
logger.log(Level.FINE, "Caught exception looking for tick times array: ", e);
6750
}
6851

6952
if (longestArray != null) {
@@ -73,47 +56,18 @@ public TickDurationCollector(Plugin plugin, Gauge gauge, String name) {
7356
tickDurationReference = new long[1];
7457
tickDurationReference[0] = -1;
7558

76-
plugin.getLogger().log(Level.WARNING, "Failed to find tick times buffer via reflection. Tick duration metrics will not be available.");
59+
logger.log(Level.WARNING, "Failed to find tick times buffer via reflection. Tick duration metrics will not be available.");
7760
}
7861
}
7962
}
8063

81-
/**
82-
* Attempts to get tick times from Paper
83-
* returns null if fails
84-
*/
85-
private static long[] getPaperTickTimes() {
86-
try {
87-
/* Get the actual minecraft server class */
88-
Server server = Bukkit.getServer();
89-
90-
/* Attempt to get Paper tick times method */
91-
Method paperGetTickTimesMethod = server.getClass().getMethod("getTickTimes");
92-
93-
Object tickTimes = paperGetTickTimesMethod.invoke(server);
94-
95-
/* Check the method actual return type */
96-
if (!(tickTimes instanceof long[])) {
97-
return null;
98-
}
99-
100-
return (long[]) tickTimes;
101-
}
102-
catch (Exception e) {
103-
return null;
104-
}
105-
}
106-
10764
/**
10865
* Returns either the internal minecraft long array for tick times in ns,
10966
* or a long array containing just one element of value -1 if reflection
11067
* was unable to locate the minecraft tick times buffer
11168
*/
112-
protected static long[] getTickDurations() {
113-
if (usePaperMethod) {
114-
return getPaperTickTimes();
115-
}
116-
117-
return tickDurationReference;
69+
public long[] getTickDurations() {
70+
// Return a copy of the array to prevent modification
71+
return tickDurationReference.clone();
11872
}
11973
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package de.sldk.mc.metrics.tick_duration;
2+
3+
import org.bukkit.Bukkit;
4+
import org.bukkit.Server;
5+
6+
import java.lang.reflect.Method;
7+
8+
public class PaperTickDurationStrategy implements TickDurationStrategy {
9+
10+
public PaperTickDurationStrategy() {
11+
}
12+
13+
/**
14+
* Attempts to get tick times from Paper
15+
* returns null if fails
16+
*/
17+
private static long[] getPaperTickTimes() {
18+
try {
19+
/* Get the actual minecraft server class */
20+
Server server = Bukkit.getServer();
21+
22+
/* Attempt to get Paper tick times method */
23+
Method paperGetTickTimesMethod = server.getClass().getMethod("getTickTimes");
24+
25+
Object tickTimes = paperGetTickTimesMethod.invoke(server);
26+
27+
/* Check the method actual return type */
28+
if (!(tickTimes instanceof long[])) {
29+
return null;
30+
}
31+
32+
return (long[]) tickTimes;
33+
} catch (Exception e) {
34+
return null;
35+
}
36+
}
37+
38+
public long[] getTickDurations() {
39+
return getPaperTickTimes();
40+
}
41+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package de.sldk.mc.metrics.tick_duration;
2+
3+
import org.bukkit.plugin.Plugin;
4+
5+
import java.util.logging.Level;
6+
import java.util.logging.Logger;
7+
8+
public class TickDurationCollector {
9+
10+
private final TickDurationStrategy strategy;
11+
12+
public TickDurationCollector(TickDurationStrategy strategy) {
13+
this.strategy = strategy;
14+
}
15+
16+
public static TickDurationCollector forServerImplementation(Plugin plugin) {
17+
Logger logger = plugin.getLogger();
18+
PaperTickDurationStrategy paperStrategy = new PaperTickDurationStrategy();
19+
20+
if (paperStrategy.getTickDurations() != null) {
21+
logger.log(Level.FINE, "Using Paper tick times method.");
22+
return new TickDurationCollector(paperStrategy);
23+
}
24+
25+
logger.log(Level.FINE, "Using default tick times guessing method.");
26+
DefaultTickDurationStrategy defaultTickDurationStrategy = new DefaultTickDurationStrategy(logger);
27+
return new TickDurationCollector(defaultTickDurationStrategy);
28+
}
29+
30+
public long[] getTickDurations() {
31+
return strategy.getTickDurations();
32+
}
33+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package de.sldk.mc.metrics.tick_duration;
2+
3+
public interface TickDurationStrategy {
4+
long[] getTickDurations();
5+
}

0 commit comments

Comments
 (0)