Skip to content

Commit 78f5ed0

Browse files
committed
r: Save underlying pending state inside PendingStatePaused
1 parent 5e3f7a4 commit 78f5ed0

11 files changed

Lines changed: 376 additions & 129 deletions

File tree

crates/concepts/src/storage.rs

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1654,14 +1654,11 @@ pub enum PendingState {
16541654
#[display("BlockedByJoinSet({_0})")]
16551655
BlockedByJoinSet(PendingStateBlockedByJoinSet),
16561656

1657-
#[display("Paused")]
1658-
Paused,
1657+
#[display("Paused({_0})")]
1658+
Paused(PendingStatePaused),
16591659

1660-
#[display("Finished({finished})")]
1661-
Finished {
1662-
#[serde(flatten)]
1663-
finished: PendingStateFinished,
1664-
},
1660+
#[display("Finished({_0})")]
1661+
Finished(PendingStateFinished),
16651662
}
16661663

16671664
#[derive(Debug, Clone, derive_more::Display, PartialEq, Eq, Serialize)]
@@ -1671,15 +1668,15 @@ pub struct PendingStateLocked {
16711668
pub lock_expires_at: DateTime<Utc>,
16721669
}
16731670

1674-
#[derive(Debug, Clone, derive_more::Display, PartialEq, Eq, Serialize, Deserialize)]
1671+
#[derive(Debug, Clone, derive_more::Display, PartialEq, Eq, Serialize)]
16751672
#[display("`{scheduled_at}`, last_lock={last_lock:?}")]
16761673
pub struct PendingStatePendingAt {
16771674
pub scheduled_at: DateTime<Utc>,
16781675
/// `last_lock` is needed for lock extension.
16791676
pub last_lock: Option<LockedBy>,
16801677
}
16811678

1682-
#[derive(Debug, Clone, derive_more::Display, PartialEq, Eq, Serialize, Deserialize)]
1679+
#[derive(Debug, Clone, derive_more::Display, PartialEq, Eq, Serialize)]
16831680
#[display("{join_set_id}, `{lock_expires_at}`, closing={closing}")]
16841681
pub struct PendingStateBlockedByJoinSet {
16851682
pub join_set_id: JoinSetId,
@@ -1689,6 +1686,17 @@ pub struct PendingStateBlockedByJoinSet {
16891686
pub closing: bool,
16901687
}
16911688

1689+
/// State of execution before it was paused.
1690+
#[derive(Debug, Clone, derive_more::Display, PartialEq, Eq, Serialize)]
1691+
pub enum PendingStatePaused {
1692+
#[display("Locked({_0})")]
1693+
Locked(PendingStateLocked),
1694+
#[display("PendingAt({_0})")]
1695+
PendingAt(PendingStatePendingAt),
1696+
#[display("BlockedByJoinSet({_0})")]
1697+
BlockedByJoinSet(PendingStateBlockedByJoinSet),
1698+
}
1699+
16921700
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
16931701
pub struct LockedBy {
16941702
pub executor_id: ExecutorId,
@@ -1703,7 +1711,8 @@ impl From<&Locked> for LockedBy {
17031711
}
17041712
}
17051713

1706-
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1714+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
1715+
#[cfg_attr(any(test, feature = "test"), derive(Deserialize))]
17071716
pub struct PendingStateFinished {
17081717
pub version: VersionType, // not Version since it must be Copy
17091718
pub finished_at: DateTime<Utc>,
@@ -1820,7 +1829,7 @@ impl PendingState {
18201829
source: None,
18211830
loc: Location::caller(),
18221831
}),
1823-
PendingState::Paused => Err(DbErrorWriteNonRetriable::IllegalState {
1832+
PendingState::Paused(..) => Err(DbErrorWriteNonRetriable::IllegalState {
18241833
reason: "cannot lock, execution is paused".into(),
18251834
context: SpanTrace::capture(),
18261835
source: None,
@@ -1833,6 +1842,11 @@ impl PendingState {
18331842
pub fn is_finished(&self) -> bool {
18341843
matches!(self, PendingState::Finished { .. })
18351844
}
1845+
1846+
#[must_use]
1847+
pub fn is_paused(&self) -> bool {
1848+
matches!(self, PendingState::Paused(_))
1849+
}
18361850
}
18371851

18381852
#[derive(Debug, Clone, Copy, PartialEq, Eq)]

crates/db-mem/src/inmemory_dao.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -574,7 +574,7 @@ mod index {
574574
}
575575
PendingState::BlockedByJoinSet(..)
576576
| PendingState::Finished { .. }
577-
| PendingState::Paused => {}
577+
| PendingState::Paused(..) => {}
578578
}
579579
// Add all open async timers
580580
let mut delay_req_resp = journal

crates/db-mem/src/journal.rs

Lines changed: 53 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ use concepts::storage::{
44
CreateRequest, DbErrorWrite, DbErrorWriteNonRetriable, ExecutionEvent, ExecutionRequest,
55
HistoryEvent, JoinSetRequest, JoinSetResponse, JoinSetResponseEvent, JoinSetResponseEventOuter,
66
Locked, LockedBy, PendingStateBlockedByJoinSet, PendingStateFinished,
7-
PendingStateFinishedResultKind, PendingStateLocked, PendingStatePendingAt, ResponseCursor,
8-
ResponseWithCursor, VersionType,
7+
PendingStateFinishedResultKind, PendingStateLocked, PendingStatePaused, PendingStatePendingAt,
8+
ResponseCursor, ResponseWithCursor, VersionType,
99
};
1010
use concepts::storage::{ExecutionLog, PendingState, Version};
1111
use concepts::{ComponentId, JoinSetId};
@@ -297,41 +297,47 @@ impl ExecutionJournal {
297297
}
298298
}
299299

300-
fn update_pending_state(&mut self) {
300+
fn find_current_pending_state(&self) -> PendingState {
301+
if let Some(last_event) = self.execution_events.last()
302+
&& let ExecutionRequest::Finished { result, .. } = &last_event.event
303+
{
304+
let idx = self.execution_events.len() - 1;
305+
return PendingState::Finished(PendingStateFinished {
306+
version: VersionType::try_from(idx).expect("version limit reached"),
307+
finished_at: last_event.created_at,
308+
result_kind: PendingStateFinishedResultKind::from(result),
309+
});
310+
}
311+
301312
let mut unpause_encountered = false;
313+
let mut is_paused = false;
302314

303-
let pending_state = self
315+
// Find the underlying state (ignoring Paused/Unpaused for now), store it in
316+
// `PendingStatePaused` independent of whether the execution is actually paused.
317+
let underlying_state: PendingStatePaused = self
304318
.execution_events
305319
.iter()
306320
.enumerate()
307321
.rev()
308-
.find_map(|(idx, event)| match &event.event {
322+
.find_map(|(_idx, event)| match &event.event {
323+
ExecutionRequest::Finished { .. } => {
324+
unreachable!("finished state was already handled above")
325+
}
309326
ExecutionRequest::Created { scheduled_at, .. } => {
310-
Some(PendingState::PendingAt(PendingStatePendingAt {
327+
Some(PendingStatePaused::PendingAt(PendingStatePendingAt {
311328
scheduled_at: *scheduled_at,
312329
last_lock: None,
313330
}))
314331
}
315332

316-
ExecutionRequest::Finished { result, .. } => {
317-
assert_eq!(self.execution_events.len() - 1, idx);
318-
Some(PendingState::Finished {
319-
finished: PendingStateFinished {
320-
version: VersionType::try_from(idx).expect("version limit reached"),
321-
finished_at: event.created_at,
322-
result_kind: PendingStateFinishedResultKind::from(result),
323-
},
324-
})
325-
}
326-
327333
ExecutionRequest::Locked(Locked {
328334
executor_id,
329335
lock_expires_at,
330336
run_id,
331337
component_id: _,
332338
deployment_id: _,
333339
retry_config: _,
334-
}) => Some(PendingState::Locked(PendingStateLocked {
340+
}) => Some(PendingStatePaused::Locked(PendingStateLocked {
335341
locked_by: LockedBy {
336342
executor_id: *executor_id,
337343
run_id: *run_id,
@@ -350,7 +356,7 @@ impl ExecutionJournal {
350356
| ExecutionRequest::Unlocked {
351357
backoff_expires_at: expires_at,
352358
..
353-
} => Some(PendingState::PendingAt(PendingStatePendingAt {
359+
} => Some(PendingStatePaused::PendingAt(PendingStatePendingAt {
354360
scheduled_at: *expires_at,
355361
last_lock: self.find_last_lock().map(LockedBy::from),
356362
})),
@@ -393,13 +399,13 @@ impl ExecutionJournal {
393399
if let Some(nth_created_at) = resp {
394400
// Original executor has a chance to continue, but after expiry any executor can pick up the execution.
395401
let scheduled_at = max(*lock_expires_at, *nth_created_at);
396-
Some(PendingState::PendingAt(PendingStatePendingAt {
402+
Some(PendingStatePaused::PendingAt(PendingStatePendingAt {
397403
scheduled_at,
398404
last_lock: self.find_last_lock().map(LockedBy::from),
399405
}))
400406
} else {
401407
// Still waiting for response
402-
Some(PendingState::BlockedByJoinSet(
408+
Some(PendingStatePaused::BlockedByJoinSet(
403409
PendingStateBlockedByJoinSet {
404410
join_set_id: expected_join_set_id.clone(),
405411
lock_expires_at: *lock_expires_at,
@@ -419,9 +425,9 @@ impl ExecutionJournal {
419425
unpause_encountered = false;
420426
None
421427
} else {
422-
// No unpauses were found with bigger version
423-
unpause_encountered = false; // For sanity check below.
424-
Some(PendingState::Paused)
428+
// No unpauses were found in the later events - execution is paused
429+
is_paused = true;
430+
None // Continue looking for underlying state
425431
}
426432
}
427433
// No pending state change for following events:
@@ -444,7 +450,29 @@ impl ExecutionJournal {
444450
.expect("journal must begin with Created event");
445451

446452
assert!(!unpause_encountered, "unpause must be preceeded with pause");
447-
self.pending_state = pending_state;
453+
454+
// Check if execution is finished (overrides paused state)
455+
456+
{
457+
if is_paused {
458+
PendingState::Paused(underlying_state)
459+
} else {
460+
// Convert PendingStatePaused to PendingState
461+
match underlying_state {
462+
PendingStatePaused::Locked(locked) => PendingState::Locked(locked),
463+
PendingStatePaused::PendingAt(pending_at) => {
464+
PendingState::PendingAt(pending_at)
465+
}
466+
PendingStatePaused::BlockedByJoinSet(blocked) => {
467+
PendingState::BlockedByJoinSet(blocked)
468+
}
469+
}
470+
}
471+
}
472+
}
473+
474+
fn update_pending_state(&mut self) {
475+
self.pending_state = self.find_current_pending_state();
448476
self.component_id = self
449477
.find_last_lock()
450478
.map(|locked| locked.component_id.clone())

0 commit comments

Comments
 (0)