Repository: tari-project/tari
Pull request #6915: fix: fix migration 5
Author: login: stringhandler
Created: 2025-04-02T19:42:28Z | Merged: 2025-04-03T07:17:07Z



<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

- **New Features**
  - Introduced functionality to retrieve all stored key-value entries, enabling comprehensive data inspection.
  - Enhanced migration process for seed and height retrieval, improving clarity and efficiency.

- **Style**
  - Improved code formatting and organization for consistency and enhanced readability.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->


Diff:
diff --git a/base_layer/core/src/chain_storage/lmdb_db/lmdb.rs b/base_layer/core/src/chain_storage/lmdb_db/lmdb.rs
index 2b104b72890..6cf0580f053 100644
--- a/base_layer/core/src/chain_storage/lmdb_db/lmdb.rs
+++ b/base_layer/core/src/chain_storage/lmdb_db/lmdb.rs
@@ -423,6 +423,29 @@ where
     Ok(result)
 }
 
+pub fn lmdb_all<V>(txn: &ConstTransaction<'_>, db: &Database) -> Result<Vec<(Vec<u8>, V)>, ChainStorageError>
+where V: DeserializeOwned {
+    let access = txn.access();
+    let mut cursor = txn.cursor(db).map_err(|e| {
+        error!(target: LOG_TARGET, "Could not get read cursor from lmdb: {:?}", e);
+        ChainStorageError::AccessError(e.to_string())
+    })?;
+
+    let iter = CursorIter::new(
+        MaybeOwned::Borrowed(&mut cursor),
+        &access,
+        |c, a| c.first(a),
+        Cursor::next::<[u8], [u8]>,
+    )?;
+    let mut result = vec![];
+
+    for row in iter {
+        let (k, v) = row?;
+        result.push((k.to_vec(), deserialize::<V>(v)?));
+    }
+    Ok(result)
+}
+
 /// Fetches the size of all key/values in the given DB. Returns the number of entries, the total size of all the
 /// keys and values in bytes.
 pub fn fetch_db_entry_sizes(txn: &ConstTransaction<'_>, db: &Database) -> Result<(u64, u64, u64), ChainStorageError> {
diff --git a/base_layer/core/src/chain_storage/lmdb_db/lmdb_db.rs b/base_layer/core/src/chain_storage/lmdb_db/lmdb_db.rs
index f4ae35eedf4..ba368a0dec7 100644
--- a/base_layer/core/src/chain_storage/lmdb_db/lmdb_db.rs
+++ b/base_layer/core/src/chain_storage/lmdb_db/lmdb_db.rs
@@ -88,6 +88,7 @@ use crate::{
             composite_key::{CompositeKey, InputKey, OutputKey},
             lmdb::{
                 fetch_db_entry_sizes,
+                lmdb_all,
                 lmdb_clear,
                 lmdb_delete,
                 lmdb_delete_each_where,
@@ -2962,6 +2963,7 @@ impl fmt::Display for MetadataValue {
     }
 }
 
+#[allow(clippy::too_many_lines)]
 fn run_migrations(db: &LMDBDatabase) -> Result<(), ChainStorageError> {
     const MIGRATION_VERSION: u64 = 6;
     let txn = db.read_transaction()?;
@@ -3026,31 +3028,44 @@ fn run_migrations(db: &LMDBDatabase) -> Result<(), ChainStorageError> {
             // lets create the monero seed height indexes
             {
                 let txn = db.write_transaction()?;
-                let heights: Vec<u64> = lmdb_filter_map_values(&txn, &db.monero_seed_height_db, Some)?;
-                let mut seeds = Vec::new();
-                for height in &heights {
-                    let header = lmdb_get::<_, BlockHeader>(&txn, &db.headers_db, height)?.ok_or_else(|| {
-                        ChainStorageError::ValueNotFound {
-                            entity: "Header",
-                            field: "height",
-                            value: height.to_string(),
+                let heights: Vec<(Vec<u8>, u64)> = lmdb_all(&txn, &db.monero_seed_height_db)?;
+                let mut final_table = heights.clone();
+                for (db_seed, height) in &heights {
+                    let mut delete = true;
+                    if let Some(header) = lmdb_get::<_, BlockHeader>(&txn, &db.headers_db, height)? {
+                        info!(target: LOG_TARGET, "Checking header for monero seed {}", header.hash().to_hex());
+                        if header.pow_algo() == PowAlgorithm::RandomX {
+                            let pow_bytes = header.pow.pow_data.to_vec();
+                            let pow_data = MoneroPowData::deserialize(&mut pow_bytes.as_slice()).unwrap();
+                            let seed = pow_data.randomx_key.to_vec();
+                            if seed == *db_seed {
+                                delete = false;
+                            } else {
+                                // delete
+                                info!(target: LOG_TARGET, "Deleting monero seed height {} because it does not match the seed", header.hash().to_hex());
+                            }
+                        } else {
+                            // delete
+                            info!(target: LOG_TARGET, "Deleting monero seed height {} because it is not RandomX", header.hash().to_hex());
                         }
-                    })?;
-                    let pow_bytes = header.pow.pow_data.to_vec();
-                    let pow_data = MoneroPowData::deserialize(&mut pow_bytes.as_slice()).unwrap();
-                    let seed = pow_data.randomx_key.to_vec();
-                    seeds.push(seed);
+                    }
+                    if delete {
+                        lmdb_delete(&txn, &db.monero_seed_height_db, db_seed, "monero_seed_height_db")?;
+                        final_table.retain(|(s, _)| s != db_seed);
+                    }
                 }
-                for (i, seed) in seeds.iter().enumerate() {
-                    let txn = db.write_transaction()?;
+                for (seed, height) in final_table {
+                    info!(target: LOG_TARGET, "Inserting new monero seed height {} with seed {}", height, seed.to_hex());
                     lmdb_insert(
                         &txn,
                         &db.monero_seed_height_index_db,
-                        &heights[i],
+                        &height,
                         &seed,
                         "monero_seed_height_index_db",
                     )?;
                 }
+
+                txn.commit()?;
             }
         }
     }


Reviews:
user: login: coderabbitai[bot]
body: **Actionable comments posted: 1**

<details>
<summary>🔭 Outside diff range comments (1)</summary><blockquote>

<details>
<summary>base_layer/core/src/chain_storage/lmdb_db/lmdb_db.rs (1)</summary><blockquote>

`2968-3013`: _💡 Verification agent_

<details>
<summary>🧩 Analysis chain</summary>

**Review Migration 5 Adjustments for Monero Seed Height Indexes**

The migration block for version 5 now performs two key actions:
  
1. It clears the bad blocks list due to the reorg issues with Monero seed heights.
2. It reconstructs the monero seed height indexes by retrieving all key-value pairs with the new `lmdb_all` function, then filtering out inconsistent entries based on deserialization of the corresponding block headers.

The logic—iterating over the retrieved heights, comparing the stored key (`db_seed`) with the deserialized seed from the block’s PoW data, and then conditionally deleting or retaining entries—is sound. However, please consider the following:

- **Error Handling & Robustness:**  
  Ensure that every deserialization (and subsequent computation) in this block gracefully handles errors (as noted above with the use of `unwrap()`). Since migration code runs on live databases, robust error handling is critical.

- **Testing & Performance:**  
  This migration touches possibly a large number of entries. Please add or update migration tests to verify that inconsistent entries are correctly removed and that the index is rebuilt accurately. Also, review performance implications if the number of entries is very high.

Overall, these adjustments appear to address the migration issue, but rigorous testing will help make sure that no unexpected data loss or inconsistency occurs.

---

Below is a shell script to verify the usage of `unwrap()` in the migration block, which is the primary focus of the error handling concern:




Based on the code snippet in question, the migration block for version 5 is performing two critical tasks:
- Clearing the bad blocks list (to mitigate reorg issues).
- Reconstructing the monero seed height indexes by iterating over stored key-value pairs and deserializing block headers.

**However, note the following points that need attention:**

- **Error Handling:**  
  The code uses `.unwrap()` during the deserialization of `MoneroPowData` (i.e.  
  ```rust
  let pow_data = MoneroPowData::deserialize(&mut pow_bytes.as_slice()).unwrap();
  ```  
  ). Since migration code interacts with live databases, it’s important to handle errors gracefully rather than risking a panic. Replacing `unwrap()` with structured error handling (using `?` or a proper `match`) is highly recommended.

- **Testing & Performance:**  
  Given that this migration might process many entries, consider adding or updating tests to ensure that:
  - Inconsistent entries are correctly removed.
  - The seed height index is rebuilt accurately.
  - Performance remains acceptable.  
  Additionally, review the design of opening new write transactions inside loops, as this pattern might impact performance if the dataset is large.

With these observations, please verify that the suggested changes (especially around error handling with deserialization) are incorporated and that adequate tests are in place before merging.

**Attention: Verify Error Handling in Migration Block for Version 5**

- **Task:** Replace the use of `unwrap()` with robust error handling.
- **Action:** Add or update tests for migration performance and correctness.



---

🏁 Script executed:

```shell
#!/bin/bash
# Check for the usage of unwrap() in the migration block file
rg "MoneroPowData::deserialize.*unwrap" base_layer/core/src/chain_storage/lmdb_db/lmdb_db.rs
```

Length of output: 197

---



</details>

**Attention: Migration 5 – Update Error Handling and Review Transaction Usage**

The migration block for version 5 correctly addresses the reorg issues by clearing the bad blocks list and rebuilding the monero seed height indexes. However, two concerns should be addressed before merging:

- **Error Handling:**  
  The current code uses `.unwrap()` during deserialization of `MoneroPowData` (line 2977). Since this migration operates on live database entries, using `.unwrap()` risks a panic on encountering corrupted or unexpected data. Please replace `.unwrap()` with robust error handling (e.g., using the `?` operator or a `match` block) to ensure the migration fails gracefully if deserialization issues occur.

- **Transaction Management & Performance:**  
  The migration opens new write transactions repeatedly in the loop that rebuilds the seed height index. Review whether this pattern could introduce performance or consistency issues when processing a large number of entries, and consider batching these operations if feasible. Also, ensure that the migration tests are updated to validate the correctness and efficiency of these changes.

Please address these points to ensure a robust and efficient migration.

</blockquote></details>

</blockquote></details>

<details>
<summary>🧹 Nitpick comments (2)</summary><blockquote>

<details>
<summary>base_layer/core/src/chain_storage/lmdb_db/lmdb_db.rs (1)</summary><blockquote>

`1423-1429`: **Improved Shard Key Computation Readability**

The reformatting of the shard key computation in `insert_validator_node` now separates the subtraction and multiplication steps clearly. This helps in visually understanding the intent of the calculation. To remove any ambiguity in the operator precedence, consider wrapping the subtraction result in parentheses, for example:

```rust
let prev_shard_key = store.get_shard_key(
    (current_epoch.as_u64().saturating_sub(constants.validator_node_validity_period_epochs().as_u64()))
        * constants.epoch_length(),
    current_epoch.as_u64() * constants.epoch_length(),
    vn_reg.public_key(),
)?;
```

This small change can further guarantee that the intended order of operations is explicit.

</blockquote></details>
<details>
<summary>base_layer/core/src/chain_storage/lmdb_db/lmdb.rs (1)</summary><blockquote>

`424-447`: **New function `lmdb_all` to retrieve all key-value pairs.**

This new function follows the same patterns as other functions in the file and properly implements:
- Error handling with appropriate logging
- Cursor-based iteration to efficiently traverse the database
- Clean collection of key-value pairs into a result vector

One small improvement would be to add a function documentation comment to describe its purpose, similar to other functions in this file.

```diff
+/// Retrieves all key-value pairs stored in the database
 pub fn lmdb_all<V>(txn: &ConstTransaction<'_>, db: &Database) -> Result<Vec<(Vec<u8>, V)>, ChainStorageError>
 where
     V: DeserializeOwned,
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

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

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between df406c6751c9524cbc577fdc807ac8eb9235d3f9 and 3cdbb4ffc44b0e7271101420ab7116ba61e86562.

</details>

<details>
<summary>📒 Files selected for processing (2)</summary>

* `base_layer/core/src/chain_storage/lmdb_db/lmdb.rs` (4 hunks)
* `base_layer/core/src/chain_storage/lmdb_db/lmdb_db.rs` (7 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🧬 Code Definitions (1)</summary>

<details>
<summary>base_layer/core/src/chain_storage/lmdb_db/lmdb_db.rs (1)</summary><blockquote>

<details>
<summary>base_layer/core/src/chain_storage/lmdb_db/lmdb.rs (10)</summary>

* `fetch_db_entry_sizes` (451-463)
* `lmdb_all` (424-447)
* `lmdb_delete` (163-176)
* `lmdb_delete_each_where` (466-501)
* `lmdb_get` (227-250)
* `lmdb_insert` (49-97)
* `deserialize` (215-215)
* `deserialize` (384-384)
* `deserialize` (416-416)
* `deserialize` (444-444)

</details>

</blockquote></details>

</details>

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (5)</summary>

* GitHub Check: test (testnet, esmeralda)
* GitHub Check: test (nextnet, nextnet)
* GitHub Check: cargo check with stable
* GitHub Check: Cucumber tests / Base Layer
* GitHub Check: Cucumber tests / FFI

</details>

<details>
<summary>🔇 Additional comments (3)</summary><blockquote>

<details>
<summary>base_layer/core/src/chain_storage/lmdb_db/lmdb.rs (3)</summary>

`30-30`: **Updated imports to support the new `lmdb_all` function.**

The import list now explicitly includes `ConstTransaction`, `Cursor`, `CursorIter`, `Database`, `Error`, `MaybeOwned`, and `WriteTransaction` from `lmdb_zero`. This properly supports both existing code and the newly added function.

---

`283-286`: **Improved code readability with better formatting.**

The `where` clause has been reformatted to span multiple lines, which improves readability and follows better Rust formatting practices.

---

`307-309`: **Improved code readability with better formatting.**

Similar to the previous change, the `where` clause has been reformatted for better readability and consistency with Rust style guidelines.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-02T19:46:54Z
user: login: SWvheerden
body: This looks much better, 
we shoudl change the migration one up thou
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-02T19:55:29Z

Review comments:
diff_hunk: @@ -3026,27 +2977,34 @@ fn run_migrations(db: &LMDBDatabase) -> Result<(), ChainStorageError> {
             // lets create the monero seed height indexes
             {
                 let txn = db.write_transaction()?;
-                let heights: Vec<u64> = lmdb_filter_map_values(&txn, &db.monero_seed_height_db, Some)?;
+                let heights: Vec<(Vec<u8>, u64)> = lmdb_all(&txn, &db.monero_seed_height_db)?;
                 let mut seeds = Vec::new();
-                for height in &heights {
-                    let header = lmdb_get::<_, BlockHeader>(&txn, &db.headers_db, height)?.ok_or_else(|| {
-                        ChainStorageError::ValueNotFound {
-                            entity: "Header",
-                            field: "height",
-                            value: height.to_string(),
+                let mut final_table = heights.clone();
+                for (db_seed, height) in &heights {
+                    let mut delete = false;
+                    if let Some(header) = lmdb_get::<_, BlockHeader>(&txn, &db.headers_db, height)? {
+                        let pow_bytes = header.pow.pow_data.to_vec();
+                        let pow_data = MoneroPowData::deserialize(&mut pow_bytes.as_slice()).unwrap();
+                        let seed = pow_data.randomx_key.to_vec();
path: base_layer/core/src/chain_storage/lmdb_db/lmdb_db.rs
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Handle Deserialization Errors in Migration 5**

In the migration block for version 5, the code calling  
```rust
let pow_data = MoneroPowData::deserialize(&mut pow_bytes.as_slice()).unwrap();
```  
could cause a panic if deserialization fails. Instead of using `unwrap()`, please handle the error gracefully using proper error propagation (for example, with `?` and an appropriate error mapping). One possible diff might look like this:

```diff
-                        let pow_data = MoneroPowData::deserialize(&mut pow_bytes.as_slice()).unwrap();
+                        let pow_data = MoneroPowData::deserialize(&mut pow_bytes.as_slice())
+                            .map_err(|e| {
+                                error!(target: LOG_TARGET, "Failed to deserialize MoneroPowData: {:?}", e);
+                                ChainStorageError::InvalidOperation(format!("Failed to deserialize MoneroPowData: {}", e))
+                            })?;
```

This avoids a panic in a migration and provides a clear error message.

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-02T19:46:54Z
author_association: CONTRIBUTOR
diff_hunk: @@ -3026,27 +2977,39 @@ fn run_migrations(db: &LMDBDatabase) -> Result<(), ChainStorageError> {
             // lets create the monero seed height indexes
             {
                 let txn = db.write_transaction()?;
-                let heights: Vec<u64> = lmdb_filter_map_values(&txn, &db.monero_seed_height_db, Some)?;
+                let heights: Vec<(Vec<u8>, u64)> = lmdb_all(&txn, &db.monero_seed_height_db)?;
                 let mut seeds = Vec::new();
-                for height in &heights {
-                    let header = lmdb_get::<_, BlockHeader>(&txn, &db.headers_db, height)?.ok_or_else(|| {
-                        ChainStorageError::ValueNotFound {
-                            entity: "Header",
-                            field: "height",
-                            value: height.to_string(),
+                let mut final_table = heights.clone();
+                for (db_seed, height) in &heights {
+                    let mut delete = false;
+                    if let Some(header) = lmdb_get::<_, BlockHeader>(&txn, &db.headers_db, height)? {
+                        if header.pow_algo() != PowAlgorithm::RandomX {
+                            // delete
+                            delete = true;
+                        } else {
+                            let pow_bytes = header.pow.pow_data.to_vec();
+                            let pow_data = MoneroPowData::deserialize(&mut pow_bytes.as_slice()).unwrap();
+                            let seed = pow_data.randomx_key.to_vec();
+                            // seeds.push(seed);
path: base_layer/core/src/chain_storage/lmdb_db/lmdb_db.rs
user: login: SWvheerden
body: ```suggestion
```
created_at: 2025-04-02T19:53:36Z
author_association: COLLABORATOR
diff_hunk: @@ -3026,27 +2977,39 @@ fn run_migrations(db: &LMDBDatabase) -> Result<(), ChainStorageError> {
             // lets create the monero seed height indexes
             {
                 let txn = db.write_transaction()?;
-                let heights: Vec<u64> = lmdb_filter_map_values(&txn, &db.monero_seed_height_db, Some)?;
+                let heights: Vec<(Vec<u8>, u64)> = lmdb_all(&txn, &db.monero_seed_height_db)?;
                 let mut seeds = Vec::new();
path: base_layer/core/src/chain_storage/lmdb_db/lmdb_db.rs
user: login: SWvheerden
body: ```suggestion
```
created_at: 2025-04-02T19:53:47Z
author_association: COLLABORATOR

PR comments:
user: login: coderabbitai[bot]
created_at: 2025-04-02T19:42:37Z
body: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- This is an auto-generated comment: failure by coderabbit.ai -->

> [!CAUTION]
> ## Review failed
> 
> The pull request is closed.

<!-- end of auto-generated comment: failure by coderabbit.ai -->
<!-- walkthrough_start -->

## Walkthrough
This pull request introduces a new public function `lmdb_all` in the LMDB module to retrieve all key-value pairs from a database using a cursor. Additionally, the formatting of `where` clauses in existing functions (`lmdb_last` and `lmdb_exists`) is updated for better readability. Changes in another module focus on updating the `run_migrations` function to utilize `lmdb_all` for retrieving database heights, enhancing the logic for seed deletion based on the `PowAlgorithm` type.

## Changes

| File(s)                  | Change Summary |
|--------------------------|----------------|
| `base_layer/.../lmdb.rs` | • Added public function `lmdb_all` to retrieve all key-value pairs from the database using a cursor.<br>• Reformatted `where` clauses in `lmdb_last` and `lmdb_exists` for consistency and readability. |
| `base_layer/.../lmdb_db.rs` | • Added import statement for `lmdb_all`.<br>• Modified `run_migrations` to use `lmdb_all` for retrieving heights, enhancing seed deletion logic. |

## Sequence Diagram(s)
```mermaid
sequenceDiagram
    participant C as Caller
    participant L as lmdb_all
    participant T as ConstTransaction
    participant D as Database
    participant DB as LMDB Cursor

    C->>L: Call lmdb_all(txn, db)
    L->>T: Use provided transaction
    L->>D: Access the database
    L->>DB: Open cursor and iterate entries
    DB-->>L: Return each (key, value) pair
    L-->>C: Return vector of records
```

## Poem
> I'm a coding bunny on a joyful spree,  
> Hopping through keys with glee and esprit.  
> New functions bloom like carrots in a row,  
> Code now prances in a delightful flow.  
> With each tweak, my heart does independently glow! 🐇🌟

<!-- walkthrough_end -->
<!-- internal state start -->


<!-- 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= -->

<!-- internal state end -->

---

<details>
<summary>📜 Recent review details</summary>

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

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 78caab903ce880081161fddcdbc0b06ab2bded27 and 6485fc18abc637b7d1b6685d52aadc85dd8187d3.

</details>

<details>
<summary>📒 Files selected for processing (1)</summary>

* `base_layer/core/src/chain_storage/lmdb_db/lmdb_db.rs` (3 hunks)

</details>

</details>
<!-- tips_start -->

---



<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=tari-project/tari&utm_content=6915):

- 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](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai plan` to trigger planning for file edits and PR creation.
- `@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](https://docs.coderabbit.ai/guides/configure-coderabbit) 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](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-04-02T20:13:01Z
body: ## Test Results (CI)
    3 files    129 suites   43m 58s ⏱️
1 353 tests 1 353 ✅ 0 💤 0 ❌
4 057 runs  4 057 ✅ 0 💤 0 ❌

Results for commit 78caab90.

[test-results]:data:application/gzip;base64,H4sIANCh7WcC/2XMSw7CIBSF4a00jB1cwAJ1MwYuNCG2xfAYNe5dJK22cfh/JzkrGf3kErl1/NKRVHxuQdlQ05aosw9LBSa4qlLX3Hbe8z3vqSD+2cM/q8EXRu2nE7gYQ9wkluXzeoVebrWfnuj32fpw2fr4iGGefa5BpEKtzQAcnVIAilJBR2vRGgQDQhtmrLNMktcbk4DEpQwBAAA=

:recycle: This comment has been updated with latest results.
author_association: NONE
user: login: github-actions[bot]
created_at: 2025-04-02T20:21:38Z
body: ## Test Results (Integration tests)
36 tests   36 ✅  16m 29s ⏱️
11 suites   0 💤
 2 files     0 ❌

Results for commit 78caab90.

[test-results]:data:application/gzip;base64,H4sIAPyi7WcC/12MwQ7CIBAFf6Xh7GGhCQV/xsACyca2GAon03+XotbW25t5yTxZoNEv7NqJS8eWQrkB55VcSSZTnCtrpauoX97eXn7hthTEP3OnRzWwi2BoPAmfUkwfk8q8F7d9Cr7Fr9f4kGt8rGGcJsoV2KDQGKuhR68UgOJc8uAcOotgQRorrPNODGx9AT7XIhUCAQAA

:recycle: This comment has been updated with latest results.
author_association: NONE