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: retry vote poll end times query and insert proof logs #81

Merged
merged 3 commits into from
Nov 22, 2024

Conversation

pauldelucia
Copy link
Member

@pauldelucia pauldelucia commented Nov 20, 2024

Insert proof logs into the db for dpns end time query and dpns vote contender query, and also add retry logic to end time query.

Summary by CodeRabbit

Release Notes

  • New Features

    • Enhanced retry mechanisms for fetching contested resources, vote contenders, and vote polls, improving reliability during transient errors.
    • Improved error logging for better tracking of issues related to proof verification and server errors.
  • UI Improvements

    • Updated timestamp rendering in the DPNS Contested Names screen to display "now" for recent events, enhancing user experience.
  • Documentation

    • Clarified database initialization and migration processes in comments for better understanding.

Copy link
Contributor

coderabbitai bot commented Nov 20, 2024

Walkthrough

This pull request introduces several modifications across multiple files, primarily focusing on implementing retry mechanisms for error handling in various querying methods within the AppContext. Constants for maximum retry attempts are defined, and logic is added to retry fetching resources or vote contenders upon encountering specific transient errors. Additionally, the database initialization logic is updated to reflect changes in versioning, and UI rendering for timestamps is refined for better user feedback. Overall, the changes enhance robustness and clarity in error handling and user interface.

Changes

File Path Change Summary
src/backend_task/contested_names/query_dpns_contested_resources.rs Added MAX_RETRIES constant; implemented retry logic for fetching contested resources with enhanced error handling.
src/backend_task/contested_names/query_dpns_vote_contenders.rs Added MAX_RETRIES constant; implemented retry mechanism for fetching vote contenders with detailed error logging. New import added for ProofLogItem.
src/backend_task/contested_names/query_ending_times.rs Added MAX_RETRIES constant; modified fetching logic for vote polls to include retry mechanism and improved error handling.
src/components/core_zmq_listener.rs Updated spawn_listener method to use zeromq crate for Windows with asynchronous handling; enhanced error logging.
src/database/initialization.rs Updated DEFAULT_DB_VERSION from 1 to 2; removed CURRENT_DB_VERSION; clarified migration logic and comments.
src/ui/dpns_contested_names_screen.rs Enhanced timestamp rendering logic in UI methods to display "now" for recent events; no changes to error handling.

Possibly related PRs

  • fix: error messages in dpns screen #72: The changes in this PR introduce a retry mechanism for querying contested resources, which is directly related to the retry logic implemented in the main PR's query_dpns_contested_resources method. Both PRs enhance error handling by allowing retries under specific error conditions.

Suggested reviewers

  • QuantumExplorer
  • ogabrielides

Poem

Hop, hop, in the code we play,
With retries added, come what may!
From resources to votes, we fetch with glee,
Errors are handled, as smooth as can be.
Timestamps now say "now," oh what a sight,
In our rabbit hole, everything feels right! 🐇✨


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 20d17bd and dfa7d80.

📒 Files selected for processing (6)
  • src/backend_task/contested_names/query_dpns_contested_resources.rs (6 hunks)
  • src/backend_task/contested_names/query_dpns_vote_contenders.rs (2 hunks)
  • src/backend_task/contested_names/query_ending_times.rs (2 hunks)
  • src/components/core_zmq_listener.rs (1 hunks)
  • src/database/initialization.rs (5 hunks)
  • src/ui/dpns_contested_names_screen.rs (2 hunks)
🔇 Additional comments (6)
src/backend_task/contested_names/query_dpns_contested_resources.rs (3)

28-28: LGTM! Well-defined retry constant.

The MAX_RETRIES constant is appropriately defined and follows Rust naming conventions.


64-65: LGTM! Robust error handling and logging.

The error handling is comprehensive with:

  • Consistent error message formatting
  • Proper error propagation
  • Detailed proof logging

Also applies to: 75-76, 95-95, 68-68, 79-79


173-173: LGTM! Improved error logging for async tasks.

The error logging has been enhanced with more descriptive messages and proper formatting for both DPNS end times and vote contenders queries.

Also applies to: 211-214

src/database/initialization.rs (2)

7-7: Verify database migration testing from version 1 to 2

The version bump indicates schema changes for proof logs. Please ensure that:

  1. Migration from version 1 to 2 has been tested with existing databases
  2. The proof log table creation in apply_version_changes handles all edge cases

124-124: LGTM! Database recreation implementation is thorough

The implementation includes proper table structure, indexing, and the new proof log table initialization.

src/ui/dpns_contested_names_screen.rs (1)

415-419: LGTM! Enhanced timestamp display for better user experience.

The change improves clarity by showing "now" instead of "x seconds ago" for very recent updates.

Also applies to: 536-540


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.

@pauldelucia pauldelucia changed the base branch from master to v0.4-dev November 20, 2024 13:39
@pauldelucia pauldelucia marked this pull request as draft November 20, 2024 13:41
@pauldelucia pauldelucia marked this pull request as ready for review November 20, 2024 13:43
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.

Caution

Inline review comments failed to post. This is likely due to GitHub's limits when posting large numbers of comments.

Actionable comments posted: 4

🧹 Outside diff range and nitpick comments (12)
src/backend_task/contested_names/query_dpns_contested_resources.rs (2)

12-12: Remove unused import.

The std::time::Instant import appears to be unused in this file.

-use std::time::Instant;

42-44: Consider adding debug logs for retry attempts.

The retry mechanism is well-implemented, but adding debug logs for retry attempts would help with monitoring and debugging.

 let mut retries = 0;

 let contested_resources = match ContestedResource::fetch_many(&sdk, query.clone()).await
 {
     Ok(contested_resources) => contested_resources,
     Err(e) => {
         tracing::error!("Error fetching contested resources: {}", e);
         if let dash_sdk::Error::Proof(
             dash_sdk::ProofVerifierError::GroveDBProofVerificationError {
                 proof_bytes,
                 path_query,
                 height,
                 time_ms,
                 error,
             },
         ) = &e
         {
             // ... existing code ...
         }
         if e.to_string().contains("try another server")
             || e.to_string().contains(
                 "contract not found when querying from value with contract info",
             )
         {
             retries += 1;
+            tracing::debug!("Retrying query attempt {}/{}", retries, MAX_RETRIES);
             if retries > MAX_RETRIES {
                 tracing::error!("Max retries reached for query: {}", e);
                 return Err(format!(
                     "Error fetching contested resources after retries: {}",
                     e
                 ));
             } else {
                 // Retry
                 continue;
             }
         } else {
             return Err(format!("Error fetching contested resources: {}", e));
         }
     }
 };

Also applies to: 98-117

src/database/initialization.rs (3)

Line range hint 17-27: Replace println with proper logging

The error handling logic is robust, but using println! for logging isn't ideal in production code. Consider using a proper logging framework like log crate with appropriate log levels.

-                    println!("Migration failed: {:?}", e);
+                    log::error!("Migration failed: {:?}", e);
-                    println!("Database reinitialized with default settings.");
+                    log::info!("Database reinitialized with default settings.");

Line range hint 78-94: Handle potential panic in version checking

The unwrap_or(0) usage could be more explicit about the error case. Consider using proper error handling:

-        let version: u16 = conn
-            .query_row(
-                "SELECT database_version FROM settings WHERE id = 1",
-                [],
-                |row| row.get(0),
-            )
-            .unwrap_or(0); // Default to version 0 if there's no version set
+        let version: u16 = conn
+            .query_row(
+                "SELECT database_version FROM settings WHERE id = 1",
+                [],
+                |row| row.get(0),
+            )
+            .map_err(|e| {
+                log::warn!("Failed to get database version: {:?}", e);
+                rusqlite::Error::QueryReturnedNoRows
+            })
+            .unwrap_or(0);

Line range hint 96-120: Consider implementing backup retention policy

While the backup functionality is solid, consider:

  1. Implementing a retention policy to prevent unlimited backup accumulation
  2. Adding a cleanup routine for old backups
  3. Logging the total number of existing backups

Would you like me to help create an issue for implementing a backup retention strategy?

src/ui/dpns_contested_names_screen.rs (1)

415-419: Consider extracting the timestamp formatting logic into a helper method.

The timestamp formatting logic is duplicated between render_table_active_contests and render_table_past_contests. Consider extracting it into a helper method to improve maintainability and reduce duplication.

impl DPNSContestedNamesScreen {
+    fn format_relative_time(&self, relative_time: String) -> String {
+        if relative_time.contains("seconds") {
+            "now".to_string()
+        } else {
+            relative_time
+        }
+    }
}

Then use it in both places:

-    if relative_time.contains("seconds") {
-        ui.label("now");
-    } else {
-        ui.label(relative_time);
-    }
+    ui.label(self.format_relative_time(relative_time));

Also applies to: 536-540

src/backend_task/contested_names/query_dpns_vote_contenders.rs (3)

74-85: Simplify error handling using the ? operator

You can make the code more concise by using the ? operator to propagate errors, improving readability.

Apply this diff to refactor the encoding of encoded_query:

-let encoded_query = match bincode::encode_to_vec(
-    &contenders_query,
-    bincode::config::standard(),
-)
-.map_err(|encode_err| {
-    tracing::error!("Error encoding query: {}", encode_err);
-    format!("Error encoding query: {}", encode_err)
-}) {
-    Ok(encoded_query) => encoded_query,
-    Err(e) => return Err(e),
-};
+let encoded_query = bincode::encode_to_vec(
+    &contenders_query,
+    bincode::config::standard(),
+)
+.map_err(|encode_err| {
+    tracing::error!("Error encoding query: {}", encode_err);
+    format!("Error encoding query: {}", encode_err)
+})?;

Similarly, refactor the encoding of verification_path_query_bytes:

-let verification_path_query_bytes = match bincode::encode_to_vec(
-    &path_query,
-    bincode::config::standard(),
-)
-.map_err(|encode_err| {
-    tracing::error!("Error encoding path_query: {}", encode_err);
-    format!("Error encoding path_query: {}", encode_err)
-}) {
-    Ok(encoded_path_query) => encoded_path_query,
-    Err(e) => return Err(e),
-};
+let verification_path_query_bytes = bincode::encode_to_vec(
+    &path_query,
+    bincode::config::standard(),
+)
+.map_err(|encode_err| {
+    tracing::error!("Error encoding path_query: {}", encode_err);
+    format!("Error encoding path_query: {}", encode_err)
+})?;

98-112: Simplify error propagation when inserting proof log items

Using the ? operator can streamline error handling when inserting into the database.

Apply this diff to simplify the code:

-if let Err(e) = self
-    .db
-    .insert_proof_log_item(ProofLogItem {
-        request_type: RequestType::GetContestedResourceIdentityVotes,
-        request_bytes: encoded_query,
-        verification_path_query_bytes,
-        height: *height,
-        time_ms: *time_ms,
-        proof_bytes: proof_bytes.clone(),
-        error: Some(error.clone()),
-    })
-    .map_err(|e| e.to_string())
-{
-    return Err(e);
-}
+self
+    .db
+    .insert_proof_log_item(ProofLogItem {
+        request_type: RequestType::GetContestedResourceIdentityVotes,
+        request_bytes: encoded_query,
+        verification_path_query_bytes,
+        height: *height,
+        time_ms: *time_ms,
+        proof_bytes: proof_bytes.clone(),
+        error: Some(error.clone()),
+    })
+    .map_err(|e| e.to_string())?;

120-133: Remove unnecessary else block after return

The else block following a return is redundant and can be omitted to simplify the code.

Apply this diff to remove the unnecessary else:

if retries > MAX_RETRIES {
    tracing::error!(
        "Max retries reached for query_dpns_vote_contenders: {}",
        e
    );
    return Err(format!(
        "Error fetching vote contenders after retries: {}",
        e
    ));
-} else {
-    continue;
-}
+}
+// Continue to the next iteration
+continue;
src/backend_task/contested_names/query_ending_times.rs (1)

86-108: Reduce code duplication by refactoring encoding logic into a helper function

The encoding of end_time_query and path_query shares similar logic, leading to duplicated code.

Consider extracting the encoding and error handling into a helper function to improve readability and maintainability. For example:

fn encode_to_vec<T: Encode>(value: &T) -> Result<Vec<u8>, String> {
    bincode::encode_to_vec(value, bincode::config::standard()).map_err(|e| {
        tracing::error!("Error encoding value: {}", e);
        format!("Error encoding value: {}", e)
    })
}

Then use it as:

let encoded_query = encode_to_vec(&end_time_query)?;
let verification_path_query_bytes = encode_to_vec(&path_query)?;
src/components/core_zmq_listener.rs (2)

Line range hint 31-35: Typo in enum variant name ChainLockedLockedTransaction

The variant ChainLockedLockedTransaction appears to have a redundant 'Locked' in its name. Consider renaming it to ChainLockedTransaction for clarity and consistency.

Apply this diff to fix the typo:

 pub enum ZMQMessage {
     ISLockedTransaction(Transaction, InstantLock),
     ChainLockedBlock(Block),
-    ChainLockedLockedTransaction(Transaction, CoreBlockHeight),
+    ChainLockedTransaction(Transaction, CoreBlockHeight),
 }

Line range hint 71-71: Consider using a logging library instead of println! and eprintln!

Currently, the code uses println! and eprintln! for logging information and errors. Consider using a logging library such as log or env_logger with appropriate logging macros (info!, warn!, error!) for better control over logging output and integration with logging frameworks.

Example of replacing println! and eprintln! with logging macros:

-println!("Subscribed to ZMQ at {}", endpoint);
+log::info!("Subscribed to ZMQ at {}", endpoint);

-eprintln!("Error receiving message: {}", e);
+log::error!("Error receiving message: {}", e);

Don't forget to initialize the logger in your main function:

env_logger::init();

Also applies to: 243-244, 251-252

🛑 Comments failed to post (4)
src/backend_task/contested_names/query_dpns_vote_contenders.rs (2)

74-112: ⚠️ Potential issue

Ensure sensitive data is handled securely when logging

Be cautious when logging detailed error information that may include sensitive data like proof_bytes or encoded queries.

Consider redacting or masking sensitive information before logging or storing it to adhere to security best practices and protect against potential data leaks.


115-119: 🛠️ Refactor suggestion

Use error variants instead of matching on error messages

Matching on error message strings can be fragile and may break if the error messages change. It's better to match on specific error types to handle errors more reliably.

You can refactor this section to match on specific error variants:

-let error_str = e.to_string();
-if error_str.contains("try another server")
-    || error_str.contains(
-        "contract not found when querying from value with contract info",
-    )
+if matches!(e, dash_sdk::Error::Transient(_)) || matches!(e, dash_sdk::Error::ContractNotFound(_))
{
    retries += 1;
    if retries > MAX_RETRIES {
        tracing::error!(
            "Max retries reached for query_dpns_vote_contenders: {}",
            e
        );
        return Err(format!(
            "Error fetching vote contenders after retries: {}",
            e
        ));
    } else {
        continue;
    }
} else {
    // For other errors, return immediately
    return Err(format!("Error fetching vote contenders: {}", e));
}

Committable suggestion skipped: line range outside the PR's diff.

src/backend_task/contested_names/query_ending_times.rs (2)

45-68: ⚠️ Potential issue

Prevent potential runtime panic by safely handling enum variants

The current use of pattern matching assumes that every vote_poll is a ContestedDocumentResourceVotePoll. If vote_poll is of a different variant, this will cause a panic at runtime.

To prevent this, consider using an if let or match statement to safely destructure vote_poll. For example:

if let VotePoll::ContestedDocumentResourceVotePoll(
    ContestedDocumentResourceVotePoll {
        contract_id,
        document_type_name,
        index_name,
        index_values,
    },
) = vote_poll
{
    // Proceed with processing
    // ...
} else {
    // Handle other variants or skip
    return None;
}

126-142: 🛠️ Refactor suggestion

Avoid string matching on errors; match on specific error types instead

Using e.to_string().contains(...) to identify error types by their message is fragile and can lead to incorrect error handling if the error messages change.

Consider matching on specific error variants to make the error handling more robust. For example:

if matches!(e, dash_sdk::Error::WrongNetwork)
    || matches!(e, dash_sdk::Error::ContractNotFound { .. })
{
    retries += 1;
    if retries >= MAX_RETRIES {
        tracing::error!("Max retries reached for query: {}", e);
        return Err(format!("Error fetching vote polls after retries: {}", e));
    } else {
        continue;
    }
} else {
    return Err(format!("Error fetching vote polls: {}", e));
}

@pauldelucia
Copy link
Member Author

@coderabbitai review

Copy link
Contributor

coderabbitai bot commented Nov 21, 2024

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@QuantumExplorer QuantumExplorer merged commit f6ad81f into v0.4-dev Nov 22, 2024
1 check passed
@pauldelucia pauldelucia deleted the feat/retry-vote-poll-end-times-query branch November 23, 2024 05:18
@coderabbitai coderabbitai bot mentioned this pull request Dec 12, 2024
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.

2 participants