Skip to content

MOSU-267 refactor: 도메인 캐싱 적용#269

Merged
jbh010204 merged 5 commits intodevelopfrom
refactor/mosu-267
Aug 10, 2025
Merged

MOSU-267 refactor: 도메인 캐싱 적용#269
jbh010204 merged 5 commits intodevelopfrom
refactor/mosu-267

Conversation

@jbh010204
Copy link
Member

@jbh010204 jbh010204 commented Aug 10, 2025

✨ 구현한 기능

  • 카페인을 이용한 캐싱을 적용하였습니다

📢 논의하고 싶은 내용

  • 추후 Composite로 레디스와 2레벨 캐싱을 적용 할 예정입니다

🎸 기타

Summary by CodeRabbit

  • New Features

    • Introduced a local cache manager using Caffeine for improved caching performance.
    • Added configurable cache groups and cache types for flexible caching strategies.
    • Enabled caching for notices, including cache invalidation on create, update, and delete actions, and caching of notice details and paginated lists.
  • Improvements

    • Enhanced notice-related operations with cache annotations to optimize read performance.
    • Updated cache configuration with new enums for cache grouping and types.
  • Refactor

    • Renamed a configuration class for clarity.
    • Removed unused repository fields from the inquiry service.
  • Bug Fixes

    • Corrected a dependency declaration typo in the build configuration.

@coderabbitai
Copy link

coderabbitai bot commented Aug 10, 2025

Walkthrough

This update introduces a local Caffeine-based caching infrastructure, including new configuration and manager classes, enums to represent cache groups and types, and an interface for updatable cache management. The NoticeService gains method-level caching annotations to optimize notice retrieval and ensure cache invalidation on updates. Minor cleanups and class renaming are also present.

Changes

Cohort / File(s) Change Summary
Caffeine Cache Infrastructure
src/main/java/life/mosu/mosuserver/application/caffeine/LocalCacheConfig.java, src/main/java/life/mosu/mosuserver/application/caffeine/LocalCacheManager.java, src/main/java/life/mosu/mosuserver/application/caffeine/UpdatableCacheManager.java, src/main/java/life/mosu/mosuserver/domain/caffeine/CacheGroup.java, src/main/java/life/mosu/mosuserver/domain/caffeine/CacheType.java
Introduces new Caffeine-based cache configuration and management classes: LocalCacheConfig (Spring config for Caffeine cache beans), LocalCacheManager (custom cache manager), and UpdatableCacheManager (interface for conditional cache updates). Also adds enums CacheGroup and CacheType to define cache groupings and types.
Notice Service Caching
src/main/java/life/mosu/mosuserver/application/notice/NoticeService.java
Adds Spring caching annotations to cache notice lists and details, and to evict cache entries on create, update, or delete operations. No business logic changes.
Repository Field Cleanup
src/main/java/life/mosu/mosuserver/application/inquiry/InquiryService.java
Removes unused repository fields and their imports from InquiryService. No logic changes.
Caffeine Config Class Rename
src/main/java/life/mosu/mosuserver/global/config/CaffeineFilterCacheConfig.java
Renames the class from CaffeineCacheConfig to CaffeineFilterCacheConfig without altering functionality.
Build Dependency Correction
build.gradle
Fixes a typographical error in the MySQL Testcontainers dependency artifact ID. No logic or build process changes.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant NoticeService
    participant CacheManager
    participant Database

    Client->>NoticeService: getNotices(page, size)
    NoticeService->>CacheManager: check cache for ("notice", page, size)
    alt Cache hit
        CacheManager-->>NoticeService: return cached notices
    else Cache miss
        NoticeService->>Database: query notices
        Database-->>NoticeService: return notices
        NoticeService->>CacheManager: store notices in cache
    end
    NoticeService-->>Client: return notices

    Client->>NoticeService: createNotice/updateNotice/deleteNotice
    NoticeService->>CacheManager: evict all "notice" cache entries
    NoticeService->>Database: perform write operation
    Database-->>NoticeService: confirm operation
    NoticeService-->>Client: return result
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~15–20 minutes

Poem

In the meadow of code where the caches now bloom,
Caffeine hops in, dispelling old gloom.
Notices cached, with a whisk of the tail,
While stale data’s swept out without fail.
With enums and configs, the garden grows bright—
This bunny approves: performance takes flight!
🐇✨

Note

🔌 MCP (Model Context Protocol) integration is now available in Early Access!

Pro users can now connect to remote MCP servers under the Integrations page to get reviews and chat conversations that understand additional development context.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch refactor/mosu-267

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary of Changes

Hello @jbh010204, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

I've implemented a local caching mechanism using Caffeine to enhance application performance. This is the first step towards a more comprehensive caching strategy, with plans to integrate Redis for a 2-level composite caching solution in the future.

Highlights

  • Local Caching with Caffeine: I've introduced Caffeine-based local caching by adding LocalCacheConfig, LocalCacheManager, and UpdatableCacheManager to manage and configure caches.
  • Cache Definitions: I've defined CacheGroup and CacheType enums to categorize and configure different cache instances, including their names, expiration policies, and types (local, global, composite).
  • Notice Service Caching: I've applied caching annotations (@Cacheable, @CacheEvict) to the NoticeService methods, enabling efficient data retrieval and invalidation for notice-related operations.
  • Build Dependency Fix: I've corrected a minor typo in the build.gradle file for the testcontainers:mysql dependency.
  • Code Refinement: I've cleaned up InquiryService by removing unused imports and dependencies, and renamed CaffeineCacheConfig to CaffeineFilterCacheConfig for better clarity and separation of concerns.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in issue comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments or fill out our survey to provide feedback.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces Caffeine-based local caching for domain objects, primarily for the Notice entity. The implementation includes a custom CacheManager and configuration enums. The changes are well-structured. I've provided a couple of suggestions to improve the caching strategy and the CacheManager implementation for better robustness and maintainability.

Comment on lines +40 to +41
@Cacheable(cacheNames = "notice",
key = "'page=' + #page + ',size=' + #size")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Storing different types of objects (paged lists of notices and individual notice details) in the same cache "notice" can lead to key collisions and is generally not a good practice. The keys for paged results are strings like 'page=0,size=10', while keys for details are Long IDs.

It's better to use separate caches for different data structures. For example, you could use a "notices" cache for the lists and a "notice" cache for the details.

To implement this, you would need to:

  1. Define separate CacheGroup enums for notice lists and notice details in CacheGroup.java.
  2. Update the @Cacheable and @CacheEvict annotations in this service to use the appropriate cache names.

For example:

// In getNotices()
@Cacheable(cacheNames = "notices", key = "'page=' + #page + ',size=' + #size")

// In getNoticeDetail()
@Cacheable(cacheNames = "notice", key = "#noticeId")

// In createNotice(), deleteNotice(), updateNotice()
@CacheEvict(cacheNames = {"notices", "notice"}, allEntries = true)

This will make your caching strategy more robust and easier to manage.

Comment on lines +17 to +37
private final List<Cache> caches;
private Map<String, Cache> cacheMap = new ConcurrentHashMap<>();
private volatile Set<String> cacheNames = Collections.emptySet();

public LocalCacheManager(List<Cache> caches) {
this.caches = (caches != null) ? caches : Collections.emptyList();
}

@PostConstruct
public void init() {
Set<String> cacheNamesSet = new LinkedHashSet<>(caches.size());
Map<String, Cache> cacheMapTemp = new ConcurrentHashMap<>(16);

for (Cache cache : caches) {
String name = cache.getName();
cacheNamesSet.add(name);
cacheMapTemp.put(name, cache);
}
this.cacheMap = cacheMapTemp;
this.cacheNames = cacheNamesSet;
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The fields cacheMap and cacheNames are initialized with empty collections and then reassigned in the @PostConstruct init() method. This can be simplified and the class can be made more robust by making these fields final and initializing them in the constructor. This removes the need for @PostConstruct and the volatile keyword on cacheNames, and improves the immutability of the class.

    private final Map<String, Cache> cacheMap;
    private final Collection<String> cacheNames;

    public LocalCacheManager(List<Cache> caches) {
        List<Cache> initialCaches = (caches != null) ? caches : Collections.emptyList();
        this.cacheMap = new ConcurrentHashMap<>(initialCaches.size());
        Set<String> names = new LinkedHashSet<>(initialCaches.size());
        for (Cache cache : initialCaches) {
            String name = cache.getName();
            this.cacheMap.put(name, cache);
            names.add(name);
        }
        this.cacheNames = Collections.unmodifiableSet(names);
    }

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (9)
src/main/java/life/mosu/mosuserver/application/caffeine/UpdatableCacheManager.java (1)

7-7: Document null handling and key typing.

Clarify whether null values are allowed (Spring Cache often disallows nulls unless configured). Consider loosening key type to Object for flexibility, or document the String constraint.

Please confirm whether your LocalCacheManager handles nulls (e.g., via allowNullValues) and whether keys must be String across all caches.

src/main/java/life/mosu/mosuserver/domain/caffeine/CacheType.java (1)

3-13: Expose enum field via Lombok getter or remove if unused.

type is private and currently unused externally. If you plan to surface this description, add @Getter; otherwise consider removing the field.

Minimal change:

 import lombok.RequiredArgsConstructor;
+import lombok.Getter;
 
-@RequiredArgsConstructor
+@Getter
+@RequiredArgsConstructor
 public enum CacheType {
     LOCAL("로컬 타입만 적용"),
     GLOBAL("분산 캐시만 적용"),
     COMPOSITE("로컬 + 분산 캐시 모두 적용");
 
     private final String type;
 }
src/main/java/life/mosu/mosuserver/application/caffeine/LocalCacheConfig.java (2)

21-35: Simplify beans: avoid duplicate bean for the same instance; mark localCacheManager as @primary

Both beans point to the same instance; keeping one (@primary) reduces confusion in autowiring.

-    @Bean
-    public LocalCacheManager localCacheManager() {
+    @Bean
+    @Primary
+    public LocalCacheManager localCacheManager() {
         List<Cache> caches = Arrays.stream(CacheGroup.values())
             ...
         return new LocalCacheManager(caches);
     }
-
-    @Bean
-    @Primary
-    public CacheManager appCacheManager(LocalCacheManager localCacheManager) {
-        return localCacheManager;
-    }

Also applies to: 37-41


29-30: Nit: recordStats has overhead—enable only if you consume metrics

If metrics aren’t scraped/observed, consider removing recordStats to shave overhead. If you do consume them, keep as-is.

src/main/java/life/mosu/mosuserver/application/caffeine/LocalCacheManager.java (1)

50-56: Optional: accept cacheName instead of Cache to reduce coupling

This avoids passing a Cache instance only to re-resolve by name and clarifies intent.

// Interface + impl change suggestion:
void putIfAbsent(String cacheName, Object key, Object value);

// Implementation:
Cache localCache = getCache(cacheName);
if (localCache != null) {
    localCache.putIfAbsent(key, value);
}
src/main/java/life/mosu/mosuserver/application/notice/NoticeService.java (4)

40-41: Prevent cache stampede on hot pages: enable sync on @Cacheable

Let only one thread compute a missing value per key; others wait for the result.

-    @Cacheable(cacheNames = "notice",
-            key = "'page=' + #page + ',size=' + #size")
+    @Cacheable(cacheNames = "notice",
+            key = "'page=' + #page + ',size=' + #size'",
+            sync = true)

52-53: Also enable sync for detail lookups to avoid thundering herd

Hot notice IDs benefit from single-flight computation.

-    @Cacheable(cacheNames = "notice", key = "#noticeId")
+    @Cacheable(cacheNames = "notice", key = "#noticeId", sync = true)

33-34: Consider splitting caches between list and detail for targeted invalidation

Using a single "notice" cache for both lists and details forces broad evictions (allEntries = true). Separate caches (e.g., notice:list and notice:detail) enable evicting only detail keys on update/delete and leave list pages intact when safe.

Example:

  • @Cacheable(cacheNames = "notice:detail", key = "#noticeId")
  • @Cacheable(cacheNames = "notice:list", key = "'page=' + #page + ',size=' + #size'")
  • @CacheEvict(cacheNames = {"notice:detail","notice:list"}, allEntries = true) where needed, or more surgical detail-only evictions on create/update/delete.

Also applies to: 60-61, 67-68


33-34: Optional: be explicit about the cacheManager to avoid future ambiguity

If you later introduce another CacheManager (e.g., Redis), specify cacheManager = "localCacheManager" (or your chosen bean name) on the annotations.

Example:
@CacheEvict(cacheManager = "localCacheManager", cacheNames = "notice", allEntries = true)
@Cacheable(cacheManager = "localCacheManager", cacheNames = "notice", ...)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a0a4473 and a0fd7db.

📒 Files selected for processing (9)
  • build.gradle (1 hunks)
  • src/main/java/life/mosu/mosuserver/application/caffeine/LocalCacheConfig.java (1 hunks)
  • src/main/java/life/mosu/mosuserver/application/caffeine/LocalCacheManager.java (1 hunks)
  • src/main/java/life/mosu/mosuserver/application/caffeine/UpdatableCacheManager.java (1 hunks)
  • src/main/java/life/mosu/mosuserver/application/inquiry/InquiryService.java (0 hunks)
  • src/main/java/life/mosu/mosuserver/application/notice/NoticeService.java (3 hunks)
  • src/main/java/life/mosu/mosuserver/domain/caffeine/CacheGroup.java (1 hunks)
  • src/main/java/life/mosu/mosuserver/domain/caffeine/CacheType.java (1 hunks)
  • src/main/java/life/mosu/mosuserver/global/config/CaffeineFilterCacheConfig.java (1 hunks)
💤 Files with no reviewable changes (1)
  • src/main/java/life/mosu/mosuserver/application/inquiry/InquiryService.java
🔇 Additional comments (4)
src/main/java/life/mosu/mosuserver/domain/caffeine/CacheGroup.java (1)

11-33: LocalCacheConfig correctly filters LOCAL and COMPOSITE caches

The LocalCacheConfig bean is already limiting registrations to CacheType.LOCAL and CacheType.COMPOSITE, so no changes are needed there.

• For operational flexibility, you may still want to externalize each cache’s TTL into application properties (e.g.
­­– cache.ttl.notice=10m
­­– cache.ttl.inquiry=...
­­– cache.ttl.composite=...
) so you can tweak durations without a code deploy.

src/main/java/life/mosu/mosuserver/global/config/CaffeineFilterCacheConfig.java (1)

15-15: ✅ No stale references to CaffeineCacheConfig remain
Ran rg -n "CaffeineCacheConfig" across the repo and found no occurrences. The rename to CaffeineFilterCacheConfig is safe.

src/main/java/life/mosu/mosuserver/application/caffeine/LocalCacheConfig.java (1)

24-26: Review LocalCacheConfig: Composite cache types included in Caffeine-only setup

I see that you’re filtering for both LOCAL and COMPOSITE cache types here:

.filter(g -> g.getCacheType() == CacheType.LOCAL
        || g.getCacheType() == CacheType.COMPOSITE)
.map(g -> new CaffeineCache(…))

– In CacheGroup.java, COMPOSITE_ALL is the only CacheType.COMPOSITE entry.
– There is no RedisCacheConfig in the codebase yet.

As a result, all composite groups will be served solely by the local Caffeine cache until an L2 (Redis) layer is introduced. Please confirm that treating COMPOSITE caches as L1-only is the intended short-term behavior.

src/main/java/life/mosu/mosuserver/application/notice/NoticeService.java (1)

40-41: CacheGroup ‘NOTICE’ exists; ensure CaffeineCacheManager is wired

  • In src/main/java/life/mosu/mosuserver/domain/caffeine/CacheGroup.java, the NOTICE entry is defined as
NOTICE("notice", Duration.ofMinutes(10), CacheType.LOCAL)
  • No custom CacheManager @bean or spring.cache.* properties found in the codebase.

Please verify that your application auto-configures a CaffeineCacheManager (e.g. via spring.cache.type=caffeine or a manual @bean) so that the “notice” cache has a backing Caffeine implementation.

Comment on lines +28 to +31
Caffeine.newBuilder()
.recordStats()
.expireAfterWrite(g.getExpiredAfterWrite())
.build()
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Bound the caches: add maximumSize to prevent unbounded memory growth

Caffeine caches are currently unbounded. Add maximumSize (ideally per CacheGroup) to avoid potential OOMs under load.

 Caffeine.newBuilder()
         .recordStats()
         .expireAfterWrite(g.getExpiredAfterWrite())
+        .maximumSize(g.getMaximumSize()) // suggest: define per CacheGroup
         .build()

If CacheGroup doesn’t expose a size, either:

  • add g.getMaximumSize() to the enum, or
  • use a sane default in this config (e.g., 100_000) and revisit per-group tuning.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/main/java/life/mosu/mosuserver/application/caffeine/LocalCacheConfig.java
around lines 28 to 31, the Caffeine cache builder lacks a maximumSize setting,
which can lead to unbounded memory growth. Fix this by adding a maximumSize
parameter to the builder; either extend the CacheGroup enum to include a
getMaximumSize() method and use that value, or apply a default maximumSize
(e.g., 100_000) directly in the config to limit cache size and prevent potential
OOM errors.

Comment on lines +35 to +37
this.cacheMap = cacheMapTemp;
this.cacheNames = cacheNamesSet;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Expose unmodifiable cache names to prevent external mutation

getCacheNames currently returns a modifiable Set reference. Wrap it to avoid accidental external modifications.

-        this.cacheNames = cacheNamesSet;
+        this.cacheNames = Collections.unmodifiableSet(cacheNamesSet);

Also applies to: 46-48

🤖 Prompt for AI Agents
In
src/main/java/life/mosu/mosuserver/application/caffeine/LocalCacheManager.java
around lines 35-37 and 46-48, the getCacheNames method returns a modifiable Set
which can be changed externally. To fix this, wrap the cacheNames Set with
Collections.unmodifiableSet before returning it to prevent external mutation and
ensure immutability of the cache names.

Comment on lines +51 to +56
public void putIfAbsent(Cache cache, String key, Object value) {
Cache localCache = getCache(cache.getName());
if (localCache != null) {
localCache.putIfAbsent(key, value);
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Align key type with Spring Cache API: use Object for keys

Spring’s Cache API accepts Object keys. Restricting to String will block non-string keys (e.g., Long IDs used by @Cacheable).

-    public void putIfAbsent(Cache cache, String key, Object value) {
+    public void putIfAbsent(Cache cache, Object key, Object value) {
         Cache localCache = getCache(cache.getName());
         if (localCache != null) {
             localCache.putIfAbsent(key, value);
         }
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public void putIfAbsent(Cache cache, String key, Object value) {
Cache localCache = getCache(cache.getName());
if (localCache != null) {
localCache.putIfAbsent(key, value);
}
}
public void putIfAbsent(Cache cache, Object key, Object value) {
Cache localCache = getCache(cache.getName());
if (localCache != null) {
localCache.putIfAbsent(key, value);
}
}
🤖 Prompt for AI Agents
In
src/main/java/life/mosu/mosuserver/application/caffeine/LocalCacheManager.java
around lines 51 to 56, the putIfAbsent method currently uses String as the key
type, which conflicts with Spring Cache API that accepts Object keys. Change the
key parameter type from String to Object to support all key types compatible
with Spring's caching mechanism.

Comment on lines +5 to +8
public interface UpdatableCacheManager {

void putIfAbsent(Cache cache, String key, Object value);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Align with atomic compute semantics; use Supplier-based computeIfAbsent.

Returning void and accepting a prebuilt value can cause double-compute and non-atomic inserts. Prefer a Supplier-based API that atomically computes if absent and returns the resulting value.

Apply this diff:

+import java.util.function.Supplier;
 import org.springframework.cache.Cache;
 
 public interface UpdatableCacheManager {
 
-    void putIfAbsent(Cache cache, String key, Object value);
+    <V> V computeIfAbsent(Cache cache, String key, Supplier<V> valueLoader);
 }

Notes:

  • Implement using cache.get(key, k -> valueLoader.get()) to leverage Spring’s atomic caching where supported (CaffeineCache supports this).
  • This avoids eager value construction and clarifies return semantics (cached or computed value).
🤖 Prompt for AI Agents
In
src/main/java/life/mosu/mosuserver/application/caffeine/UpdatableCacheManager.java
around lines 5 to 8, the current putIfAbsent method accepts a prebuilt value and
returns void, which can cause double computation and is not atomic. Change the
method signature to accept a Supplier for the value and return the cached or
computed value. Implement this using cache.get(key, k -> valueLoader.get()) to
ensure atomic compute-if-absent semantics and avoid eager value construction.

@jbh010204 jbh010204 merged commit 50915bc into develop Aug 10, 2025
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant