|
@@ -1,6 +1,5 @@
|
|
|
use {
|
|
use {
|
|
|
crate::{nonblocking::quic::ConnectionPeerType, quic::StreamerStats},
|
|
crate::{nonblocking::quic::ConnectionPeerType, quic::StreamerStats},
|
|
|
- percentage::Percentage,
|
|
|
|
|
std::{
|
|
std::{
|
|
|
cmp,
|
|
cmp,
|
|
|
sync::{
|
|
sync::{
|
|
@@ -12,7 +11,6 @@ use {
|
|
|
tokio::time::sleep,
|
|
tokio::time::sleep,
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
-const MAX_UNSTAKED_STREAMS_PERCENT: u64 = 20;
|
|
|
|
|
pub const STREAM_THROTTLING_INTERVAL_MS: u64 = 100;
|
|
pub const STREAM_THROTTLING_INTERVAL_MS: u64 = 100;
|
|
|
pub const STREAM_THROTTLING_INTERVAL: Duration =
|
|
pub const STREAM_THROTTLING_INTERVAL: Duration =
|
|
|
Duration::from_millis(STREAM_THROTTLING_INTERVAL_MS);
|
|
Duration::from_millis(STREAM_THROTTLING_INTERVAL_MS);
|
|
@@ -20,6 +18,9 @@ const STREAM_LOAD_EMA_INTERVAL_MS: u64 = 5;
|
|
|
const STREAM_LOAD_EMA_INTERVAL_COUNT: u64 = 10;
|
|
const STREAM_LOAD_EMA_INTERVAL_COUNT: u64 = 10;
|
|
|
const EMA_WINDOW_MS: u64 = STREAM_LOAD_EMA_INTERVAL_MS * STREAM_LOAD_EMA_INTERVAL_COUNT;
|
|
const EMA_WINDOW_MS: u64 = STREAM_LOAD_EMA_INTERVAL_MS * STREAM_LOAD_EMA_INTERVAL_COUNT;
|
|
|
|
|
|
|
|
|
|
+/// Maximum TPS for unstaked connections
|
|
|
|
|
+const UNSTAKED_MAX_TPS: u64 = 200;
|
|
|
|
|
+
|
|
|
pub(crate) struct StakedStreamLoadEMA {
|
|
pub(crate) struct StakedStreamLoadEMA {
|
|
|
current_load_ema: AtomicU64,
|
|
current_load_ema: AtomicU64,
|
|
|
load_in_recent_interval: AtomicU64,
|
|
load_in_recent_interval: AtomicU64,
|
|
@@ -42,18 +43,10 @@ impl StakedStreamLoadEMA {
|
|
|
max_streams_per_ms: u64,
|
|
max_streams_per_ms: u64,
|
|
|
) -> Self {
|
|
) -> Self {
|
|
|
let allow_unstaked_streams = max_unstaked_connections > 0;
|
|
let allow_unstaked_streams = max_unstaked_connections > 0;
|
|
|
- let max_staked_load_in_ema_window = if allow_unstaked_streams {
|
|
|
|
|
- (max_streams_per_ms
|
|
|
|
|
- - Percentage::from(MAX_UNSTAKED_STREAMS_PERCENT).apply_to(max_streams_per_ms))
|
|
|
|
|
- * EMA_WINDOW_MS
|
|
|
|
|
- } else {
|
|
|
|
|
- max_streams_per_ms * EMA_WINDOW_MS
|
|
|
|
|
- };
|
|
|
|
|
|
|
+ let max_staked_load_in_ema_window = max_streams_per_ms * EMA_WINDOW_MS;
|
|
|
|
|
|
|
|
let max_unstaked_load_in_throttling_window = if allow_unstaked_streams {
|
|
let max_unstaked_load_in_throttling_window = if allow_unstaked_streams {
|
|
|
- Percentage::from(MAX_UNSTAKED_STREAMS_PERCENT)
|
|
|
|
|
- .apply_to(max_streams_per_ms * STREAM_THROTTLING_INTERVAL_MS)
|
|
|
|
|
- .saturating_div(max_unstaked_connections as u64)
|
|
|
|
|
|
|
+ UNSTAKED_MAX_TPS * STREAM_THROTTLING_INTERVAL_MS / 1000
|
|
|
} else {
|
|
} else {
|
|
|
0
|
|
0
|
|
|
};
|
|
};
|
|
@@ -287,7 +280,7 @@ pub mod test {
|
|
|
DEFAULT_MAX_UNSTAKED_CONNECTIONS,
|
|
DEFAULT_MAX_UNSTAKED_CONNECTIONS,
|
|
|
DEFAULT_MAX_STREAMS_PER_MS,
|
|
DEFAULT_MAX_STREAMS_PER_MS,
|
|
|
));
|
|
));
|
|
|
- // 50K packets per ms * 20% / 500 max unstaked connections
|
|
|
|
|
|
|
+ // should be 20 for 200 TPS (as throttling interval is 100ms)
|
|
|
assert_eq!(
|
|
assert_eq!(
|
|
|
load_ema.available_load_capacity_in_throttling_duration(
|
|
load_ema.available_load_capacity_in_throttling_duration(
|
|
|
ConnectionPeerType::Unstaked,
|
|
ConnectionPeerType::Unstaked,
|
|
@@ -307,73 +300,73 @@ pub mod test {
|
|
|
|
|
|
|
|
// EMA load is used for staked connections to calculate max number of allowed streams.
|
|
// EMA load is used for staked connections to calculate max number of allowed streams.
|
|
|
// EMA window = 5ms interval * 10 intervals = 50ms
|
|
// EMA window = 5ms interval * 10 intervals = 50ms
|
|
|
- // max streams per window = 500K streams/sec * 80% = 400K/sec = 20K per 50ms
|
|
|
|
|
- // max_streams in 50ms = ((20K * 20K) / ema_load) * stake / total_stake
|
|
|
|
|
|
|
+ // max streams per window = 500K = 25K per 50ms
|
|
|
|
|
+ // max_streams in 50ms = ((25K * 25K) / ema_load) * stake / total_stake
|
|
|
//
|
|
//
|
|
|
// Stream throttling window is 100ms. So it'll double the amount of max streams.
|
|
// Stream throttling window is 100ms. So it'll double the amount of max streams.
|
|
|
- // max_streams in 100ms (throttling window) = 2 * ((20K * 20K) / ema_load) * stake / total_stake
|
|
|
|
|
|
|
+ // max_streams in 100ms (throttling window) = 2 * ((25K * 25K) / ema_load) * stake / total_stake
|
|
|
|
|
|
|
|
- load_ema.current_load_ema.store(20000, Ordering::Relaxed);
|
|
|
|
|
- // ema_load = 20K, stake = 15, total_stake = 10K
|
|
|
|
|
- // max_streams in 100ms (throttling window) = 2 * ((20K * 20K) / 20K) * 15 / 10K = 60
|
|
|
|
|
|
|
+ load_ema.current_load_ema.store(10000, Ordering::Relaxed);
|
|
|
|
|
+ // ema_load = 20K, stake = 40, total_stake = 10K
|
|
|
|
|
+ // max_streams in 100ms (throttling window) = 2 * ((25K * 25K) / 10K) * 40 / 10K = 50
|
|
|
assert_eq!(
|
|
assert_eq!(
|
|
|
load_ema.available_load_capacity_in_throttling_duration(
|
|
load_ema.available_load_capacity_in_throttling_duration(
|
|
|
- ConnectionPeerType::Staked(15),
|
|
|
|
|
|
|
+ ConnectionPeerType::Staked(40),
|
|
|
10000,
|
|
10000,
|
|
|
),
|
|
),
|
|
|
- 60
|
|
|
|
|
|
|
+ 500
|
|
|
);
|
|
);
|
|
|
|
|
|
|
|
// ema_load = 20K, stake = 1K, total_stake = 10K
|
|
// ema_load = 20K, stake = 1K, total_stake = 10K
|
|
|
- // max_streams in 100ms (throttling window) = 2 * ((20K * 20K) / 20K) * 1K / 10K = 4K
|
|
|
|
|
|
|
+ // max_streams in 100ms (throttling window) = 2 * ((25K * 25K) / 10K) * 1000 / 10K = 50
|
|
|
assert_eq!(
|
|
assert_eq!(
|
|
|
load_ema.available_load_capacity_in_throttling_duration(
|
|
load_ema.available_load_capacity_in_throttling_duration(
|
|
|
ConnectionPeerType::Staked(1000),
|
|
ConnectionPeerType::Staked(1000),
|
|
|
10000,
|
|
10000,
|
|
|
),
|
|
),
|
|
|
- 4000
|
|
|
|
|
|
|
+ 12500
|
|
|
);
|
|
);
|
|
|
|
|
|
|
|
load_ema.current_load_ema.store(5000, Ordering::Relaxed);
|
|
load_ema.current_load_ema.store(5000, Ordering::Relaxed);
|
|
|
// ema_load = 5K, stake = 15, total_stake = 10K
|
|
// ema_load = 5K, stake = 15, total_stake = 10K
|
|
|
- // max_streams in 100ms (throttling window) = 2 * ((20K * 20K) / 5K) * 15 / 10K = 240
|
|
|
|
|
|
|
+ // max_streams in 100ms (throttling window) = 2 * ((25K * 25K) / 10K) * 15 / 10K = 300
|
|
|
assert_eq!(
|
|
assert_eq!(
|
|
|
load_ema.available_load_capacity_in_throttling_duration(
|
|
load_ema.available_load_capacity_in_throttling_duration(
|
|
|
ConnectionPeerType::Staked(15),
|
|
ConnectionPeerType::Staked(15),
|
|
|
10000,
|
|
10000,
|
|
|
),
|
|
),
|
|
|
- 240
|
|
|
|
|
|
|
+ 300
|
|
|
);
|
|
);
|
|
|
|
|
|
|
|
// ema_load = 5K, stake = 1K, total_stake = 10K
|
|
// ema_load = 5K, stake = 1K, total_stake = 10K
|
|
|
- // max_streams in 100ms (throttling window) = 2 * ((20K * 20K) / 5K) * 1K / 10K = 16000
|
|
|
|
|
|
|
+ // max_streams in 100ms (throttling window) = 2 * ((25K * 25K) / 5K) * 1K / 10K = 16000
|
|
|
assert_eq!(
|
|
assert_eq!(
|
|
|
load_ema.available_load_capacity_in_throttling_duration(
|
|
load_ema.available_load_capacity_in_throttling_duration(
|
|
|
ConnectionPeerType::Staked(1000),
|
|
ConnectionPeerType::Staked(1000),
|
|
|
10000,
|
|
10000,
|
|
|
),
|
|
),
|
|
|
- 16000
|
|
|
|
|
|
|
+ 20000
|
|
|
);
|
|
);
|
|
|
|
|
|
|
|
- // At 4000, the load is less than 25% of max_load (20K).
|
|
|
|
|
|
|
+ // At 4000, the load is less than 25% of max_load (25K).
|
|
|
// Test that we cap it to 25%, yielding the same result as if load was 5000.
|
|
// Test that we cap it to 25%, yielding the same result as if load was 5000.
|
|
|
load_ema.current_load_ema.store(4000, Ordering::Relaxed);
|
|
load_ema.current_load_ema.store(4000, Ordering::Relaxed);
|
|
|
- // function = ((20K * 20K) / 25% of 20K) * stake / total_stake
|
|
|
|
|
|
|
+ // function = ((25K * 25K) / 25% of 20K) * stake / total_stake
|
|
|
assert_eq!(
|
|
assert_eq!(
|
|
|
load_ema.available_load_capacity_in_throttling_duration(
|
|
load_ema.available_load_capacity_in_throttling_duration(
|
|
|
ConnectionPeerType::Staked(15),
|
|
ConnectionPeerType::Staked(15),
|
|
|
10000,
|
|
10000,
|
|
|
),
|
|
),
|
|
|
- 240
|
|
|
|
|
|
|
+ 300
|
|
|
);
|
|
);
|
|
|
|
|
|
|
|
- // function = ((20K * 20K) / 25% of 20K) * stake / total_stake
|
|
|
|
|
|
|
+ // function = ((25K * 25K) / 25% of 20K) * stake / total_stake
|
|
|
assert_eq!(
|
|
assert_eq!(
|
|
|
load_ema.available_load_capacity_in_throttling_duration(
|
|
load_ema.available_load_capacity_in_throttling_duration(
|
|
|
ConnectionPeerType::Staked(1000),
|
|
ConnectionPeerType::Staked(1000),
|
|
|
10000,
|
|
10000,
|
|
|
),
|
|
),
|
|
|
- 16000
|
|
|
|
|
|
|
+ 20000
|
|
|
);
|
|
);
|
|
|
|
|
|
|
|
// At 1/40000 stake weight, and minimum load, it should still allow
|
|
// At 1/40000 stake weight, and minimum load, it should still allow
|