Skip to content

Conversation

@jgmontoya
Copy link
Contributor

@jgmontoya jgmontoya commented Oct 5, 2025

Adds ensure_all_subscriptions to be called from a background periodic task in an attempt to keep the connections/subscriptions alive and functioning.

It also fixes docstrings/comment and changes renames the fetch_relay_status method to get_account_relay_statuses to be more explicit.

Summary by CodeRabbit

  • New Features

    • Holistic subscription management: ensure per-account, global, and all-subscriptions refresh paths with operational checks.
    • Account-focused relay status retrieval and connectivity detection.
    • Utilities to count subscriptions and detect any connected relays for monitoring/UI.
  • Tests

    • Expanded test suite covering subscription counts, global checks, relay connectivity scenarios, group relay extraction, and refresh/idempotency behavior.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 5, 2025

Walkthrough

Adds crate-visible subscription-count and relay-connectivity helpers to NostrManager; widens visibility for pubkey-hash and group-relay extraction; replaces a relay-status API with an account-scoped variant; exposes ensure/is methods for subscription orchestration; and adds unit tests covering these behaviors.

Changes

Cohort / File(s) Summary
NostrManager helpers & tests
src/nostr_manager/mod.rs
Adds async helpers count_subscriptions_for_account(&PublicKey) -> usize, count_global_subscriptions() -> usize, and has_any_relay_connected(&[RelayUrl]) -> bool; adds subscription_monitoring_tests.
Subscription hashing visibility
src/nostr_manager/subscriptions.rs
Changes create_pubkey_hash(&PublicKey) -> String visibility from private to pub(crate).
Whitenoise account relay extraction & tests
src/whitenoise/accounts.rs
Makes extract_groups_relays_and_ids pub(crate) async and adds tests for no-groups and with-groups scenarios.
Whitenoise subscription orchestration
src/whitenoise/mod.rs
Changes setup_global_users_subscriptions/compute_global_since_timestamp to take &Whitenoise; adds public methods ensure_account_subscriptions, ensure_global_subscriptions, ensure_all_subscriptions, is_account_subscriptions_operational, is_global_subscriptions_operational; adjusts control flow and adds tests for operational checks and refresh behavior.
Relays API replacement
src/whitenoise/relays.rs
Replaces fetch_relay_status with get_account_relay_statuses(&Account) -> Result<Vec<(RelayUrl, RelayStatus)>>; aggregates relay URLs from the account, deduplicates, and returns statuses (defaults to Disconnected on error).

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Caller
  participant Whitenoise
  participant NostrManager
  participant RelayClientPool as RelayPool

  rect rgba(230,245,255,0.5)
  note right of Caller: ensure_account_subscriptions(account)
  Caller->>Whitenoise: ensure_account_subscriptions(account)
  Whitenoise->>Whitenoise: is_account_subscriptions_operational(account)
  Whitenoise->>NostrManager: count_subscriptions_for_account(pubkey)
  Whitenoise->>Whitenoise: get_account_relay_statuses(account)  %% calls into relay helper
  Whitenoise->>RelayPool: has_any_relay_connected(relays)
  alt Not operational
    Whitenoise->>Whitenoise: refresh per-account subscriptions
    Whitenoise-->>Caller: Ok(()) / Err
  else Operational
    Whitenoise-->>Caller: Ok(())
  end
  end
Loading
sequenceDiagram
  autonumber
  participant Caller
  participant Whitenoise
  participant NostrManager
  participant RelayClientPool as RelayPool

  rect rgba(240,255,240,0.5)
  note right of Caller: ensure_global_subscriptions()
  Caller->>Whitenoise: ensure_global_subscriptions()
  Whitenoise->>Whitenoise: is_global_subscriptions_operational()
  Whitenoise->>NostrManager: count_global_subscriptions()
  Whitenoise->>RelayPool: has_any_relay_connected(all_relays)
  alt Not operational
    Whitenoise->>Whitenoise: setup_global_users_subscriptions()
    Whitenoise-->>Caller: Ok(()) / Err
  else Operational
    Whitenoise-->>Caller: Ok(())
  end
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • delcin-raj
  • erskingardner

Poem

I hop through code and count each thread with care,
I sniff each relay's whisper floating in the air.
When subscriptions slumber, I polish every link,
A rabbit patch of logic — quick as lightning's blink. 🐇✨

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title "Feat/subscription monitoring" succinctly captures the primary change introduced by this PR, which is the addition of subscription monitoring functionality through new helper methods and background task support. It clearly reflects the main feature without unnecessary detail, allowing reviewers to understand the intent at a glance. Although the slash is slightly unconventional, the wording remains concise and focused on the core enhancement.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/subscription-monitoring

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

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: 1

🧹 Nitpick comments (1)
src/whitenoise/relays.rs (1)

129-158: Consider stabilizing relay status ordering.

Iterating a HashSet produces a non-deterministic order, so callers may see the relay list jump around between invocations. Sorting the deduplicated URLs before querying the client keeps the output stable for logs/UI.

-        for relay_url in unique_relay_urls {
+        let mut relay_urls: Vec<RelayUrl> = unique_relay_urls.into_iter().collect();
+        relay_urls.sort_by(|a, b| a.as_str().cmp(b.as_str()));
+
+        for relay_url in relay_urls {
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d849ca9 and 4b0ee1d.

📒 Files selected for processing (5)
  • src/nostr_manager/mod.rs (1 hunks)
  • src/nostr_manager/subscriptions.rs (1 hunks)
  • src/whitenoise/accounts.rs (2 hunks)
  • src/whitenoise/mod.rs (4 hunks)
  • src/whitenoise/relays.rs (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rs

📄 CodeRabbit inference engine (.cursor/rules/rust-code-style.mdc)

**/*.rs: Write all trait bounds in a where clause for functions and impl blocks
Prefer using Self when referring to the implementing type (including return types and enum variant matches)
For public types in libraries, derive Debug, Clone, Copy, PartialEq, Eq, and Hash (in this order) when possible
Derive Default only when a reasonable default value exists
Use full paths for tracing macros (tracing::!(...)) instead of importing macro names
Prefer .to_string() or .to_owned() for &str -> String conversion over .into() or String::from
Order imports: (1) core/alloc/std, (2) external crates, (3) current sub-modules (mod x;), (4) internal crate modules (use crate::, super::, self::)
When implementing fmt traits, import core::fmt and use fmt::Display, fmt::Formatter, etc.
After declaring mod x;, import sub-items via self::x::Y instead of x::Y
Avoid if let ... { } else { } in favor of match when both branches do work
Use if let ... { } when the alternative match arm is intentionally empty
Avoid inline sub-modules (mod x { ... }); prefer a separate file x.rs with mod x; (except tests and benches)
For tests, prefer inline #[cfg(test)] mod tests { ... } and avoid mod tests;
For benches, prefer inline #[cfg(bench)] mod benches { ... } and avoid mod benches;

Files:

  • src/nostr_manager/subscriptions.rs
  • src/nostr_manager/mod.rs
  • src/whitenoise/mod.rs
  • src/whitenoise/accounts.rs
  • src/whitenoise/relays.rs
🧠 Learnings (2)
📚 Learning: 2025-09-14T19:36:21.619Z
Learnt from: jgmontoya
PR: parres-hq/whitenoise#365
File: src/whitenoise/accounts.rs:296-298
Timestamp: 2025-09-14T19:36:21.619Z
Learning: Repository parres-hq/whitenoise: For the process_user_event_streams method in src/whitenoise/accounts.rs, the team prefers to address the concurrent stream processing improvement (processing streams concurrently with per-event error logging instead of sequential processing with early returns) in a separate PR, as requested by jgmontoya in PR #365.

Applied to files:

  • src/whitenoise/mod.rs
📚 Learning: 2025-08-11T10:23:59.406Z
Learnt from: jgmontoya
PR: parres-hq/whitenoise#310
File: src/whitenoise/accounts/relays.rs:260-278
Timestamp: 2025-08-11T10:23:59.406Z
Learning: In `src/whitenoise/accounts/relays.rs`, the `publish_relay_list_for_account` method is only called after the account setup is complete, which guarantees that `account.nip65_relays` is already populated and will never be empty. Therefore, no fallback logic is needed when `target_relays` is None.

Applied to files:

  • src/whitenoise/relays.rs
🧬 Code graph analysis (2)
src/whitenoise/mod.rs (1)
src/whitenoise/accounts.rs (3)
  • account (1032-1038)
  • account (1130-1136)
  • inbox_relays (116-120)
src/whitenoise/accounts.rs (3)
src/whitenoise/mod.rs (2)
  • create_mock_whitenoise (580-639)
  • new (56-70)
src/whitenoise/groups.rs (1)
  • relays (201-201)
src/whitenoise/database/users.rs (1)
  • relays (166-198)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Test (ubuntu-latest)
  • GitHub Check: Test (macos-latest)
  • GitHub Check: Test (macos-14)
  • GitHub Check: Security Audit
🔇 Additional comments (10)
src/nostr_manager/subscriptions.rs (1)

19-25: Helper visibility change looks good.

Making create_pubkey_hash crate-visible lets the monitoring helpers reuse it without expanding the public API surface.

src/whitenoise/accounts.rs (2)

770-788: Sharing group relay extractor is appropriate.

Opening this helper to the crate keeps the monitoring paths from reimplementing MLS group plumbing while still limiting visibility to internal callers.


1413-1486: Great to see coverage for both group/no-group cases.

These tests lock in the expected outputs and give confidence while the extractor is reused by the monitoring flows.

src/nostr_manager/mod.rs (2)

427-447: Global subscription and relay health helpers look solid.

The lightweight filters over the client state keep these checks cheap while reusing existing primitives.


451-519: Thanks for the smoke tests around the new helpers.

Even the empty-path assertions provide useful guardrails against regressions in future nostr-sdk updates.

src/whitenoise/mod.rs (5)

235-265: Adjusting the global subscription helper signature makes it reusable.

Dropping the 'static requirement lets the ensure path share the same setup logic without spinning up duplicate plumbing.


444-471: Ensure helpers are a good safety net.

The early exit prevents unnecessary teardown, and falling back to the existing refresh/setup flows keeps behaviour consistent for callers.


473-513: Per-account operational check balances presence and connectivity well.

Combining the subscription-count gate with a deduped relay set makes the predicate deterministic once the counting helper matches by prefix.


515-528: Global health check reads clean.

Reusing the client’s relay inventory with the shared connectivity helper keeps the logic compact and composable.


966-1138: The new subscription monitoring tests add valuable coverage.

Exercising healthy, broken, and idempotent paths ensures the ensure/is helpers stay honest over time.

@jgmontoya jgmontoya force-pushed the feat/subscription-monitoring branch from 4b0ee1d to 72b8742 Compare October 6, 2025 01:22
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

♻️ Duplicate comments (1)
src/nostr_manager/mod.rs (1)

417-424: Filter by prefix, not substring, for pubkey-scoped subscriptions.

Reusing contains means any unrelated ID embedding the 12-char hash fragment (e.g. global_users_<hash>_...) keeps the account marked healthy, so we skip refreshes despite missing subscriptions. Match on the canonical prefix {hash}_ we emit when creating IDs to avoid these false positives.

Apply this diff:

-            .filter(|id| id.as_str().contains(&hash))
+            .filter(|id| id.as_str().starts_with(&format!("{}_", hash)))
🧹 Nitpick comments (1)
src/nostr_manager/mod.rs (1)

428-433: Use starts_with for global subscription IDs as well.

All global IDs are emitted as global_users_<suffix>, so prefix matching is tighter while keeping behavior identical.

Apply this diff:

-            .filter(|id| id.as_str().contains("global_users_"))
+            .filter(|id| id.as_str().starts_with("global_users_"))
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4b0ee1d and 72b8742.

📒 Files selected for processing (5)
  • src/nostr_manager/mod.rs (1 hunks)
  • src/nostr_manager/subscriptions.rs (1 hunks)
  • src/whitenoise/accounts.rs (2 hunks)
  • src/whitenoise/mod.rs (4 hunks)
  • src/whitenoise/relays.rs (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/nostr_manager/subscriptions.rs
  • src/whitenoise/relays.rs
  • src/whitenoise/accounts.rs
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rs

📄 CodeRabbit inference engine (.cursor/rules/rust-code-style.mdc)

**/*.rs: Write all trait bounds in a where clause for functions and impl blocks
Prefer using Self when referring to the implementing type (including return types and enum variant matches)
For public types in libraries, derive Debug, Clone, Copy, PartialEq, Eq, and Hash (in this order) when possible
Derive Default only when a reasonable default value exists
Use full paths for tracing macros (tracing::!(...)) instead of importing macro names
Prefer .to_string() or .to_owned() for &str -> String conversion over .into() or String::from
Order imports: (1) core/alloc/std, (2) external crates, (3) current sub-modules (mod x;), (4) internal crate modules (use crate::, super::, self::)
When implementing fmt traits, import core::fmt and use fmt::Display, fmt::Formatter, etc.
After declaring mod x;, import sub-items via self::x::Y instead of x::Y
Avoid if let ... { } else { } in favor of match when both branches do work
Use if let ... { } when the alternative match arm is intentionally empty
Avoid inline sub-modules (mod x { ... }); prefer a separate file x.rs with mod x; (except tests and benches)
For tests, prefer inline #[cfg(test)] mod tests { ... } and avoid mod tests;
For benches, prefer inline #[cfg(bench)] mod benches { ... } and avoid mod benches;

Files:

  • src/nostr_manager/mod.rs
  • src/whitenoise/mod.rs
🧠 Learnings (1)
📚 Learning: 2025-09-04T03:38:54.953Z
Learnt from: jgmontoya
PR: parres-hq/whitenoise#340
File: src/nostr_manager/subscriptions.rs:72-89
Timestamp: 2025-09-04T03:38:54.953Z
Learning: In the global subscription batching system for Whitenoise (src/nostr_manager/subscriptions.rs), the current hash(pubkey) % batch_count approach causes batch ID reshuffling when user count changes, potentially leading to stale/duplicate author lists since only the triggering user's batch is refreshed. This is a known limitation documented in PR #340 that the team is aware of and will address in future iterations.

Applied to files:

  • src/nostr_manager/mod.rs
🧬 Code graph analysis (2)
src/nostr_manager/mod.rs (1)
src/whitenoise/event_tracker.rs (2)
  • new (127-129)
  • new (246-248)
src/whitenoise/mod.rs (2)
src/whitenoise/accounts.rs (3)
  • account (1033-1039)
  • account (1131-1137)
  • inbox_relays (116-120)
src/nostr_manager/subscriptions.rs (1)
  • user_relays (338-343)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Test (macos-14)
  • GitHub Check: Test (ubuntu-latest)
  • GitHub Check: Security Audit

@jgmontoya jgmontoya force-pushed the feat/subscription-monitoring branch from 72b8742 to ac3f240 Compare October 6, 2025 14:18
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: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ac3f240 and c1c6967.

📒 Files selected for processing (1)
  • src/whitenoise/mod.rs (4 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rs

📄 CodeRabbit inference engine (.cursor/rules/rust-code-style.mdc)

**/*.rs: Write all trait bounds in a where clause for functions and impl blocks
Prefer using Self when referring to the implementing type (including return types and enum variant matches)
For public types in libraries, derive Debug, Clone, Copy, PartialEq, Eq, and Hash (in this order) when possible
Derive Default only when a reasonable default value exists
Use full paths for tracing macros (tracing::!(...)) instead of importing macro names
Prefer .to_string() or .to_owned() for &str -> String conversion over .into() or String::from
Order imports: (1) core/alloc/std, (2) external crates, (3) current sub-modules (mod x;), (4) internal crate modules (use crate::, super::, self::)
When implementing fmt traits, import core::fmt and use fmt::Display, fmt::Formatter, etc.
After declaring mod x;, import sub-items via self::x::Y instead of x::Y
Avoid if let ... { } else { } in favor of match when both branches do work
Use if let ... { } when the alternative match arm is intentionally empty
Avoid inline sub-modules (mod x { ... }); prefer a separate file x.rs with mod x; (except tests and benches)
For tests, prefer inline #[cfg(test)] mod tests { ... } and avoid mod tests;
For benches, prefer inline #[cfg(bench)] mod benches { ... } and avoid mod benches;

Files:

  • src/whitenoise/mod.rs
🧬 Code graph analysis (1)
src/whitenoise/mod.rs (2)
src/whitenoise/accounts.rs (3)
  • account (1033-1039)
  • account (1131-1137)
  • inbox_relays (116-120)
src/whitenoise/database/accounts.rs (1)
  • all (93-103)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Test (ubuntu-latest)
  • GitHub Check: Test (macos-latest)
  • GitHub Check: Security Audit

@jgmontoya jgmontoya merged commit ae54a8d into master Oct 7, 2025
9 checks passed
@jgmontoya jgmontoya deleted the feat/subscription-monitoring branch October 7, 2025 13:37
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