Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: hessian deserialize support sofa.serialize.dynamic.load.enable #1463

Open
wants to merge 4 commits into
base: master
Choose a base branch
from

Conversation

Just-CJ
Copy link

@Just-CJ Just-CJ commented Dec 25, 2024

Motivation:

In sofa-hessian, there is a switch called sofa.serialize.dynamic.load.enable. When set to true, if a class is not found during deserialization, the result will be recorded in the internal cache to avoid repeated class loading operations in the future. In sofa-rpc, due to the rewriting of related classes, this feature is missing.

Modification:

Referring to the implementation of sofa-hessian, support for the sofa.serialize.dynamic.load.enable switch has been added.

Summary by CodeRabbit

  • New Features
    • Introduced a mechanism to cache and manage types that cannot be found during deserialization.
    • Added a flag to control dynamic loading of classes.
  • Bug Fixes
    • Enhanced error handling related to class loading and deserialization processes.
  • Tests
    • Added tests to verify behavior when dynamic loading is enabled and disabled, improving test coverage.

@sofastack-cla sofastack-cla bot added bug Something isn't working cla:yes CLA is ok First-time contributor First-time contributor size/M labels Dec 25, 2024
Copy link
Contributor

coderabbitai bot commented Dec 25, 2024

Walkthrough

The changes modify the SingleClassLoaderSofaSerializerFactory class in the SOFA RPC codec module to enhance class loading and deserialization error handling. A new mechanism is introduced to track and cache types that cannot be found during deserialization. This involves adding a concurrent map to store unloadable types, a marker object for not found types, and a system property-controlled flag to enable or disable dynamic class loading. Additionally, new test methods are added to verify the behavior of the serializer factory under different dynamic loading conditions.

Changes

File Change Summary
codec/codec-sofa-hessian/src/main/java/com/alipay/sofa/rpc/codec/sofahessian/SingleClassLoaderSofaSerializerFactory.java - Added _typeNotFoundMap to track unloadable types
- Introduced NOT_FOUND static marker object
- Added dynamicLoadEnable flag controlled by system property
- Modified getDeserializer method to handle class loading more robustly
codec/codec-sofa-hessian/src/test/java/com/alipay/sofa/rpc/codec/sofahessian/SingleClassLoaderSofaSerializerFactoryTest.java - Added testDynamicLoadDisabled to check behavior with dynamic loading disabled
- Added testDynamicLoadEnabled to check behavior with dynamic loading enabled
- Included necessary imports for new test methods

Poem

🐰 Hop, hop, through the codec's maze,
Where classes hide in misty haze,
A rabbit's map of types unknown,
Tracks secrets that were never shown,
Dynamic loading, now with care! 🔍

Tip

CodeRabbit's docstrings feature is now available as part of our Early Access Program! Simply use the command @coderabbitai generate docstrings to have CodeRabbit automatically generate docstrings for your pull request. We would love to hear your feedback on Discord.


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ 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.
    • Generate unit testing code for this file.
    • 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 generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • 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 generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

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. (Beta)
  • @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
Contributor

@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: 0

🧹 Nitpick comments (2)
codec/codec-sofa-hessian/src/main/java/com/alipay/sofa/rpc/codec/sofahessian/SingleClassLoaderSofaSerializerFactory.java (2)

52-55: Consider improving configurability & debugging.
Reading dynamicLoadEnable from a system property is straightforward. For easier maintenance, you might log whether dynamic loading is enabled at startup, or allow overrides via environment variables or a config file.


100-112: Refactor synchronization to use computeIfAbsent.
In Java 8 and above, you can simplify the synchronization block by using ConcurrentHashMap.computeIfAbsent. It avoids explicit locking and ensures thread-safe creation of new sub-maps:

- Map<String, Object> typeMap = _typeNotFoundMap.get(appClassLoader);
- if (typeMap == null) {
-     synchronized (this) {
-         typeMap = _typeNotFoundMap.get(appClassLoader);
-         if (typeMap == null) {
-             _typeNotFoundMap.put(appClassLoader, new ConcurrentHashMap<String, Object>(8));
-             typeMap = _typeNotFoundMap.get(appClassLoader);
-         }
-     }
- }
- typeMap.put(type, NOT_FOUND);
+ Map<String, Object> typeMap = _typeNotFoundMap.computeIfAbsent(
+     appClassLoader, cl -> new ConcurrentHashMap<>(8));
+ typeMap.put(type, NOT_FOUND);
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between be93251 and 2b0af38.

📒 Files selected for processing (1)
  • codec/codec-sofa-hessian/src/main/java/com/alipay/sofa/rpc/codec/sofahessian/SingleClassLoaderSofaSerializerFactory.java (3 hunks)
🔇 Additional comments (4)
codec/codec-sofa-hessian/src/main/java/com/alipay/sofa/rpc/codec/sofahessian/SingleClassLoaderSofaSerializerFactory.java (4)

30-31: Imports are well-chosen.
Bringing in Map and ConcurrentHashMap is correct and lays the foundation for a thread-safe caching mechanism.


49-50: Potential unbounded growth.
Be mindful that _typeNotFoundMap may grow unbounded if many non-existent types are attempted. Consider an explicit strategy for cache invalidation or size-limiting if necessary.


51-51: Appropriate use of a sentinel object.
Using NOT_FOUND as a marker is a clean approach, but ensure future expansions don't require additional metadata for missing types.


88-95: Short-circuit usage when dynamic loading is disabled.
Your logic correctly avoids repeated failed lookups by consulting _typeNotFoundMap. This is an efficient approach for reducing overhead.

@sofastack-cla sofastack-cla bot added size/S and removed size/M labels Dec 26, 2024
Copy link
Contributor

@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: 0

🧹 Nitpick comments (1)
codec/codec-sofa-hessian/src/main/java/com/alipay/sofa/rpc/codec/sofahessian/SingleClassLoaderSofaSerializerFactory.java (1)

49-54: LGTM! Thread-safe implementation of type caching.

The implementation uses appropriate concurrent collections and follows the existing naming conventions. The system property control provides the necessary configuration flexibility.

Note: Consider documenting the system property key DYNAMIC_LOAD_ENABLE_KEY in the class-level JavaDoc to make it more discoverable for users.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 2b0af38 and f8015f6.

📒 Files selected for processing (1)
  • codec/codec-sofa-hessian/src/main/java/com/alipay/sofa/rpc/codec/sofahessian/SingleClassLoaderSofaSerializerFactory.java (3 hunks)
🔇 Additional comments (3)
codec/codec-sofa-hessian/src/main/java/com/alipay/sofa/rpc/codec/sofahessian/SingleClassLoaderSofaSerializerFactory.java (3)

88-95: LGTM! Efficient early-return optimization for known missing types.

The implementation correctly checks the cache before attempting class loading, providing an optimization for scenarios where types are known to be unavailable.


100-102: LGTM! Thread-safe caching of not found types.

The implementation correctly uses computeIfAbsent for thread-safe updates to the cache when a ClassNotFoundException occurs. This aligns with the PR objective of caching class loading results.

Note: This addresses the previous review comment by @OrezzerO about using computeIfAbsent.


52-54: Verify consistent system property usage across the codebase.

Let's ensure the system property DYNAMIC_LOAD_ENABLE_KEY is consistently used and documented across the codebase.

✅ Verification successful

System property usage is properly scoped and implemented

The verification shows that:

  • The DYNAMIC_LOAD_ENABLE_KEY is only used within SingleClassLoaderSofaSerializerFactory class
  • The property controls class loading optimization by caching "not found" classes when dynamic loading is disabled
  • The implementation is thread-safe using ConcurrentHashMap
  • The default value is properly set to Boolean.FALSE.toString()

The system property usage is consistent and well-contained within its specific use case of controlling dynamic class loading behavior in the Hessian serializer factory.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for other usages of the DYNAMIC_LOAD_ENABLE_KEY constant
rg -l "DYNAMIC_LOAD_ENABLE_KEY"

# Search for direct string references to the property
rg -l "sofa\.serialize\.dynamic\.load\.enable"

# Look for documentation of the property
fd README.md | xargs rg "sofa\.serialize\.dynamic\.load\.enable"

Length of output: 268


Script:

#!/bin/bash
# Let's examine the actual implementation and usage of DYNAMIC_LOAD_ENABLE_KEY
ast-grep --pattern 'class $_ {
  $$$
  DYNAMIC_LOAD_ENABLE_KEY
  $$$
}'

# Let's also check the content of the file to understand the constant definition
cat codec/codec-sofa-hessian/src/main/java/com/alipay/sofa/rpc/codec/sofahessian/SingleClassLoaderSofaSerializerFactory.java

# Check for any configuration or property-related files
fd -e properties -e yaml -e yml | xargs rg "sofa\.serialize\.dynamic\.load\.enable"

Length of output: 6285

@sofastack-cla sofastack-cla bot added size/M and removed size/S labels Dec 26, 2024
Copy link
Contributor

@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: 0

🧹 Nitpick comments (1)
codec/codec-sofa-hessian/src/test/java/com/alipay/sofa/rpc/codec/sofahessian/SingleClassLoaderSofaSerializerFactoryTest.java (1)

70-80: Reflection usage for internal state testing.

Accessing the private _typeNotFoundMap field validates genuine behavior but introduces potential brittleness if the field’s name or access level changes in future refactors. Consider providing a public getter for test-only or a dedicated test utility to reduce reflection reliance. Otherwise, the logic verifying _typeNotFoundMap size changes is correct.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between f8015f6 and 9785dc8.

📒 Files selected for processing (1)
  • codec/codec-sofa-hessian/src/test/java/com/alipay/sofa/rpc/codec/sofahessian/SingleClassLoaderSofaSerializerFactoryTest.java (2 hunks)
🔇 Additional comments (2)
codec/codec-sofa-hessian/src/test/java/com/alipay/sofa/rpc/codec/sofahessian/SingleClassLoaderSofaSerializerFactoryTest.java (2)

19-19: Imports are consistent and necessary.

The newly added imports for Deserializer, SerializerFactory, Assert, Field, and various utilities are correct and do not introduce conflicts. Good to see that each import is strictly required for the added tests.

Also applies to: 22-22, 29-29


82-98: System property manipulation is well-handled.

Toggling the dynamic loading behavior via System.setProperty and reverting it in a finally block is a good practice. This ensures the test environment remains clean. The checks on _typeNotFoundMap again validate the internal caching mechanism accurately.

Copy link
Contributor

@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: 0

🧹 Nitpick comments (4)
codec/codec-sofa-hessian/src/main/java/com/alipay/sofa/rpc/codec/sofahessian/SingleClassLoaderSofaSerializerFactory.java (2)

49-54: LGTM! Consider renaming constant to follow Java naming convention.

The field declarations look good. The use of ConcurrentHashMap is appropriate for thread-safe caching of not found types.

Consider renaming NOT_FOUND to TYPE_NOT_FOUND to better reflect its purpose and follow Java constant naming conventions.


88-95: Simplify nested conditions for better readability.

The logic is correct but can be simplified to reduce nesting.

Consider this more concise version:

-                if (!dynamicLoadEnable) {
-                    Map<String, Object> typeMap = _typeNotFoundMap.get(appClassLoader);
-                    if (typeMap != null) {
-                        if (typeMap.containsKey(type)) {
-                            return null;
-                        }
-                    }
-                }
+                if (!dynamicLoadEnable && 
+                    _typeNotFoundMap.containsKey(appClassLoader) && 
+                    _typeNotFoundMap.get(appClassLoader).containsKey(type)) {
+                    return null;
+                }
codec/codec-sofa-hessian/src/test/java/com/alipay/sofa/rpc/codec/sofahessian/SingleClassLoaderSofaSerializerFactoryTest.java (2)

70-81: LGTM! Consider using BDD-style test naming.

The test effectively verifies the caching behavior when dynamic loading is disabled.

Consider renaming the test method to better describe the behavior being tested:

-    public void testDynamicLoadDisabled() throws Exception {
+    public void shouldCacheNotFoundTypesWhenDynamicLoadingIsDisabled() throws Exception {

83-99: LGTM! Consider improving system property handling.

The test effectively verifies that type not found caching is disabled when dynamic loading is enabled.

Consider these improvements:

  1. Rename the test method for better clarity:
-    public void testDynamicLoadEnabled() throws Exception {
+    public void shouldNotCacheNotFoundTypesWhenDynamicLoadingIsEnabled() throws Exception {
  1. Use a helper method or JUnit rule to manage system properties:
private void withSystemProperty(String key, String value, Runnable test) {
    String original = System.getProperty(key);
    try {
        System.setProperty(key, value);
        test.run();
    } finally {
        if (original == null) {
            System.clearProperty(key);
        } else {
            System.setProperty(key, original);
        }
    }
}
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 9785dc8 and d9745e6.

📒 Files selected for processing (2)
  • codec/codec-sofa-hessian/src/main/java/com/alipay/sofa/rpc/codec/sofahessian/SingleClassLoaderSofaSerializerFactory.java (3 hunks)
  • codec/codec-sofa-hessian/src/test/java/com/alipay/sofa/rpc/codec/sofahessian/SingleClassLoaderSofaSerializerFactoryTest.java (2 hunks)
🔇 Additional comments (1)
codec/codec-sofa-hessian/src/main/java/com/alipay/sofa/rpc/codec/sofahessian/SingleClassLoaderSofaSerializerFactory.java (1)

100-102: LGTM! Efficient implementation using computeIfAbsent.

The error handling and caching implementation looks good. The use of computeIfAbsent provides thread-safe initialization of the inner map.

Copy link
Contributor

@OrezzerO OrezzerO left a comment

Choose a reason for hiding this comment

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

LGTM

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working cla:yes CLA is ok First-time contributor First-time contributor size/M
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants