Skip to content

test(cron): cover ticker inter-tick interval pacing (#67102) - #67204

Closed
xxiaoxiong wants to merge 1 commit into
NousResearch:mainfrom
xxiaoxiong:pr67102-cron-ticker-interval
Closed

test(cron): cover ticker inter-tick interval pacing (#67102)#67204
xxiaoxiong wants to merge 1 commit into
NousResearch:mainfrom
xxiaoxiong:pr67102-cron-ticker-interval

Conversation

@xxiaoxiong

Copy link
Copy Markdown

What

Adds one targeted characterization test to tests/cron/test_scheduler_provider.py locking the inter-tick interval pacing of InProcessCronScheduler.start() — the only piece of the ticker contract not already covered.

Why

Issue #67102 asks for dedicated cron-ticker unit tests. Audit showed the suite is already broad (38 tests), but every existing test drives the loop with interval=0 to keep it tight. The production pacing contract — stop_event.wait(interval) parking the ticker for ~60s between iterations, NOT busy-looping — was not actually exercised by any test.

This fills that one gap with interval=0.2.

Mapping to #67102 issue's suggested test names

Issue suggestion This PR Status
test_cron_ticker_fires_at_interval NEW: test_inprocess_provider_sleeps_for_interval_between_ticks Added here
test_cron_ticker_stop_event test_inprocess_provider_ticks_and_stops Already covered
test_cron_ticker_with_adapter test_inprocess_provider_skips_dispatch_while_draining (covers the can_dispatch adapter gate path) Already covered
test_cron_ticker_handles_adapter_error test_ticker_survives_baseexception_from_tick Already covered

So this PR is intentionally a focused one-test gap-fill, not a duplicate suite.

RED → GREEN verification

To prove the new test actually locks the timing contract (not just incidentally passes), I verified bidirectionally:

  • RED: Temporarily edited cron/scheduler_provider.py to call stop_event.wait(0) instead of stop_event.wait(interval) — simulating a busy-loop regression. The new test fails as expected:
    AssertionError: ticker did not sleep for the configured interval between ticks:
    gap=0.000s, expected >=0.2s — likely a busy-loop regression in InProcessCronScheduler.start()
    
  • GREEN: Restored the real source. The new test passes (gap ≥ 0.2s).

The other 38 tests remain green in both directions.

Notes for maintainer

  • Touches only one test file, no production code changes.
  • Uses the existing _wait_until polling helper (the suite already uses it to avoid fixed-sleep flakiness under loaded CI).
  • Asserts gap >= interval cleanly with no jitter margin (interval=0.2 is small enough that scheduling jitter is negligible); a busy-loop regression would land at gap≈0, well below the threshold.
  • The PR base main is up-to-date with upstream.

The cron ticker characterization suite in tests/cron/test_scheduler_provider.py
covers stop_event semantics, BaseException survival, heartbeat recording, and
drain-pause gating — but every existing test drives the loop with
interval=0 to keep it tight. That means the production pacing contract —
InProcessCronScheduler.start() parking on stop_event.wait(interval)
between iterations, NOT busy-looping — is unverified.

This adds one targeted test using interval=0.2 that asserts the second
call to cron.scheduler.tick arrives no sooner than ~0.2s after the first,
while still exiting cleanly when stop_event is set. Verified RED→GREEN: when
stop_event.wait(interval) is intentionally broken to stop_event.wait(0)
the test fails (gap=0.000s < 0.2s); restored code passes.

Issue NousResearch#67102 noted the cron ticker lacked dedicated unit tests; this fills
the one piece of contract not already exercised. The other three test names
suggested in NousResearch#67102 (test_cron_ticker_stop_event, test_cron_ticker_with_adapter,
test_cron_ticker_handles_adapter_error) are conceptually already covered by
test_inprocess_provider_ticks_and_stops, test_inprocess_provider_skips_dispatch_
while_draining, and test_ticker_survives_baseexception_from_tick respectively,
so this PR is intentionally a focused one-test gap-fill, not a duplicate
suite.
@alt-glitch alt-glitch added type/test Test coverage or test infrastructure comp/cron Cron scheduler and job management P3 Low — cosmetic, nice to have needs-decision Awaiting maintainer decision before any implementation labels Jul 19, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

This was generated by AI during triage.

Related to #67168 and #67198: all cover cron ticker pacing for #67102. This PR narrowly measures real threading.Event inter-tick timing, while #67168 provides broader lifecycle coverage and #67198 asserts the exact configured wait argument.

@teknium1 teknium1 left a comment

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.

Thanks for the focused characterization test. It exercises the production pacing operation at cron/scheduler_provider.py:219 without changing runtime behavior.

Problems

  • tests/cron/test_scheduler_provider.py:246 says the test allows a 1.5×-interval jitter margin, but the assertion at tests/cron/test_scheduler_provider.py:249 is exactly gap >= interval; no such margin exists.

Suggested changes

  • Update or remove the inaccurate comment so the documented contract matches the assertion.

Automated hermes-sweeper review.

assert not t.is_alive(), "ticker did not exit after stop_event was set"
assert len(calls) >= 2, "ticker did not fire at least two ticks"
gap = calls[1] - calls[0]
# Allow a small scheduling-jitter margin (default = 1.5x interval) so this

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.

The assertion below is exactly gap >= interval; it does not implement a 1.5× jitter margin. Please update or remove this comment so it matches the executable contract.

@GottZ

GottZ commented Aug 3, 2026

Copy link
Copy Markdown

This was generated by AI during triage.

Summary

Nine PRs are associated with this issue complex: #44049, #44050, #46207, #47358, #52259, and #67418 address the Desktop-versus-Gateway cron race, while #67135, #67198, and #67204 add scheduler-provider tests without changing ownership or delivery behavior. The fix diffs range from obsolete direct-ticker guards and lifecycle suppression to provider-aware runtime-lock deferral; the recorded cross-issue best-fix verdict selects #44050, although each PR header records verify_verdikt=n/a.

Related pull requests

Duplicates

#44049 and #44050 implement substantially the same runtime-lock-backed scheduler deferral; #46207 and #47358 are obsolete direct-ticker variants, while the ownership portions of #52259 and #67418 address the same race through broader lifecycle, timeout, or status-probe changes. #67135, #67198, and #67204 are independent test PRs, not ownership-fix duplicates.

Suggested consolidation

Author action: rebase #44050 onto current main, retaining its built-in-only provider integration and regression coverage; this preserves the recorded best-fix verdict without recommending a merge. Close #44049, #46207, and #47358 as duplicates of #44050; for #52259, split out the standalone-send timeout before closing its ownership portion as duplicate, and for #67418, split independently justified status/profile hardening before closing its ownership gate as duplicate. These closure recommendations explicitly differ from the keep_open reviews on #46207, #47358, #52259, and #67418 because the first two target the obsolete direct-ticker path, while the latter two combine overlapping ownership behavior with distinct salvageable work. Keep #67135 and #67198 open with their complementary #67102 coverage as the salvage path, and keep #67204 open with its real-time pacing test after correcting the inaccurate jitter-margin comment.

Cross-PR triage: Reviewed 9 pull requests and 3 issues in this complex. Each diff was read against this issue; Assessment working set: 114 kB of PR diffs, 42 kB of issue/PR text, 15 kB of discussion (22 comments), 25 verify verdicts. verdicts reflect diff content, not PR titles. Part of an automated triage batch.

@xxiaoxiong

Copy link
Copy Markdown
Author

Closing stale PR — superseded by upstream work / no longer relevant.

@xxiaoxiong xxiaoxiong closed this Aug 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/cron Cron scheduler and job management needs-decision Awaiting maintainer decision before any implementation P3 Low — cosmetic, nice to have sweeper:blast-contained Sweeper blast radius: contained — one narrow path / opt-in / few users sweeper:risk-automation Sweeper risk: may affect CI, automerge, label sync, or maintainer automation type/test Test coverage or test infrastructure

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants