Skip to content

feat(job): configure the jobs tick route's batch size, timeout and queues - #4524

Merged
bpamiri merged 3 commits into
developfrom
peter/jobs-tick-settings
Oct 9, 2026
Merged

bpamiri merged 3 commits into
developfrom
peter/jobs-tick-settings

Conversation

@bpamiri

@bpamiri bpamiri commented Oct 9, 2026 •

Copy link
Copy Markdown
Collaborator

Closes #4520

Problem

The /wheels/jobs/tick route called JobRunner.tick() with no arguments, so every call ran at most one job, capped at 300 s, from any queue. Nothing could raise that. A scheduler hitting the route once a minute drained about one job a minute per server.

Change

JobTickEndpoint.$runTick() now passes three app settings through to tick(), read by a new $tickArguments():

Setting Default Meaning
jobsRunnerTickMaxJobs 1 most jobs per call, run one after another; a whole number from 1 to 1000
jobsRunnerTickTimeout 300 seconds each job may run; a whole number from 1 to 86400
jobsRunnerTickQueues "" comma list of queues; "" means every queue
  • Defaults unchanged: the defaults are set in onapplicationstart and match today's behaviour.

  • Invalid values: a missing value, or one that is fractional, out of range, not a number, or a non-string queue list, falls back to its default. Values are never truncated: Int() is never applied, because Lucee truncates it to 32 bits.

  • Settings, not request parameters, so a caller holding the token can't widen the work one request does.

  • Docs: the multi-server jobs guide's tick-route section documents the settings and their ranges, and explains:

    • a batch must fit inside the engine's request timeout, which the setting doesn't extend; for long or many jobs, use wheels jobs work;
    • a request killed mid-job leaves the job for the reaper, which retries it, or marks it interrupted if the job isn't idempotent.

    A changelog fragment is included.

Tests

JobTickEndpointSpec has three new specs:

  • the configured values reach tick(), checked on a stub runner's call log;
  • with the settings absent, tick() gets 1, 300 and every queue;
  • boundary values, for each setting on its own:
    • fall back to the default: 0, negative, 0.5, 1.5, 1001 / 86401, 2147483648, non-numeric strings, an array, a struct;
    • accepted: 1, a numeric string, 1000 / 86400;
  • with jobsRunnerTickMaxJobs = 3, a real request through handle() drains three enqueued jobs in one call.

Local results (jobs suite, 288 specs each):

Engine Databases Result
Lucee 7 SQLite, MySQL all pass
BoxLang SQLite all pass
Adobe 2025 SQLite all pass

Other checks:

  • The complexity and docblock gates pass.
  • The guides site builds (pnpm build, 854 pages), and the page renders the new settings.
  • verify-docs on the page: 13 of 13 blocks pass.

🤖 Generated with Claude Code

…eues

The /wheels/jobs/tick route ran JobRunner.tick() with its defaults, so each call ran at most
one job and nothing could raise that. It now passes three settings through:
jobsRunnerTickMaxJobs (default 1), jobsRunnerTickTimeout (default 300) and
jobsRunnerTickQueues (default every queue). The defaults keep today's behaviour. They are
settings rather than request parameters, so a caller holding the token can't widen the work
one request does; an invalid value falls back to its default. Documented in the multi-server
jobs guide.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Peter Amiri <peter@alurium.com>
@bpamiri bpamiri added this to the v4.2.0 milestone Oct 9, 2026
@bpamiri

bpamiri commented Oct 9, 2026

Copy link
Copy Markdown
Collaborator Author

REQUEST CHANGES at 5742398.

  • MEDIUM — validate the integer range before conversion. In JobTickEndpoint.$tickArguments(), IsNumeric() plus a lower bound accepts arbitrarily large values before Int(). The timeout subsequently becomes JobWorker.$claimJobUpdate()'s cf_sql_integer parameter, so a value such as 2147483648 is outside its storage contract. Also, timeout=0.5 passes validation then becomes zero, which the worker replaces with 300 seconds. Define supported positive whole-number ranges for both settings, check them before Int(), and fall back consistently. Add fractional and out-of-range cases alongside zero, negative and non-numeric values.
  • LOW — document the HTTP request budget. The guide's new 10-job/600-second example and scheduler-interval advice omit the engine request timeout. This setting caps individual jobs; it does not extend the containing HTTP request. Explain that the batch must also fit that budget (with overhead), or use a continuous worker. A killed request can leave a claimed job for recovery: fencing protects queue-state writes, not arbitrary external side effects, so idempotency still matters.

The source correctly reads these arguments from application settings only: respond() forwards only the optional token from the URL, and $runTick() constructs its own argument collection. Defaults match the previous call. The new integration spec goes through authenticated handle() and checks three real completed jobs. Final matrix and PR-CI audit remains pending.

bpamiri and others added 2 commits October 8, 2026 22:42
…number ranges

jobsRunnerTickMaxJobs takes a whole number from 1 to 1000 and jobsRunnerTickTimeout one from 1
to 86400; a fractional, out-of-range or non-numeric value falls back to the default instead of
being truncated (0.5 became 0) or passed through past the INTEGER columns it reaches. The
guide says a batch must fit inside the engine's request timeout, which the setting doesn't
extend, and that a request killed mid-job leaves the job for the reaper to recover.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Peter Amiri <peter@alurium.com>
…numbers

IsNumeric() accepts exponent notation, and Val() reads only a numeric prefix, so "1e3" became
1. A setting now has to read as plain digits (an engine's ".0" tail allowed); anything else,
including "1e3", "1e99" and a leading sign, falls back to the default.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Peter Amiri <peter@alurium.com>
@bpamiri

bpamiri commented Oct 9, 2026

Copy link
Copy Markdown
Collaborator Author

Source APPROVE at 63b9c77. The settings now require plain whole-number syntax and enforce the documented ranges before they reach the runner. The added cases cover fractional, out-of-range, exponent, signed, invalid and complex values, with defaults preserved. The guide also explains the containing HTTP request budget and recovery/idempotency requirements. Both review findings are resolved; final approval remains pending the matrix and PR CI.

@github-actions

github-actions Bot commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

Wheels Test Results

74 285 tests   72 937 ✅  27m 12s ⏱️
 5 960 suites   1 348 💤
    10 files         0 ❌

Results for commit 63b9c77.

@bpamiri

bpamiri commented Oct 9, 2026

Copy link
Copy Markdown
Collaborator Author

Final APPROVE at 63b9c77.

Audited all 32 raw JVM results from full matrix 37889345954 and all 10 from final-head narrowed matrix 37890779947: 596 bundles per leg, zero failures/errors. JobTickEndpointSpec passed all 15 cases on the baseline and all 16 on every final-head leg, including the numeric validation boundaries and the authenticated three-job drain; none skipped.

Final-head RustCFML reports 7256 passed, zero failures/errors, with its baseline comparison and matrix gate successful. PR CI: 32 passed, 2 skipped, no failures or pending checks, including successful LuCLI and CLI test jobs. Both review findings are resolved.

@bpamiri
bpamiri merged commit 056404d into develop Oct 9, 2026
50 checks passed
@bpamiri
bpamiri deleted the peter/jobs-tick-settings branch October 9, 2026 08:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(job): make the jobs tick endpoint's batch size and timeout configurable

1 participant