Skip to content

캐시 원자 단위 의존성 주입 Resolver 추가 및 동적 Bean 등록#260

Merged
polyglot-k merged 10 commits intodevelopfrom
refactor/enhanced-redis
Aug 8, 2025
Merged

캐시 원자 단위 의존성 주입 Resolver 추가 및 동적 Bean 등록#260
polyglot-k merged 10 commits intodevelopfrom
refactor/enhanced-redis

Conversation

@polyglot-k
Copy link
Contributor

@polyglot-k polyglot-k commented Aug 8, 2025

✨ 연결 된 이슈

This pull request refactors how Lua scripts and cache atomic operators are registered and injected for exam quota operations. The main improvements are the introduction of dynamic, domain-based registration for Lua scripts and atomic operators, replacing the previous static configuration. This change enhances scalability, maintainability, and reduces boilerplate when adding new scripts or operators.

Key changes:

Dynamic Lua Script Registration

  • Added LuaScriptsFunctionalRegistrar to automatically discover and register all Lua scripts under the scripts/ directory, grouping them by domain and exposing them as beans (e.g., examLuaScripts). This replaces manual bean definitions for each script. [1] [2]
  • Updated AtomicExamQuotaIncrementOperator and AtomicExamQuotaDecrementOperator to receive a map of scripts (examLuaScripts) and select the appropriate script by name, instead of using individual script beans. [1] [2] [3] [4]

Dynamic Atomic Operator Registration

  • Introduced AtomicOperatorAutoRegistrar to automatically group and register all CacheAtomicOperator beans by domain, exposing them as maps (e.g., examQuotaCacheAtomicOperatorMap). This eliminates the need for manual mapping in configuration.

Configuration Cleanup

  • Removed the now-unnecessary ExamQuotaAtomicOperationConfig class, which previously handled static script and operator bean definitions.

Dependency Injection Updates

  • Updated ExamQuotaCacheManager to inject the new dynamically registered operator map bean (@Qualifier("examQuotaCacheAtomicOperatorMap")) and added @Lazy to avoid circular dependencies. [1] [2]

These changes make it easier to add new Lua scripts or atomic operators for other domains and reduce the risk of bean naming conflicts or manual registration errors.

Summary by CodeRabbit

  • Refactor

    • Improved the management and registration of Lua scripts and cache atomic operators for exam quotas, streamlining how these components are loaded and injected.
    • Updated internal configuration to support dynamic grouping and registration of related scripts and operators.
  • Chores

    • Removed obsolete configuration files and introduced automated registration for Lua scripts and cache operators.
    • Enhanced Spring context initialization to automatically detect and register Lua scripts by domain.

@coderabbitai
Copy link

coderabbitai bot commented Aug 8, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Caution

Review failed

The pull request is closed.

Walkthrough

This change refactors the injection and registration of Redis Lua scripts and atomic cache operators in the exam quota management subsystem. It replaces direct bean injection with map-based and domain-based registrations, introduces dynamic bean registration for Lua scripts and atomic operators, and removes the old static configuration class.

Changes

Cohort / File(s) Change Summary
Atomic Exam Quota Operators Refactor
src/main/java/life/mosu/mosuserver/application/exam/cache/AtomicExamQuotaDecrementOperator.java, src/main/java/life/mosu/mosuserver/application/exam/cache/AtomicExamQuotaIncrementOperator.java
Refactored constructors to inject a map of Lua scripts instead of a single script; updated field assignments to retrieve scripts from the map; renamed fields for clarity; added @Slf4j annotation.
ExamQuotaCacheManager Injection Update
src/main/java/life/mosu/mosuserver/application/exam/cache/ExamQuotaCacheManager.java
Updated constructor parameter annotations for the atomic operator map, changing the qualifier and adding @Lazy and suppression annotations.
Remove Static Lua Script Config
src/main/java/life/mosu/mosuserver/global/config/ExamQuotaAtomicOperationConfig.java
Removed the entire configuration class that previously loaded and exposed Lua scripts and atomic cache operator maps as beans.
Dynamic Atomic Operator Registration
src/main/java/life/mosu/mosuserver/infra/config/AtomicOperatorAutoRegistrar.java
Added a new configuration class that dynamically registers maps of atomic operators grouped by domain and action name after all singletons are instantiated.
Dynamic Lua Script Registration
src/main/java/life/mosu/mosuserver/infra/persistence/redis/support/LuaScriptsFunctionalRegistrar.java
Introduced a context initializer to scan, load, and register Lua scripts from the classpath by domain, making them available as beans keyed by domain and script name.
Spring Factories Registration
src/main/resources/META-INF/spring.factories
Registered the new Lua scripts registrar as an ApplicationContextInitializer for automatic context initialization.

Sequence Diagram(s)

sequenceDiagram
    participant SpringContext
    participant LuaScriptsFunctionalRegistrar
    participant AtomicOperatorAutoRegistrar
    participant ApplicationBeans

    SpringContext->>LuaScriptsFunctionalRegistrar: initialize(context)
    LuaScriptsFunctionalRegistrar->>SpringContext: Register <domain>LuaScripts beans

    SpringContext->>ApplicationBeans: Instantiate beans (AtomicExamQuotaIncrementOperator, etc.)
    SpringContext->>AtomicOperatorAutoRegistrar: afterSingletonsInstantiated()
    AtomicOperatorAutoRegistrar->>SpringContext: Register <domain>CacheAtomicOperatorMap beans
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Assessment against linked issues

Objective Addressed Explanation
Refactor to add a resolver for cache atomic dependency injection (#256)
Remove static configuration and migrate to dynamic registration for Lua scripts and operators (#256)

Assessment against linked issues: Out-of-scope changes

No out-of-scope changes found.

Possibly related PRs

Poem

In the warren where Lua scripts dwell,
Beans once static now dynamically swell.
Operators grouped, by action and name,
Spring finds them all, never the same.
With registrars hopping, the context anew—
This rabbit approves, and so should you!
🐇✨

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.


📜 Recent review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between e5076ec and 1daba52.

📒 Files selected for processing (3)
  • src/main/java/life/mosu/mosuserver/application/exam/cache/AtomicExamQuotaDecrementOperator.java (2 hunks)
  • src/main/java/life/mosu/mosuserver/application/exam/cache/AtomicExamQuotaIncrementOperator.java (2 hunks)
  • src/main/java/life/mosu/mosuserver/infra/persistence/redis/support/LuaScriptsFunctionalRegistrar.java (1 hunks)
✨ 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/enhanced-redis

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.

@polyglot-k polyglot-k self-assigned this Aug 8, 2025
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 @polyglot-k, 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 refactored the application's approach to managing Redis Lua scripts and cache atomic operators, specifically for exam quota operations. The core change involves transitioning from static, manual configuration to dynamic, domain-based registration. This significantly enhances the system's scalability and maintainability by automating the discovery and registration of new scripts and operators, thereby reducing boilerplate code and potential configuration errors. This change simplifies the process of adding new functionalities related to exam quotas.

Highlights

  • Dynamic Lua Script Registration: I've introduced LuaScriptsFunctionalRegistrar to automatically discover and register all Lua scripts located under the scripts/ directory. These scripts are now grouped by domain and exposed as beans (e.g., examLuaScripts), replacing the previous manual bean definitions for each script.
  • Refactored Script Usage in Operators: The AtomicExamQuotaIncrementOperator and AtomicExamQuotaDecrementOperator classes have been updated. They now receive a map of scripts (examLuaScripts) and select the appropriate script by name, rather than relying on individual script beans.
  • Automated Atomic Operator Registration: I've added AtomicOperatorAutoRegistrar to automatically group and register all CacheAtomicOperator beans by domain, exposing them as maps (e.g., examQuotaCacheAtomicOperatorMap). This removes the need for manual mapping in configuration.
  • Configuration Cleanup: The ExamQuotaAtomicOperationConfig class, which previously handled static script and operator bean definitions, has been removed as its functionality is now superseded by the dynamic registration mechanisms.
  • Dependency Injection Updates: The ExamQuotaCacheManager has been updated to inject the new dynamically registered operator map bean (@Qualifier("examQuotaCacheAtomicOperatorMap")) and includes @Lazy to prevent potential circular dependencies during bean initialization.
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 a significant improvement by refactoring the registration of Lua scripts and cache atomic operators from a static configuration to a dynamic, domain-based approach. The use of LuaScriptsFunctionalRegistrar and AtomicOperatorAutoRegistrar to automatically discover and register these components is a solid architectural change that enhances maintainability and scalability. The code is generally well-structured. My review includes suggestions to improve the robustness of the script loading mechanism, add fail-fast checks for script resolution to prevent potential runtime NullPointerExceptions, and enhance documentation for better clarity.

@polyglot-k polyglot-k merged commit 4233ed8 into develop Aug 8, 2025
2 checks passed
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: 4

🔭 Outside diff range comments (1)
src/main/java/life/mosu/mosuserver/application/exam/cache/ExamQuotaCacheManager.java (1)

103-108: Unchecked cast to VoidCacheAtomicOperator may blow up

cacheAtomicOperatorMap.get(operationType.key()) returns a CacheAtomicOperator, but the code blindly casts to VoidCacheAtomicOperator.
If a non-void operator is ever registered under the same key, this will fail at runtime.

Guard the cast:

CacheAtomicOperator<String, Long> op = cacheAtomicOperatorMap.get(operationType.key());
if (!(op instanceof VoidCacheAtomicOperator<?> vOp)) {
    log.error("Operator {} is not VoidCacheAtomicOperator", operationType.key());
    throw new CustomRuntimeException(ErrorCode.CACHE_OPERATION_NOT_SUPPORTED);
}
VoidCacheAtomicOperator<String, Long> operator = vOp;
🧹 Nitpick comments (2)
src/main/java/life/mosu/mosuserver/infra/persistence/redis/support/LuaScriptsFunctionalRegistrar.java (1)

62-71: Expose an unmodifiable, parameterised Map bean

Registering with Map.class loses generic info and returns a mutable instance:

- context.registerBean(beanName, Map.class, () -> scripts);
+ context.registerBean(beanName,
+     new ParameterizedTypeReference<Map<String, DefaultRedisScript<Long>>>() {},
+     () -> Collections.unmodifiableMap(scripts));

• Preserves type safety for injection points
• Prevents accidental mutation after context start-up

src/main/java/life/mosu/mosuserver/application/exam/cache/AtomicExamQuotaIncrementOperator.java (1)

15-15: Remove unused @slf4j or add minimal logging

@slf4j is currently unused. Either remove it or log failures for easier diagnostics.

Example usage:

// in constructor (after resolving the script)
log.debug("Loaded Lua script 'incrementQuota' for examQuota");

// in catch block (just before mapping to CustomRuntimeException)
log.warn("Increment quota script failed for key {}: {}", key, msg, e);
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between ef2a45f and e5076ec.

📒 Files selected for processing (7)
  • src/main/java/life/mosu/mosuserver/application/exam/cache/AtomicExamQuotaDecrementOperator.java (2 hunks)
  • src/main/java/life/mosu/mosuserver/application/exam/cache/AtomicExamQuotaIncrementOperator.java (2 hunks)
  • src/main/java/life/mosu/mosuserver/application/exam/cache/ExamQuotaCacheManager.java (2 hunks)
  • src/main/java/life/mosu/mosuserver/global/config/ExamQuotaAtomicOperationConfig.java (0 hunks)
  • src/main/java/life/mosu/mosuserver/infra/config/AtomicOperatorAutoRegistrar.java (1 hunks)
  • src/main/java/life/mosu/mosuserver/infra/persistence/redis/support/LuaScriptsFunctionalRegistrar.java (1 hunks)
  • src/main/resources/META-INF/spring.factories (1 hunks)
💤 Files with no reviewable changes (1)
  • src/main/java/life/mosu/mosuserver/global/config/ExamQuotaAtomicOperationConfig.java
🔇 Additional comments (6)
src/main/java/life/mosu/mosuserver/infra/persistence/redis/support/LuaScriptsFunctionalRegistrar.java (1)

77-88: toCamelCase ignores hyphens and consecutive underscores

If future script files contain - or __, the current algorithm produces unexpected names (e.g., foo-bar.luafoo-bar).
Either document the naming convention strictly (snake_case only) or extend the helper to normalise additional delimiters.

src/main/resources/META-INF/spring.factories (1)

1-2: Confirm no other ApplicationContextInitializer entries are required

spring.factories allows multiple initialisers separated by commas.
If another library also registers one, this single-line property will silently override it.
Double-check merged resources in the final JAR (e.g., via the spring.factories Maven/Gradle merger plugin).

src/main/java/life/mosu/mosuserver/application/exam/cache/AtomicExamQuotaIncrementOperator.java (4)

23-27: Good move to domain-grouped, map-based script injection

Switching to a domain-scoped script map reduces wiring boilerplate and aligns with the new dynamic registrar.


4-4: Imports/field refactor looks correct

Importing Map and keeping DefaultRedisScript for the typed result are appropriate for the new lookup approach.

Also applies to: 19-19


25-27: examLuaScripts bean and “incrementQuota” key are correctly registered

The LuaScriptsFunctionalRegistrar builds a bean named examLuaScripts (domain “exam” + “LuaScripts”) and converts increment_quota.lua to the key incrementQuota. The qualifier and lookup in AtomicExamQuotaIncrementOperator align with what the registrar produces—no changes required.


45-48: ✅ Java toolchain targets Java 21, so List.of is supported
The Gradle build config uses a Java toolchain with languageVersion = JavaLanguageVersion.of(21), which is well beyond Java 9. No change needed for List.of.

Comment on lines +38 to +41
Map<String, CacheAtomicOperator> mapValue = operators.stream()
.collect(Collectors.toMap(CacheAtomicOperator::getActionName,
Function.identity()));

Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Duplicate getActionName() values explode the context

Collectors.toMap with no merge function throws IllegalStateException when two operators in the same domain share an action name.
Either enforce uniqueness with a clear error or supply a merge strategy:

.collect(Collectors.toMap(
        CacheAtomicOperator::getActionName,
        Function.identity(),
        (a, b) -> {
            throw new IllegalStateException("Duplicate actionName: " + a.getActionName());
        }));

Explicit handling yields a descriptive startup failure instead of cryptic bean-creation errors.

🤖 Prompt for AI Agents
In
src/main/java/life/mosu/mosuserver/infra/config/AtomicOperatorAutoRegistrar.java
around lines 38 to 41, the use of Collectors.toMap without a merge function
causes an IllegalStateException if duplicate getActionName() values exist. Fix
this by adding a merge function that throws an IllegalStateException with a
clear message indicating the duplicate actionName, ensuring descriptive failure
on startup instead of cryptic errors.

Comment on lines 27 to 41
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
Resource[] resources = resolver.getResources(SCRIPT_BASE_PATH + "**/*.lua");

Map<String, Map<String, DefaultRedisScript<Long>>> domainScriptsMap = new HashMap<>();

for (Resource resource : resources) {
String path = resource.getURL().getPath();
int idx = path.indexOf(SCRIPT_PATH_PREFIX);
if (idx < 0) {
continue;
}

String relativePath = path.substring(idx + SCRIPT_PATH_PREFIX.length());
String[] parts = relativePath.split("/");
if (parts.length < 2) {
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Path parsing brittle when running from a shaded JAR or on Windows

resource.getURL().getPath() returns strings such as
jar:file:/app.jar!/BOOT-INF/classes!/scripts/exam/increment.lua.
The current indexOf("/scripts/") + split("/") logic still works for the happy path but silently skips any resource whose URL does not contain /scripts/ (e.g., VFS, Windows back-slashes).
Consider using Spring’s ResourceUtils.toURI(resource) plus Paths.get(uri) to obtain platform-independent segments, or rely on resource.getFilename() and FilenameUtils.getPathNoEndSeparator() (Apache Commons) to extract the domain.
This avoids edge-case misses and makes unit-testing simpler.

🤖 Prompt for AI Agents
In
src/main/java/life/mosu/mosuserver/infra/persistence/redis/support/LuaScriptsFunctionalRegistrar.java
between lines 27 and 41, the current method of parsing resource paths using
resource.getURL().getPath() and string operations is brittle and fails on shaded
JARs or Windows due to different URL formats and path separators. Refactor the
code to use Spring's ResourceUtils.toURI(resource) combined with Paths.get(uri)
to get platform-independent path segments, or alternatively use
resource.getFilename() with Apache Commons FilenameUtils.getPathNoEndSeparator()
to reliably extract the domain and script path. This will ensure consistent path
parsing across environments and improve testability.

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.

[🛠 리팩토링] 캐시 원자 단위 의존성 주입 Resolver 추가

1 participant