Skip to content

Conversation

@varonix0
Copy link
Member

@varonix0 varonix0 commented Apr 13, 2025

This PR implements automatic retries of network related errors such as ECONNRESET or host not found. It will retry 4 times, exponentially increasing the retry delay. The highest retry delay will be 8 seconds, which means the total max retry time will be 15 seconds in total.

Summary by CodeRabbit

  • New Features
    • Introduced an automatic retry mechanism with exponential backoff and jitter to enhance network request reliability, providing a smoother experience during temporary connectivity disruptions.

@varonix0 varonix0 self-assigned this Apr 13, 2025
@coderabbitai
Copy link

coderabbitai bot commented Apr 13, 2025

Walkthrough

The changes introduce a new with_retry decorator in the InfisicalRequests module. This decorator adds exponential backoff retry logic to the HTTP request methods (get, post, patch, and delete) when encountering defined network-related exceptions. The decorator accepts configurable parameters, and default options are provided through a new constant listing common network errors. Imports and exception handling have been updated to support the new functionality.

Changes

File Summary
infisical_sdk/infisical_requests.py Added a new with_retry decorator implementing exponential backoff with jitter, defined a NETWORK_ERRORS list, updated imports, and applied the decorator to the get, post, patch, and delete methods in the InfisicalRequests class.

Sequence Diagram(s)

sequenceDiagram
    participant C as Client
    participant IR as InfisicalRequests
    participant DR as with_retry Decorator
    participant HTTP as HTTP Request

    C->>IR: Call get/post/patch/delete
    IR->>DR: Enter retry-decorated method
    DR->>HTTP: Attempt request
    alt Request Fails with network error
        HTTP-->>DR: Exception raised
        DR->>DR: Check retry count & delay (exponential backoff)
        loop If retries remain
            DR->>HTTP: Retry request
        end
    else Request Succeeds
        HTTP-->>DR: Return response
    end
    DR-->>IR: Return final response or raise final exception
    IR-->>C: Return response / error
Loading

Poem

"Hippity hop, a code bunny's cheer,
Retrying with each network fear.
Exponential waits all in a row,
With every fail, our fixes grow.
A code leap forward, oh so bright,
In retrying steps we find delight! 🐇"

✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 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.
  • @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

@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

🧹 Nitpick comments (2)
infisical_sdk/infisical_requests.py (2)

236-236: Fix the documentation comment for the delete method.

The comment incorrectly states "Make a PATCH request with JSON data" instead of "Make a DELETE request with JSON data".

-        """Make a PATCH request with JSON data"""
+        """Make a DELETE request with JSON data"""

89-101: Consider capturing the original exception for better error reporting.

When raising the exception after maximum retries, it would be helpful to capture the original exception details to provide more context to the caller. This can be done using raise-from syntax or by creating a custom exception.

                 except tuple(network_errors) as error:
                     retry_count += 1
                     if retry_count > max_retries:
                         print(f"Max retries ({max_retries}) exceeded. Giving up.")
-                        raise
+                        raise InfisicalError(f"Network error after {max_retries} retries: {error.__class__.__name__}: {str(error)}") from error
                     
                     delay = base_delay * (2 ** (retry_count - 1))
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 807da32 and f77841e.

📒 Files selected for processing (1)
  • infisical_sdk/infisical_requests.py (6 hunks)
🔇 Additional comments (6)
infisical_sdk/infisical_requests.py (6)

1-6: Well-structured imports for the new functionality.

The new imports are appropriately added to support the retry mechanism functionality, including Callable, List, socket, functools, and time.


10-22: Good implementation of network error types.

Comprehensive list of network-related exceptions covering both requests library and socket module errors, which is ideal for identifying retry-worthy conditions.


151-151: Consistent application of the retry decorator.

The decorator is appropriately applied to the get method with 4 retries and a 1-second base delay, aligning with the PR objectives.


178-178: Consistent application of the retry decorator.

The decorator is appropriately applied to the post method with the same parameters as other methods for consistent behavior.


203-203: Consistent application of the retry decorator.

The decorator is appropriately applied to the patch method with the same parameters as other methods for consistent behavior.


228-228: Consistent application of the retry decorator.

The decorator is appropriately applied to the delete method with the same parameters as other methods for consistent behavior.

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

🔭 Outside diff range comments (1)
infisical_sdk/infisical_requests.py (1)

150-250: 🛠️ Refactor suggestion

Update all retry decorators with the max_delay parameter once implemented.

After implementing the max_delay parameter in the with_retry decorator, update all decorator calls to include this parameter.

-    @with_retry(max_retries=4, base_delay=1.0)
+    @with_retry(max_retries=4, base_delay=1.0, max_delay=8.0)

Apply this change to all four HTTP methods (GET, POST, PATCH, DELETE).

♻️ Duplicate comments (1)
infisical_sdk/infisical_requests.py (1)

70-104: 🛠️ Refactor suggestion

Add maximum delay cap and error logging as per the PR objectives.

The PR objectives mention a maximum delay cap of 8 seconds, but this is missing from the implementation. Additionally, the caught error is unused, and there's no logging to help with debugging or traceability.

 def with_retry(
     max_retries: int = 3, 
     base_delay: float = 1.0, 
+    max_delay: float = 8.0,
     network_errors: Optional[List[Type[Exception]]] = None
 ) -> Callable:
     """
     Decorator to add retry logic with exponential backoff to requests methods.
+    
+    Args:
+        max_retries: Maximum number of retry attempts
+        base_delay: Base delay in seconds between retries
+        max_delay: Maximum delay cap in seconds for any single retry
+        network_errors: List of exceptions that should trigger retries
     """
     if network_errors is None:
         network_errors = NETWORK_ERRORS
         
     def decorator(func: Callable) -> Callable:
         @functools.wraps(func)
         def wrapper(*args, **kwargs):
+            import logging
+            logger = logging.getLogger(__name__)
             retry_count = 0
             
             while True:
                 try:
                     return func(*args, **kwargs)
                 except tuple(network_errors) as error:
                     retry_count += 1
                     if retry_count > max_retries:
+                        logger.error(f"Max retries ({max_retries}) exceeded for {func.__name__}. Last error: {error}")
                         raise
                     
                     base_delay_with_backoff = base_delay * (2 ** (retry_count - 1))
+                    base_delay_with_backoff = min(base_delay_with_backoff, max_delay)
                     
                     # +/-20% jitter
                     jitter = random.uniform(-0.2, 0.2) * base_delay_with_backoff
                     delay = base_delay_with_backoff + jitter
                     
+                    logger.warning(
+                        f"Network error {error.__class__.__name__}: {str(error)}. "
+                        f"Retrying in {delay:.2f}s (attempt {retry_count}/{max_retries})"
+                    )
                     time.sleep(delay)
         
         return wrapper
     
     return decorator
🧰 Tools
🪛 Ruff (0.8.2)

89-89: Local variable error is assigned to but never used

Remove assignment to unused variable error

(F841)

🧹 Nitpick comments (1)
infisical_sdk/infisical_requests.py (1)

227-236: Fix incorrect docstring for delete method.

The docstring incorrectly states this is a "PATCH request" when it should be a "DELETE request".

     @with_retry(max_retries=4, base_delay=1.0)
     def delete(
             self,
             path: str,
             model: Type[T],
             json: Optional[Dict[str, Any]] = None
           ) -> APIResponse[T]:
 
-        """Make a PATCH request with JSON data"""
+        """Make a DELETE request with JSON data"""
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between f77841e and b276b41.

📒 Files selected for processing (1)
  • infisical_sdk/infisical_requests.py (6 hunks)
🧰 Additional context used
🪛 Ruff (0.8.2)
infisical_sdk/infisical_requests.py

89-89: Local variable error is assigned to but never used

Remove assignment to unused variable error

(F841)

🔇 Additional comments (4)
infisical_sdk/infisical_requests.py (4)

12-23: LGTM: Comprehensive list of network errors for retry.

The list of network-related exceptions is well-thought-out, covering the common connection, timeout and socket errors that would benefit from retries.


150-150: LGTM: Appropriate retry configuration for GET requests.

The retry configuration with max_retries=4 and base_delay=1.0 aligns with the PR objectives for improving network resilience.


177-177: LGTM: Appropriate retry configuration for POST requests.

The retry configuration with max_retries=4 and base_delay=1.0 aligns with the PR objectives for improving network resilience.


202-202: LGTM: Appropriate retry configuration for PATCH requests.

The retry configuration with max_retries=4 and base_delay=1.0 aligns with the PR objectives for improving network resilience.

@varonix0 varonix0 merged commit eaf1f82 into main Apr 16, 2025
10 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.

3 participants