|
|
|
|
use rand::RngExt;
|
|
|
|
|
use std::time::Duration;
|
refactor: remove low-value thin wrappers (#426)
## Summary
- remove unused compatibility APIs and low-value forwarding wrappers
across the workspace
- inline service, repository, provider, cluster, proxy, and protocol
paths while preserving existing behavior
- remove stale dependencies, metrics, tests, and SQLx metadata generated
for deleted queries
## Validation
- `cargo check --workspace --all-targets`
- `cargo fmt --all -- --check`
- `git diff --check`
- relevant test suites: 1,408 passed, 184 ignored
- broader test run: 2,286 passed
1 month ago
|
|
|
use synctv_core::models::RealtimeActor;
|
|
|
|
|
|
|
|
|
|
/// Default TTL for membership cache entries (30 seconds).
|
|
|
|
|
///
|
|
|
|
|
/// This TTL is chosen to balance between:
|
|
|
|
|
/// - Reducing database load (longer TTL = fewer queries)
|
|
|
|
|
/// - Responsiveness to membership changes (shorter TTL = faster detection of bans/removals)
|
|
|
|
|
///
|
|
|
|
|
/// With a 30-second TTL and 25-35 second heartbeat interval, we ensure:
|
|
|
|
|
/// - At most 1 DB query per connection per 30 seconds (vs. every heartbeat without cache)
|
|
|
|
|
/// - Banned/removed users are disconnected within ~30-65 seconds worst case
|
|
|
|
|
/// - The disconnect signal channel (Redis `PubSub`) provides immediate notification in most cases
|
|
|
|
|
pub const MEMBERSHIP_CACHE_TTL: Duration = Duration::from_secs(30);
|
|
|
|
|
|
|
|
|
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
|
|
|
|
pub struct HeartbeatSchedule {
|
|
|
|
|
membership_cache_ttl: Duration,
|
|
|
|
|
base_interval: Duration,
|
|
|
|
|
max_jitter_secs: u64,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl HeartbeatSchedule {
|
|
|
|
|
#[must_use]
|
|
|
|
|
pub const fn production() -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
membership_cache_ttl: MEMBERSHIP_CACHE_TTL,
|
|
|
|
|
base_interval: Duration::from_secs(25),
|
|
|
|
|
max_jitter_secs: 10,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[must_use]
|
|
|
|
|
pub const fn fixed(membership_cache_ttl: Duration, base_interval: Duration) -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
membership_cache_ttl,
|
|
|
|
|
base_interval,
|
|
|
|
|
max_jitter_secs: 0,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[must_use]
|
|
|
|
|
pub const fn membership_cache_ttl(self) -> Duration {
|
|
|
|
|
self.membership_cache_ttl
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[must_use]
|
|
|
|
|
pub fn period_with_random_jitter(self) -> Duration {
|
|
|
|
|
self.base_interval
|
|
|
|
|
+ Duration::from_secs(rand::rng().random_range(0u64..=self.max_jitter_secs))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[must_use]
|
|
|
|
|
pub fn period_for_actor(self, actor: &RealtimeActor) -> Duration {
|
|
|
|
|
use std::hash::{Hash, Hasher};
|
|
|
|
|
|
|
|
|
|
let jitter_secs = if self.max_jitter_secs == 0 {
|
|
|
|
|
0
|
|
|
|
|
} else {
|
|
|
|
|
let mut hasher = std::collections::hash_map::DefaultHasher::new();
|
|
|
|
|
actor.connection_key().hash(&mut hasher);
|
|
|
|
|
hasher.finish() % (self.max_jitter_secs + 1)
|
|
|
|
|
};
|
|
|
|
|
self.base_interval + Duration::from_secs(jitter_secs)
|
|
|
|
|
}
|
|
|
|
|
}
|