Skip to content

Commit d53c5d7

Browse files
committed
sim-lib: only use start delay if explicitly specified
As is, we'll run with a 0 second start delay for: - Random activities - Defined activities with no `start_secs` specified This very likely isn't the intent for defined activities (people should set `start_secs=0` if they want their payments to fire immediately), and doesn't work for random activity because we just fire all payments on start. Instead, we switch over to using our payment wait as the start delay in the absence of this field being set for defined activities and always for random activities.
1 parent f5c8430 commit d53c5d7

File tree

3 files changed

+119
-29
lines changed

3 files changed

+119
-29
lines changed

sim-lib/src/defined_activity.rs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use tokio::time::Duration;
88
#[derive(Clone)]
99
pub struct DefinedPaymentActivity {
1010
destination: NodeInfo,
11-
start: Duration,
11+
start: Option<Duration>,
1212
count: Option<u64>,
1313
wait: ValueOrRange<u16>,
1414
amount: ValueOrRange<u64>,
@@ -17,7 +17,7 @@ pub struct DefinedPaymentActivity {
1717
impl DefinedPaymentActivity {
1818
pub fn new(
1919
destination: NodeInfo,
20-
start: Duration,
20+
start: Option<Duration>,
2121
count: Option<u64>,
2222
wait: ValueOrRange<u16>,
2323
amount: ValueOrRange<u64>,
@@ -52,7 +52,7 @@ impl DestinationGenerator for DefinedPaymentActivity {
5252
}
5353

5454
impl PaymentGenerator for DefinedPaymentActivity {
55-
fn payment_start(&self) -> Duration {
55+
fn payment_start(&self) -> Option<Duration> {
5656
self.start
5757
}
5858

@@ -81,7 +81,6 @@ impl PaymentGenerator for DefinedPaymentActivity {
8181
#[cfg(test)]
8282
mod tests {
8383
use super::DefinedPaymentActivity;
84-
use super::*;
8584
use crate::test_utils::{create_nodes, get_random_keypair};
8685
use crate::{DestinationGenerator, PaymentGenerationError, PaymentGenerator};
8786

@@ -95,7 +94,7 @@ mod tests {
9594

9695
let generator = DefinedPaymentActivity::new(
9796
node.clone(),
98-
Duration::from_secs(0),
97+
None,
9998
None,
10099
crate::ValueOrRange::Value(60),
101100
crate::ValueOrRange::Value(payment_amt),

sim-lib/src/lib.rs

Lines changed: 113 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -185,8 +185,7 @@ pub struct ActivityParser {
185185
#[serde(with = "serializers::serde_node_id")]
186186
pub destination: NodeId,
187187
/// The time in the simulation to start the payment.
188-
#[serde(default)]
189-
pub start_secs: u16,
188+
pub start_secs: Option<u16>,
190189
/// The number of payments to send over the course of the simulation.
191190
#[serde(default)]
192191
pub count: Option<u64>,
@@ -207,7 +206,7 @@ pub struct ActivityDefinition {
207206
/// The destination of the payment.
208207
pub destination: NodeInfo,
209208
/// The time in the simulation to start the payment.
210-
pub start_secs: u16,
209+
pub start_secs: Option<u16>,
211210
/// The number of payments to send over the course of the simulation.
212211
pub count: Option<u64>,
213212
/// The interval of the event, as in every how many seconds the payment is performed.
@@ -328,7 +327,7 @@ pub struct PaymentGenerationError(String);
328327

329328
pub trait PaymentGenerator: Display + Send {
330329
/// Returns the time that the payments should start
331-
fn payment_start(&self) -> Duration;
330+
fn payment_start(&self) -> Option<Duration>;
332331

333332
/// Returns the number of payments that should be made
334333
fn payment_count(&self) -> Option<u64>;
@@ -819,7 +818,9 @@ impl Simulation {
819818
for description in self.activity.iter() {
820819
let activity_generator = DefinedPaymentActivity::new(
821820
description.destination.clone(),
822-
Duration::from_secs(description.start_secs.into()),
821+
description
822+
.start_secs
823+
.map(|start| Duration::from_secs(start.into())),
823824
description.count,
824825
description.interval_secs,
825826
description.amount_msat,
@@ -1087,22 +1088,7 @@ async fn produce_events<N: DestinationGenerator + ?Sized, A: PaymentGenerator +
10871088
}
10881089
}
10891090

1090-
let wait: Duration = if current_count == 0 {
1091-
let start = node_generator.payment_start();
1092-
if start != Duration::from_secs(0) {
1093-
log::debug!(
1094-
"Using a start delay. The first payment for {source} will be at {:?}.",
1095-
start
1096-
);
1097-
}
1098-
start
1099-
} else {
1100-
let wait = node_generator
1101-
.next_payment_wait()
1102-
.map_err(SimulationError::PaymentGenerationError)?;
1103-
log::debug!("Next payment for {source} in {:?}.", wait);
1104-
wait
1105-
};
1091+
let wait = get_payment_delay(current_count, &source, node_generator.as_ref())?;
11061092

11071093
select! {
11081094
biased;
@@ -1144,6 +1130,38 @@ async fn produce_events<N: DestinationGenerator + ?Sized, A: PaymentGenerator +
11441130
}
11451131
}
11461132

1133+
/// Gets the wait time for the next payment. If this is the first payment being generated, and a specific start delay
1134+
/// was set we return a once-off delay. Otherwise, the interval between payments is used.
1135+
fn get_payment_delay<A: PaymentGenerator + ?Sized>(
1136+
call_count: u64,
1137+
source: &NodeInfo,
1138+
node_generator: &A,
1139+
) -> Result<Duration, SimulationError> {
1140+
// Note: we can't check if let Some() && call_count (syntax not allowed) so we add an additional branch in here.
1141+
// The alternative is to call payment_start twice (which is _technically_ fine because it always returns the same
1142+
// value), but this approach only costs us a few extra lines so we go for the more verbose approach so that we
1143+
// don't have to make any assumptions about the underlying operation of payment_start.
1144+
if call_count != 0 {
1145+
let wait = node_generator
1146+
.next_payment_wait()
1147+
.map_err(SimulationError::PaymentGenerationError)?;
1148+
log::debug!("Next payment for {source} in {:?}.", wait);
1149+
Ok(wait)
1150+
} else if let Some(start) = node_generator.payment_start() {
1151+
log::debug!(
1152+
"First payment for {source} will be after a start delay of {:?}.",
1153+
start
1154+
);
1155+
Ok(start)
1156+
} else {
1157+
let wait = node_generator
1158+
.next_payment_wait()
1159+
.map_err(SimulationError::PaymentGenerationError)?;
1160+
log::debug!("First payment for {source} in {:?}.", wait);
1161+
Ok(wait)
1162+
}
1163+
}
1164+
11471165
async fn consume_simulation_results(
11481166
logger: Arc<Mutex<PaymentResultLogger>>,
11491167
mut receiver: Receiver<(Payment, PaymentResult)>,
@@ -1380,7 +1398,10 @@ async fn track_payment_result(
13801398

13811399
#[cfg(test)]
13821400
mod tests {
1383-
use crate::MutRng;
1401+
use crate::{get_payment_delay, test_utils, MutRng, PaymentGenerationError, PaymentGenerator};
1402+
use mockall::mock;
1403+
use std::fmt;
1404+
use std::time::Duration;
13841405

13851406
#[test]
13861407
fn create_seeded_mut_rng() {
@@ -1407,4 +1428,74 @@ mod tests {
14071428

14081429
assert_ne!(rng_1.next_u64(), rng_2.next_u64())
14091430
}
1431+
1432+
mock! {
1433+
pub Generator {}
1434+
1435+
impl fmt::Display for Generator {
1436+
fn fmt<'a>(&self, f: &mut fmt::Formatter<'a>) -> fmt::Result;
1437+
}
1438+
1439+
impl PaymentGenerator for Generator {
1440+
fn payment_start(&self) -> Option<Duration>;
1441+
fn payment_count(&self) -> Option<u64>;
1442+
fn next_payment_wait(&self) -> Result<Duration, PaymentGenerationError>;
1443+
fn payment_amount(&self, destination_capacity: Option<u64>) -> Result<u64, PaymentGenerationError>;
1444+
}
1445+
}
1446+
1447+
#[test]
1448+
fn test_no_payment_delay() {
1449+
let node = test_utils::create_nodes(1, 100_000)
1450+
.first()
1451+
.unwrap()
1452+
.0
1453+
.clone();
1454+
1455+
// Setup mocked generator to have no start time and send payments every 5 seconds.
1456+
let mut mock_generator = MockGenerator::new();
1457+
mock_generator.expect_payment_start().return_once(|| None);
1458+
let payment_interval = Duration::from_secs(5);
1459+
mock_generator
1460+
.expect_next_payment_wait()
1461+
.returning(move || Ok(payment_interval));
1462+
1463+
assert_eq!(
1464+
get_payment_delay(0, &node, &mock_generator).unwrap(),
1465+
payment_interval
1466+
);
1467+
assert_eq!(
1468+
get_payment_delay(1, &node, &mock_generator).unwrap(),
1469+
payment_interval
1470+
);
1471+
}
1472+
1473+
#[test]
1474+
fn test_payment_delay() {
1475+
let node = test_utils::create_nodes(1, 100_000)
1476+
.first()
1477+
.unwrap()
1478+
.0
1479+
.clone();
1480+
1481+
// Setup mocked generator to have a start delay and payment interval with different values.
1482+
let mut mock_generator = MockGenerator::new();
1483+
let start_delay = Duration::from_secs(10);
1484+
mock_generator
1485+
.expect_payment_start()
1486+
.return_once(move || Some(start_delay));
1487+
let payment_interval = Duration::from_secs(5);
1488+
mock_generator
1489+
.expect_next_payment_wait()
1490+
.returning(move || Ok(payment_interval));
1491+
1492+
assert_eq!(
1493+
get_payment_delay(0, &node, &mock_generator).unwrap(),
1494+
start_delay
1495+
);
1496+
assert_eq!(
1497+
get_payment_delay(1, &node, &mock_generator).unwrap(),
1498+
payment_interval
1499+
);
1500+
}
14101501
}

sim-lib/src/random_activity.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -214,8 +214,8 @@ fn events_per_month(source_capacity_msat: u64, multiplier: f64, expected_payment
214214

215215
impl PaymentGenerator for RandomPaymentActivity {
216216
/// Returns the time that the payments should start. This will always be 0 for the RandomPaymentActivity type.
217-
fn payment_start(&self) -> Duration {
218-
Duration::from_secs(0)
217+
fn payment_start(&self) -> Option<Duration> {
218+
None
219219
}
220220

221221
/// Returns the number of payments that should be made. This will always be None for the RandomPaymentActivity type.

0 commit comments

Comments
 (0)