Skip to content

fix(gateway): refresh WireGuard handshakes off hot path - #740

Merged
kvinwang merged 2 commits into
masterfrom
fix/gateway-handshake-cache
Jun 25, 2026
Merged

kvinwang merged 2 commits into
masterfrom
fix/gateway-handshake-cache

Conversation

@kvinwang

@kvinwang kvinwang commented Jun 25, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

  • Add a reusable cached-cell crate with TtlCell<T>: a small OnceCell/LazyCell-style abstraction for TTL-bound cached snapshots.
  • TtlCell owns the generic mechanics: storage, freshness checks, stale reads, spawn_blocking refresh, and one-shot periodic refresh scheduling.
  • Keep WireGuard-specific logic in gateway: LatestHandshakesCache now only defines how to fetch/parse wg show <iface> latest-handshakes and delegates caching/expiration to cached-cell.
  • Gateway hot path reads a fresh snapshot instead of running the blocking wg show command under ProxyState lock.

Tests

  • cargo check --manifest-path gateway/Cargo.toml
  • cargo test -p cached-cell
  • cargo test --manifest-path gateway/Cargo.toml handshakes -- --nocapture

Copilot AI review requested due to automatic review settings June 25, 2026 02:31

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR refactors WireGuard wg show <iface> latest-handshakes collection so it no longer runs under the ProxyState hot-path lock, by introducing a dedicated cache that is refreshed asynchronously.

Changes:

  • Add LatestHandshakesCache helper to fetch/parse and store a process-local snapshot of latest-handshakes.
  • Preload the cache at startup and refresh it periodically from a background task using spawn_blocking.
  • Keep ProxyState::latest_handshakes() as a snapshot-read wrapper and add a parser unit test.

Reviewed changes

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

File Description
gateway/src/main_service/handshakes.rs Introduces the LatestHandshakesCache, parsing, and a basic parser test.
gateway/src/main_service.rs Wires the cache into proxy state/inner, preloads it, starts the refresh task, and routes latest_handshakes() through the cache.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread gateway/src/main_service.rs
Comment thread gateway/src/main_service/handshakes.rs Outdated
@kvinwang
kvinwang force-pushed the fix/gateway-handshake-cache branch 2 times, most recently from 0c121b8 to 5b5d616 Compare June 25, 2026 03:00

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 8 out of 9 changed files in this pull request and generated 2 comments.

Comment on lines +40 to +46
pub(crate) async fn refresh(&self) -> Result<()> {
let interface = self.interface.clone();
self.cell
.refresh_blocking(move || fetch_latest_handshake_timestamps(&interface))
.await
.map_err(|err| anyhow::anyhow!("{err}"))?;
Ok(())
Comment on lines +49 to +53
pub(crate) fn latest(&self, stale_timeout: Option<Duration>) -> Result<HandshakesWithAge> {
let snapshot = self.cell.get()?;
add_elapsed_time(snapshot.value(), stale_timeout)
}
}
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