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

[WIP]feat: reduce conflicts when update configmap in k8s #89 #93

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

24kpure
Copy link

@24kpure 24kpure commented Dec 9, 2024

What's the purpose of this PR

fix #89

Which issue(s) this PR fixes:

fix #89

Brief changelog

reduce conflicts when update configmap in k8s

Follow this checklist to help us incorporate your contribution quickly and easily:

  • Read the Contributing Guide before making this pull request.
  • Write a pull request description that is detailed enough to understand what the pull request does, how, and why.
  • Write necessary unit tests to verify the code.
  • Run mvn clean test to make sure this pull request doesn't break anything.
  • Update the CHANGES log.

Summary by CodeRabbit

  • New Features

    • Enhanced functionality for Kubernetes ConfigMaps, including improved logic for determining writable pods.
    • New configuration options for managing Kubernetes cache behavior.
  • Bug Fixes

    • Improved error handling and logging for configuration-related operations.
  • Tests

    • Expanded test coverage for Kubernetes-related functionalities, including new test cases and refined assertions.
  • Documentation

    • Updated constants for better configurability in Kubernetes settings.

Copy link
Contributor

coderabbitai bot commented Dec 9, 2024

Walkthrough

The changes in this pull request enhance the KubernetesManager and ConfigUtil classes to improve the handling of Kubernetes ConfigMaps. A new field for the maximum number of writable pods is introduced, along with methods to check pod permissions and retrieve configuration properties. The updateConfigMap method is modified to prevent unnecessary updates by checking if the current pod is writable. Additionally, the test suite for KubernetesManager is expanded to ensure comprehensive coverage of these new functionalities.

Changes

File Change Summary
apollo-client/src/main/java/com/ctrip/framework/apollo/kubernetes/KubernetesManager.java - Added field propertyKubernetesMaxWritePods.
- Added field localPodName.
- Added method isWritePod(String k8sNamespace).
- Modified constructor to accept new parameters.
- Modified updateConfigMap to check writable pods.
apollo-client/src/main/java/com/ctrip/framework/apollo/util/ConfigUtil.java - Added field propertyKubernetesMaxWritePods.
- Added method getPropertyKubernetesMaxWritePods().
- Refactored getK8sNamespace() and added getK8sConfigProperties().
apollo-client/src/test/java/com/ctrip/framework/apollo/kubernetes/KubernetesManagerTest.java - Enhanced test coverage for KubernetesManager.
- Updated constructor signature of KubernetesManager.
- Improved assertions and added new test scenarios.
apollo-core/src/main/java/com/ctrip/framework/apollo/core/ApolloClientSystemConsts.java - Added constants APOLLO_CACHE_KUBERNETES_MAX_WRITE_PODS and APOLLO_CACHE_KUBERNETES_MAX_WRITE_PODS_ENVIRONMENT_VARIABLES.

Assessment against linked issues

Objective Addressed Explanation
Ensure only a limited number of nodes handle configuration persistence (Issue #89)

Possibly related PRs

Suggested reviewers

  • shoothzj

🐰 In the Kubernetes land, we hop and play,
With pods and configs, we find our way.
A limit on writes, oh what a delight,
Keeping our cluster both healthy and bright!
So let’s cheer for the code, and all that it brings,
For in the world of tech, we’re all clever bunnies with wings! 🐇✨


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

github-actions bot commented Dec 9, 2024

CLA Assistant Lite bot All contributors have signed the CLA ✍️ ✅

@24kpure
Copy link
Author

24kpure commented Dec 9, 2024

@nobodyiam @shoothzj Clould you review the idea of code

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: 6

🧹 Outside diff range and nitpick comments (2)
apollo-client/src/main/java/com/ctrip/framework/apollo/kubernetes/KubernetesManager.java (1)

48-48: Consider making propertyKubernetesMaxWritePods final

To ensure immutability after initialization, consider declaring propertyKubernetesMaxWritePods as final.

Apply this diff:

-private int propertyKubernetesMaxWritePods;
+private final int propertyKubernetesMaxWritePods;
apollo-client/src/main/java/com/ctrip/framework/apollo/util/ConfigUtil.java (1)

417-433: Variable naming in getK8sConfigProperties method

The variable k8sNamespace is used to store generic property values, which may not always represent a namespace. To improve code readability and maintainability, consider renaming the variable to a more generic name like propertyValue.

Apply this diff:

-    String k8sNamespace = System.getProperty(key);
-    if (Strings.isNullOrEmpty(k8sNamespace)) {
+    String propertyValue = System.getProperty(key);
+    if (Strings.isNullOrEmpty(propertyValue)) {
       // ...
-      k8sNamespace = System.getenv(environmentKey);
+      propertyValue = System.getenv(environmentKey);
       // ...
-    return k8sNamespace;
+    return propertyValue;
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between e64c35b and 45d2cce.

📒 Files selected for processing (4)
  • apollo-client/src/main/java/com/ctrip/framework/apollo/kubernetes/KubernetesManager.java (4 hunks)
  • apollo-client/src/main/java/com/ctrip/framework/apollo/util/ConfigUtil.java (4 hunks)
  • apollo-client/src/test/java/com/ctrip/framework/apollo/kubernetes/KubernetesManagerTest.java (2 hunks)
  • apollo-core/src/main/java/com/ctrip/framework/apollo/core/ApolloClientSystemConsts.java (1 hunks)
🔇 Additional comments (1)
apollo-client/src/main/java/com/ctrip/framework/apollo/kubernetes/KubernetesManager.java (1)

144-146: ⚠️ Potential issue

Verify the return value when skipping ConfigMap update

In the updateConfigMap method, if the current pod is not a write pod, the method returns true. Please verify that this behavior correctly indicates that the update is not necessary. Returning true might imply that the ConfigMap was successfully updated, which could be misleading.

Run the following script to check how updateConfigMap is used and how its return value is handled:

✅ Verification successful

Let me gather more context about the isWritePod method and its usage to better understand the logic.


Let me check the complete implementation of the updateConfigMap method to understand the full context.


Let me try a different pattern to get the method implementation.


Based on the implementation I can now generate the final response.

Return value in updateConfigMap is correct for non-write pods

The return value of true when the pod is not a write pod is correct and not misleading. Here's why:

  • The method returns true to indicate a successful outcome, not specifically a successful update
  • For non-write pods, skipping the update is the expected behavior and represents a successful execution of the method
  • This aligns with the method's contract where true indicates the operation completed as intended, whether that means updating (for write pods) or intentionally skipping (for non-write pods)
  • The actual update logic is only attempted for write pods, maintaining the proper separation of responsibilities in a multi-pod environment
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Find usages of `updateConfigMap` and inspect return value handling.

rg -A 3 'updateConfigMap\('

Length of output: 2898


Script:

#!/bin/bash
# Find the implementation and usage of isWritePod
ast-grep --pattern 'isWritePod($_) {
  $$$
}'

# Also search for any documentation or comments about write pod behavior
rg -B 2 -A 2 'isWritePod'

Length of output: 1259


Script:

#!/bin/bash
# Get the complete implementation of updateConfigMap method
ast-grep --pattern 'updateConfigMap($_, $_, $_) {
  $$$
}'

# Also get any related tests that verify this behavior
rg -B 2 -A 10 'void test.*updateConfigMap'

Length of output: 105


Script:

#!/bin/bash
# Get the complete implementation using a more specific context
rg -B 5 -A 20 'public boolean updateConfigMap\(' apollo-client/src/main/java/com/ctrip/framework/apollo/kubernetes/KubernetesManager.java

# Get test cases with different pattern
rg -B 5 -A 15 'updateConfigMap.*assert' apollo-client/src/test/java/com/ctrip/framework/apollo/kubernetes/KubernetesManagerTest.java

Length of output: 1323

@24kpure 24kpure force-pushed the feature/reduce_conflicts_when_update_configmap_in_k8s branch from 45d2cce to b56e717 Compare December 10, 2024 05:51
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: 1

🧹 Outside diff range and nitpick comments (7)
apollo-client/src/main/java/com/ctrip/framework/apollo/util/ConfigUtil.java (2)

415-433: Consider adding input validation for the default value.

The getK8sConfigProperties method accepts a default value parameter but doesn't validate it. For properties that expect specific formats or ranges, consider adding validation to ensure the default value meets the requirements.

  private String getK8sConfigProperties(String key, String environmentKey, String defaultValue) {
+   if (defaultValue == null) {
+     throw new IllegalArgumentException("Default value cannot be null");
+   }
    // 1. Get from System Property
    String k8sNamespace = System.getProperty(key);
    // ... rest of the method

401-433: Consider adding documentation for the new configuration property.

The new Kubernetes max write pods configuration would benefit from documentation explaining its purpose, acceptable values, and impact on the system. This helps users understand how to properly configure this value.

Add JavaDoc comments:

+ /**
+  * Initializes the maximum number of pods that can write to the Kubernetes ConfigMap.
+  * This helps reduce conflicts and pressure on the Kubernetes service when multiple
+  * pods attempt to update configurations simultaneously.
+  */
  private void initPropertyKubernetesMaxWritePods() {
    // ... implementation
  }

+ /**
+  * Retrieves configuration properties from various sources in the following order:
+  * 1. System Property
+  * 2. Environment Variable
+  * 3. Server Properties
+  * 4. Application Properties
+  * 5. Default Value
+  *
+  * @param key The system property key
+  * @param environmentKey The environment variable key
+  * @param defaultValue The default value if not found in any source
+  * @return The configuration value
+  */
  private String getK8sConfigProperties(String key, String environmentKey, String defaultValue) {
    // ... implementation
  }
apollo-client/src/main/java/com/ctrip/framework/apollo/kubernetes/KubernetesManager.java (3)

49-57: Add validation for propertyKubernetesMaxWritePods

The max write pods value should be validated to ensure it's positive.

 public KubernetesManager() {
     try {
         client = Config.defaultClient();
         coreV1Api = new CoreV1Api(client);
         ConfigUtil configUtil = ApolloInjector.getInstance(ConfigUtil.class);
         propertyKubernetesMaxWritePods = configUtil.getPropertyKubernetesMaxWritePods();
+        if (propertyKubernetesMaxWritePods <= 0) {
+            throw new IllegalArgumentException("Max write pods must be positive");
+        }
     } catch (Exception e) {

149-151: Add debug logging for write pod eligibility check

Consider adding debug logging to help troubleshoot pod selection behavior.

     if (!isWritePod(k8sNamespace)) {
+        logger.debug("Pod {} is not eligible for writing ConfigMap", localPodName);
         return true;
     }

243-244: Make app label key configurable

The "app" label key is hardcoded, which reduces flexibility. Consider making it configurable.

-    String appName = localMetadata.getLabels().get("app");
-    String labelSelector = "app=" + appName;
+    String appLabelKey = configUtil.getPropertyKubernetesAppLabelKey("app");
+    String appName = localMetadata.getLabels().get(appLabelKey);
+    String labelSelector = appLabelKey + "=" + appName;
apollo-client/src/test/java/com/ctrip/framework/apollo/kubernetes/KubernetesManagerTest.java (2)

155-161: Add test cases for edge cases in pod selection

The current test only covers the happy path. Consider adding test cases for:

  • Multiple pods with different creation timestamps
  • Pods without required labels
  • Error scenarios in pod listing
+    @Test
+    public void testUpdateConfigMapWithMultiplePods() throws Exception {
+        // arrange
+        String namespace = "default";
+        String name = "testConfigMap";
+        
+        // Create multiple pods with different timestamps
+        V1Pod pod1 = createPodWithTimestamp("pod1", OffsetDateTime.now().minusHours(1));
+        V1Pod pod2 = createPodWithTimestamp("pod2", OffsetDateTime.now());
+        V1PodList v1PodList = new V1PodList().addItemsItem(pod1).addItemsItem(pod2);
+        
+        // Setup mocks and verify behavior
+        // ... implementation details ...
+    }

178-180: Add assertions for ConfigMap data integrity

The test should verify that the ConfigMap data is updated correctly when the pod is eligible to write.

     HashMap<String, String> updateData = new HashMap<>(existData);
     updateData.put("newKey","newValue");
     boolean success = kubernetesManager.updateConfigMap(namespace, name, updateData);
+    
+    // Verify the ConfigMap was updated with the correct data
+    ArgumentCaptor<V1ConfigMap> configMapCaptor = ArgumentCaptor.forClass(V1ConfigMap.class);
+    verify(coreV1Api).replaceNamespacedConfigMap(eq(name), eq(namespace), configMapCaptor.capture(), 
+        isNull(), isNull(), isNull(), isNull());
+    
+    Map<String, String> updatedData = configMapCaptor.getValue().getData();
+    assertEquals("newValue", updatedData.get("newKey"));
+    assertEquals("value", updatedData.get("key"));
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 45d2cce and b56e717.

📒 Files selected for processing (4)
  • apollo-client/src/main/java/com/ctrip/framework/apollo/kubernetes/KubernetesManager.java (4 hunks)
  • apollo-client/src/main/java/com/ctrip/framework/apollo/util/ConfigUtil.java (4 hunks)
  • apollo-client/src/test/java/com/ctrip/framework/apollo/kubernetes/KubernetesManagerTest.java (5 hunks)
  • apollo-core/src/main/java/com/ctrip/framework/apollo/core/ApolloClientSystemConsts.java (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apollo-core/src/main/java/com/ctrip/framework/apollo/core/ApolloClientSystemConsts.java
🔇 Additional comments (6)
apollo-client/src/main/java/com/ctrip/framework/apollo/util/ConfigUtil.java (5)

75-75: LGTM: Field declaration with reasonable default value.

The default value of 3 for propertyKubernetesMaxWritePods seems reasonable as it provides redundancy while limiting the number of pods that can write to the ConfigMap.


Line range hint 82-98: LGTM: Constructor properly initializes the new field.

The constructor has been updated to include initPropertyKubernetesMaxWritePods() in the initialization sequence, maintaining consistency with other property initializations.


396-399: LGTM: Refactored method improves code reusability.

The getK8sNamespace() method has been refactored to use the new getK8sConfigProperties() helper method, which reduces code duplication and improves maintainability.


543-545: LGTM: Getter method follows Java conventions.

The getter method is properly implemented following Java naming conventions and visibility modifiers.


401-411: ⚠️ Potential issue

Fix incorrect constant in error logging.

The error message references APOLLO_CACHE_KUBERNETES_NAMESPACE instead of APOLLO_CACHE_KUBERNETES_MAX_WRITE_PODS. This may lead to confusion when diagnosing configuration issues.

Apply this diff to correct the constant:

    logger.error("Config for {} is invalid: {}",
-           ApolloClientSystemConsts.APOLLO_CACHE_KUBERNETES_NAMESPACE, propertyKubernetesMaxWritePodsStr);
+           ApolloClientSystemConsts.APOLLO_CACHE_KUBERNETES_MAX_WRITE_PODS, propertyKubernetesMaxWritePodsStr);
apollo-client/src/main/java/com/ctrip/framework/apollo/kubernetes/KubernetesManager.java (1)

260-263: ⚠️ Potential issue

Fix error handling in isWritePod method

Currently, the method returns true on errors, which could lead to multiple pods attempting to write simultaneously. This defeats the purpose of write pod selection.

     } catch (Exception e) {
-        logger.info("Error determining write pod eligibility:{}", e.getMessage(), e);
-        return true;
+        logger.error("Error determining write pod eligibility: {}", e.getMessage(), e);
+        return false;
     }

Likely invalid or redundant comment.

@24kpure
Copy link
Author

24kpure commented Dec 10, 2024

I have read the CLA Document and I hereby sign the CLA

Choose a reason for hiding this comment

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

Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.

@nobodyiam
Copy link
Member

@dyx1234 @shoothzj Would you please help to take a look first?

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.

关于使用k8s configmap作为持久化策略时潜在的压力风险与写一致性问题
2 participants