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

feat(sdk)!: ban addresses failed in sdk #2351

Merged
merged 15 commits into from
Dec 4, 2024
Merged

Conversation

shumkov
Copy link
Member

@shumkov shumkov commented Nov 26, 2024

Issue being fixed or feature implemented

We should ban failed nodes in case if SDK verification logic failed too. Currently, it's happening only on DAPI client (transport) level.

What was done?

  • Refactored AddressList to remove RwLock which doesn't fit greatly for mixed async and sync code.
  • Ban and unban nodes in SDK retry
  • Removed unused AddressList.available
  • Removed AddressList.into_iter which shouldn't be a case.
  • Ignore missing address in the list on ban and unban. The background process (SML) could remove/add addresses to sync with masternode list. Which is fine.

How Has This Been Tested?

With existing tests

Breaking Changes

  • AddressList.into_iter and AddressList.available removed

Checklist:

  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have added or updated relevant unit/integration/functional/e2e tests
  • I have added "!" to the title and described breaking changes in the corresponding section if my code contains any
  • I have made corresponding changes to the documentation if needed

For repository code-owners and collaborators only

  • I have assigned this pull request to a milestone

Summary by CodeRabbit

Release Notes

  • New Features

    • Enhanced address management with improved concurrency support in the AddressList.
    • Simplified DapiClient structure for more straightforward address list handling.
    • Introduced a new function for banning failed addresses in the DAPI client.
    • Updated retry logic to utilize the address list across various components.
  • Bug Fixes

    • Streamlined error handling and address management in the retry mechanism.
  • Documentation

    • Updated comments and documentation to reflect changes in address management and retry mechanisms.
  • Chores

    • Removed unused imports to clean up the codebase.
    • Adjusted a script to use a static value for configuration instead of a dynamic calculation.
    • Updated version number and edition in the Cargo.toml for the rs-drive-verify-c-binding package.

Copy link
Contributor

coderabbitai bot commented Nov 26, 2024

Walkthrough

The pull request introduces significant modifications to the rs-dapi-client and rs-sdk packages. Key changes include the addition of a new development dependency tokio, updates to the AddressList structure for improved concurrency, and alterations to the DapiClient to simplify its ownership model. Various methods across the SDK have been adjusted to utilize the new address list handling, particularly in retry mechanisms. Additionally, several methods and structures related to address management have been restructured for clarity and efficiency.

Changes

File Path Change Summary
packages/rs-dapi-client/Cargo.toml Added new [dev-dependencies] section with tokio = { version = "1.40", features = ["macros"] }.
packages/rs-dapi-client/src/address_list.rs Simplified Address struct; added AddressStatus struct; updated AddressList to use Arc<RwLock<HashMap<Address, AddressStatus>>; modified methods for address management.
packages/rs-dapi-client/src/dapi_client.rs Changed address_list from Arc<RwLock<AddressList>> to AddressList; updated related methods for direct access.
packages/rs-drive-proof-verifier/src/error.rs Removed unused import: GroveDBProof.
packages/rs-sdk/src/platform/fetch.rs Updated fetch_with_metadata_and_proof method to use sdk.address_list() in the retry call.
packages/rs-sdk/src/platform/fetch_many.rs Modified fetch_many_with_metadata_and_proof and fetch_many methods to use sdk.address_list() in retry calls.
packages/rs-sdk/src/platform/fetch_unproved.rs Updated fetch_unproved_with_settings to use sdk.address_list() in the retry call.
packages/rs-sdk/src/platform/transition/broadcast.rs Modified broadcast and wait_for_response methods to include sdk.address_list() in retry calls.
packages/rs-sdk/src/sdk.rs Changed address_list method return type from Result<AddressList, String> to &AddressList.
packages/rs-sdk/src/sync.rs Updated retry function to accept &AddressList as a new parameter for address management logic.
packages/rs-sdk/tests/fetch/evonode.rs Altered iteration over addresses to use a tuple destructuring approach for clarity in assertions.
packages/rs-dapi-client/src/lib.rs Added new function update_address_ban_status to the public interface of the dapi_client module; updated exports to include this function.
scripts/configure_test_suite_network.sh Changed SKIP_SYNC_BEFORE_HEIGHT from a dynamic calculation to a static value of 4800.
packages/rs-drive-verify-c-binding/Cargo.toml Updated version from 1.1.0 to 1.6.2, added edition 2021, and specified rust-version.workspace = true.

Possibly related PRs

Suggested labels

enhancement, rs-sdk, cleanup

Suggested reviewers

  • QuantumExplorer
  • lklimek

Poem

🐇 In the meadow where the code does play,
A new map of addresses leads the way.
With tokio added, oh what a sight,
Concurrency blooms, making everything right!
So hop along, let’s ban and unban,
In the world of code, we’re the best we can! 🌼


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between a15f3ce and 40faca9.

📒 Files selected for processing (1)
  • packages/rs-dapi-client/src/dapi_client.rs (8 hunks)
🔇 Additional comments (6)
packages/rs-dapi-client/src/dapi_client.rs (6)

76-76: LGTM: Simplified ownership model

The removal of Arc<RwLock<>> wrapper from AddressList field simplifies the ownership model and eliminates potential deadlocks in mixed async/sync code.

Also applies to: 90-90, 99-99


104-106: Consider renaming the function for clarity

Based on the previous discussion, consider renaming to update_address_banned_status to better reflect its purpose of updating the banned status based on execution results.


113-164: LGTM: Comprehensive address status management

The implementation properly handles both success and error cases with appropriate logging levels. The code gracefully handles cases where addresses might have been removed by background processes.

Consider adding explicit error handling for InvalidAddressUri error as discussed in previous reviews.


281-285: LGTM: Clean integration of address ban status updates

The integration of update_address_ban_status is well-placed after handling the execution result and before returning it.


104-165: Add test coverage for address banning functionality

The new address banning functionality requires comprehensive test coverage, including:

  • Unit tests for update_address_ban_status
  • Integration tests verifying the banning/unbanning behavior
  • Tests for error cases and edge conditions

237-237: Verify the safety of using Relaxed ordering

While using Relaxed ordering might be sufficient here as discussed, we should verify that there are no concurrent accesses to retries_counter that would require stronger ordering guarantees.

Also applies to: 250-250, 259-259, 297-297

✅ Verification successful

Let me analyze the usage patterns further to ensure there are no potential race conditions or scenarios requiring stronger ordering guarantees.


Let me analyze the usage of retries_counter further by getting more context around its concurrent access patterns.


After analyzing the code context, I can now generate the final response regarding the safety of using Relaxed ordering for the retries counter.

Using Relaxed ordering for retries_counter is safe

The retries_counter is only used for error reporting and logging purposes, with no dependencies on its exact value for program correctness. The counter is:

  • Initialized once at the start of execution
  • Incremented only in the retry notification callback
  • Read only when constructing error messages
  • Not used for any synchronization or control flow decisions

Since there are no ordering requirements with other operations and the exact count is not critical for program correctness, Relaxed ordering provides sufficient guarantees while being the most performant option.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for any other uses of retries_counter in the codebase
rg "retries_counter" --type rust

# Look for any concurrent access patterns
ast-grep --pattern 'AtomicUsize::new($_)'

Length of output: 1241


Script:

#!/bin/bash
# Get more context around the retries_counter usage
rg "retries_counter" -B 5 -A 5 --type rust

# Look for any async or concurrent patterns in the same file
ast-grep --pattern 'async fn $_($$$) {
  $$$
}'

Length of output: 66387


Script:

#!/bin/bash
# Get more context around the retries_counter usage in dapi_client.rs
rg "retries_counter" -C 20 packages/rs-dapi-client/src/dapi_client.rs

# Look for any async or concurrent patterns in dapi_client.rs
ast-grep --pattern 'async fn $_($$$) {
  $$$
}' packages/rs-dapi-client/src/dapi_client.rs

Length of output: 5585


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 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.

@shumkov shumkov self-assigned this Nov 26, 2024
@shumkov shumkov marked this pull request as ready for review November 26, 2024 15:49
@shumkov shumkov changed the title feat(sdk): ban addresses failed in sdk feat(sdk)!: ban addresses failed in sdk Nov 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: 3

🧹 Outside diff range and nitpick comments (6)
packages/rs-dapi-client/Cargo.toml (1)

40-41: Consider adding version constraints

While using the exact version "6.1.0" works, consider using semantic versioning constraints to automatically receive compatible bug fixes:

-dashmap = "6.1.0"
+dashmap = "~6.1"

This will allow minor version updates within the 6.1.x range while preventing breaking changes.

packages/rs-sdk/src/platform/transition/broadcast.rs (1)

Line range hint 125-144: Enhance error messages for address banning scenarios.

While the error handling is comprehensive for timeouts and state transition details, consider adding information about banned addresses when retries are exhausted.

Add address context to timeout error message:

     Error::TimeoutReached(
         timeout,
-        format!("Timeout waiting for result of {} (tx id: {}) affecting object {}: {:?}",
+        format!("Timeout waiting for result of {} (tx id: {}) affecting object {} (available addresses: {}): {:?}",
         self.name(),
         self.transaction_id().map(hex::encode).unwrap_or("UNKNOWN".to_string()),
         self.unique_identifiers().join(","),
+        sdk.address_list().available().join(","),
          e),
     )
packages/rs-sdk/src/platform/fetch_many.rs (1)

330-330: Consider using the default trait implementation.

While the change to use sdk.address_list() is correct, this implementation could potentially be simplified by using the default trait implementation of fetch_many_with_metadata_and_proof instead of maintaining a custom one. This would reduce code duplication and maintenance overhead.

Consider removing the custom implementation if there are no specific requirements for the Document type that differ from the default implementation.

packages/rs-dapi-client/src/address_list.rs (2)

Line range hint 139-151: Possible Race Conditions in ban_address Method

The ban_address method removes an Address from self.addresses, modifies it, and reinserts it. In a concurrent environment, this remove-modify-insert pattern can lead to race conditions, causing inconsistent states or lost updates if multiple threads access the method simultaneously. Since DashSet is designed for concurrent access, directly removing and reinserting items without proper synchronization may result in unpredictable behavior.

Suggestion:

Consider using an atomic update mechanism to modify the Address in place without removing it from the DashSet. One approach is to switch to a DashMap<Uri, Address>, where the Uri is the key, allowing you to use methods like entry or get_mut to safely mutate the Address under a lock. This ensures thread safety and prevents potential race conditions.

Another option is to use the DashSet::get method to obtain a reference to the Address and employ interior mutability (e.g., using AtomicUsize for ban_count and AtomicOption<DateTime<Utc>> for banned_until) within the Address struct. This would allow you to modify the fields atomically without removing the Address from the set.


Line range hint 153-165: Possible Race Conditions in unban_address Method

Similar to ban_address, the unban_address method removes an Address, modifies it, and reinserts it into self.addresses. This remove-modify-insert pattern is susceptible to race conditions in concurrent usage, potentially leading to inconsistent states or lost updates when accessed by multiple threads simultaneously.

Suggestion:

Adopt an atomic update strategy as suggested for the ban_address method. Using a DashMap<Uri, Address> would allow safe in-place modifications of the Address without removing it from the collection. Alternatively, implement interior mutability within the Address struct to atomically modify its state while it remains in the DashSet. This change will enhance thread safety and prevent race conditions.

packages/rs-dapi-client/src/dapi_client.rs (1)

201-220: Refactor ban/unban logic to reduce code duplication

The ban and unban logic share similar patterns, including error handling and logging. Consider refactoring this into a common helper function to improve maintainability and reduce duplication.

Also applies to: 228-250

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 840dcec and 54c8fc4.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (10)
  • packages/rs-dapi-client/Cargo.toml (1 hunks)
  • packages/rs-dapi-client/src/address_list.rs (7 hunks)
  • packages/rs-dapi-client/src/dapi_client.rs (6 hunks)
  • packages/rs-drive-proof-verifier/src/error.rs (0 hunks)
  • packages/rs-sdk/src/platform/fetch.rs (1 hunks)
  • packages/rs-sdk/src/platform/fetch_many.rs (2 hunks)
  • packages/rs-sdk/src/platform/fetch_unproved.rs (1 hunks)
  • packages/rs-sdk/src/platform/transition/broadcast.rs (2 hunks)
  • packages/rs-sdk/src/sdk.rs (1 hunks)
  • packages/rs-sdk/src/sync.rs (6 hunks)
💤 Files with no reviewable changes (1)
  • packages/rs-drive-proof-verifier/src/error.rs
🔇 Additional comments (14)
packages/rs-dapi-client/Cargo.toml (1)

40-41: LGTM! Good choice for concurrent operations

The addition of dashmap is well-aligned with the PR objectives. It's an excellent choice for replacing RwLock<HashSet> in mixed async/sync code contexts, providing better performance characteristics for concurrent operations.

packages/rs-sdk/src/platform/fetch_unproved.rs (1)

109-111: LGTM! The retry function update aligns with SDK-level address banning.

The change to include sdk.address_list() as the first parameter enables address banning during retries, which is consistent with the PR's objective of implementing address banning at the SDK level.

Let's verify that this pattern is consistently applied across the codebase:

✅ Verification successful

All retry function calls consistently use address_list as the first parameter

The verification confirms that all retry function calls across the codebase follow the expected pattern:

  • All instances pass address_list as the first parameter (either directly or via sdk.address_list())
  • The function consistently takes three parameters in the order: address_list, settings, and closure
  • This pattern is uniformly applied across different modules:
    • platform/fetch_unproved.rs
    • platform/fetch_many.rs
    • platform/fetch.rs
    • platform/transition/broadcast.rs
    • sync.rs
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify consistent usage of retry function with address_list
# Expect: All retry calls should follow the pattern: retry(sdk.address_list(), settings, closure)

# Search for retry function calls
ast-grep --pattern 'retry($_, $_, $_)'

Length of output: 3049

packages/rs-sdk/src/platform/transition/broadcast.rs (1)

55-58: LGTM! Verify error handling for address banning.

The addition of sdk.address_list() to the retry call enables proper address management during retries, aligning with the PR's objective to implement address banning at the SDK level.

Let's verify the error handling chain:

✅ Verification successful

LGTM! Error handling for address banning is properly implemented.

The verification shows that the error handling chain for address banning is correctly implemented:

  • ExecutionError properly includes address information and retry counts
  • The retry mechanism in sync.rs handles address banning/unbanning through address_list.ban_address() and address_list.unban_address()
  • The broadcast implementation correctly passes the address_list to the retry mechanism, enabling proper address management during retries
  • Error propagation includes address context for both successful and failed cases
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify that ExecutionError properly propagates address information for banning
# Look for ExecutionError construction with address context in retry implementations

rg -A 3 "ExecutionError\s*{\s*inner:" packages/rs-sdk/src/

Length of output: 188


Script:

#!/bin/bash
# Let's try a different approach to find ExecutionError usage patterns
rg -A 5 "ExecutionError" packages/rs-sdk/src/

# Also search for the retry implementation to understand error handling
ast-grep --pattern 'pub async fn retry<$_>($_) -> $_'

Length of output: 7774


Script:

#!/bin/bash
# Let's examine the retry implementation in the rs-dapi-client crate
rg -A 10 "retry\(" packages/rs-sdk/src/

# Also check for address list usage in error handling
rg -A 5 "address_list" packages/rs-sdk/src/

Length of output: 17322

packages/rs-sdk/src/platform/fetch.rs (2)

198-198: LGTM: Enhanced retry mechanism with address list management

The change properly integrates address list management into the retry mechanism, enabling address banning at the SDK level as intended.


198-198: Verify thread-safety of AddressList access

Since AddressList is now shared between retries and the RwLock was removed, we should verify that the concurrent access is properly handled.

✅ Verification successful

Let me search for the AddressList implementation to verify its thread-safety.


Let me search for the AddressList struct definition and its thread-safety mechanisms.


AddressList implementation is thread-safe

The AddressList implementation is thread-safe by design:

  • It uses Arc<DashSet<Address>> internally where:
    • Arc provides thread-safe shared ownership
    • DashSet is a concurrent hash set implementation that allows multiple threads to access it safely
  • All methods operate on &self using the thread-safe DashSet operations
  • No additional synchronization is needed as the concurrent collection handles it internally
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check AddressList implementation for thread-safety mechanisms
# Look for DashMap or other concurrent collections usage in AddressList

# Search for AddressList implementation
ast-grep --pattern 'struct AddressList {
  $$$
}'

# Search for concurrent collection usage
rg --type rust 'use.*dashmap|DashMap|Arc|Mutex|RwLock' -A 5

Length of output: 157932


Script:

#!/bin/bash
# Search for AddressList implementation
ast-grep --pattern 'struct AddressList {
  $$$
}'

# Search for AddressList methods
ast-grep --pattern 'impl AddressList {
  $$$
}'

# Search for AddressList usage in retry function
rg --type rust 'retry.*AddressList' -A 5

Length of output: 7416


Script:

#!/bin/bash
# Search for AddressList struct definition and imports
rg --type rust -A 10 'struct AddressList \{'

# Search for DashSet usage in AddressList
rg --type rust -B 5 'DashSet::new\(\)'

Length of output: 1229

packages/rs-sdk/src/platform/fetch_many.rs (1)

255-255: LGTM! Verify implementations handle the address list correctly.

The change to use sdk.address_list() directly aligns with the PR objectives for improved address management at the SDK level. This allows for better control over address banning during retries.

Let's verify that all FetchMany implementations handle the address list correctly:

packages/rs-dapi-client/src/dapi_client.rs (5)

8-8: Import Arc for shared ownership

The addition of use std::sync::Arc; is necessary for managing shared ownership in the updated code.


75-75: Simplify address_list ownership by removing Arc<RwLock>

Changing the address_list field to own AddressList directly simplifies concurrency management, assuming AddressList internally handles synchronization.


89-89: Directly initialize address_list in the constructor

Assigning address_list directly in the new constructor aligns with the updated struct definition and ensures consistency.


98-98: Update address_list method to return a reference

Returning a reference to AddressList simplifies access and reflects the updated ownership model.


143-146: Directly retrieve live address without locking

The code correctly retrieves a live address from address_list using get_live_address(), leveraging internal concurrency mechanisms.

packages/rs-sdk/src/sync.rs (2)

193-261: Ensure thread safety when accessing AddressList in asynchronous context

The address_list is being accessed and modified inside an asynchronous closure to ban and unban addresses based on execution results. Verify that the AddressList implementation is thread-safe and that its methods (ban_address and unban_address) can be safely called concurrently without causing data races or synchronization issues.


267-277: ⚠️ Potential issue

Review retry logic for correct calculation of remaining retries

In the when closure of the backon::Retryable::retry function, the calculation of all_requests_sent and the update of settings.retries may lead to potential inaccuracies:

  • Ensure that all_requests_sent accurately reflects the total number of requests made, including retries.
  • Verify that settings.retries is correctly updated to prevent negative values, which could cause unexpected behavior.
  • Confirm that the condition all_requests_sent <= max_retries properly guards against exceeding the maximum allowed retries.
packages/rs-sdk/src/sdk.rs (1)

557-560: LGTM: Simplifying address_list method by returning a reference

Changing the address_list method to return a reference to AddressList directly simplifies the API and eliminates unnecessary error handling. This aligns with the refactoring goals and may improve performance by avoiding cloning.

packages/rs-dapi-client/src/dapi_client.rs Outdated Show resolved Hide resolved
packages/rs-dapi-client/src/dapi_client.rs Outdated Show resolved Hide resolved
packages/rs-sdk/src/sync.rs Show resolved Hide resolved
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

🧹 Outside diff range and nitpick comments (1)
packages/rs-sdk/tests/fetch/evonode.rs (1)

Line range hint 26-52: Consider adding test coverage for address banning

Since the PR introduces address banning at the SDK level, consider adding test cases to verify:

  • Address gets banned after failures
  • Banned addresses are not retried
  • Address unbanning works correctly

Would you like me to help draft these additional test cases?

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 54c8fc4 and 5aadfd0.

📒 Files selected for processing (1)
  • packages/rs-sdk/tests/fetch/evonode.rs (2 hunks)
🔇 Additional comments (2)
packages/rs-sdk/tests/fetch/evonode.rs (2)

8-8: LGTM: Import aligns with new dereferencing usage


22-23: LGTM: Correctly adapted to new AddressList API

The changes appropriately handle the removal of into_iter by using iter() with explicit dereferencing.

Let's verify the removal of into_iter implementation:

✅ Verification successful

Verified: IntoIterator implementation has been removed from AddressList

The search results confirm that the IntoIterator implementation for AddressList has been removed. The struct now only implements FromIterator and provides iter() method, which aligns with the changes made in the test file. The adaptation in the test code is correct.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify that into_iter is completely removed from AddressList
# Expected: No matches should be found

rg "impl.*IntoIterator.*for.*AddressList" 

Length of output: 43


Script:

#!/bin/bash
# Let's try a broader search to find any IntoIterator implementations
rg "impl.*IntoIterator" -A 3

# Also search for AddressList struct definition and its implementations
rg "struct AddressList|impl.*AddressList" -A 3

Length of output: 11217

packages/rs-sdk/src/sync.rs Outdated Show resolved Hide resolved
packages/rs-sdk/src/sync.rs Outdated Show resolved Hide resolved
packages/rs-dapi-client/src/dapi_client.rs Outdated Show resolved Hide resolved
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: 2

🧹 Outside diff range and nitpick comments (4)
packages/rs-dapi-client/src/lib.rs (1)

Line range hint 71-89: Consider enhancing error handling integration.

Given the new address banning functionality, consider integrating it more tightly with the CanRetry trait. This could provide a more cohesive approach to handling retries and address banning.

Consider:

  1. Adding a method to CanRetry that indicates if a failure should trigger address banning
  2. Documenting the relationship between retries and address banning in the trait documentation
packages/rs-dapi-client/src/dapi_client.rs (2)

142-146: Enhance error logging with additional context

Consider adding more context to the error log by including the specific error type or category. This would help in debugging and monitoring banned addresses.

 tracing::warn!(
     ?address,
     ?error,
+    error_type = %std::any::type_name::<E>(),
     "ban address {address} due to error: {error}"
 );

275-284: Consider simplifying execution result construction

The success case mapping could be more concise by using struct initialization shorthand.

 let execution_result = result
     .map(|inner| {
         tracing::trace!(response = ?inner, "received {} response", response_name);
         ExecutionResponse {
-            inner,
-            retries,
-            address: address.clone(),
+            inner,
+            retries,
+            address,
         }
     })
packages/rs-sdk/src/sync.rs (1)

Line range hint 365-376: Add test coverage for address banning behavior

While the current tests verify retry counts, they don't explicitly verify that addresses are correctly banned and unbanned.

Consider adding test cases that verify:

  1. Addresses are banned after failures
  2. Banned addresses are not reused in subsequent retries
  3. Addresses are correctly unbanned on success

Example addition:

#[tokio::test]
async fn test_address_banning() {
    let address_list = AddressList::default();
    let test_uri = Uri::from_static("http://localhost").into();
    
    // Verify address is banned after failure
    let result = retry(&address_list, RequestSettings::default(), |s| {
        async move {
            Err(ExecutionError {
                inner: MockError::Generic,
                retries: 0,
                address: Some(test_uri.clone()),
            })
        }
    }).await;
    
    assert!(result.is_err());
    assert!(address_list.is_banned(&test_uri));
}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 5aadfd0 and 945ddf6.

📒 Files selected for processing (4)
  • packages/rs-dapi-client/src/dapi_client.rs (6 hunks)
  • packages/rs-dapi-client/src/lib.rs (1 hunks)
  • packages/rs-sdk/src/sync.rs (8 hunks)
  • packages/rs-sdk/tests/fetch/evonode.rs (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/rs-sdk/tests/fetch/evonode.rs
🔇 Additional comments (6)
packages/rs-dapi-client/src/dapi_client.rs (3)

Line range hint 76-99: LGTM: Simplified ownership model

The removal of Arc<RwLock<AddressList>> in favor of direct AddressList ownership simplifies the code and aligns with the PR objective of eliminating RwLock for mixed async/sync code.


130-132: Handle InvalidAddressUri error explicitly

Using unreachable!() for InvalidAddressUri may cause unintended panics. Even if this error is unexpected, it's safer to handle it explicitly.

Also applies to: 157-159


295-299: LGTM: Well-integrated address banning

The integration of ban_failed_address is well-placed after execution but before returning the result, ensuring proper address management without affecting the execution flow.

packages/rs-sdk/src/sync.rs (3)

9-10: LGTM: Import changes align with new functionality

The new imports support the address banning feature and enhanced error handling capabilities.


160-167: Update function documentation to include the address_list parameter

The function signature has been updated but the documentation doesn't reflect the new parameter.


194-199: Verify address banning integration

The address banning logic is correctly placed after obtaining the execution result. Let's verify the integration with the address list management system.

✅ Verification successful

Based on the results, I can generate the final response as we have sufficient information about the address banning integration:

Address banning integration is properly implemented

The implementation correctly uses the centralized ban_failed_address function from rs-dapi-client, which:

  • Is properly imported and used in the retry logic
  • Integrates with the configurable settings system via ban_failed_address flag
  • Follows the consistent error handling pattern used elsewhere in the codebase
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the usage of ban_failed_address across the codebase
# to ensure consistent error handling patterns

# Check for other occurrences of ban_failed_address
rg -A 3 "ban_failed_address" 

# Check for any custom address banning implementations that might conflict
ast-grep --pattern 'fn $name($params) {
  $$$
  ban($$$)
  $$$
}'

Length of output: 3268

packages/rs-dapi-client/src/lib.rs Outdated Show resolved Hide resolved
packages/rs-dapi-client/src/dapi_client.rs Outdated Show resolved Hide resolved
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: 2

🧹 Outside diff range and nitpick comments (6)
packages/rs-dapi-client/src/address_list.rs (5)

57-61: Add more detailed documentation for the uri method.

While the method is straightforward, consider adding more context about when this method should be used and any important considerations when working with the Uri.

     /// Get [Uri] of a node.
+    /// Returns a reference to the underlying Uri that can be used for making connections.
+    /// This Uri is guaranteed to be valid as it was validated during Address creation.
     pub fn uri(&self) -> &Uri {
         &self.0
     }

66-69: Add documentation for struct fields.

The struct fields would benefit from detailed documentation explaining their purpose and constraints.

 pub struct AddressStatus {
+    /// Number of times this address has been banned
+    /// Used to calculate exponential backoff duration
     ban_count: usize,
+    /// Timestamp until which the address is banned
+    /// None if the address is not currently banned
     banned_until: Option<chrono::DateTime<Utc>>,
 }

Line range hint 73-78: Protect against potential overflow in ban duration calculation.

The exponential calculation could lead to overflow for large ban counts. Consider adding a maximum ban duration.

     pub fn ban(&mut self, base_ban_period: &Duration) {
+        const MAX_COEFFICIENT: f64 = 100.0; // Example maximum
         let coefficient = (self.ban_count as f64).exp();
+        let coefficient = coefficient.min(MAX_COEFFICIENT);
         let ban_period = Duration::from_secs_f64(base_ban_period.as_secs_f64() * coefficient);
 
         self.banned_until = Some(chrono::Utc::now() + ban_period);
         self.ban_count += 1;
     }

107-108: Consider using parking_lot::RwLock for better performance.

The standard library's RwLock can be less performant and more prone to writer starvation compared to parking_lot's implementation.

+use parking_lot::RwLock;
-use std::sync::RwLock;

 pub struct AddressList {
-    addresses: Arc<RwLock<HashMap<Address, AddressStatus>>>,
+    addresses: Arc<RwLock<HashMap<Address, AddressStatus>>>, // Now using parking_lot::RwLock
     base_ban_period: Duration,
 }

253-253: Address the TODO comment about FromStr implementation.

The comment indicates that the FromStr implementation needs to be changed. Consider creating a tracking issue for this work.

Would you like me to help create a GitHub issue to track this TODO item?

packages/rs-dapi-client/src/dapi_client.rs (1)

254-285: Consider extracting result mapping to a separate function

The result mapping logic (lines 261-279) is quite complex and could benefit from being extracted into a separate function for better readability and maintainability.

Consider refactoring like this:

- let execution_result = result
-     .map(|inner| {
-         tracing::trace!(response = ?inner, "received {} response", response_name);
-         ExecutionResponse {
-             inner,
-             retries,
-             address: address.clone(),
-         }
-     })
-     .map_err(|inner| {
-         tracing::debug!(error = ?inner, "received error: {inner}");
-         ExecutionError {
-             inner,
-             retries,
-             address: Some(address.clone()),
-         }
-     });
+ let execution_result = map_transport_result(result, retries, address.clone(), response_name);

+ fn map_transport_result<R>(
+     result: Result<R, TransportError>,
+     retries: usize,
+     address: Address,
+     response_name: &str,
+ ) -> ExecutionResult<R, DapiClientError> {
+     result
+         .map(|inner| {
+             tracing::trace!(response = ?inner, "received {} response", response_name);
+             ExecutionResponse {
+                 inner,
+                 retries,
+                 address: address.clone(),
+             }
+         })
+         .map_err(|inner| {
+             tracing::debug!(error = ?inner, "received error: {inner}");
+             ExecutionError {
+                 inner,
+                 retries,
+                 address: Some(address),
+             }
+         })
+ }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 945ddf6 and db7ce77.

📒 Files selected for processing (5)
  • packages/rs-dapi-client/Cargo.toml (1 hunks)
  • packages/rs-dapi-client/src/address_list.rs (7 hunks)
  • packages/rs-dapi-client/src/dapi_client.rs (6 hunks)
  • packages/rs-dapi-client/src/lib.rs (1 hunks)
  • scripts/configure_test_suite_network.sh (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • scripts/configure_test_suite_network.sh
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/rs-dapi-client/Cargo.toml
  • packages/rs-dapi-client/src/lib.rs
🔇 Additional comments (4)
packages/rs-dapi-client/src/address_list.rs (1)

Line range hint 1-240: Verify thread safety of the new implementation.

The changes introduce concurrent access patterns that should be thoroughly tested.

✅ Verification successful

Thread safety implementation is correct and well-designed

The implementation uses appropriate synchronization mechanisms:

  • Uses Arc<RwLock<HashMap>> for thread-safe concurrent access
  • Read operations (get_live_address, is_banned) use read locks
  • Write operations (ban, unban, add, remove) use write locks
  • No potential for deadlocks as locks are not held across async boundaries
  • Lock granularity is appropriate with a single lock protecting the entire collection

The design follows Rust's safety guarantees and standard concurrent programming patterns.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for potential deadlock scenarios in tests
rg -l "AddressList.*\.(ban|unban|get_live_address)" -t rust | xargs rg "tokio::(test|spawn)"

# Search for existing concurrent tests
rg -l "tokio::test.*AddressList" -t rust

Length of output: 6290


Script:

#!/bin/bash
# Let's search for tests specifically related to AddressList
rg -l "AddressList" -t rust | xargs rg "test.*AddressList" -B 2 -A 5

# Let's also check for any concurrent operations in the implementation
ast-grep --pattern 'impl AddressList {
  $$$
  async fn $$$($$$) {
    $$$
  }
  $$$
}'

# Check for any test files containing AddressList
fd "test.*\.rs" | xargs rg "AddressList"

Length of output: 199


Script:

#!/bin/bash
# Let's check for unit tests in the same file or module
rg -t rust "mod\s+tests" -A 10 packages/rs-dapi-client/src/address_list.rs

# Let's also check how AddressList is used in the codebase
rg "AddressList::" -A 3 -B 1

# Check for any mutex or lock usage patterns
rg "RwLock|Mutex" -A 2 packages/rs-dapi-client/src/address_list.rs

Length of output: 3394

packages/rs-dapi-client/src/dapi_client.rs (3)

104-165: LGTM: Well-structured address banning implementation

The new ban_failed_address function is well-implemented with:

  • Comprehensive error handling
  • Appropriate logging levels
  • Clear handling of edge cases (e.g., addresses removed by background processes)

However, there's a missing test coverage concern that was previously raised.


207-211: LGTM: Simplified address retrieval

The address retrieval has been simplified while maintaining proper error handling.


76-76: Breaking change: Simplified ownership model

The ownership model has been simplified by removing Arc<RwLock<>> wrapper from address_list. While this is a good improvement for mixed async/sync code, it's a breaking change that affects the public API.

Let's verify the impact of this breaking change:

Also applies to: 90-90, 99-99

packages/rs-dapi-client/src/address_list.rs Show resolved Hide resolved
packages/rs-dapi-client/src/address_list.rs Outdated Show resolved Hide resolved
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

🧹 Outside diff range and nitpick comments (2)
packages/rs-dapi-client/src/address_list.rs (1)

Line range hint 63-90: LGTM: Well-implemented ban management with exponential backoff

The AddressStatus struct provides a clean separation of concerns for ban management. The exponential backoff in the ban duration is a good practice for handling problematic nodes.

Consider adding documentation for the exponential coefficient calculation to explain why e^ban_count was chosen as the multiplier.

packages/rs-sdk/src/sdk.rs (1)

1021-1025: Consider allowing custom initial addresses in mock mode

While the current implementation correctly initializes an empty address list, consider allowing custom initial addresses to be provided in mock mode for more flexible testing scenarios.

Consider modifying the builder to accept optional initial addresses:

 inner: SdkInstance::Mock {
     mock: mock_sdk.clone(),
     dapi,
-    address_list: AddressList::new(),
+    address_list: self.mock_addresses.unwrap_or_else(AddressList::new),
     version: self.version,
 },
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between db7ce77 and 57c2871.

📒 Files selected for processing (3)
  • packages/rs-dapi-client/src/address_list.rs (7 hunks)
  • packages/rs-sdk/src/sdk.rs (3 hunks)
  • packages/rs-sdk/tests/fetch/evonode.rs (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/rs-sdk/tests/fetch/evonode.rs
🧰 Additional context used
📓 Learnings (1)
packages/rs-dapi-client/src/address_list.rs (1)
Learnt from: shumkov
PR: dashpay/platform#2351
File: packages/rs-dapi-client/src/address_list.rs:241-250
Timestamp: 2024-11-28T14:08:53.428Z
Learning: In the `IntoIterator` implementation for `AddressList` in `packages/rs-dapi-client/src/address_list.rs`, since `self` is consumed, holding a write lock while converting to an iterator does not block other operations.
🔇 Additional comments (6)
packages/rs-dapi-client/src/address_list.rs (4)

Line range hint 20-61: LGTM: Clean Address struct implementation

The simplified Address struct with proper trait implementations and encapsulated URI access follows Rust best practices.


Line range hint 190-238: LGTM: Well-implemented address management methods

The methods provide a complete set of operations for address management. The get_live_address() implementation appropriately handles random selection while holding the read lock, which is suitable for the expected size of the address list.


241-250: LGTM: Correct IntoIterator implementation

The implementation properly consumes self and handles the transfer of ownership. As discussed in previous reviews, holding the write lock is acceptable here since self is consumed.


Line range hint 107-188: Verify RwLock usage against PR objectives

The PR objectives mention removing RwLock as it was "unsuitable for mixed async and sync code", but the implementation still uses RwLock. Please clarify if this is intentional or if further changes are planned.

The current implementation with Arc looks correct for concurrent access, and the Entry API usage is idiomatic. However, if async code needs to interact with this list, consider using tokio::sync::RwLock instead.

Run this script to check for async code usage:

packages/rs-sdk/src/sdk.rs (2)

187-187: LGTM: Addition of address_list field to Mock variant

The addition of the address_list field to SdkInstance::Mock provides consistent address list handling between mock and real implementations.


557-562: LGTM: Simplified address_list method signature

The method now returns a direct reference to AddressList instead of a Result, which simplifies the API while maintaining functionality. The implementation correctly handles both real and mock SDK instances.

packages/rs-dapi-client/src/address_list.rs Show resolved Hide resolved
packages/rs-dapi-client/src/dapi_client.rs Outdated Show resolved Hide resolved
packages/rs-dapi-client/src/dapi_client.rs Outdated Show resolved Hide resolved
@shumkov shumkov deleted the branch v1.7-dev November 29, 2024 10:09
@shumkov shumkov closed this Nov 29, 2024
@shumkov shumkov reopened this Nov 29, 2024
@shumkov shumkov changed the base branch from v1.6-dev to v1.7-dev November 29, 2024 10:30
@lklimek lklimek self-assigned this Dec 3, 2024
@lklimek lklimek removed their assignment Dec 3, 2024
@shumkov shumkov merged commit 30a5aad into v1.7-dev Dec 4, 2024
30 checks passed
@shumkov shumkov deleted the feat/sdk/ban-failed-nodes branch December 4, 2024 07:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

2 participants