introduced an LRU eviction cache for SailingServiceImpl.getLeaderboardByName

This commit is contained in:
Axel Uhl committed 2012-07-19 16:40:59 +02:00
1 parent 9431badcab
commit bafab3a7a0
4 files changed
+59 -6

No files matched your search

@@ -68,7 +68,7 @@ public class RaceTimePanel extends TimePanel<RaceTimePanelSettings> implements R
}
boolean timerAlreadyInitialized = getMin() != null && getMax() != null && timeSlider.getCurrentValue() != null;
if(!isTimeZoomed) {
if (!isTimeZoomed) {
updateMinMax(raceTimesInfo);
if (!timerAlreadyInitialized) {
initTimerPosition(raceTimesInfo);
@@ -109,7 +109,7 @@ public interface SailingService extends RemoteService {
LeaderboardDTO getLeaderboardByName(String leaderboardName, Date date,
Collection<String> namesOfRaceColumnsForWhichToLoadLegDetails, boolean waitForLatestAnalyses)
throws NoWindException;
throws Exception;
List<StrippedLeaderboardDTO> getLeaderboards();
@@ -329,7 +329,6 @@ public class TimePanel<T extends TimePanelSettings> extends FormPanel implements
/**
* @param min must not be <code>null</code>
* @param max must not be <code>null</code>
* @param fireEvent TODO
*/
public void setMinMax(Date min, Date max, boolean fireEvent) {
assert min != null && max != null;
@@ -241,6 +241,17 @@ public class SailingServiceImpl extends RemoteServiceServlet implements SailingS
private final com.sap.sailing.domain.common.CountryCodeFactory countryCodeFactory;
private final Executor executor;
/**
* In live operations, {@link #getLeaderboardByName(String, Date, Collection, boolean)} is the application's
* bottleneck. When two clients ask the same data for the same leaderboard with their
* <code>waitForLatestAnalyses</code> parameters set to <code>false</code>, expansion state and (quantized) time
* stamp, no two computations should be spawned for the two clients. Instead, if the computation is still running,
* all clients asking the same wait for the single result. Results are cached in this LRU-based evicting cache.
*/
private final Map<Triple<String, Date, Collection<String>>, FutureTask<LeaderboardDTO>> leaderboardByNameCache;
private int leaderboardByNameCacheHitCount;
private int leaderboardByNameCacheMissCount;
/**
* This executor needs to be a different one than {@link #executor} because the tasks run by {@link #executor}
@@ -276,6 +287,14 @@ public class SailingServiceImpl extends RemoteServiceServlet implements SailingS
countryCodeFactory = com.sap.sailing.domain.common.CountryCodeFactory.INSTANCE;
executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
raceDetailsExecutor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
leaderboardByNameCache = new LinkedHashMap<Triple<String,Date,Collection<String>>, FutureTask<LeaderboardDTO>>() {
private static final long serialVersionUID = 3775119859130148488L;
private static final int MAX_ENTRIES = 100;
@Override
protected boolean removeEldestEntry(Map.Entry<Triple<String,Date,Collection<String>>, FutureTask<LeaderboardDTO>> eldest) {
return size() > MAX_ENTRIES;
}
};
}
public void destroy() {
@@ -349,7 +368,42 @@ public class SailingServiceImpl extends RemoteServiceServlet implements SailingS
}
@Override
public LeaderboardDTO getLeaderboardByName(String leaderboardName, Date date,
public LeaderboardDTO getLeaderboardByName(final String leaderboardName, final Date date,
final Collection<String> namesOfRaceColumnsForWhichToLoadLegDetails, final boolean waitForLatestAnalyses)
throws NoWindException, InterruptedException, ExecutionException {
Triple<String, Date, Collection<String>> key = new Triple<String, Date, Collection<String>>(
leaderboardName, date, namesOfRaceColumnsForWhichToLoadLegDetails);
FutureTask<LeaderboardDTO> future;
boolean cacheHit = false;
synchronized (leaderboardByNameCache) {
future = leaderboardByNameCache.get(key);
if (future == null) {
future = new FutureTask<LeaderboardDTO>(new Callable<LeaderboardDTO>() {
@Override
public LeaderboardDTO call() throws Exception {
LeaderboardDTO result = computeLeaderboardByName(leaderboardName, date,
namesOfRaceColumnsForWhichToLoadLegDetails, waitForLatestAnalyses);
return result;
}
});
executor.execute(future);
leaderboardByNameCache.put(key, future);
} else {
cacheHit = true;
}
}
if (cacheHit) {
leaderboardByNameCacheHitCount++;
logger.info("Cache hit in getLeaderboardByName("+leaderboardName+", "+date+", "+namesOfRaceColumnsForWhichToLoadLegDetails+")");
} else {
leaderboardByNameCacheMissCount++;
}
logger.info("getLeaderboardByName cache hit vs. miss: "+leaderboardByNameCacheHitCount+"/"+leaderboardByNameCacheMissCount);
LeaderboardDTO result = future.get();
return result;
}
private LeaderboardDTO computeLeaderboardByName(String leaderboardName, Date date,
final Collection<String> namesOfRaceColumnsForWhichToLoadLegDetails, final boolean waitForLatestAnalyses)
throws NoWindException {
long startOfRequestHandling = System.currentTimeMillis();
@@ -420,7 +474,7 @@ public class SailingServiceImpl extends RemoteServiceServlet implements SailingS
}
}
}
logger.fine("getLeaderboardByName("+leaderboardName+", "+date+", "+namesOfRaceColumnsForWhichToLoadLegDetails+") took "+
logger.fine("computeLeaderboardByName("+leaderboardName+", "+date+", "+namesOfRaceColumnsForWhichToLoadLegDetails+") took "+
(System.currentTimeMillis()-startOfRequestHandling)+"ms");
return result;
}
@@ -2067,7 +2121,7 @@ public class SailingServiceImpl extends RemoteServiceServlet implements SailingS
// Filling the mark passings
Set<MarkPassing> competitorMarkPassings = race.getMarkPassings(competitor);
if (competitorMarkPassings != null) {
for (MarkPassing markPassing : race.getMarkPassings(competitor)) {
for (MarkPassing markPassing : competitorMarkPassings) {
MillisecondsTimePoint time = new MillisecondsTimePoint(markPassing.getTimePoint().asMillis());
markPassingsData.add(new Triple<String, Date, Double>(markPassing.getWaypoint().getName(), time
.asDate(), getCompetitorRaceDataEntry(detailType, race, competitor, time)));