Skip to content

Commit

Permalink
Implement rudimentary caching
Browse files Browse the repository at this point in the history
  • Loading branch information
Fabricio20 committed Sep 26, 2024
1 parent abc6b72 commit e4ae31a
Show file tree
Hide file tree
Showing 13 changed files with 172 additions and 41 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package net.lindseybot.bot.listener;

import jakarta.annotation.PreDestroy;
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
import net.dv8tion.jda.api.hooks.IEventManager;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
Expand All @@ -10,6 +11,9 @@
import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Component;

import java.util.ArrayDeque;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.TimeUnit;

@Component
Expand All @@ -19,9 +23,10 @@ public class UserNameListener extends ListenerAdapter implements ExpirationListe
private final ExpiringMap<Long, String> users = ExpiringMap.builder()
.expirationPolicy(ExpirationPolicy.CREATED)
.expiration(1, TimeUnit.MINUTES)
.asyncExpirationListener(this)
.expirationListener(this)
.maxSize(15_000)
.build();
private final Queue<UserUpdate> queue = new ArrayDeque<>();

public UserNameListener(IEventManager api, ProfileServiceImpl profiles) {
this.profiles = profiles;
Expand All @@ -39,7 +44,22 @@ public void onMessageReceived(@NotNull MessageReceivedEvent event) {

@Override
public void expired(Long userId, String name) {
this.profiles.updateName(userId, name);
this.queue.add(new UserUpdate(userId, name));
if (queue.size() > 250) {
List<UserUpdate> updates = queue.stream()
.limit(250).toList();
this.profiles.updateNames(updates);
}
}

@PreDestroy
public void onDestroy() {
List<UserUpdate> updates = queue.stream()
.toList();
this.profiles.updateNames(updates);
}

public record UserUpdate(long id, String name) {
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,6 @@ public interface UserRepository extends JpaRepository<UserProfile, Long> {

int countByUser(long id);

@Modifying
@Query("update UserProfile profile set profile.name = ?1, profile.lastSeen = ?2 where profile.user = ?3")
void updateName(String name, long seen, long user);

@Modifying
@Query("update UserProfile pr set pr.cookieStreak = 0 where pr.cookieStreak > 0 and pr.lastDailyCookies < ?1")
int deleteOutdatedStreaks(long timestamp);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import lombok.extern.slf4j.Slf4j;
import net.jodah.expiringmap.ExpirationPolicy;
import net.jodah.expiringmap.ExpiringMap;
import net.lindseybot.bot.listener.UserNameListener;
import net.lindseybot.bot.repositories.sql.MemberRepository;
import net.lindseybot.bot.repositories.sql.ServerRepository;
import net.lindseybot.bot.repositories.sql.UserRepository;
Expand All @@ -12,12 +13,17 @@
import net.lindseybot.shared.entities.profile.members.MemberId;
import net.lindseybot.shared.worker.services.ProfileService;
import org.jetbrains.annotations.NotNull;
import org.springframework.jdbc.core.BatchPreparedStatementSetter;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

Expand All @@ -28,19 +34,23 @@ public class ProfileServiceImpl implements ProfileService {
private final UserRepository users;
private final MemberRepository members;
private final ServerRepository servers;
private final JdbcTemplate template;

private final ExpiringMap<Long, UserProfile> userCache = ExpiringMap.builder()
.expirationPolicy(ExpirationPolicy.ACCESSED)
.expiration(15, TimeUnit.MINUTES)
.maxSize(10_000)
.build();

public ProfileServiceImpl(UserRepository users,
MemberRepository members,
ServerRepository servers) {
public ProfileServiceImpl(
UserRepository users,
MemberRepository members,
ServerRepository servers,
JdbcTemplate template) {
this.users = users;
this.members = members;
this.servers = servers;
this.template = template;
}

@Override
Expand Down Expand Up @@ -117,8 +127,20 @@ public void updateSeen(Set<Long> guilds) {
}

@Transactional
public void updateName(long user, String name) {
this.users.updateName(name, System.currentTimeMillis(), user);
public void updateNames(List<UserNameListener.UserUpdate> updates) {
this.template.batchUpdate("update user_settings set name = ?, last_seen = ? where user = ?",
new BatchPreparedStatementSetter() {
public void setValues(@NotNull PreparedStatement ps, int i) throws SQLException {
var update = updates.get(i);
ps.setString(1, update.name());
ps.setLong(2, System.currentTimeMillis());
ps.setLong(3, update.id());
}

public int getBatchSize() {
return updates.size();
}
});
}

}
6 changes: 6 additions & 0 deletions bot/src/main/java/net/lindseybot/bot/spring/WorkerConfig.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package net.lindseybot.bot.spring;

import net.lindseybot.shared.properties.BotProperties;
import net.lindseybot.shared.services.CacheService;
import net.lindseybot.shared.worker.impl.MessengerImpl;
import net.lindseybot.shared.worker.services.DiscordAdapter;
import net.lindseybot.shared.worker.services.Messenger;
Expand Down Expand Up @@ -34,4 +35,9 @@ public Translator translator(ProfileService profiles) {
return new Translator(profiles);
}

@Bean
public CacheService cacheService() {
return new CacheService();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package net.lindseybot.shared.services;

import lombok.Getter;
import net.jodah.expiringmap.ExpirationPolicy;
import net.jodah.expiringmap.ExpiringMap;
import net.lindseybot.shared.entities.profile.servers.AntiAd;
import net.lindseybot.shared.entities.profile.servers.AntiScam;
import net.lindseybot.shared.entities.profile.servers.Registration;

import java.util.Map;
import java.util.concurrent.TimeUnit;

@Getter
public class CacheService {

private final Map<Long, Boolean> roleHistory = ExpiringMap.builder()
.expirationPolicy(ExpirationPolicy.ACCESSED)
.expiration(10, TimeUnit.MINUTES)
.maxSize(50_000)
.build();

private final Map<Long, AntiScam> antiScam = ExpiringMap.builder()
.expirationPolicy(ExpirationPolicy.ACCESSED)
.expiration(10, TimeUnit.MINUTES)
.maxSize(50_000)
.build();

private final Map<Long, AntiAd> antiAd = ExpiringMap.builder()
.expirationPolicy(ExpirationPolicy.ACCESSED)
.expiration(30, TimeUnit.MINUTES)
.maxSize(50_000)
.build();

private final Map<Long, Registration> registration = ExpiringMap.builder()
.expirationPolicy(ExpirationPolicy.ACCESSED)
.expiration(30, TimeUnit.MINUTES)
.maxSize(50_000)
.build();

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,28 @@
import net.dv8tion.jda.api.entities.Guild;
import net.lindseybot.automod.repositories.sql.AntiAdRepository;
import net.lindseybot.shared.entities.profile.servers.AntiAd;
import net.lindseybot.shared.services.CacheService;
import org.springframework.stereotype.Service;

@Service
public class AntiAdService {

private final AntiAdRepository repository;
private final CacheService cache;

public AntiAdService(AntiAdRepository repository) {
public AntiAdService(AntiAdRepository repository, CacheService cache) {
this.repository = repository;
this.cache = cache;
}

public AntiAd find(Guild guild) {
return repository.findById(guild.getIdLong())
if (this.cache.getAntiAd().containsKey(guild.getIdLong())) {
return this.cache.getAntiAd().get(guild.getIdLong());
}
var data = repository.findById(guild.getIdLong())
.orElse(new AntiAd(guild.getIdLong()));
this.cache.getAntiAd().put(guild.getIdLong(), data);
return data;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,28 @@
import net.dv8tion.jda.api.entities.Guild;
import net.lindseybot.automod.repositories.sql.AntiScamRepository;
import net.lindseybot.shared.entities.profile.servers.AntiScam;
import net.lindseybot.shared.services.CacheService;
import org.springframework.stereotype.Service;

@Service
public class AntiScamService {

private final AntiScamRepository repository;
private final CacheService cache;

public AntiScamService(AntiScamRepository repository) {
public AntiScamService(AntiScamRepository repository, CacheService cache) {
this.repository = repository;
this.cache = cache;
}

public AntiScam find(Guild guild) {
return repository.findById(guild.getIdLong())
if (this.cache.getAntiScam().containsKey(guild.getIdLong())) {
return this.cache.getAntiScam().get(guild.getIdLong());
}
var data = repository.findById(guild.getIdLong())
.orElse(new AntiScam(guild.getIdLong()));
this.cache.getAntiScam().put(guild.getIdLong(), data);
return data;
}

}
Original file line number Diff line number Diff line change
@@ -1,43 +1,36 @@
package net.lindseybot.automod.services;

import net.dv8tion.jda.api.entities.Guild;
import net.jodah.expiringmap.ExpirationPolicy;
import net.jodah.expiringmap.ExpiringMap;
import net.lindseybot.automod.repositories.sql.RegistrationRepository;
import net.lindseybot.shared.entities.profile.servers.Registration;
import net.lindseybot.shared.services.CacheService;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

@Service
public class RegistrationService {

private final RegistrationRepository repository;
private final ExpiringMap<Long, Registration> cache = ExpiringMap.builder()
.expirationPolicy(ExpirationPolicy.ACCESSED)
.expiration(1, TimeUnit.MINUTES)
.maxSize(10_000)
.build();
private final CacheService cache;

public RegistrationService(RegistrationRepository repository) {
public RegistrationService(RegistrationRepository repository, CacheService cache) {
this.repository = repository;
this.cache = cache;
}

public Registration find(Guild guild) {
Registration cached = this.cache.get(guild.getIdLong());
if (cached != null) {
return cached;
if (this.cache.getRegistration().containsKey(guild.getIdLong())) {
return this.cache.getRegistration().get(guild.getIdLong());
}
Registration registration = repository.findById(guild.getIdLong())
var data = repository.findById(guild.getIdLong())
.orElse(new Registration(guild.getIdLong()));
this.cache.put(guild.getIdLong(), registration);
return registration;
this.cache.getRegistration().put(guild.getIdLong(), data);
return data;
}

public void disable(Registration registration) {
registration.setEnabled(false);
this.repository.save(registration);
this.cache.remove(registration.getGuild());
this.cache.getRegistration().remove(registration.getGuild());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import net.lindseybot.shared.entities.profile.members.MemberId;
import net.lindseybot.shared.entities.profile.members.RoleHistory;
import net.lindseybot.shared.entities.profile.servers.KeepRoles;
import net.lindseybot.shared.services.CacheService;
import net.lindseybot.shared.worker.services.NotificationService;
import org.springframework.stereotype.Service;

Expand All @@ -17,19 +18,28 @@ public class RoleHistoryService {
private final KeepRolesRepository repository;
private final RoleHistoryRepository history;
private final NotificationService notifications;
private final CacheService cache;

public RoleHistoryService(KeepRolesRepository repository,
RoleHistoryRepository history,
NotificationService notifications) {
public RoleHistoryService(
KeepRolesRepository repository,
RoleHistoryRepository history,
NotificationService notifications,
CacheService cache) {
this.repository = repository;
this.history = history;
this.notifications = notifications;
this.cache = cache;
}

public boolean isActive(Guild guild) {
return this.repository.findById(guild.getIdLong())
if (this.cache.getRoleHistory().containsKey(guild.getIdLong())) {
return this.cache.getRoleHistory().get(guild.getIdLong());
}
boolean active = this.repository.findById(guild.getIdLong())
.orElse(new KeepRoles())
.isEnabled();
this.cache.getRoleHistory().put(guild.getIdLong(), active);
return active;
}

public RoleHistory findByMember(Member member) {
Expand All @@ -52,6 +62,7 @@ public void disable(Guild guild, Label keepRoles) {
}
config.setEnabled(false);
this.repository.save(config);
this.cache.getRoleHistory().remove(guild.getIdLong());
this.notifications.notify(guild, keepRoles);
}

Expand Down
Loading

0 comments on commit e4ae31a

Please sign in to comment.