Skip to content

feat: add dynamic schedule step control (pause/resume/list cron jobs)#1257

Open
raj-kochale wants to merge 1 commit intoiii-hq:mainfrom
raj-kochale:feature/dynamic-schedule-control
Open

feat: add dynamic schedule step control (pause/resume/list cron jobs)#1257
raj-kochale wants to merge 1 commit intoiii-hq:mainfrom
raj-kochale:feature/dynamic-schedule-control

Conversation

@raj-kochale
Copy link
Copy Markdown

@raj-kochale raj-kochale commented Mar 7, 2026

Summary

Fixes #1219

Adds the ability to dynamically control cron/schedule trigger steps at runtime — pause, resume, and list cron jobs — instead of requiring hard-coded cron expressions with no way to stop or control them after deployment.

Changes

Engine - Cron Module

  • pause_cron function: Pause a running cron job by trigger ID
  • resume_cron function: Resume a paused cron job by trigger ID
  • list_cron_jobs function: List all cron jobs with their current status (active/paused)
  • Added pause/resume state tracking in CronAdapter (engine/src/modules/cron/structs.rs)
  • Exposed new functions via the cron module (engine/src/modules/cron/cron.rs)

Engine - Queue Module

  • Added delayed enqueue support with configurable delay_ms (engine/src/modules/queue/queue.rs)

Framework (motia-js)

  • Updated TypeScript types to support schedule control options
  • Updated build utils for new step configurations

Documentation

  • Added how-to guide for scheduling cron tasks with dynamic control
  • Added how-to guide for using queues with delay support

Code Quality Fixes

  • Fixed skip_while_next clippy lint in function-macros
  • Fixed needless_borrows_for_generic_args in console build script
  • Fixed collapsible_if warnings in queue module
  • Fixed manual_inspect and manual_ok_err in CLI crate
  • Applied cargo fmt formatting fixes

Testing

  • cargo fmt --all -- --check
  • cargo clippy --all-targets --all-features -- -D warnings

Summary by CodeRabbit

Release Notes

  • New Features

    • Added delayed message enqueuing with configurable delays (maximum 15 minutes)
    • Added runtime cron job controls: pause, resume, and list active jobs
  • Documentation

    • Added guide for controlling cron jobs at runtime with code examples
    • Added guide for delayed message enqueuing across framework implementations

@vercel
Copy link
Copy Markdown
Contributor

vercel bot commented Mar 7, 2026

@raj-kochale is attempting to deploy a commit to the motia Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 7, 2026

📝 Walkthrough

Walkthrough

This PR adds runtime control for cron jobs (pause, resume, list) and delayed message enqueuing for queues. The cron module exposes control APIs with atomic state management, queues support optional delay_ms with 15-minute caps, and Motia framework exposes these via FlowContext. Includes minor code improvements and documentation.

Changes

Cohort / File(s) Summary
Cron Control Feature
engine/src/modules/cron/cron.rs, engine/src/modules/cron/structs.rs
Introduced pause_cron, resume_cron, and list_cron_jobs public APIs on CronCoreModule. Added per-job paused state via Arc in CronJobInfo. CronAdapter now checks paused before execution and provides control methods (pause, resume, is_paused, list_jobs). Tests added for pause/resume lifecycle.
Queue Delayed Enqueue Feature
engine/src/modules/queue/queue.rs
Added optional delay_ms field to QueueInput with MAX_DELAY_MS (900s) validation. Implemented delayed re-enqueue path: when delay_ms is set and exceeds 0, spawns a task to sleep then re-enqueue the message.
Motia Framework Updates
frameworks/motia/motia-js/packages/motia/src/new/build/utils.ts, frameworks/motia/motia-js/packages/motia/src/types.ts
Extended FlowContext with delay() helper and cron control interface. Updated EnqueueData type to include optional delayMs. Mapped queue payload to include delay_ms field for backend API consumption.
Documentation
docs/content/how-to/schedule-cron-task.mdx, docs/content/how-to/use-queues.mdx
Added "Controlling Cron Jobs at Runtime" section with pause, resume, list examples for Motia framework and direct SDK usage. Added "Delayed Enqueue" section documenting delay_ms with 15-minute maximum and code examples across multiple frameworks.
Minor Code Improvements
cli/src/state.rs, cli/src/update.rs, console/packages/console-rust/build.rs, engine/function-macros/src/lib.rs
Error handling refactoring in state.rs using inspect_err; timeout handling in update.rs simplified with .await.ok(); borrow semantics adjusted in build.rs; iterator pattern optimized in lib.rs with find() replacing skip_while().next().

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • set template default #156: Modifies the same cron module files (structs.rs) and queue enqueue path where telemetry hooks are inserted alongside the pause/resume and delay functionality, requiring coordinated understanding of job lifecycle changes.

Suggested reviewers

  • guibeira
  • sergiofilhowz
  • ytallo

Poem

🐰 The humble cron can pause and play,
While queues now wait to have their day,
A pause, resume, and list so fine,
Control the timing, draw the line! ⏰✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly describes the main change—adding dynamic control features (pause, resume, list) for cron/schedule step triggers at runtime.
Linked Issues check ✅ Passed All coding requirements from issue #1219 are met: pause_cron and resume_cron functions are implemented in engine/src/modules/cron/cron.rs; list_cron_jobs function is added; runtime control via pause/resume state tracking in CronAdapter (engine/src/modules/cron/structs.rs) is provided; and framework-level APIs expose these controls through FlowContext (motia-js types and build utilities).
Out of Scope Changes check ✅ Passed The PR includes delayed queue enqueue support (engine/src/modules/queue/queue.rs) and corresponding documentation, which extends the scope beyond pause/resume/list cron control but is directly related to the broader issue of dynamic schedule/trigger management and is mentioned in PR objectives.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs).
Share your feedback on Discord.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
engine/src/modules/queue/queue.rs (1)

91-110: Spawned task is fire-and-forget without completion tracking.

The delayed enqueue spawns a detached task. While this is acceptable for non-critical background work, be aware that:

  1. If the process shuts down during the sleep period, the delayed message is lost
  2. There's no way to cancel a pending delayed enqueue

For queues requiring stronger delivery guarantees, consider persisting delayed messages with a scheduler pattern instead. This may be acceptable for the current use case.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@engine/src/modules/queue/queue.rs` around lines 91 - 110, The delayed enqueue
uses a fire-and-forget tokio::spawn inside the conditional in queue.rs (the
block that calls adapter.enqueue, tokio::spawn, and
crate::modules::telemetry::collector::track_queue_emit), which means delayed
tasks cannot be tracked, canceled, or guaranteed on shutdown; fix by either
persisting the delayed message to a durable store and scheduling it via a
background scheduler (so enqueue is replayed reliably on restart) or by
returning/managing a JoinHandle/JoinSet from the spawn and wiring it into the
process shutdown/cancellation logic so pending sleeps can be awaited or
canceled; update the code paths around the tokio::spawn, adapter.enqueue call,
and where tasks are created so delayed messages are either persisted or
registered with a shutdown-aware task manager.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@engine/src/modules/queue/queue.rs`:
- Around line 101-107: The spawned task currently swallows errors from
adapter.enqueue() (let _ = ...), so change it to await the Result and log
failures instead of discarding them: capture the result of
adapter.enqueue(&topic, event_data, traceparent, baggage).await (e.g., if let
Err(e) = ... or match ...) and call a logger (tracing::error! or log::error!)
including the error and contextual info (topic, maybe delay) so enqueue failures
are visible; keep the call to
crate::modules::telemetry::collector::track_queue_emit() after handling the
result.

---

Nitpick comments:
In `@engine/src/modules/queue/queue.rs`:
- Around line 91-110: The delayed enqueue uses a fire-and-forget tokio::spawn
inside the conditional in queue.rs (the block that calls adapter.enqueue,
tokio::spawn, and crate::modules::telemetry::collector::track_queue_emit), which
means delayed tasks cannot be tracked, canceled, or guaranteed on shutdown; fix
by either persisting the delayed message to a durable store and scheduling it
via a background scheduler (so enqueue is replayed reliably on restart) or by
returning/managing a JoinHandle/JoinSet from the spawn and wiring it into the
process shutdown/cancellation logic so pending sleeps can be awaited or
canceled; update the code paths around the tokio::spawn, adapter.enqueue call,
and where tasks are created so delayed messages are either persisted or
registered with a shutdown-aware task manager.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 640305c9-b2a3-442b-b402-5e70466c1828

📥 Commits

Reviewing files that changed from the base of the PR and between 56abfcd and e4cb4d9.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (11)
  • cli/src/state.rs
  • cli/src/update.rs
  • console/packages/console-rust/build.rs
  • docs/content/how-to/schedule-cron-task.mdx
  • docs/content/how-to/use-queues.mdx
  • engine/function-macros/src/lib.rs
  • engine/src/modules/cron/cron.rs
  • engine/src/modules/cron/structs.rs
  • engine/src/modules/queue/queue.rs
  • frameworks/motia/motia-js/packages/motia/src/new/build/utils.ts
  • frameworks/motia/motia-js/packages/motia/src/types.ts

Comment on lines +101 to +107
tokio::spawn(async move {
tokio::time::sleep(std::time::Duration::from_millis(delay)).await;
let _ = adapter
.enqueue(&topic, event_data, traceparent, baggage)
.await;
crate::modules::telemetry::collector::track_queue_emit();
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Delayed enqueue errors are silently swallowed.

The spawned task ignores the result of adapter.enqueue() with let _ = .... If the delayed enqueue fails, there's no logging or error handling, making failures invisible. Consider logging errors:

🛡️ Proposed fix to log enqueue errors
 tokio::spawn(async move {
     tokio::time::sleep(std::time::Duration::from_millis(delay)).await;
-    let _ = adapter
+    if let Err(e) = adapter
         .enqueue(&topic, event_data, traceparent, baggage)
-        .await;
-    crate::modules::telemetry::collector::track_queue_emit();
+        .await
+    {
+        tracing::error!(
+            topic = %topic,
+            error = ?e,
+            "Delayed enqueue failed"
+        );
+    } else {
+        crate::modules::telemetry::collector::track_queue_emit();
+    }
 });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
tokio::spawn(async move {
tokio::time::sleep(std::time::Duration::from_millis(delay)).await;
let _ = adapter
.enqueue(&topic, event_data, traceparent, baggage)
.await;
crate::modules::telemetry::collector::track_queue_emit();
});
tokio::spawn(async move {
tokio::time::sleep(std::time::Duration::from_millis(delay)).await;
if let Err(e) = adapter
.enqueue(&topic, event_data, traceparent, baggage)
.await
{
tracing::error!(
topic = %topic,
error = ?e,
"Delayed enqueue failed"
);
} else {
crate::modules::telemetry::collector::track_queue_emit();
}
});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@engine/src/modules/queue/queue.rs` around lines 101 - 107, The spawned task
currently swallows errors from adapter.enqueue() (let _ = ...), so change it to
await the Result and log failures instead of discarding them: capture the result
of adapter.enqueue(&topic, event_data, traceparent, baggage).await (e.g., if let
Err(e) = ... or match ...) and call a logger (tracing::error! or log::error!)
including the error and contextual info (topic, maybe delay) so enqueue failures
are visible; keep the call to
crate::modules::telemetry::collector::track_queue_emit() after handling the
result.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Can not control cron trigger after deployment

1 participant