In-Memory Ban Status Caching
Implement a high-performance in-memory cache (Caffeine) to eliminate the PostgreSQL query currently happening on every authenticated API request in BannedUserFilter.
Design Choices / Fallback (Answering your question)
What happens if the system reboots and the cache is cleared?
We use a Read-Through Cache pattern via Spring's @Cacheable.
- When the system boots, the in-memory Caffeine cache is completely empty.
- When a banned user (or any user) makes their very first API request post-reboot,
BannedUserFilter checks the cache.
- Because it's a cache miss, Spring automatically executes the underlying database query (
UserRepository), retrieves the current status, and stores it in the Caffeine cache.
- For all subsequent requests from that user, the system reads from the lightning-fast memory cache.
Conclusion: No banned users are "lost" on reboot. The system gracefully falls back to the database precisely once per user.
Proposed Changes
Configuration
[MODIFY] CacheConfig.java
- Define a new constant:
public static final String CACHE_USER_BAN_STATUS = "userBanStatus";
- Register the cache in the
cacheManager() bean with a reasonable configuration (e.g., maximum size 10,000 users, expire after write 2 hours).
Application Layer
[MODIFY] UserService.java
- Add a new method
public boolean isUserBanned(String username) that queries the database.
- Annotate
isUserBanned with @Cacheable(value = CacheConfig.CACHE_USER_BAN_STATUS, key = "#username").
- Annotate the existing
banUser(String username) method with @CacheEvict(value = CacheConfig.CACHE_USER_BAN_STATUS, key = "#username") to wipe the cache when a ban happens.
- Annotate the existing
unbanUser(String username) method with @CacheEvict(value = CacheConfig.CACHE_USER_BAN_STATUS, key = "#username").
Infrastructure / Security Layer
[MODIFY] BannedUserFilter.java
- Change the dependency from
UserRepository to UserService.
- Replace the live database fetch
userRepository.findByUsername(username) with a call to userService.isUserBanned(username).
Verification Plan
Automated Tests
- Run
npm run test:e2e:stories (already running) and standard backend tests.
- Verify
BannedUserFilterTest (if it exists) passes with mocked cache/service.
Manual Verification
- Log in as a normal user. Verify the system works.
- Admin bans the user. Verify the user is immediately blocked (403).
- Restart the backend server.
- The banned user attempts an API call. Verify they are still blocked (403), confirming the cache-miss fallback works perfectly.
In-Memory Ban Status Caching
Implement a high-performance in-memory cache (Caffeine) to eliminate the PostgreSQL query currently happening on every authenticated API request in
BannedUserFilter.Design Choices / Fallback (Answering your question)
What happens if the system reboots and the cache is cleared?
We use a Read-Through Cache pattern via Spring's
@Cacheable.BannedUserFilterchecks the cache.UserRepository), retrieves the current status, and stores it in the Caffeine cache.Conclusion: No banned users are "lost" on reboot. The system gracefully falls back to the database precisely once per user.
Proposed Changes
Configuration
[MODIFY] CacheConfig.java
public static final String CACHE_USER_BAN_STATUS = "userBanStatus";cacheManager()bean with a reasonable configuration (e.g., maximum size 10,000 users, expire after write 2 hours).Application Layer
[MODIFY] UserService.java
public boolean isUserBanned(String username)that queries the database.isUserBannedwith@Cacheable(value = CacheConfig.CACHE_USER_BAN_STATUS, key = "#username").banUser(String username)method with@CacheEvict(value = CacheConfig.CACHE_USER_BAN_STATUS, key = "#username")to wipe the cache when a ban happens.unbanUser(String username)method with@CacheEvict(value = CacheConfig.CACHE_USER_BAN_STATUS, key = "#username").Infrastructure / Security Layer
[MODIFY] BannedUserFilter.java
UserRepositorytoUserService.userRepository.findByUsername(username)with a call touserService.isUserBanned(username).Verification Plan
Automated Tests
npm run test:e2e:stories(already running) and standard backend tests.BannedUserFilterTest(if it exists) passes with mocked cache/service.Manual Verification