Skip to content

feat(rq): Add an option to monitor rq-scheduler jobs - #8053

Merged
sentrivana merged 6 commits into
masterfrom
danf/rq-scheduler-crons
Oct 21, 2026
Merged

sentrivana merged 6 commits into
masterfrom
danf/rq-scheduler-crons

Conversation

@wedamija

@wedamija wedamija commented Oct 17, 2026 •

Copy link
Copy Markdown
Member

Part of a series adding automatic Crons monitoring for the most-used job schedulers in each SDK. Check-ins for a monitor that doesn't exist yet are dropped, so today each monitor has to be created by hand with its schedule copied from the code. Sending the job's schedule as the monitor config lets Sentry create the monitor and keep it in sync.

This adds monitor_cron_jobs to RqIntegration for recurring jobs from rq-scheduler's scheduler.cron(...) and scheduler.schedule(..., interval=...). Only the worker needs the option, and it sends the cron string or interval as the monitor config. The slug is the job id, and jobs without one share a monitor named after the function's import path. It's off by default.

sentry_sdk.init(
    integrations=[RqIntegration(monitor_cron_jobs=True, exclude_cron_jobs=["cleanup"])],
)

Jobs aren't monitored when Sentry may read the cron string differently (6 fields, @daily, MON, both day fields set), the interval isn't whole minutes, the job has repeat, or the worker's local timezone isn't UTC. RQ's built-in rq cron scheduler isn't covered.

The rq test envs now install rq-scheduler for RQ 2.x.

Docs: getsentry/sentry-docs#20058

Send check-ins around each run of a job that rq-scheduler re-enqueues
(scheduler.cron() and scheduler.schedule(interval=...)) when
RqIntegration(monitor_cron_jobs=True) is set. The schedule is sent as the
monitor config when Sentry reads it the same way, so Sentry creates the
monitor and keeps it in sync. One-off jobs (enqueue_at, enqueue_in) are
not monitored.

Co-Authored-By: Claude <[email protected]>
@github-actions

github-actions Bot commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor

Coverage Results 📊

✅ 134037 passed | ⏭️ 7317 skipped | Total: 141354 | Pass Rate: 94.82% | Execution Time: 441m 7s

📊 Comparison with Base Branch

Metric Change
Total Tests 📈 +3157
Passed Tests 📈 +3174
Failed Tests 📉 -58
Skipped Tests 📈 +41

➖ Removed Tests (58)

View removed tests
  • test_input_attributes_nonstreaming_chat[False-True-False-messages2-expected_system_instructions2-expected_input_messages2]
    • File: tests.integrations.mistral.test_mistral
  • test_input_attributes_nonstreaming_chat[False-True-False-messages3-expected_system_instructions3-expected_input_messages3]
    • File: tests.integrations.mistral.test_mistral
  • test_input_attributes_nonstreaming_chat[False-False-True-messages0-expected_system_instructions0-expected_input_messages0]
    • File: tests.integrations.mistral.test_mistral
  • test_input_attributes_nonstreaming_chat[False-False-True-messages1-expected_system_instructions1-expected_input_messages1]
    • File: tests.integrations.mistral.test_mistral
  • test_input_attributes_nonstreaming_chat[False-False-True-messages2-expected_system_instructions2-expected_input_messages2]
    • File: tests.integrations.mistral.test_mistral
  • test_input_attributes_nonstreaming_chat[False-False-True-messages3-expected_system_instructions3-expected_input_messages3]
    • File: tests.integrations.mistral.test_mistral
  • test_input_attributes_nonstreaming_chat[False-False-False-messages0-expected_system_instructions0-expected_input_messages0]
    • File: tests.integrations.mistral.test_mistral
  • test_input_attributes_nonstreaming_chat[False-False-False-messages1-expected_system_instructions1-expected_input_messages1]
    • File: tests.integrations.mistral.test_mistral
  • test_input_attributes_nonstreaming_chat[False-False-False-messages2-expected_system_instructions2-expected_input_messages2]
    • File: tests.integrations.mistral.test_mistral
  • test_input_attributes_nonstreaming_chat[False-False-False-messages3-expected_system_instructions3-expected_input_messages3]
    • File: tests.integrations.mistral.test_mistral
  • test_input_attributes_nonstreaming_chat_async[True-True-True-messages0-expected_system_instructions0-expected_input_messages0]
    • File: tests.integrations.mistral.test_mistral
  • test_input_attributes_nonstreaming_chat_async[True-True-True-messages1-expected_system_instructions1-expected_input_messages1]
    • File: tests.integrations.mistral.test_mistral
  • test_input_attributes_nonstreaming_chat_async[True-True-True-messages2-expected_system_instructions2-expected_input_messages2]
    • File: tests.integrations.mistral.test_mistral
  • test_input_attributes_nonstreaming_chat_async[True-True-True-messages3-expected_system_instructions3-expected_input_messages3]
    • File: tests.integrations.mistral.test_mistral
  • test_input_attributes_nonstreaming_chat_async[True-True-False-messages0-expected_system_instructions0-expected_input_messages0]
    • File: tests.integrations.mistral.test_mistral
  • test_input_attributes_nonstreaming_chat_async[True-True-False-messages1-expected_system_instructions1-expected_input_messages1]
    • File: tests.integrations.mistral.test_mistral
  • test_input_attributes_nonstreaming_chat_async[True-True-False-messages2-expected_system_instructions2-expected_input_messages2]
    • File: tests.integrations.mistral.test_mistral
  • test_input_attributes_nonstreaming_chat_async[True-True-False-messages3-expected_system_instructions3-expected_input_messages3]
    • File: tests.integrations.mistral.test_mistral
  • test_input_attributes_nonstreaming_chat_async[True-False-True-messages0-expected_system_instructions0-expected_input_messages0]
    • File: tests.integrations.mistral.test_mistral
  • test_input_attributes_nonstreaming_chat_async[True-False-True-messages1-expected_system_instructions1-expected_input_messages1]
    • File: tests.integrations.mistral.test_mistral
  • test_input_attributes_nonstreaming_chat_async[True-False-True-messages2-expected_system_instructions2-expected_input_messages2]
    • File: tests.integrations.mistral.test_mistral
  • test_input_attributes_nonstreaming_chat_async[True-False-True-messages3-expected_system_instructions3-expected_input_messages3]
    • File: tests.integrations.mistral.test_mistral
  • test_input_attributes_nonstreaming_chat_async[True-False-False-messages0-expected_system_instructions0-expected_input_messages0]
    • File: tests.integrations.mistral.test_mistral
  • test_input_attributes_nonstreaming_chat_async[True-False-False-messages1-expected_system_instructions1-expected_input_messages1]
    • File: tests.integrations.mistral.test_mistral
  • test_input_attributes_nonstreaming_chat_async[True-False-False-messages2-expected_system_instructions2-expected_input_messages2]
    • File: tests.integrations.mistral.test_mistral
  • test_input_attributes_nonstreaming_chat_async[True-False-False-messages3-expected_system_instructions3-expected_input_messages3]
    • File: tests.integrations.mistral.test_mistral
  • test_input_attributes_nonstreaming_chat_async[False-True-True-messages0-expected_system_instructions0-expected_input_messages0]
    • File: tests.integrations.mistral.test_mistral
  • test_input_attributes_nonstreaming_chat_async[False-True-True-messages1-expected_system_instructions1-expected_input_messages1]
    • File: tests.integrations.mistral.test_mistral
  • test_input_attributes_nonstreaming_chat_async[False-True-True-messages2-expected_system_instructions2-expected_input_messages2]
    • File: tests.integrations.mistral.test_mistral
  • test_input_attributes_nonstreaming_chat_async[False-True-True-messages3-expected_system_instructions3-expected_input_messages3]
    • File: tests.integrations.mistral.test_mistral
  • test_input_attributes_nonstreaming_chat_async[False-True-False-messages0-expected_system_instructions0-expected_input_messages0]
    • File: tests.integrations.mistral.test_mistral
  • test_input_attributes_nonstreaming_chat_async[False-True-False-messages1-expected_system_instructions1-expected_input_messages1]
    • File: tests.integrations.mistral.test_mistral
  • test_input_attributes_nonstreaming_chat_async[False-True-False-messages2-expected_system_instructions2-expected_input_messages2]
    • File: tests.integrations.mistral.test_mistral
  • test_input_attributes_nonstreaming_chat_async[False-True-False-messages3-expected_system_instructions3-expected_input_messages3]
    • File: tests.integrations.mistral.test_mistral
  • test_input_attributes_nonstreaming_chat_async[False-False-True-messages0-expected_system_instructions0-expected_input_messages0]
    • File: tests.integrations.mistral.test_mistral
  • test_input_attributes_nonstreaming_chat_async[False-False-True-messages1-expected_system_instructions1-expected_input_messages1]
    • File: tests.integrations.mistral.test_mistral
  • test_input_attributes_nonstreaming_chat_async[False-False-True-messages2-expected_system_instructions2-expected_input_messages2]
    • File: tests.integrations.mistral.test_mistral
  • test_input_attributes_nonstreaming_chat_async[False-False-True-messages3-expected_system_instructions3-expected_input_messages3]
    • File: tests.integrations.mistral.test_mistral
  • test_input_attributes_nonstreaming_chat_async[False-False-False-messages0-expected_system_instructions0-expected_input_messages0]
    • File: tests.integrations.mistral.test_mistral
  • test_input_attributes_nonstreaming_chat_async[False-False-False-messages1-expected_system_instructions1-expected_input_messages1]
    • File: tests.integrations.mistral.test_mistral
  • test_input_attributes_nonstreaming_chat_async[False-False-False-messages2-expected_system_instructions2-expected_input_messages2]
    • File: tests.integrations.mistral.test_mistral
  • test_input_attributes_nonstreaming_chat_async[False-False-False-messages3-expected_system_instructions3-expected_input_messages3]
    • File: tests.integrations.mistral.test_mistral
  • test_output_attributes_nonstreaming_chat[True-True-True]
    • File: tests.integrations.mistral.test_mistral
  • test_output_attributes_nonstreaming_chat[True-True-False]
    • File: tests.integrations.mistral.test_mistral
  • test_output_attributes_nonstreaming_chat[True-False-True]
    • File: tests.integrations.mistral.test_mistral
  • test_output_attributes_nonstreaming_chat[True-False-False]
    • File: tests.integrations.mistral.test_mistral
  • test_output_attributes_nonstreaming_chat[False-True-True]
    • File: tests.integrations.mistral.test_mistral
  • test_output_attributes_nonstreaming_chat[False-True-False]
    • File: tests.integrations.mistral.test_mistral
  • test_output_attributes_nonstreaming_chat[False-False-True]
    • File: tests.integrations.mistral.test_mistral
  • test_output_attributes_nonstreaming_chat[False-False-False]
    • File: tests.integrations.mistral.test_mistral
  • test_output_attributes_nonstreaming_chat_async[True-True-True]
    • File: tests.integrations.mistral.test_mistral
  • test_output_attributes_nonstreaming_chat_async[True-True-False]
    • File: tests.integrations.mistral.test_mistral
  • test_output_attributes_nonstreaming_chat_async[True-False-True]
    • File: tests.integrations.mistral.test_mistral
  • test_output_attributes_nonstreaming_chat_async[True-False-False]
    • File: tests.integrations.mistral.test_mistral
  • test_output_attributes_nonstreaming_chat_async[False-True-True]
    • File: tests.integrations.mistral.test_mistral
  • test_output_attributes_nonstreaming_chat_async[False-True-False]
    • File: tests.integrations.mistral.test_mistral
  • test_output_attributes_nonstreaming_chat_async[False-False-True]
    • File: tests.integrations.mistral.test_mistral
  • test_output_attributes_nonstreaming_chat_async[False-False-False]
    • File: tests.integrations.mistral.test_mistral

All tests are passing successfully.

✅ Patch coverage is 98.96% (95 of 96 changed executable lines covered; target 80%).
Project statement coverage is 90.27% (up 0.01 percentage points from base (0f7acd5) to head (08d6a3d)).

Changed files with executable lines (2)
File Patch coverage Changed executable lines
sentry_sdk/integrations/rq.py 98.88% 88/89 covered; missed: 174; partial branches: 95, 130, 170, 322
sentry_sdk/crons/utils.py 100.00% 7/7 covered
Coverage diff
@@            Coverage Diff             @@
##        master     #8053       +/-##
==========================================
+ Coverage    90.26%    90.27%    +0.01%
==========================================
  Files          202       203        +1
  Tracked lines     26660     26813      +153
  Branches      9926     10008       +82
==========================================
+ Hits         24062     24204      +142
- Misses        2598      2609       +11
- Partials      1510      1515        +5

Generated by Coverage Action

Comment thread sentry_sdk/integrations/rq.py
Comment thread sentry_sdk/integrations/rq.py
Comment thread sentry_sdk/integrations/rq.py
Comment thread sentry_sdk/integrations/rq.py

@sentrivana sentrivana left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Ty, left a few comments 🙏🏻

Comment thread sentry_sdk/integrations/rq.py Outdated
Comment thread sentry_sdk/integrations/rq.py Outdated
Comment thread sentry_sdk/integrations/rq.py Outdated
@wedamija wedamija changed the title feat(rq): Add cron monitoring for rq-scheduler jobs feat(rq): Add an option to monitor rq-scheduler jobs Oct 20, 2026
rq-scheduler's crontab parser and Sentry disagree on `n/step` in day of
week, single-value ranges like `0-0` and wrapping ranges like `5-1`
(which Sentry rejects). Those jobs now check in without a config.

Also move the interval conversion to `sentry_sdk/crons/utils.py` (same
helper as the APScheduler integration), drop the warning for jobs
without an `id`, inline the closing check-in and move the module
constants up.

Co-Authored-By: Claude <[email protected]>
Comment thread sentry_sdk/integrations/rq.py Outdated
Match the Celery beat integration: skip jobs whose schedule can't be
expressed as a Sentry Crons monitor config instead of sending check-ins
without one. Also close the in_progress check-in with an error if
perform_job raises.

Co-Authored-By: Claude <[email protected]>
Comment thread sentry_sdk/integrations/rq.py
Close the check-in in a finally block instead of a helper, and keep the
job body in place.

Co-Authored-By: Claude <[email protected]>
@wedamija
wedamija requested a review from sentrivana October 20, 2026 23:06
@wedamija
wedamija marked this pull request as ready for review October 20, 2026 23:06
@wedamija
wedamija requested a review from a team as a code owner October 20, 2026 23:06

@sentrivana sentrivana left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

🙏🏻

@sentrivana
sentrivana merged commit 4e19592 into master Oct 21, 2026
168 checks passed
@sentrivana
sentrivana deleted the danf/rq-scheduler-crons branch October 21, 2026 10:25
@linear-code

linear-code Bot commented Oct 21, 2026

Copy link
Copy Markdown

PY-3032

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.

2 participants