Split pregen queue metrics

- Publish in-flight pregen chunk requests as iris.pregen-queue
- Publish chunks left in the job as iris.pregen-remaining
- Mark generation timings unavailable after 10 seconds without generation
This commit is contained in:
Brian Neumann-Fopiano committed 2026-09-29 18:09:55 -04:00
1 parent 593c3f732a
commit b8433340c9
16 files changed
+183 -21

No files matched your search

@@ -52,7 +52,12 @@ public record EngineTelemetrySnapshot(
generationTimingsMs = sanitizeTimings(generationTimingsMs);
}
public static EngineTelemetrySnapshot capture(Engine engine, double chunksPerSecond, long sampledAtMs) {
public static EngineTelemetrySnapshot capture(
Engine engine,
double chunksPerSecond,
boolean generationTimingsFresh,
long sampledAtMs
) {
if (engine == null) {
throw new IllegalArgumentException("Engine cannot be null");
}
@@ -85,7 +90,7 @@ public record EngineTelemetrySnapshot(
engine.getMantle().getLoadedRegionCount(),
engine.getMantle().getUnloadRegionCount(),
engine.getMantle().getAdjustedIdleDuration(),
engine.getMetrics().telemetryAverages()
generationTimingsFresh ? engine.getMetrics().telemetryAverages() : Map.of()
);
}
@@ -302,7 +302,8 @@ public class PregeneratorJob implements PregenListener, PregenRenderSource {
public record PregenProgress(double percent, long generated, long totalChunks, double chunksPerSecond,
double overallChunksPerSecond, double thirtySecondChunksPerSecond,
double sixtySecondChunksPerSecond, long chunksRemaining, long eta, long elapsed,
double sixtySecondChunksPerSecond, long chunksRemaining, long chunksInFlight,
long eta, long elapsed,
String method, boolean paused, long failed, String worldName, String worldIdentity) {
}
@@ -322,6 +323,7 @@ public class PregeneratorJob implements PregenListener, PregenRenderSource {
Math.max(0D, lastThirtySecondChunksPerSecond),
Math.max(0D, lastSixtySecondChunksPerSecond),
Math.max(0L, lastChunksRemaining),
pregenerator.getInFlightChunks(),
lastEta,
lastElapsed,
lastMethod,
@@ -105,6 +105,11 @@ public class AsyncOrMedievalPregenMethod implements PregeneratorMethod {
method.reclaimMemory();
}
@Override
public int inFlightChunks() {
return method.inFlightChunks();
}
@Override
public Mantle getMantle() {
return method.getMantle();
@@ -1127,6 +1127,11 @@ public class AsyncPregenMethod implements PregeneratorMethod {
return null;
}
@Override
public int inFlightChunks() {
return Math.max(0, inFlight.get());
}
@Override
public Mantle getMantle() {
if (IrisToolbelt.isIrisWorld(world)) {
@@ -213,6 +213,11 @@ public class CachedPregenMethod implements PregeneratorMethod {
method.reclaimMemory();
}
@Override
public int inFlightChunks() {
return method.inFlightChunks();
}
@Override
public Mantle getMantle() {
return method.getMantle();
@@ -98,6 +98,11 @@ public class HybridPregenMethod implements PregeneratorMethod {
inWorld.reclaimMemory();
}
@Override
public int inFlightChunks() {
return inWorld.inFlightChunks();
}
@Override
public Mantle getMantle() {
return inWorld.getMantle();
@@ -464,6 +464,10 @@ public class IrisPregenerator {
return failed.get();
}
public int getInFlightChunks() {
return generator.inFlightChunks();
}
public PregenRates getRates() {
return rates;
}
@@ -52,6 +52,7 @@ public class MedievalPregenMethod implements PregeneratorMethod {
private static final long UNLOAD_TIMEOUT_SECONDS = 120L;
private final World world;
private final KList<CompletableFuture<?>> futures;
private final AtomicInteger inFlight;
private final Map<Chunk, Long> lastUse;
private final int maxFutures;
private final AtomicBoolean directAsyncDisabled;
@@ -67,6 +68,7 @@ public class MedievalPregenMethod implements PregeneratorMethod {
this.chunkFlush = new PregenSerialWorker("Iris Pregen Chunk Flush", world == null ? "unknown" : world.getName());
this.chunkIoExecutor = chunkFlush.executor();
futures = new KList<>();
this.inFlight = new AtomicInteger();
this.lastUse = new ConcurrentHashMap<>();
int configuredThreads = IrisSettings.getThreadCount(IrisSettings.get().getConcurrency().getParallelism());
this.maxFutures = J.isFolia() ? Math.max(2, Math.min(64, configuredThreads)) : Math.max(16, Math.min(128, configuredThreads * 4));
@@ -229,6 +231,7 @@ public class MedievalPregenMethod implements PregeneratorMethod {
}
listener.onChunkGenerating(x, z);
inFlight.incrementAndGet();
// Single choke point for failure accounting: without onChunkFailed a lossy run can
// never satisfy allVisitsComplete, so a finished pregen reports as aborted forever.
CompletableFuture<?> chunkFuture = J.isFolia()
@@ -247,6 +250,7 @@ public class MedievalPregenMethod implements PregeneratorMethod {
})
: scheduleChunkLoad(x, z, listener);
futures.add(chunkFuture.whenComplete((r, err) -> {
inFlight.decrementAndGet();
if (err != null) {
listener.onChunkFailed(x, z);
}
@@ -358,6 +362,11 @@ public class MedievalPregenMethod implements PregeneratorMethod {
}
}
@Override
public int inFlightChunks() {
return Math.max(0, inFlight.get());
}
@Override
public Mantle getMantle() {
if (IrisToolbelt.isIrisWorld(world)) {
@@ -95,5 +95,9 @@ public interface PregeneratorMethod {
default void reclaimMemory() {
}
default int inFlightChunks() {
return 0;
}
Mantle getMantle();
}
@@ -62,6 +62,13 @@ public class CachedPregenMethodCompletionTest {
assertEquals(1, listener.generated.get());
}
@Test
public void inFlightChunksReportTheWrappedMethodsOutstandingRequests() {
underlying.inFlight.set(37);
assertEquals(37, method.inFlightChunks());
}
@Test
public void generateChunkFailureIsNotCached() {
method.generateChunk(6, 9, listener);
@@ -186,11 +193,17 @@ public class CachedPregenMethodCompletionTest {
private final AtomicInteger centerBlockX = new AtomicInteger();
private final AtomicInteger centerBlockZ = new AtomicInteger();
private final AtomicReference<PregenListener> capturedListener = new AtomicReference<>();
private final AtomicInteger inFlight = new AtomicInteger();
@Override
public void init() {
}
@Override
public int inFlightChunks() {
return inFlight.get();
}
@Override
public void close() {
}