Cache implementation
All checks were successful
Deploy API / docker (ubuntu-latest, 2.44.0) (push) Successful in 52s

This commit is contained in:
Braydon 2024-09-08 23:36:05 -04:00
parent d2937cc0aa
commit b00694ecf8
3 changed files with 58 additions and 4 deletions

@ -2,6 +2,6 @@
An API designed to provide real-time access to a user's Discord data.
## TODO
- [ ] Caching
- [x] Caching
- [ ] WebSockets
- [ ] User account for extra data? (about me, connections, etc)

@ -0,0 +1,30 @@
package me.braydon.tether.model;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NonNull;
import lombok.Setter;
import net.dv8tion.jda.api.entities.User;
/**
* A model representing a cached Discord user.
*
* @author Braydon
*/
@AllArgsConstructor @Setter @Getter
public final class CachedDiscordUser {
/**
* The cached user.
*/
@NonNull private final User user;
/**
* The cached user profile.
*/
@NonNull private final User.Profile profile;
/**
* The unix time of when this user was cached.
*/
private long cached;
}

@ -1,5 +1,7 @@
package me.braydon.tether.service;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import jakarta.annotation.PostConstruct;
import lombok.NonNull;
import lombok.SneakyThrows;
@ -7,6 +9,7 @@ import lombok.extern.log4j.Log4j2;
import me.braydon.tether.exception.impl.BadRequestException;
import me.braydon.tether.exception.impl.ResourceNotFoundException;
import me.braydon.tether.exception.impl.ServiceUnavailableException;
import me.braydon.tether.model.CachedDiscordUser;
import me.braydon.tether.model.DiscordUser;
import me.braydon.tether.model.response.DiscordUserResponse;
import net.dv8tion.jda.api.JDA;
@ -18,6 +21,8 @@ import net.dv8tion.jda.api.utils.cache.CacheFlag;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;
/**
* This service is responsible for
* interacting with the Discord API.
@ -35,6 +40,13 @@ public final class DiscordService {
*/
private JDA jda;
/**
* A cache of users retrieved from Discord.
*/
private final Cache<Long, CachedDiscordUser> cachedUsers = Caffeine.newBuilder()
.expireAfterAccess(3L, TimeUnit.MINUTES)
.build();
@PostConstruct
public void onInitialize() {
connectBot();
@ -67,9 +79,21 @@ public final class DiscordService {
break;
}
}
User user = jda.retrieveUserById(snowflake).complete();
User.Profile profile = user.retrieveProfile().complete();
return new DiscordUserResponse(DiscordUser.buildFromEntity(user, profile, member), -1L);
// Then retrieve the user
CachedDiscordUser cachedUser = cachedUsers.getIfPresent(snowflake);
boolean fromCache = cachedUser != null;
if (cachedUser == null) { // No cache, retrieve fresh data
User user = jda.retrieveUserById(snowflake).complete();
cachedUser = new CachedDiscordUser(
user, user.retrieveProfile().complete(), System.currentTimeMillis()
);
cachedUsers.put(snowflake, cachedUser);
}
// Finally build the response and respond with it
return new DiscordUserResponse(
DiscordUser.buildFromEntity(cachedUser.getUser(), cachedUser.getProfile(), member),
fromCache ? cachedUser.getCached() : -1L
);
} catch (ErrorResponseException ex) {
// Failed to lookup the user, handle appropriately
if (ex.getErrorCode() == 10013) {