Skip to content

fix: warn on misleading cron step values#1948

Merged
yottahmd merged 3 commits intodagucloud:mainfrom
tmchow:osc/639-warn-misleading-cron-step
Apr 3, 2026
Merged

fix: warn on misleading cron step values#1948
yottahmd merged 3 commits intodagucloud:mainfrom
tmchow:osc/639-warn-misleading-cron-step

Conversation

@tmchow
Copy link
Copy Markdown
Contributor

@tmchow tmchow commented Apr 2, 2026

Summary

Adds warnings for cron step values that don't evenly divide their field range, like */33 in the minute field. These are valid cron but confuse users who expect interval semantics.

Why this matters

Users in #639 report that */33 * * * * doesn't run "every 33 minutes" as they expect. Standard cron */N means "values where value % N == 0", so */33 fires at :00 and :33 only. The same issue applies to */7, */13, and any step that doesn't divide 60 (minutes) or 24 (hours).

Changes

  • internal/core/dag.go: Added Warnings []string field to Schedule struct
  • internal/core/schedule.go: Added checkMisleadingStepValues() that detects non-divisor steps in minute and hour fields. Called from NewCronSchedule() and attached to the returned Schedule. Warning text explains which values actually fire and lists valid divisors.
  • internal/core/schedule_test.go: Tests covering */33, */7 (should warn), */5, */15 (should not warn), and hour field variants.

The schedule still executes normally. Warnings are informational only.

Testing

go test ./internal/core/... passes. 6 test cases cover the warning detection for both minute and hour fields.

Fixes #639

This contribution was developed with AI assistance (Codex).

Summary by CodeRabbit

  • New Features
    • Schedule definitions now include validation warnings to identify timing expression patterns that could potentially cause unexpected execution behavior. These diagnostic messages help you identify and correct potential configuration issues, thereby ensuring your schedules execute at the correct intervals and improving the overall reliability and stability of your automated business processes.

Standard cron step syntax `*/N` means "values where value % N == 0",
not "every N units". For example, `*/33` in the minute field fires at
:00 and :33 only, not every 33 minutes. This confuses users who expect
interval semantics.

Add checkMisleadingStepValues() that detects step values that don't
evenly divide their field range (60 for minutes, 24 for hours) and
attaches warning strings to the Schedule struct. The warning explains
which values the schedule actually fires at and lists valid divisors.

The schedule still executes normally -- warnings are informational.

Fixes dagucloud#639
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 2, 2026

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1919b6c9-cfbd-46b9-b1dd-41b18f5ffe59

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The changes add diagnostic warnings to cron schedule expressions with misleading step values (like */33 in the minute field). A new Warnings field is added to the Schedule struct, populated by validation logic that detects non-compliant cron patterns and included in JSON marshalling output.

Changes

Cohort / File(s) Summary
Schedule Data Structure
internal/core/dag.go
Added Warnings []string field to Schedule struct with JSON serialization support (json:"warnings,omitempty"). Updated MarshalJSON to include non-empty warnings in the marshaled output.
Warning Detection Logic
internal/core/schedule.go
Added checkMisleadingStepValues(expr string) []string function to validate 5-field cron expressions and detect misleading step patterns (e.g., */33 for minutes). Added helper isExpectedStepValue() for interval validation. Updated NewCronSchedule to compute and store warnings on the returned Schedule.
Validation Tests
internal/core/schedule_test.go
Added TestCheckMisleadingStepValues test with parallel subtests covering multiple cron expressions, verifying warning generation for misleading step values and absence of warnings for compliant expressions using testify assertions.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: adding warnings for misleading cron step values that don't evenly divide their field ranges.
Linked Issues check ✅ Passed The changes implement informational warnings for non-divisor cron steps, directly addressing issue #639's user confusion about cron step semantics without changing execution behavior.
Out of Scope Changes check ✅ Passed All changes are scoped to adding warning detection and messaging for misleading cron steps; no unrelated modifications to execution logic or other features are present.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@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: 2

🧹 Nitpick comments (2)
internal/core/schedule.go (1)

332-344: Special-case for */5 in hour field is reasonable but consider documenting the rationale.

The exception for */5 in the hour field (which fires at 0, 5, 10, 15, 20 — not evenly distributed across 24 hours) is a pragmatic choice to reduce noise for a commonly-used pattern.

Consider whether similar exceptions might be useful for other common patterns (e.g., */45 in minutes fires at 0, 45 which is often intentional for "twice per hour"). This could be addressed in a follow-up if users report false-positive warnings.

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

In `@internal/core/schedule.go` around lines 332 - 344, The isExpectedStepValue
function currently special-cases step==5 for the "hour" field; update this code
to include a clear inline comment documenting the rationale (that "*/5" in hours
is a common intentional pattern and results in uneven final interval at day
boundaries) and reference that similar special-cases could be added later (e.g.,
minute steps like 45) if users report false positives; make the comment adjacent
to the existing if block for fieldName == "hour" && step == 5 so future
maintainers know why this exception exists and where to extend it.
internal/core/schedule_test.go (1)

61-67: Test only validates first warning; consider checking all warnings for multi-field expressions.

When an expression triggers warnings for both minute and hour fields (e.g., */7 */7 * * *), only warnings[0] is validated. This is fine for current test cases since each tests a single field, but consider adding a test case that verifies both fields generate warnings independently.

💡 Example additional test case
{
    name:        "both minute and hour step 7 warns",
    expr:        "*/7 */7 * * *",
    wantWarning: true,
    contains:    []string{"minute field"}, // or validate both warnings
},
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/core/schedule_test.go` around lines 61 - 67, The test only asserts
on warnings[0], so add coverage that validates all returned warnings for
multi-field expressions: update the test in internal/core/schedule_test.go that
calls checkMisleadingStepValues (the tt loop) to either add a new case for "*/7
*/7 * * *" (wantWarning true) or change the assertions to iterate over warnings
and ensure each expected substring in tt.contains appears in some warning (e.g.,
check that for each s in tt.contains there exists an entry in warnings
containing s) rather than only checking warnings[0]; reference the
checkMisleadingStepValues call and the warnings variable when implementing this
broadened validation.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@internal/core/dag.go`:
- Around line 741-742: core.Schedule now carries Warnings (populated by
checkMisleadingStepValues and serialized in MarshalJSON) but the API layer drops
them; update the API surface by adding a "warnings" property to the Schedule
schema in api/v1/api.yaml, regenerate the API client/server code so api.Schedule
gains a Warnings field, and modify the transformer function toSchedule() to copy
core.Schedule.Warnings into api.Schedule.Warnings so warnings are returned to
API consumers (or explicitly document if you intend warnings to remain
internal).

In `@internal/core/schedule.go`:
- Around line 315-326: The formatted warning's arguments are misordered causing
the unit and the list of times to appear in the wrong places; update the
fmt.Sprintf call that builds warnings (using expr, field, rule.fieldName,
firesAt, rule.unit, step, rule.base, divisors) so the arguments match the format
string: pass expr, field, rule.fieldName, strings.Join(firesAt, ", "),
rule.unit, step, rule.unit, rule.base, strings.Join(divisors, ", ") to
fmt.Sprintf in that order (ensure you reference the existing variables used in
this function: expr, field, rule.fieldName, firesAt, rule.unit, step, rule.base,
divisors).

---

Nitpick comments:
In `@internal/core/schedule_test.go`:
- Around line 61-67: The test only asserts on warnings[0], so add coverage that
validates all returned warnings for multi-field expressions: update the test in
internal/core/schedule_test.go that calls checkMisleadingStepValues (the tt
loop) to either add a new case for "*/7 */7 * * *" (wantWarning true) or change
the assertions to iterate over warnings and ensure each expected substring in
tt.contains appears in some warning (e.g., check that for each s in tt.contains
there exists an entry in warnings containing s) rather than only checking
warnings[0]; reference the checkMisleadingStepValues call and the warnings
variable when implementing this broadened validation.

In `@internal/core/schedule.go`:
- Around line 332-344: The isExpectedStepValue function currently special-cases
step==5 for the "hour" field; update this code to include a clear inline comment
documenting the rationale (that "*/5" in hours is a common intentional pattern
and results in uneven final interval at day boundaries) and reference that
similar special-cases could be added later (e.g., minute steps like 45) if users
report false positives; make the comment adjacent to the existing if block for
fieldName == "hour" && step == 5 so future maintainers know why this exception
exists and where to extend it.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: cf78d7c3-c600-48fd-b2ff-65b2f7f5f573

📥 Commits

Reviewing files that changed from the base of the PR and between 7f6de95 and 10dccb1.

📒 Files selected for processing (3)
  • internal/core/dag.go
  • internal/core/schedule.go
  • internal/core/schedule_test.go

Comment thread internal/core/dag.go
Comment thread internal/core/schedule.go
- Fix fmt.Sprintf argument order in warning message (unit and firesAt
  were swapped, producing "fires at minutes 0, 33" instead of
  "fires at 0, 33 minutes")
- Surface schedule warnings through BuildWarnings so they appear in
  DAG validation output alongside other build warnings
@yottahmd
Copy link
Copy Markdown
Collaborator

yottahmd commented Apr 2, 2026

@tmchow Thank you for the nice PR! Could you run make fmt to format go code?

@tmchow
Copy link
Copy Markdown
Contributor Author

tmchow commented Apr 3, 2026

@tmchow Thank you for the nice PR! Could you run make fmt to format go code?

Done!

Copy link
Copy Markdown
Collaborator

@yottahmd yottahmd left a comment

Choose a reason for hiding this comment

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

LGTM ! Thanks for making cron schedule debugging easier!

@yottahmd yottahmd merged commit d7dadd8 into dagucloud:main Apr 3, 2026
5 checks passed
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 3, 2026

Codecov Report

❌ Patch coverage is 96.55172% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 68.09%. Comparing base (5f5f73a) to head (d9f4d7c).
⚠️ Report is 10 commits behind head on main.

Files with missing lines Patch % Lines
internal/core/schedule.go 96.00% 1 Missing and 1 partial ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #1948      +/-   ##
==========================================
- Coverage   68.62%   68.09%   -0.54%     
==========================================
  Files         468      475       +7     
  Lines       59992    61368    +1376     
==========================================
+ Hits        41171    41787     +616     
- Misses      14959    15618     +659     
- Partials     3862     3963     +101     
Files with missing lines Coverage Δ
internal/core/dag.go 88.65% <100.00%> (+0.09%) ⬆️
internal/core/spec/dag.go 85.00% <100.00%> (+0.09%) ⬆️
internal/core/schedule.go 79.82% <96.00%> (+4.41%) ⬆️

... and 45 files with indirect coverage changes


Continue to review full report in Codecov by Sentry.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 5f5f73a...d9f4d7c. Read the comment docs.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@tmchow
Copy link
Copy Markdown
Contributor Author

tmchow commented Apr 3, 2026

Thanks for the review!

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.

schedule is not executed according to the schedule

2 participants