Skip to content

Add choose_relay method in RelayManager#1628

Merged
benalleng merged 3 commits into
payjoin:masterfrom
benalleng:choose-relays
Jun 9, 2026
Merged

Add choose_relay method in RelayManager#1628
benalleng merged 3 commits into
payjoin:masterfrom
benalleng:choose-relays

Conversation

@benalleng

Copy link
Copy Markdown
Collaborator

Closes #1391

This could supersede #1399

Instead of having relay selection loose state and each session spawns a new relay manager this PR instead just removes the state from RelayManager istelf by using a choose_relay method which simply randomly selects a new relay per-request. Comes from this #1399 (review).

Pull Request Checklist

Please confirm the following before requesting review:

@benalleng
benalleng requested a review from xstoicunicornx June 8, 2026 20:27
@benalleng

Copy link
Copy Markdown
Collaborator Author

@va-an if this end up being the approach we go with I think merging in the fetch_ohttp_keys functions to be methods will be similarly trivial.

@coveralls

coveralls commented Jun 8, 2026

Copy link
Copy Markdown
Collaborator

Coverage Report for CI Build 27210341068

Coverage decreased (-0.2%) to 85.199%

Details

  • Coverage decreased (-0.2%) from the base build.
  • Patch coverage: 20 uncovered changes across 3 files (41 of 61 lines covered, 67.21%).
  • 18 coverage regressions across 2 files.

Uncovered Changes

File Changed Covered %
payjoin-cli/src/app/v2/mod.rs 37 24 64.86%
payjoin-cli/src/app/v2/ohttp.rs 19 13 68.42%
payjoin-cli/src/app/config.rs 5 4 80.0%

Coverage Regressions

18 previously-covered lines in 2 files lost coverage.

File Lines Losing Coverage Coverage
payjoin-cli/src/app/v2/ohttp.rs 17 34.55%
payjoin-cli/src/app/v2/mod.rs 1 50.83%

Coverage Stats

Coverage Status
Relevant Lines: 14708
Covered Lines: 12531
Line Coverage: 85.2%
Coverage Strength: 371.21 hits per line

💛 - Coveralls

@xstoicunicornx xstoicunicornx left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

utACK 4ff6a0c

Reviewed and tested the code. Strongly prefer this solution over #1399 . Couple items of feedback that I think are fine left as follow ups if preferred.

One other follow up is that we probably want to add handling for the failed relay scenario, but its not clear to me what sorts of errors should result in the relay being added to the failed relay list. This could be added via the post_request function with something similar to this (note this does not work as is... req.url needs to be converted to Url):

    async fn post_request(&self, req: payjoin::Request) -> Result<reqwest::Response> {
        let http = http_agent(&self.config)?;
        match http
            .post(req.url)
            .header("Content-Type", req.content_type)
            .body(req.body)
            .send()
            .await
            .and_then(|r| r.error_for_status())
            .context("HTTP request failed")
        {
            res @ Ok(_) => res,
            e @ Err(_) => {
                self.relay_manager
                    .lock()
                    .expect("Lock should not be poisoned")
                    .add_failed_relay(req.url);
                e
            }
        }

Comment thread payjoin-cli/src/app/v2/mod.rs Outdated
Comment thread payjoin-cli/src/app/v2/mod.rs Outdated
@benalleng
benalleng force-pushed the choose-relays branch 3 times, most recently from 4544367 to ac2fd57 Compare June 8, 2026 21:40
@benalleng

benalleng commented Jun 8, 2026

Copy link
Copy Markdown
Collaborator Author

Curious if you like the current callsite logic or if you would prefer a post_via_relay function that handles this.

async fn post_via_relay<F, T, E>(&self, mut build: F) -> Result<(reqwest::Response, T)>                                                                       
      where                                                                        
        F: FnMut(&str) -> std::result::Result<(payjoin::Request, T), E>,         
        E: Into<anyhow::Error>,                                                  
      {                                                                            
          loop {                                                                   
              let relay =                                                          
                  self.relay_manager.lock().expect("Lock should not be poisoned").choose_relay()?;                                                                  
              let (req, ctx) = build(relay.as_str()).map_err(Into::into)?;         
              match self.post_request(req).await {                                 
                Ok(resp) => return Ok((resp, ctx)),                              
                Err(e) => {                                                      
                    tracing::debug!("Request to relay {relay} failed: {e:?}");   
                    self.relay_manager                                           
                        .lock()                                                  
                        .expect("Lock should not be poisoned")                   
                        .add_failed_relay(relay);                                
                }                                                                
            }                                                                    
        }                                                                        
    }                                                                            
 }

@benalleng
benalleng requested a review from xstoicunicornx June 8, 2026 21:47
@xstoicunicornx

Copy link
Copy Markdown
Collaborator

Curious if you like the current callsite logic or if you would prefer a post_via_relay function that handles this.

I like the post_via_relay function a lot. Was actually gonna suggest something similar except as a RelayManager method but it didn't seem right. This is better.

@benalleng
benalleng force-pushed the choose-relays branch 2 times, most recently from 72dd0d4 to 4e256b8 Compare June 9, 2026 13:40

@xstoicunicornx xstoicunicornx left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

utACK be38713

Reviewed the code and ran test scripts locally, looks good. This greatly simplifies the ohttp key fetching logic and relay selection logic, while disentangling them as separate concerns. It also enforces mandatory randomness in relay selection whenever relay manager is called to provide a relay. Also love to see net negative lines :).
Image

Just one small nit below.

//!
//! `fetch_ohttp_keys` selects a relay at random from the configured list,
//! `unwrap_ohttp_keys_or_else_fetch` returns user-supplied keys when present,
//! otherwise selects a relay at random from the configured list,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
//! otherwise selects a relay at random from the configured list,
//! otherwise fetches the keys from the config directory using a
//! relay selected at random from the configured list,

Nit: this comment doesn't read clearly as is

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Going to put this in the followup with @va-an in moving this under the RelayManager

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.

All sessions in resume share the same relay instead of randomizing independently

3 participants