use ehcache

This commit is contained in:
Thomas Rudin 2018-07-17 10:39:17 +02:00
parent fc518ee0ca
commit 24c015341b
9 changed files with 156 additions and 24 deletions

View File

@ -137,6 +137,14 @@
<version>1.1.1</version>
</dependency>
<!-- Caching -->
<dependency>
<groupId>org.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>3.5.2</version>
</dependency>
</dependencies>
<build>

View File

@ -101,17 +101,17 @@ public class TileRenderer {
long start = System.currentTimeMillis();
Integer count = ctx
.select(DSL.count())
.from(BLOCKS)
.where(
BLOCKS.POSX.ge(Math.min(x1, x2))
.and(BLOCKS.POSX.le(Math.max(x1, x2)))
.and(BLOCKS.POSZ.ge(Math.min(z1, z2)))
.and(BLOCKS.POSZ.le(Math.max(z1, z2)))
.and(yCondition)
)
.fetchOne(DSL.count());
Result<BlocksRecord> firstResult = ctx
.selectFrom(BLOCKS)
.where(
BLOCKS.POSX.ge(Math.min(x1, x2))
.and(BLOCKS.POSX.le(Math.max(x1, x2)))
.and(BLOCKS.POSZ.ge(Math.min(z1, z2)))
.and(BLOCKS.POSZ.le(Math.max(z1, z2)))
.and(yCondition)
)
.limit(1)
.fetch();
long diff = System.currentTimeMillis() - start;
@ -119,7 +119,7 @@ public class TileRenderer {
logger.warn("white-count-query took {} ms", diff);
}
if (count == 0) {
if (firstResult.isEmpty()) {
logger.debug("Fail-fast, got zero mapblock count for x={}-{} z={}-{}", x1,x2, z1,z2);
byte[] data = WhiteTile.getPNG();
@ -293,15 +293,15 @@ public class TileRenderer {
start = now;
Integer count = ctx
.select(DSL.count())
.from(BLOCKS)
Result<BlocksRecord> countList = ctx
.selectFrom(BLOCKS)
.where(
BLOCKS.POSX.eq(mapblockX)
.and(BLOCKS.POSZ.eq(mapblockZ))
.and(yCondition)
)
.fetchOne(DSL.count());
.limit(1)
.fetch();
now = System.currentTimeMillis();
long timingZeroCountCheck = now - start;
@ -314,7 +314,7 @@ public class TileRenderer {
long timingRender = 0;
if (count > 0) {
if (!countList.isEmpty()) {
blockRenderer.render(mapblockX, mapblockZ, graphics, 16);

View File

@ -42,6 +42,10 @@ public interface TileServerConfig extends Config {
@DefaultValue("2")
int playerUpdateInterval();
@Key("tile.route.reentrycount")
@DefaultValue("5000")
int tileRouteReentryCount();
/*
Logging stuff
*/
@ -67,12 +71,13 @@ public interface TileServerConfig extends Config {
*/
@Key("tile.cache.impl")
@DefaultValue("FILE")
@DefaultValue("EHCACHE")
CacheType tileCacheType();
enum CacheType {
DATABASE,
FILE
FILE,
EHCACHE
}
@Key("tiles.directory")

View File

@ -119,6 +119,12 @@ public class UpdateChangedTilesJob implements Runnable {
int count = blocks.size();
int invalidatedTiles = 0;
long diff = start - System.currentTimeMillis();
if (diff > 1000 && cfg.logQueryPerformance()){
logger.warn("updated-tiles-query took {} ms", diff);
}
if (blocks.size() == LIMIT) {
logger.warn("Got max-blocks ({}) from update-queue", LIMIT);
}

View File

@ -12,7 +12,7 @@ import com.zaxxer.hikari.HikariDataSource;
public class DBModule extends AbstractModule {
public DBModule(TileServerConfig cfg) throws Exception {
public DBModule(TileServerConfig cfg) {
this.cfg = cfg;
}

View File

@ -25,7 +25,7 @@ public class LoggingExecuteListener extends DefaultExecuteListener {
super.executeEnd(ctx);
if (watch.split() > 5_000_000_000L)
logger.warn(
"Slow SQL",
"Slow SQL: " + ctx.sql(),
new SQLPerformanceWarning());
}
}

View File

@ -12,13 +12,14 @@ import io.rudin.minetest.tileserver.provider.ExecutorProvider;
import io.rudin.minetest.tileserver.service.EventBus;
import io.rudin.minetest.tileserver.service.TileCache;
import io.rudin.minetest.tileserver.service.impl.DatabaseTileCache;
import io.rudin.minetest.tileserver.service.impl.EHTileCache;
import io.rudin.minetest.tileserver.service.impl.EventBusImpl;
import io.rudin.minetest.tileserver.service.impl.FileTileCache;
import org.jooq.util.jaxb.Database;
public class ServiceModule extends AbstractModule {
public ServiceModule(TileServerConfig cfg) throws Exception {
public ServiceModule(TileServerConfig cfg) {
this.cfg = cfg;
}
@ -30,10 +31,13 @@ public class ServiceModule extends AbstractModule {
if (cfg.tileCacheType() == TileServerConfig.CacheType.DATABASE)
bind(TileCache.class).to(DatabaseTileCache.class);
else if (cfg.tileCacheType() == TileServerConfig.CacheType.EHCACHE)
bind(TileCache.class).to(EHTileCache.class);
else
bind(TileCache.class).to(FileTileCache.class);
//bind(TileCache.class).to(FileTileCache.class);
bind(EventBus.class).to(EventBusImpl.class);
bind(ColorTable.class).toProvider(ColorTableProvider.class);
bind(ExecutorService.class).toProvider(ExecutorProvider.class);

View File

@ -15,6 +15,7 @@ import spark.Route;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicInteger;
@Singleton
public class TileRoute implements Route {
@ -25,12 +26,18 @@ public class TileRoute implements Route {
public TileRoute(TileRenderer renderer, TileServerConfig cfg, TileCache cache) {
this.renderer = renderer;
this.cache = cache;
this.cfg = cfg;
}
private final TileRenderer renderer;
private final TileCache cache;
private final TileServerConfig cfg;
//TODO: proper rate limit
private final AtomicInteger entryCount = new AtomicInteger();
@Override
public Object handle(Request req, Response res) throws Exception {
res.header("Content-Type", "image/png");
@ -46,7 +53,17 @@ public class TileRoute implements Route {
logger.debug("Rendering tile @ {}/{} zoom: {}", x,y,z);
return renderer.render(x, y, z);
try {
while (entryCount.get() > cfg.tileRouteReentryCount()){
Thread.sleep(50);
}
entryCount.incrementAndGet();
return renderer.render(x, y, z);
} finally {
entryCount.decrementAndGet();
}
}
}

View File

@ -0,0 +1,92 @@
package io.rudin.minetest.tileserver.service.impl;
import io.rudin.minetest.tileserver.config.TileServerConfig;
import io.rudin.minetest.tileserver.service.TileCache;
import org.ehcache.Cache;
import org.ehcache.PersistentCacheManager;
import org.ehcache.config.builders.CacheConfigurationBuilder;
import org.ehcache.config.builders.CacheManagerBuilder;
import org.ehcache.config.builders.ResourcePoolsBuilder;
import org.ehcache.config.units.EntryUnit;
import org.ehcache.config.units.MemoryUnit;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
@Singleton
public class EHTileCache implements TileCache {
@Inject
public EHTileCache(TileServerConfig cfg){
this.cfg = cfg;
this.timestampMarker = new File(cfg.tileDirectory(), "timestampmarker-ehcache");
if (!timestampMarker.isFile()){
try (OutputStream output = new FileOutputStream(timestampMarker)){
output.write(0x00);
} catch (Exception e){
throw new IllegalArgumentException("could not create timestamp marker!", e);
}
timestampMarker.setLastModified(0);
}
PersistentCacheManager persistentCacheManager = CacheManagerBuilder.newCacheManagerBuilder()
.with(CacheManagerBuilder.persistence(new File(cfg.tileDirectory(), "ehcache")))
.withCache("cache",
CacheConfigurationBuilder.newCacheConfigurationBuilder(String.class, byte[].class,
ResourcePoolsBuilder.newResourcePoolsBuilder()
.heap(10, MemoryUnit.MB)
.offheap(50, MemoryUnit.MB)
.disk(10, MemoryUnit.GB, true)
)
).build(true);
cache = persistentCacheManager.getCache("cache", String.class, byte[].class);
Runtime.getRuntime().addShutdownHook(new Thread(persistentCacheManager::close));
}
private final File timestampMarker;
private final TileServerConfig cfg;
private final Cache<String, byte[]> cache;
private String getKey(int x, int y, int z){
//TODO: long key
return x + "/" + y + "/" + z;
}
@Override
public void put(int x, int y, int z, byte[] data) throws IOException {
cache.put(getKey(x,y,z), data);
timestampMarker.setLastModified(System.currentTimeMillis());
}
@Override
public byte[] get(int x, int y, int z) throws IOException {
return cache.get(getKey(x,y,z));
}
@Override
public boolean has(int x, int y, int z) {
return cache.containsKey(getKey(x,y,z));
}
@Override
public void remove(int x, int y, int z) {
cache.remove(getKey(x,y,z));
}
@Override
public long getLatestTimestamp() {
return timestampMarker.lastModified();
}
}