fix: compose passive notify with queued submit

This commit is contained in:
2026-09-06 04:15:30 +09:00
parent 2456d6fda5
commit 9da20d15da
3 changed files with 68 additions and 5 deletions
+33
View File
@@ -325,8 +325,13 @@ fn prepare_pending_run<St: Store + Clone>(
notify_buffer: &NotifyBuffer, notify_buffer: &NotifyBuffer,
fence: Option<(u64, &str)>, fence: Option<(u64, &str)>,
) -> Result<Option<PendingRun>, crate::worker::PendingSubmissionError> { ) -> Result<Option<PendingRun>, crate::worker::PendingSubmissionError> {
let staged_passive_notification = pending_submissions.activating_passive_notification_id();
Ok(match pending_submissions.prepare_next_activation(fence)? { Ok(match pending_submissions.prepare_next_activation(fence)? {
Some(crate::worker::PendingActivation::Submission(submission)) => { Some(crate::worker::PendingActivation::Submission(submission)) => {
if staged_passive_notification.is_some() {
let extension = pending_submissions.notification_activation_extension();
debug_assert!(notify_buffer.replace_durable_notification_extension(extension));
}
Some(PendingRun::Submit(submission)) Some(PendingRun::Submit(submission))
} }
Some(crate::worker::PendingActivation::Notification(notification)) => { Some(crate::worker::PendingActivation::Notification(notification)) => {
@@ -2709,6 +2714,34 @@ mod tests {
.. ..
}) if request_id == "notify-first" }) if request_id == "notify-first"
)); ));
let pending =
crate::worker::PendingSubmissionHandle::for_test(&temp.path().join("passive-first"));
pending
.accept_notification("passive-first".into(), "passive notification".into(), false)
.unwrap();
pending
.accept(
"submit-after-passive".into(),
vec![protocol::Segment::Text {
content: "queued after passive".into(),
}],
false,
)
.unwrap();
let snapshot = pending.snapshot();
let head_id = snapshot.head_id.clone().expect("queued Submit is the head");
let notify_buffer = NotifyBuffer::new();
assert!(stage_oldest_passive_notification(&pending, &notify_buffer));
assert!(matches!(
prepare_pending_run(
&pending,
&notify_buffer,
Some((snapshot.revision + 1, &head_id)),
)
.unwrap(),
Some(PendingRun::Submit(_))
));
} }
#[test] #[test]
+19
View File
@@ -110,6 +110,25 @@ impl NotifyBuffer {
}); });
} }
pub(crate) fn replace_durable_notification_extension(
&self,
extension: SessionExtension,
) -> bool {
let mut queue = self.inner.lock().expect("notify buffer poisoned");
let Some(extensions) = queue.iter_mut().rev().find_map(|pending| match pending {
PendingNotify::Notify {
auto_run: false,
extensions,
..
} if !extensions.is_empty() => Some(extensions),
_ => None,
}) else {
return false;
};
*extensions = vec![extension];
true
}
/// Push a typed worker-event entry onto the queue. /// Push a typed worker-event entry onto the queue.
pub fn push_worker_event(&self, event: WorkerEvent) { pub fn push_worker_event(&self, event: WorkerEvent) {
self.push_entry(PendingNotify::WorkerEvent { event }); self.push_entry(PendingNotify::WorkerEvent { event });
+14 -3
View File
@@ -1674,14 +1674,24 @@ where
if let Some((expected_revision, expected_head_id)) = fence { if let Some((expected_revision, expected_head_id)) = fence {
Self::validate_fence(&state, expected_revision, Some(expected_head_id))?; Self::validate_fence(&state, expected_revision, Some(expected_head_id))?;
} }
if state.activating.is_some() || state.activating_notification.is_some() { if state.activating.is_some()
|| state
.activating_notification
.as_ref()
.is_some_and(|notification| notification.auto_run)
{
return Ok(None); return Ok(None);
} }
let has_staged_passive_notification = state.activating_notification.is_some();
let submission_sequence = state.pending.front().map(|item| item.activation_sequence); let submission_sequence = state.pending.front().map(|item| item.activation_sequence);
let notification_index = state let notification_index = if has_staged_passive_notification {
None
} else {
state
.pending_notifications .pending_notifications
.iter() .iter()
.position(|item| item.auto_run); .position(|item| item.auto_run)
};
let notification_sequence = notification_index let notification_sequence = notification_index
.and_then(|index| state.pending_notifications.get(index)) .and_then(|index| state.pending_notifications.get(index))
.map(|item| item.activation_sequence); .map(|item| item.activation_sequence);
@@ -1785,6 +1795,7 @@ where
.lock() .lock()
.expect("pending activation state poisoned"); .expect("pending activation state poisoned");
let mut committed = state.clone(); let mut committed = state.clone();
committed.activating = None;
committed.activating_notification = None; committed.activating_notification = None;
committed.revision = committed.revision.saturating_add(1); committed.revision = committed.revision.saturating_add(1);
pending_activation_extension(&committed) pending_activation_extension(&committed)