Skip to content

Commit 1fcb037

Browse files
Implemented Route Caching using Caffeine
1 parent fc50264 commit 1fcb037

File tree

4 files changed

+116
-41
lines changed

4 files changed

+116
-41
lines changed
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package net.javadiscord.javabot.api.routes;
2+
3+
import com.github.benmanes.caffeine.cache.Cache;
4+
import lombok.Getter;
5+
6+
/**
7+
* Simple parent class which enables all extending classes to have their own
8+
* {@link Cache}.
9+
*
10+
* @param <K> The caches' key.
11+
* @param <V> The caches' value.
12+
*/
13+
public abstract class CaffeineCache<K, V> {
14+
15+
@Getter
16+
private final Cache<K, V> cache;
17+
18+
protected CaffeineCache(Cache<K, V> cache) {
19+
this.cache = cache;
20+
}
21+
}

src/main/java/net/javadiscord/javabot/api/routes/metrics/MetricsController.java

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
package net.javadiscord.javabot.api.routes.metrics;
22

3+
import com.github.benmanes.caffeine.cache.Caffeine;
34
import lombok.extern.slf4j.Slf4j;
45
import net.dv8tion.jda.api.entities.Guild;
56
import net.javadiscord.javabot.Bot;
67
import net.javadiscord.javabot.api.response.ApiResponseBuilder;
78
import net.javadiscord.javabot.api.response.ApiResponses;
9+
import net.javadiscord.javabot.api.routes.CaffeineCache;
810
import net.javadiscord.javabot.api.routes.JDAEntity;
911
import net.javadiscord.javabot.api.routes.metrics.model.MetricsData;
1012
import org.springframework.http.HttpStatus;
@@ -14,12 +16,24 @@
1416
import org.springframework.web.bind.annotation.PathVariable;
1517
import org.springframework.web.bind.annotation.RestController;
1618

19+
import java.util.concurrent.TimeUnit;
20+
1721
/**
1822
* Handles all GET-Requests on the {guild_id}/metrics route.
1923
*/
2024
@Slf4j
2125
@RestController
22-
public class MetricsController implements JDAEntity {
26+
public class MetricsController extends CaffeineCache<Long, MetricsData> implements JDAEntity {
27+
28+
/**
29+
* The constructor of this class which initializes the {@link Caffeine} cache.
30+
*/
31+
public MetricsController() {
32+
super(Caffeine.newBuilder()
33+
.expireAfterWrite(15, TimeUnit.MINUTES)
34+
.build()
35+
);
36+
}
2337

2438
/**
2539
* Serves metrics for the specified guild.
@@ -36,10 +50,14 @@ public ResponseEntity<String> getMetrics(@PathVariable(value = "guild_id") Strin
3650
if (guild == null) {
3751
return new ResponseEntity<>(ApiResponses.INVALID_GUILD_IN_REQUEST, HttpStatus.BAD_REQUEST);
3852
}
39-
MetricsData data = new MetricsData();
40-
data.setMemberCount(guild.getMemberCount());
41-
data.setOnlineCount(guild.retrieveMetaData().complete().getApproximatePresences());
42-
data.setWeeklyMessages(Bot.getConfig().get(guild).getMetricsConfig().getWeeklyMessages());
53+
MetricsData data = getCache().getIfPresent(guild.getIdLong());
54+
if (data == null) {
55+
data = new MetricsData();
56+
data.setMemberCount(guild.getMemberCount());
57+
data.setOnlineCount(guild.retrieveMetaData().complete().getApproximatePresences());
58+
data.setWeeklyMessages(Bot.getConfig().get(guild).getMetricsConfig().getWeeklyMessages());
59+
getCache().put(guild.getIdLong(), data);
60+
}
4361
return new ResponseEntity<>(new ApiResponseBuilder().add("metrics", data).build(), HttpStatus.OK);
4462
}
4563
}

src/main/java/net/javadiscord/javabot/api/routes/qotw_leaderboard/QOTWLeaderboardController.java

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
package net.javadiscord.javabot.api.routes.qotw_leaderboard;
22

3+
import com.github.benmanes.caffeine.cache.Caffeine;
34
import net.dv8tion.jda.api.entities.Guild;
45
import net.javadiscord.javabot.Bot;
56
import net.javadiscord.javabot.api.response.ApiResponseBuilder;
67
import net.javadiscord.javabot.api.response.ApiResponses;
8+
import net.javadiscord.javabot.api.routes.CaffeineCache;
79
import net.javadiscord.javabot.api.routes.JDAEntity;
810
import net.javadiscord.javabot.api.routes.qotw_leaderboard.model.QOTWMemberData;
911
import net.javadiscord.javabot.systems.qotw.QOTWPointsService;
@@ -17,12 +19,23 @@
1719
import org.springframework.web.bind.annotation.RestController;
1820

1921
import java.util.List;
22+
import java.util.concurrent.TimeUnit;
2023

2124
/**
2225
* Handles all GET-Requests on the {guild_id}/qotw/leaderboard route.
2326
*/
2427
@RestController
25-
public class QOTWLeaderboardController implements JDAEntity {
28+
public class QOTWLeaderboardController extends CaffeineCache<Long, List<QOTWMemberData>> implements JDAEntity {
29+
30+
/**
31+
* The constructor of this class which initializes the {@link Caffeine} cache.
32+
*/
33+
public QOTWLeaderboardController() {
34+
super(Caffeine.newBuilder()
35+
.expireAfterWrite(10, TimeUnit.MINUTES)
36+
.build()
37+
);
38+
}
2639

2740
/**
2841
* Serves the specified amount of users. Sorted by the
@@ -49,17 +62,21 @@ public ResponseEntity<String> getQOTWLeaderboard(
4962
}
5063
int amount = Integer.parseInt(amountParam);
5164
QOTWPointsService service = new QOTWPointsService(Bot.getDataSource());
52-
List<QOTWMemberData> members = service.getTopMembers(amount, guild).stream()
53-
.map(p -> {
54-
QOTWMemberData data = new QOTWMemberData();
55-
data.setUserId(p.second().getIdLong());
56-
data.setUserName(p.second().getUser().getName());
57-
data.setDiscriminator(p.second().getUser().getDiscriminator());
58-
data.setEffectiveAvatarUrl(p.second().getEffectiveAvatarUrl());
59-
data.setAccount(p.first());
60-
return data;
61-
})
62-
.toList();
63-
return new ResponseEntity<>(new ApiResponseBuilder().add("leaderboard", members).build(), HttpStatus.OK);
65+
List<QOTWMemberData> members = getCache().getIfPresent(guild.getIdLong());
66+
if (members == null || members.isEmpty()) {
67+
members = service.getTopMembers(amount, guild).stream()
68+
.map(p -> {
69+
QOTWMemberData data = new QOTWMemberData();
70+
data.setUserId(p.second().getIdLong());
71+
data.setUserName(p.second().getUser().getName());
72+
data.setDiscriminator(p.second().getUser().getDiscriminator());
73+
data.setEffectiveAvatarUrl(p.second().getEffectiveAvatarUrl());
74+
data.setAccount(p.first());
75+
return data;
76+
})
77+
.toList();
78+
getCache().put(guild.getIdLong(), members);
79+
}
80+
return new ResponseEntity<>(new ApiResponseBuilder().add("leaderboard", members.stream().limit(amount).toList()).build(), HttpStatus.OK);
6481
}
6582
}

src/main/java/net/javadiscord/javabot/api/routes/user_profile/UserProfileController.java

Lines changed: 42 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
package net.javadiscord.javabot.api.routes.user_profile;
22

3+
import com.github.benmanes.caffeine.cache.Caffeine;
34
import net.dv8tion.jda.api.entities.Guild;
45
import net.dv8tion.jda.api.entities.User;
56
import net.javadiscord.javabot.Bot;
67
import net.javadiscord.javabot.api.response.ApiResponseBuilder;
78
import net.javadiscord.javabot.api.response.ApiResponses;
9+
import net.javadiscord.javabot.api.routes.CaffeineCache;
810
import net.javadiscord.javabot.api.routes.JDAEntity;
911
import net.javadiscord.javabot.api.routes.user_profile.model.HelpAccountData;
1012
import net.javadiscord.javabot.api.routes.user_profile.model.UserProfileData;
@@ -17,6 +19,7 @@
1719
import net.javadiscord.javabot.systems.user_preferences.model.Preference;
1820
import net.javadiscord.javabot.systems.user_preferences.model.UserPreference;
1921
import net.javadiscord.javabot.util.ExceptionLogger;
22+
import net.javadiscord.javabot.util.Pair;
2023
import org.springframework.http.HttpStatus;
2124
import org.springframework.http.MediaType;
2225
import org.springframework.http.ResponseEntity;
@@ -29,12 +32,23 @@
2932
import java.time.LocalDateTime;
3033
import java.util.Arrays;
3134
import java.util.List;
35+
import java.util.concurrent.TimeUnit;
3236

3337
/**
3438
* Handles all GET-Requests on the {guild_id}/{user_id} route.
3539
*/
3640
@RestController
37-
public class UserProfileController implements JDAEntity {
41+
public class UserProfileController extends CaffeineCache<Pair<Long, Long>, UserProfileData> implements JDAEntity {
42+
43+
/**
44+
* The constructor of this class which initializes the {@link Caffeine} cache.
45+
*/
46+
public UserProfileController() {
47+
super(Caffeine.newBuilder()
48+
.expireAfterWrite(10, TimeUnit.SECONDS)
49+
.build()
50+
);
51+
}
3852

3953
/**
4054
* Serves a single users' profile in a specified guild.
@@ -60,28 +74,33 @@ public ResponseEntity<String> getUserProfile(
6074
return new ResponseEntity<>(ApiResponses.INVALID_USER_IN_REQUEST, HttpStatus.BAD_REQUEST);
6175
}
6276
try (Connection con = Bot.getDataSource().getConnection()) {
63-
UserProfileData data = new UserProfileData();
64-
data.setUserId(user.getIdLong());
65-
data.setUserName(user.getName());
66-
data.setDiscriminator(user.getDiscriminator());
67-
data.setEffectiveAvatarUrl(user.getEffectiveAvatarUrl());
68-
// Question of the Week Account
69-
QOTWPointsService qotwService = new QOTWPointsService(Bot.getDataSource());
70-
QOTWAccount qotwAccount = qotwService.getOrCreateAccount(user.getIdLong());
71-
data.setQotwAccount(qotwAccount);
72-
// Help Account
73-
HelpExperienceService helpService = new HelpExperienceService(Bot.getDataSource());
74-
HelpAccount helpAccount = helpService.getOrCreateAccount(user.getIdLong());
75-
data.setHelpAccount(HelpAccountData.of(helpAccount, guild));
76-
// User Preferences
77-
UserPreferenceService preferenceService = new UserPreferenceService(Bot.getDataSource());
78-
List<UserPreference> preferences = Arrays.stream(Preference.values()).map(p -> preferenceService.getOrCreate(user.getIdLong(), p)).toList();
79-
data.setPreferences(preferences);
80-
// User Warns
81-
WarnRepository warnRepository = new WarnRepository(con);
82-
LocalDateTime cutoff = LocalDateTime.now().minusDays(Bot.getConfig().get(guild).getModerationConfig().getWarnTimeoutDays());
83-
data.setWarns(warnRepository.getWarnsByUserId(user.getIdLong(), cutoff));
84-
77+
// Check Cache
78+
UserProfileData data = getCache().getIfPresent(new Pair<>(guild.getIdLong(), user.getIdLong()));
79+
if (data == null) {
80+
data = new UserProfileData();
81+
data.setUserId(user.getIdLong());
82+
data.setUserName(user.getName());
83+
data.setDiscriminator(user.getDiscriminator());
84+
data.setEffectiveAvatarUrl(user.getEffectiveAvatarUrl());
85+
// Question of the Week Account
86+
QOTWPointsService qotwService = new QOTWPointsService(Bot.getDataSource());
87+
QOTWAccount qotwAccount = qotwService.getOrCreateAccount(user.getIdLong());
88+
data.setQotwAccount(qotwAccount);
89+
// Help Account
90+
HelpExperienceService helpService = new HelpExperienceService(Bot.getDataSource());
91+
HelpAccount helpAccount = helpService.getOrCreateAccount(user.getIdLong());
92+
data.setHelpAccount(HelpAccountData.of(helpAccount, guild));
93+
// User Preferences
94+
UserPreferenceService preferenceService = new UserPreferenceService(Bot.getDataSource());
95+
List<UserPreference> preferences = Arrays.stream(Preference.values()).map(p -> preferenceService.getOrCreate(user.getIdLong(), p)).toList();
96+
data.setPreferences(preferences);
97+
// User Warns
98+
WarnRepository warnRepository = new WarnRepository(con);
99+
LocalDateTime cutoff = LocalDateTime.now().minusDays(Bot.getConfig().get(guild).getModerationConfig().getWarnTimeoutDays());
100+
data.setWarns(warnRepository.getWarnsByUserId(user.getIdLong(), cutoff));
101+
// Insert into cache
102+
getCache().put(new Pair<>(guild.getIdLong(), user.getIdLong()), data);
103+
}
85104
return new ResponseEntity<>(new ApiResponseBuilder().add("profile", data).build(), HttpStatus.OK);
86105
} catch (SQLException e) {
87106
ExceptionLogger.capture(e, getClass().getSimpleName());

0 commit comments

Comments
 (0)