Repository: commonwarexyz/monorepo
Pull request #572: Nits from Consensus Example
Author: login: patrick-ogrady
Created: 2025-03-08T20:55:49Z | Merged: 2025-03-08T21:56:26Z



Diff:
diff --git a/consensus/src/simplex/actors/voter/actor.rs b/consensus/src/simplex/actors/voter/actor.rs
index 2ff226ad34..a63e68f2ba 100644
--- a/consensus/src/simplex/actors/voter/actor.rs
+++ b/consensus/src/simplex/actors/voter/actor.rs
@@ -2176,13 +2176,26 @@ impl<
         let mut shutdown = self.context.stopped();
 
         // Process messages
+        let mut pending_set = None;
         let mut pending_propose_context = None;
         let mut pending_propose = None;
         let mut pending_verify_context = None;
         let mut pending_verify = None;
         loop {
+            // Reset pending set if we have moved to a new view
+            if let Some(view) = pending_set {
+                if view != self.view {
+                    pending_set = None;
+                    pending_propose_context = None;
+                    pending_propose = None;
+                    pending_verify_context = None;
+                    pending_verify = None;
+                }
+            }
+
             // Attempt to propose a container
             if let Some((context, new_propose)) = self.propose(&mut backfiller).await {
+                pending_set = Some(self.view);
                 pending_propose_context = Some(context);
                 pending_propose = Some(new_propose);
             }
@@ -2193,6 +2206,7 @@ impl<
 
             // Attempt to verify current view
             if let Some((context, new_verify)) = self.peer_proposal().await {
+                pending_set = Some(self.view);
                 pending_verify_context = Some(context);
                 pending_verify = Some(new_verify);
             }
diff --git a/consensus/src/threshold_simplex/actors/voter/actor.rs b/consensus/src/threshold_simplex/actors/voter/actor.rs
index 11c9aa7467..aad5aaac5f 100644
--- a/consensus/src/threshold_simplex/actors/voter/actor.rs
+++ b/consensus/src/threshold_simplex/actors/voter/actor.rs
@@ -2332,13 +2332,26 @@ impl<
         let mut shutdown = self.context.stopped();
 
         // Process messages
+        let mut pending_set = None;
         let mut pending_propose_context = None;
         let mut pending_propose = None;
         let mut pending_verify_context = None;
         let mut pending_verify = None;
         loop {
+            // Reset pending set if we have moved to a new view
+            if let Some(view) = pending_set {
+                if view != self.view {
+                    pending_set = None;
+                    pending_propose_context = None;
+                    pending_propose = None;
+                    pending_verify_context = None;
+                    pending_verify = None;
+                }
+            }
+
             // Attempt to propose a container
             if let Some((context, new_propose)) = self.propose(&mut backfiller).await {
+                pending_set = Some(self.view);
                 pending_propose_context = Some(context);
                 pending_propose = Some(new_propose);
             }
@@ -2349,6 +2362,7 @@ impl<
 
             // Attempt to verify current view
             if let Some((context, new_verify)) = self.peer_proposal().await {
+                pending_set = Some(self.view);
                 pending_verify_context = Some(context);
                 pending_verify = Some(new_verify);
             }
diff --git a/resolver/src/p2p/engine.rs b/resolver/src/p2p/engine.rs
index e16d7a4a82..2c03760572 100644
--- a/resolver/src/p2p/engine.rs
+++ b/resolver/src/p2p/engine.rs
@@ -31,7 +31,7 @@ use governor::clock::Clock as GClock;
 use prost::Message as _;
 use rand::Rng;
 use std::{collections::HashMap, marker::PhantomData};
-use tracing::{debug, error, warn};
+use tracing::{debug, error, trace, warn};
 
 /// Represents a pending serve operation.
 struct Serve<E: Clock, P: Array> {
@@ -202,11 +202,11 @@ impl<
                     };
                     match msg {
                         Message::Fetch { key } => {
-                            debug!(?key, "mailbox: fetch");
+                            trace!(?key, "mailbox: fetch");
 
                             // Check if the fetch is already in progress
                             if self.fetch_timers.contains_key(&key) {
-                                warn!(?key, "duplicate fetch");
+                                trace!(?key, "duplicate fetch");
                                 self.metrics.fetch.inc(Status::Dropped);
                                 continue;
                             }
@@ -216,7 +216,7 @@ impl<
                             self.fetcher.fetch(&mut sender, key, true).await;
                         }
                         Message::Cancel { key } => {
-                            debug!(?key, "mailbox: cancel");
+                            trace!(?key, "mailbox: cancel");
                             let mut guard = self.metrics.cancel.guard(Status::Dropped);
                             if self.fetcher.cancel(&key) {
                                 guard.set(Status::Success);
@@ -237,7 +237,7 @@ impl<
                             self.metrics.serve.inc(Status::Success);
                         }
                         Err(err) => {
-                            warn!(?err, ?peer, ?id, "serve failed");
+                            debug!(?err, ?peer, ?id, "serve failed");
                             timer.cancel();
                             self.metrics.serve.inc(Status::Failure);
                         }
@@ -259,7 +259,7 @@ impl<
                     let msg = match wire::PeerMsg::decode(msg) {
                         Ok(msg) => msg,
                         Err(err) => {
-                            warn!(?err, ?peer, "decode failed");
+                            trace!(?err, ?peer, "decode failed");
                             continue;
                         }
                     };
@@ -316,7 +316,7 @@ impl<
         match result {
             Err(err) => error!(?err, ?peer, ?id, "serve send failed"),
             Ok(to) if to.is_empty() => warn!(?peer, ?id, "serve send failed"),
-            Ok(_) => debug!(?peer, ?id, "serve sent"),
+            Ok(_) => trace!(?peer, ?id, "serve sent"),
         };
     }
 
@@ -324,13 +324,13 @@ impl<
     async fn handle_network_request(&mut self, peer: P, id: u64, request: Bytes) {
         // Parse request
         let Ok(key) = Key::try_from(request.to_vec()) else {
-            warn!(?peer, ?id, "peer invalid request");
+            trace!(?peer, ?id, "peer invalid request");
             self.metrics.serve.inc(Status::Invalid);
             return;
         };
 
         // Serve the request
-        debug!(?peer, ?id, "peer request");
+        trace!(?peer, ?id, "peer request");
         let mut producer = self.producer.clone();
         let timer = self.metrics.serve_duration.timer();
         self.serves.push(async move {
@@ -353,7 +353,7 @@ impl<
         id: u64,
         response: Bytes,
     ) {
-        debug!(?peer, ?id, "peer response: data");
+        trace!(?peer, ?id, "peer response: data");
 
         // Get the key associated with the response, if any
         let Some(key) = self.fetcher.pop_by_id(id, &peer, true) else {
@@ -377,7 +377,7 @@ impl<
 
     /// Handle a network response from a peer that did not have the data.
     async fn handle_network_response_empty(&mut self, sender: &mut NetS, peer: P, id: u64) {
-        warn!(?peer, ?id, "peer response: error");
+        trace!(?peer, ?id, "peer response: empty");
 
         // Get the key associated with the response, if any
         let Some(key) = self.fetcher.pop_by_id(id, &peer, false) else {
diff --git a/storage/src/archive/mod.rs b/storage/src/archive/mod.rs
index d02acb6e16..f3e2de7774 100644
--- a/storage/src/archive/mod.rs
+++ b/storage/src/archive/mod.rs
@@ -162,8 +162,6 @@ pub enum Error {
     Journal(#[from] crate::journal::Error),
     #[error("record corrupted")]
     RecordCorrupted,
-    #[error("duplicate index")]
-    DuplicateIndex,
     #[error("already pruned to: {0}")]
     AlreadyPrunedTo(u64),
     #[error("record too large")]
@@ -537,8 +535,10 @@ mod tests {
                 .expect("Failed to put data");
 
             // Put the key-data pair again
-            let result = archive.put(index, key.clone(), data2.clone()).await;
-            assert!(matches!(result, Err(Error::DuplicateIndex)));
+            archive
+                .put(index, key.clone(), data2.clone())
+                .await
+                .expect("Duplicate put should not fail");
 
             // Get the data back
             let retrieved = archive
diff --git a/storage/src/archive/storage.rs b/storage/src/archive/storage.rs
index a9386168e1..48a434080a 100644
--- a/storage/src/archive/storage.rs
+++ b/storage/src/archive/storage.rs
@@ -259,7 +259,7 @@ impl<T: Translator, K: Array, B: Blob, E: Storage<B> + Metrics> Archive<T, K, B,
 
     /// Store an item in `Archive`. Both indices and keys are assumed to both be globally unique.
     ///
-    /// If the index already exists, an error is returned. If the same key is stored multiple times
+    /// If the index already exists, put does nothing and returns. If the same key is stored multiple times
     /// at different indices (not recommended), any value associated with the key may be returned.
     pub async fn put(&mut self, index: u64, key: K, data: Bytes) -> Result<(), Error> {
         // Check last pruned
@@ -270,7 +270,7 @@ impl<T: Translator, K: Array, B: Blob, E: Storage<B> + Metrics> Archive<T, K, B,
 
         // Check for existing index
         if self.indices.contains_key(&index) {
-            return Err(Error::DuplicateIndex);
+            return Ok(());
         }
 
         // If compression is enabled, compress the data before storing it.


Reviews:
user: login: patrick-ogrady
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-08T20:56:05Z
user: login: patrick-ogrady
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-08T20:56:35Z
user: login: patrick-ogrady
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-08T20:57:20Z
user: login: copilot-pull-request-reviewer[bot]
body: ## PR Overview

This PR "Nits from Consensus Example" addresses minor stylistic and behavioral nits across several modules. The key changes include:
- Adding a mechanism to reset pending proposal and verification state when the view changes in both simplex and threshold_simplex voter actors.
- Adjusting logging levels in the P2P engine to use "trace" for lower-severity events.
- Changing the archive storage behavior so that attempting to put an already indexed record no longer results in an error.

### Reviewed Changes

| File                                                         | Description                                                                                         |
| ------------------------------------------------------------ | --------------------------------------------------------------------------------------------------- |
| consensus/src/threshold_simplex/actors/voter/actor.rs       | Introduces the pending_set variable to reset pending states when the view changes.                  |
| consensus/src/simplex/actors/voter/actor.rs                   | Mirrors the changes from the threshold_simplex actor for consistency in handling pending states.    |
| storage/src/archive/mod.rs                                    | Updates the duplicate index handling by removing the DuplicateIndex error and returning Ok instead. |
| resolver/src/p2p/engine.rs                                    | Modifies several log statements to use trace level instead of debug/warn for finer-grained logging.  |
| storage/src/archive/storage.rs                                | Updates documentation and behavior for duplicate key handling in the put method.                    |

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



state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-08T21:12:28Z
user: login: patrick-ogrady
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-08T21:12:35Z
user: login: BrendanChou
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-03-08T21:44:45Z

Review comments:
diff_hunk: @@ -2176,13 +2176,26 @@ impl<
         let mut shutdown = self.context.stopped();
 
         // Process messages
+        let mut pending_set = None;
         let mut pending_propose_context = None;
         let mut pending_propose = None;
         let mut pending_verify_context = None;
         let mut pending_verify = None;
         loop {
+            // Reset pending set if we have moved to a new view
+            if let Some(view) = pending_set {
path: consensus/src/simplex/actors/voter/actor.rs
user: login: patrick-ogrady
body: This ensures the oneshots actually get cancelled
created_at: 2025-03-08T20:56:05Z
author_association: CONTRIBUTOR
path: resolver/src/p2p/engine.rs
user: login: patrick-ogrady
body: Most of the debug logs should be trace logs in the `resolver` (per request messages are way to loud)
created_at: 2025-03-08T20:56:35Z
author_association: CONTRIBUTOR
diff_hunk: @@ -270,7 +270,7 @@ impl<T: Translator, K: Array, B: Blob, E: Storage<B> + Metrics> Archive<T, K, B,
 
         // Check for existing index
         if self.indices.contains_key(&index) {
-            return Err(Error::DuplicateIndex);
+            return Ok(());
path: storage/src/archive/storage.rs
user: login: patrick-ogrady
body: It is very common to get data from multiple places (and to insert it). This behavior makes the DX cleaner (somewhat akin to calling `fetch`/`cancel` multiple times in `resolver`).

This may hide some error if you try to put multiple digests at the same index but 🤷 
created_at: 2025-03-08T20:57:20Z
author_association: CONTRIBUTOR
diff_hunk: @@ -270,7 +270,7 @@ impl<T: Translator, K: Array, B: Blob, E: Storage<B> + Metrics> Archive<T, K, B,
 
         // Check for existing index
         if self.indices.contains_key(&index) {
-            return Err(Error::DuplicateIndex);
+            return Ok(());
path: storage/src/archive/storage.rs
user: login: patrick-ogrady
body: I suppose I could alternatively revert this and add a `put_duplicate` (or something) function that returns all errors other than this one.
created_at: 2025-03-08T21:12:35Z
author_association: CONTRIBUTOR

PR comments:
user: login: codecov[bot]
created_at: 2025-03-08T21:56:47Z
body: ## [Codecov](https://app.codecov.io/gh/commonwarexyz/monorepo/pull/572?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=commonwarexyz) Report
Attention: Patch coverage is `94.87179%` with `2 lines` in your changes missing coverage. Please review.
> Project coverage is 89.07%. Comparing base [(`09979a1`)](https://app.codecov.io/gh/commonwarexyz/monorepo/commit/09979a1dcd827a9f858468e0273743ac4705a297?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=commonwarexyz) to head [(`04c2eb8`)](https://app.codecov.io/gh/commonwarexyz/monorepo/commit/04c2eb8793f2a45bf358420203622b9ec3c99acd?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=commonwarexyz).
> Report is 1 commits behind head on main.

| [Files with missing lines](https://app.codecov.io/gh/commonwarexyz/monorepo/pull/572?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=commonwarexyz) | Patch % | Lines |
|---|---|---|
| [resolver/src/p2p/engine.rs](https://app.codecov.io/gh/commonwarexyz/monorepo/pull/572?src=pr&el=tree&filepath=resolver%2Fsrc%2Fp2p%2Fengine.rs&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=commonwarexyz#diff-cmVzb2x2ZXIvc3JjL3AycC9lbmdpbmUucnM=) | 80.00% | [2 Missing :warning: ](https://app.codecov.io/gh/commonwarexyz/monorepo/pull/572?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=commonwarexyz) |

```diff
@@            Coverage Diff             @@
##             main     #572      +/-   ##
==========================================
+ Coverage   89.06%   89.07%   +0.01%     
==========================================
  Files         132      132              
  Lines       34109    34137      +28     
==========================================
+ Hits        30378    30408      +30     
+ Misses       3731     3729       -2     
```

| [Files with missing lines](https://app.codecov.io/gh/commonwarexyz/monorepo/pull/572?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=commonwarexyz) | Coverage Δ | |
|---|---|---|
| [consensus/src/simplex/actors/voter/actor.rs](https://app.codecov.io/gh/commonwarexyz/monorepo/pull/572?src=pr&el=tree&filepath=consensus%2Fsrc%2Fsimplex%2Factors%2Fvoter%2Factor.rs&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=commonwarexyz#diff-Y29uc2Vuc3VzL3NyYy9zaW1wbGV4L2FjdG9ycy92b3Rlci9hY3Rvci5ycw==) | `93.12% <100.00%> (+0.04%)` | :arrow_up: |
| [...sensus/src/threshold\_simplex/actors/voter/actor.rs](https://app.codecov.io/gh/commonwarexyz/monorepo/pull/572?src=pr&el=tree&filepath=consensus%2Fsrc%2Fthreshold_simplex%2Factors%2Fvoter%2Factor.rs&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=commonwarexyz#diff-Y29uc2Vuc3VzL3NyYy90aHJlc2hvbGRfc2ltcGxleC9hY3RvcnMvdm90ZXIvYWN0b3IucnM=) | `91.97% <100.00%> (-0.06%)` | :arrow_down: |
| [storage/src/archive/mod.rs](https://app.codecov.io/gh/commonwarexyz/monorepo/pull/572?src=pr&el=tree&filepath=storage%2Fsrc%2Farchive%2Fmod.rs&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=commonwarexyz#diff-c3RvcmFnZS9zcmMvYXJjaGl2ZS9tb2QucnM=) | `100.00% <100.00%> (ø)` | |
| [storage/src/archive/storage.rs](https://app.codecov.io/gh/commonwarexyz/monorepo/pull/572?src=pr&el=tree&filepath=storage%2Fsrc%2Farchive%2Fstorage.rs&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=commonwarexyz#diff-c3RvcmFnZS9zcmMvYXJjaGl2ZS9zdG9yYWdlLnJz) | `95.45% <100.00%> (ø)` | |
| [resolver/src/p2p/engine.rs](https://app.codecov.io/gh/commonwarexyz/monorepo/pull/572?src=pr&el=tree&filepath=resolver%2Fsrc%2Fp2p%2Fengine.rs&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=commonwarexyz#diff-cmVzb2x2ZXIvc3JjL3AycC9lbmdpbmUucnM=) | `90.54% <80.00%> (ø)` | |

... and [4 files with indirect coverage changes](https://app.codecov.io/gh/commonwarexyz/monorepo/pull/572/indirect-changes?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=commonwarexyz)

------

[Continue to review full report in Codecov by Sentry](https://app.codecov.io/gh/commonwarexyz/monorepo/pull/572?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=commonwarexyz).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=commonwarexyz)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://app.codecov.io/gh/commonwarexyz/monorepo/pull/572?dropdown=coverage&src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=commonwarexyz). Last update [09979a1...04c2eb8](https://app.codecov.io/gh/commonwarexyz/monorepo/pull/572?dropdown=coverage&src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=commonwarexyz). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=commonwarexyz).

<details><summary>🚀 New features to boost your workflow: </summary>

- ❄ [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
</details>
author_association: NONE