Skip to content

Commit ee19e73

Browse files
Rounding time to step in flow system to get stable activation times
1 parent 0ebf865 commit ee19e73

File tree

5 files changed

+64
-36
lines changed

5 files changed

+64
-36
lines changed

src/app/cli/src/app.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,9 @@ pub fn configure_base_catalog(
266266

267267
b.add::<kamu_flow_system_inmem::FlowConfigurationServiceInMemory>();
268268
b.add::<kamu_flow_system_inmem::FlowServiceInMemory>();
269+
b.add_value(kamu_flow_system_inmem::domain::FlowServiceRunConfig::new(
270+
chrono::Duration::seconds(1),
271+
));
269272

270273
b.add::<kamu_flow_system_inmem::FlowEventStoreInMem>();
271274
b.add::<kamu_flow_system_inmem::FlowConfigurationEventStoreInMem>();

src/app/cli/src/explore/api_server.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use std::sync::Arc;
1212

1313
use dill::Catalog;
1414
use internal_error::*;
15-
use kamu_flow_system_inmem::domain::{FlowService, FlowServiceRunConfig};
15+
use kamu_flow_system_inmem::domain::FlowService;
1616
use kamu_task_system_inmem::domain::TaskExecutor;
1717

1818
/////////////////////////////////////////////////////////////////////////////////////////
@@ -97,12 +97,10 @@ impl APIServer {
9797
}
9898

9999
pub async fn run(self) -> Result<(), InternalError> {
100-
let flow_service_run_config = FlowServiceRunConfig::new(std::time::Duration::from_secs(1));
101-
102100
tokio::select! {
103101
res = self.server => { res.int_err() },
104102
res = self.task_executor.run() => { res.int_err() },
105-
res = self.flow_service.run(flow_service_run_config) => { res.int_err() }
103+
res = self.flow_service.run() => { res.int_err() }
106104
}
107105
}
108106
}

src/domain/flow-system/src/services/flow/flow_service.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use crate::{DatasetFlowType, FlowID, FlowKey, FlowState, SystemFlowType};
2020
#[async_trait::async_trait]
2121
pub trait FlowService: Sync + Send {
2222
/// Runs the update main loop
23-
async fn run(&self, run_config: FlowServiceRunConfig) -> Result<(), InternalError>;
23+
async fn run(&self) -> Result<(), InternalError>;
2424

2525
/// Triggers the specified flow manually, unless it's already waiting
2626
async fn trigger_manual_flow(
@@ -175,11 +175,11 @@ impl From<LoadError<FlowState>> for CancelFlowError {
175175

176176
#[derive(Debug)]
177177
pub struct FlowServiceRunConfig {
178-
pub awaiting_step: std::time::Duration,
178+
pub awaiting_step: chrono::Duration,
179179
}
180180

181181
impl FlowServiceRunConfig {
182-
pub fn new(awaiting_step: std::time::Duration) -> Self {
182+
pub fn new(awaiting_step: chrono::Duration) -> Self {
183183
Self { awaiting_step }
184184
}
185185
}

src/infra/flow-system-inmem/src/services/flow/flow_service_inmem.rs

Lines changed: 48 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
use std::sync::{Arc, Mutex};
1313

14-
use chrono::{DateTime, Utc};
14+
use chrono::{DateTime, DurationRound, Utc};
1515
use dill::*;
1616
use event_bus::{AsyncEventHandler, EventBus};
1717
use futures::TryStreamExt;
@@ -30,6 +30,7 @@ use super::pending_flows_state::PendingFlowsState;
3030

3131
pub struct FlowServiceInMemory {
3232
state: Arc<Mutex<State>>,
33+
run_config: Arc<FlowServiceRunConfig>,
3334
event_bus: Arc<EventBus>,
3435
flow_event_store: Arc<dyn FlowEventStore>,
3536
time_source: Arc<dyn SystemTimeSource>,
@@ -57,6 +58,7 @@ struct State {
5758
#[scope(Singleton)]
5859
impl FlowServiceInMemory {
5960
pub fn new(
61+
run_config: Arc<FlowServiceRunConfig>,
6062
event_bus: Arc<EventBus>,
6163
flow_event_store: Arc<dyn FlowEventStore>,
6264
time_source: Arc<dyn SystemTimeSource>,
@@ -66,6 +68,7 @@ impl FlowServiceInMemory {
6668
) -> Self {
6769
Self {
6870
state: Arc::new(Mutex::new(State::default())),
71+
run_config,
6972
event_bus,
7073
flow_event_store,
7174
time_source,
@@ -75,6 +78,13 @@ impl FlowServiceInMemory {
7578
}
7679
}
7780

81+
fn round_time(&self, time: DateTime<Utc>) -> Result<DateTime<Utc>, InternalError> {
82+
let rounded_time = time
83+
.duration_round(self.run_config.awaiting_step)
84+
.int_err()?;
85+
Ok(rounded_time)
86+
}
87+
7888
#[tracing::instrument(level = "debug", skip_all)]
7989
async fn run_current_timeslot(&self) {
8090
let planned_flows: Vec<_> = {
@@ -105,7 +115,10 @@ impl FlowServiceInMemory {
105115
}
106116

107117
#[tracing::instrument(level = "debug", skip_all)]
108-
async fn initialize_auto_polling_flows_from_configurations(&self) -> Result<(), InternalError> {
118+
async fn initialize_auto_polling_flows_from_configurations(
119+
&self,
120+
start_time: DateTime<Utc>,
121+
) -> Result<(), InternalError> {
109122
let enabled_configurations: Vec<_> = self
110123
.flow_configuration_service
111124
.list_enabled_configurations()
@@ -114,8 +127,12 @@ impl FlowServiceInMemory {
114127
.int_err()?;
115128

116129
for enabled_config in enabled_configurations {
117-
self.activate_flow_configuration(enabled_config.flow_key, enabled_config.rule)
118-
.await?;
130+
self.activate_flow_configuration(
131+
start_time,
132+
enabled_config.flow_key,
133+
enabled_config.rule,
134+
)
135+
.await?;
119136
}
120137

121138
Ok(())
@@ -124,13 +141,15 @@ impl FlowServiceInMemory {
124141
#[tracing::instrument(level = "trace", skip_all, fields(?flow_key, ?rule))]
125142
async fn activate_flow_configuration(
126143
&self,
144+
start_time: DateTime<Utc>,
127145
flow_key: FlowKey,
128146
rule: FlowConfigurationRule,
129147
) -> Result<(), InternalError> {
130148
match &flow_key {
131149
FlowKey::Dataset(dataset_flow_key) => {
132150
if let FlowConfigurationRule::Schedule(schedule) = &rule {
133-
self.enqueue_auto_polling_flow(&flow_key, schedule).await?;
151+
self.enqueue_auto_polling_flow(start_time, &flow_key, schedule)
152+
.await?;
134153
}
135154

136155
let mut state = self.state.lock().unwrap();
@@ -140,7 +159,8 @@ impl FlowServiceInMemory {
140159
}
141160
FlowKey::System(system_flow_key) => {
142161
if let FlowConfigurationRule::Schedule(schedule) = &rule {
143-
self.enqueue_auto_polling_flow(&flow_key, schedule).await?;
162+
self.enqueue_auto_polling_flow(start_time, &flow_key, schedule)
163+
.await?;
144164

145165
let mut state = self.state.lock().unwrap();
146166
state
@@ -158,6 +178,7 @@ impl FlowServiceInMemory {
158178
#[tracing::instrument(level = "trace", skip_all, fields(?flow_key))]
159179
async fn try_enqueue_auto_polling_flow_if_enabled(
160180
&self,
181+
start_time: DateTime<Utc>,
161182
flow_key: &FlowKey,
162183
) -> Result<(), InternalError> {
163184
let maybe_active_schedule = self
@@ -168,7 +189,7 @@ impl FlowServiceInMemory {
168189
.try_get_flow_schedule(flow_key);
169190

170191
if let Some(active_schedule) = maybe_active_schedule {
171-
self.enqueue_auto_polling_flow(flow_key, &active_schedule)
192+
self.enqueue_auto_polling_flow(start_time, flow_key, &active_schedule)
172193
.await?;
173194
}
174195

@@ -178,6 +199,7 @@ impl FlowServiceInMemory {
178199
#[tracing::instrument(level = "trace", skip_all, fields(?flow_key, ?schedule))]
179200
async fn enqueue_auto_polling_flow(
180201
&self,
202+
start_time: DateTime<Utc>,
181203
flow_key: &FlowKey,
182204
schedule: &Schedule,
183205
) -> Result<FlowState, InternalError> {
@@ -191,7 +213,7 @@ impl FlowServiceInMemory {
191213
None => {
192214
let mut flow = self.make_new_flow(flow_key.clone(), trigger).await?;
193215

194-
let next_activation_time = schedule.next_activation_time(self.time_source.now());
216+
let next_activation_time = schedule.next_activation_time(start_time);
195217
self.enqueue_flow(flow.flow_id, next_activation_time)?;
196218

197219
flow.activate_at_time(self.time_source.now(), next_activation_time)
@@ -207,6 +229,7 @@ impl FlowServiceInMemory {
207229
#[tracing::instrument(level = "trace", skip_all, fields(%dataset_id, ?flow_type, %flow_id))]
208230
async fn enqueue_dependent_dataset_flows(
209231
&self,
232+
start_time: DateTime<Utc>,
210233
dataset_id: &DatasetID,
211234
flow_type: DatasetFlowType,
212235
flow_id: FlowID,
@@ -259,15 +282,14 @@ impl FlowServiceInMemory {
259282
if let Some(throttling_period) = start_condition.throttling_period {
260283
// TODO: throttle not from NOW,
261284
// but from last flow of the dependent daataset
262-
let now = self.time_source.now();
263285
self.enqueue_flow(
264286
dependent_dataset_flow.flow_id,
265-
now + throttling_period,
287+
start_time + throttling_period,
266288
)?;
267289

268290
dependent_dataset_flow
269291
.define_start_condition(
270-
now,
292+
self.time_source.now(),
271293
FlowStartCondition::Throttling(FlowStartConditionThrottling {
272294
interval: throttling_period,
273295
}),
@@ -390,22 +412,24 @@ impl FlowServiceInMemory {
390412
impl FlowService for FlowServiceInMemory {
391413
/// Runs the update main loop
392414
#[tracing::instrument(level = "info", skip_all)]
393-
async fn run(&self, run_config: FlowServiceRunConfig) -> Result<(), InternalError> {
415+
async fn run(&self) -> Result<(), InternalError> {
394416
// Initial scheduling
395-
self.initialize_auto_polling_flows_from_configurations()
417+
let start_time = self.round_time(self.time_source.now())?;
418+
self.initialize_auto_polling_flows_from_configurations(start_time)
396419
.await?;
397420

398421
// Publish progress event
399422
self.event_bus
400423
.dispatch_event(FlowServiceEventConfigurationLoaded {
401-
event_time: self.time_source.now(),
424+
event_time: start_time,
402425
})
403426
.await
404427
.int_err()?;
405428

406429
// Main scanning loop
407430
let main_loop_span = tracing::debug_span!("FlowService main loop");
408431
let _ = main_loop_span.enter();
432+
let std_awaiting_step = self.run_config.awaiting_step.to_std().int_err()?;
409433

410434
loop {
411435
// Do we have a timeslot scheduled?
@@ -425,13 +449,13 @@ impl FlowService for FlowServiceInMemory {
425449
// Publish progress event
426450
self.event_bus
427451
.dispatch_event(FlowServiceEventExecutedTimeSlot {
428-
event_time: current_time,
452+
event_time: nearest_activation_time,
429453
})
430454
.await
431455
.int_err()?;
432456
}
433457

434-
tokio::time::sleep(run_config.awaiting_step).await;
458+
tokio::time::sleep(std_awaiting_step).await;
435459
continue;
436460
}
437461
}
@@ -664,6 +688,7 @@ impl AsyncEventHandler<TaskEventFinished> for FlowServiceInMemory {
664688
&& flow_key.flow_type.is_dataset_update()
665689
{
666690
self.enqueue_dependent_dataset_flows(
691+
self.round_time(event.event_time)?,
667692
&flow_key.dataset_id,
668693
flow_key.flow_type,
669694
flow.flow_id,
@@ -681,7 +706,7 @@ impl AsyncEventHandler<TaskEventFinished> for FlowServiceInMemory {
681706
// In case of success:
682707
// - enqueue next auto-polling flow cycle
683708
if event.outcome == TaskOutcome::Success {
684-
self.try_enqueue_auto_polling_flow_if_enabled(&flow.flow_key)
709+
self.try_enqueue_auto_polling_flow_if_enabled(event.event_time, &flow.flow_key)
685710
.await?;
686711
}
687712

@@ -703,8 +728,12 @@ impl AsyncEventHandler<FlowConfigurationEventModified> for FlowServiceInMemory {
703728
state.active_configs.drop_flow_config(&event.flow_key);
704729
// TODO: should we unqueue pending flows / abort scheduled tasks?
705730
} else {
706-
self.activate_flow_configuration(event.flow_key.clone(), event.rule.clone())
707-
.await?
731+
self.activate_flow_configuration(
732+
self.round_time(event.event_time)?,
733+
event.flow_key.clone(),
734+
event.rule.clone(),
735+
)
736+
.await?
708737
}
709738

710739
Ok(())

src/infra/flow-system-inmem/tests/tests/test_flow_service_inmem.rs

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,9 @@ async fn test_read_initial_config() {
4747
)
4848
.await;
4949

50-
let config = FlowServiceRunConfig::new(std::time::Duration::from_millis(5));
51-
5250
let _ = tokio::select! {
53-
res = harness.flow_service.run(config) => res.int_err(),
54-
_ = tokio::time::sleep(std::time::Duration::from_millis(50)) => Ok(()),
51+
res = harness.flow_service.run() => res.int_err(),
52+
_ = tokio::time::sleep(std::time::Duration::from_millis(60)) => Ok(()),
5553
}
5654
.unwrap();
5755

@@ -64,13 +62,12 @@ async fn test_read_initial_config() {
6462
assert_eq!(3, state.snapshots.len());
6563

6664
let start_moment = state.snapshots[0].0;
67-
let foo_scheduled_moment = state.snapshots[1].0;
68-
let bar_scheduled_moment = state.snapshots[2].0;
65+
let foo_moment = state.snapshots[1].0;
66+
let bar_moment = state.snapshots[2].0;
6967

70-
assert!(start_moment < foo_scheduled_moment && foo_scheduled_moment < bar_scheduled_moment);
71-
assert!((foo_scheduled_moment - start_moment) >= Duration::milliseconds(30));
72-
assert!((foo_scheduled_moment - start_moment) < Duration::milliseconds(45));
73-
assert!((bar_scheduled_moment - start_moment) >= Duration::milliseconds(45));
68+
assert!(start_moment < foo_moment && foo_moment < bar_moment);
69+
assert_eq!((foo_moment - start_moment), Duration::milliseconds(30));
70+
assert_eq!((bar_moment - start_moment), Duration::milliseconds(45));
7471

7572
let flow_test_checks = [
7673
// Snapshot 0: after initial queueing
@@ -258,6 +255,7 @@ impl FlowHarness {
258255

259256
let catalog = dill::CatalogBuilder::new()
260257
.add::<EventBus>()
258+
.add_value(FlowServiceRunConfig::new(Duration::milliseconds(5)))
261259
.add::<FlowServiceInMemory>()
262260
.add::<FlowEventStoreInMem>()
263261
.add::<FlowConfigurationServiceInMemory>()

0 commit comments

Comments
 (0)