Fix chunk updates not applying as soon as they should

This commit is contained in:
James Seibel
2025-01-11 20:11:42 -06:00
parent 9a3d36757f
commit 4f47c78acc
3 changed files with 98 additions and 3 deletions
@@ -8,21 +8,35 @@ import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSour
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.pos.DhSectionPos;
import com.seibel.distanthorizons.core.util.KeyedLockContainer;
import com.seibel.distanthorizons.core.util.ThreadUtil;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.NotNull;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.lang.ref.WeakReference;
import java.util.Collections;
import java.util.Set;
import java.util.concurrent.*;
import java.util.concurrent.locks.ReentrantLock;
/**
* Used to batch together multiple data source updates that all
* affect the same position.
*/
public class DelayedFullDataSourceSaveCache
public class DelayedFullDataSourceSaveCache implements AutoCloseable
{
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
/**
* a cache won't automatically clean itself unless we trigger it's clean method
* if not done then we'd only see the cache invalidate when new inserts happen,
* which causes weird behavior when placing/breaking blocks.
*/
private static final ThreadPoolExecutor BACKGROUND_CLEAN_UP_THREAD = ThreadUtil.makeSingleDaemonThreadPool("delayed save cache cleaner");
private static final Set<WeakReference<DelayedFullDataSourceSaveCache>> SAVE_CACHE_SET = Collections.newSetFromMap(new ConcurrentHashMap<>());
/** how long between clean up checks */
private static final int CLEANUP_CHECK_TIME_IN_MS = 1_000;
private final Cache<Long, FullDataSourceV2> dataSourceByPosition;
@@ -38,6 +52,11 @@ public class DelayedFullDataSourceSaveCache
// constructor //
//=============//
static
{
BACKGROUND_CLEAN_UP_THREAD.execute(() -> runCleanupLoop());
}
public DelayedFullDataSourceSaveCache(@NotNull ISaveDataSourceFunc onSaveTimeoutAsyncFunc, int saveDelayInMs)
{
this.onSaveTimeoutAsyncFunc = onSaveTimeoutAsyncFunc;
@@ -51,6 +70,7 @@ public class DelayedFullDataSourceSaveCache
.removalListener(this::handleDataSourceRemoval)
.<Long, FullDataSourceV2>build();
SAVE_CACHE_SET.add(new WeakReference<>(this));
}
@@ -124,6 +144,63 @@ public class DelayedFullDataSourceSaveCache
//================//
// static cleanup //
//================//
private static void runCleanupLoop()
{
while (true)
{
try
{
try
{
Thread.sleep(CLEANUP_CHECK_TIME_IN_MS);
}
catch (InterruptedException ignore) { }
SAVE_CACHE_SET.forEach((cacheRef) ->
{
DelayedFullDataSourceSaveCache cache = cacheRef.get();
if (cache == null)
{
// shouldn't be necessary, but if we forget to manually close a cache, this will prevent leaking
SAVE_CACHE_SET.remove(cacheRef);
}
else
{
cache.dataSourceByPosition.cleanUp();
}
});
}
catch (Exception e)
{
LOGGER.error("Unexpected error in cleanup thread: [" + e.getMessage() + "].", e);
}
}
}
//================//
// base overrides //
//================//
@Override
public void close()
{
// not the fastest way to handle removing,
// but we shouldn't have more than 20 or so at once
// so this should be just fine
SAVE_CACHE_SET.removeIf((cacheRef) ->
{
DelayedFullDataSourceSaveCache cache = cacheRef.get();
return cache != null && cache.equals(this);
});
}
//================//
// helper classes //
@@ -417,6 +417,22 @@ public class GeneratedFullDataSourceProvider extends FullDataSourceProviderV2 im
//================//
// base overrides //
//================//
@Override
public void close()
{
super.close();
this.delayedFullDataSourceSaveCache.close();
}
//================//
// helper classes //
//================//
@@ -271,6 +271,8 @@ public abstract class AbstractDhLevel implements IDhLevel
{
this.beaconBeamRepo.close();
}
this.delayedFullDataSourceSaveCache.close();
}
}