Repository navigation
Conversation
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 <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
Coverage Results 📊✅ 133762 passed | ⏭️ 7299 skipped | Total: 141061 | Pass Rate: 94.83% | Execution Time: 427m 47s 📊 Comparison with Base Branch
All tests are passing successfully. ✅ Patch coverage is 100.00% (60 of 60 changed executable lines covered; target 80%). Changed files with executable lines (1)
Coverage diff@@ Coverage Diff @@
## master #8053 +/-##
==========================================
+ Coverage 90.25% 90.28% +0.03%
==========================================
Files 202 202 —
Tracked lines 26660 26719 +59
Branches 9926 9956 +30
==========================================
+ Hits 24062 24122 +60
+ Misses 2598 2597 -1
- Partials 1510 1511 +1Generated by Coverage Action |
| check_in = None | ||
| with capture_internal_exceptions(): | ||
| check_in = _start_check_in(integration, job) |
There was a problem hiding this comment.
Generated-ID cron jobs use dotted func_name as monitor_slug
_start_check_in falls back to job.func_name (e.g. module.task) when the job ID is a UUID, but Sentry monitor slugs only allow [a-zA-Z0-9_-]; sanitize or replace dots so check-ins are not rejected.
Evidence
sentry_patched_perform_jobcalls_start_check_in(integration, job)for every job when the integration is enabled.- In
_start_check_in,monitor_slug = job.func_name if _GENERATED_JOB_ID.fullmatch(job.id) else job.id. - RQ
job.func_nameis a dotted import path (confirmed bytest_cron_job_generated_idexpectingtests.integrations.rq.test_rq_crons.ok_job). - Sentry monitor slugs are validated as
^[a-zA-Z0-9_-]+-sentry_patched_perform_jobcalls_start_check_in(integration, job)` for every job when the integration is enabled. - In
_start_check_in,monitor_slug = job.func_name if _GENERATED_JOB_ID.fullmatch(job.id) else job.id. - RQ
job.func_nameis a dotted import path (confirmed bytest_cron_job_generated_idexpectingtests.integrations.rq.test_rq_crons.ok_job). - Sentry monitor slugs are validated as , so these check-ins fail for the common
scheduler.cron(..., func=task)path without an explicitid.
Identified by Warden · code-review, find-bugs · 8PJ-S34
| check_in = None | ||
| with capture_internal_exceptions(): | ||
| check_in = _start_check_in(integration, job) |
There was a problem hiding this comment.
Generated IDs collapse distinct schedules for the same function
When scheduled jobs have generated IDs, _start_check_in uses job.func_name as the monitor slug. Two recurring schedules for the same function therefore share one monitor, and each check-in sends its own schedule config to that monitor. The monitor cannot represent both schedules independently; its config is left reflecting whichever schedule was sent last.
Evidence
_start_check_insubstitutesjob.func_namewhenjob.idmatches_GENERATED_JOB_ID;test_cron_job_generated_idconfirms the fallback is used for generated IDs.- The fallback does not include the job’s
cron_stringorinterval, so separate schedules for the same function produce the same slug. _get_monitor_configderives config from each job’s schedule metadata, and_start_check_inpasses that config tocapture_checkin.- The tests cover a single generated-ID schedule, but do not cover distinct schedules for the same function.
Identified by Warden · find-bugs · FW4-M52
There was a problem hiding this comment.
Fix attempt detected (commit 6f68bc3)
The change warns that generated IDs for the same function share a monitor, but still uses the function name as the slug, so distinct schedules continue to collide unless callers provide explicit IDs.
The original issue appears unresolved. Please review and try again.
Evaluated by Warden
Co-Authored-By: Claude <noreply@anthropic.com>
|
|
||
| check_in = None | ||
| with capture_internal_exceptions(): | ||
| check_in = _start_check_in(integration, job) |
There was a problem hiding this comment.
Crontab monitor timezone taken from worker, not scheduler
_get_crontab_config (via _start_check_in) treats the worker being UTC as proof the cron runs in UTC, so a non-UTC rqscheduler with UTC workers upserts the wrong monitor timezone and causes false missed check-ins.
Evidence
_start_check_in()→_get_monitor_config()→_get_crontab_config()builds monitor config on the worker only._get_crontab_configcheckstime.timezone/time.daylight/time.altzonein the worker process, then hard-codes"timezone": "UTC".- Comments and PR design state rq-scheduler evaluates crontabs in the scheduler process local time, and nothing runs in
rqscheduler. - Tests only cover worker TZ non-UTC → no config; they never cover scheduler TZ ≠ worker TZ.
Identified by Warden · find-bugs · 4XW-77D
| if "cron_string" not in job.meta and "interval" not in job.meta: | ||
| return None |
There was a problem hiding this comment.
One-off schedule() jobs are monitored when meta interval is None
Gate on recurring jobs with truthiness (not job.meta.get("cron_string") and not job.meta.get("interval")) instead of key presence—rq-scheduler always sets meta['interval'] = interval, so one-off schedule() jobs still get check-ins.
Evidence
_start_check_inreturns early only when both"cron_string"and"interval"are absent fromjob.meta.- rq-scheduler's
schedule()always assignsjob.meta['interval'] = interval, including whenintervalisNonefor a one-off run. - With the key present, execution continues to
capture_checkin, so one-offschedule()jobs emit check-ins. test_one_off_jobs_not_monitoredexpects no check-ins forscheduler.schedule(..., ok_job)without an interval.
Identified by Warden · find-bugs · WBR-F4V
Description
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 opt-in check-ins for recurring jobs scheduled with rq-scheduler, the most used way to run recurring RQ jobs, modelled on
CeleryIntegration(monitor_beat_tasks=True, exclude_beat_tasks=[...]):in_progresswhen the job starts andokorerrorwhenperform_jobfinishes. rq-scheduler stores the schedule in the job's meta, so nothing runs in therqschedulerprocess.scheduler.cron(...)andscheduler.schedule(..., interval=...)are monitored. One-off jobs (enqueue_at,enqueue_in,schedulewithoutinterval) are not.idpassed tocron()/schedule(). Without one, the job gets a random id on every deploy, so the function's import path is used instead.@daily, names such asMON,L, or both day of month and day of week set), sub-minute or uneven intervals, and jobs withrepeat, since they stop after a number of runs.RQ's built-in
rq cronscheduler isn't covered here. It doesn't mark the jobs it enqueues, so it needs a patch in the scheduler process (see the closed #7841).The rq test envs now install
rq-schedulerfor RQ 2.x.Issues
Docs: getsentry/sentry-docs#20058