Make the concurrent worker count configurable - #83
Conversation
Each worker spawns a full WordPress process, so the worker count sets the Runner's peak memory use — but it has been hardcoded to 4 with no way to change it. On memory-constrained hosts that overcommit pushes the host into swap, and the resulting swap I/O costs far more than the extra cron throughput is worth. Read the default from CAVALCADE_MAX_WORKERS so a deployment can match the worker count to its host. The default stays at 4 and explicitly-passed constructor options still take precedence, so existing behaviour is unchanged. Invalid values fall back to the default with a warning rather than leaving the limit check unmatchable. Also relax the worker limit check from === to >=, since an exact-equality check against an externally-supplied value would silently never fire if that value arrived as a numeric string. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G3gUDee9ohEwvEty2eE14M
rmccue
left a comment
There was a problem hiding this comment.
Impl needs to move, but conceptually makes sense.
In re where the config is passed, I would say whatever works best across containers + systemd, the two places that I suspect are the common deployment option. For systemd, best practice is env vars which are set via systemctl edit cavalcade + Environment=... lines - so I think env vars are the way to go here.
(Also, your PR description could use clean-up here as it seems to be AI generated, with questions more for you as the prompter; see https://journal.rmccue.io/498/pull-requests-are-for-humans/ :) )
| public function __construct( $options = [] ) { | ||
| $defaults = [ | ||
| 'max_workers' => 4, | ||
| 'max_workers' => static::get_max_workers_from_env(), |
There was a problem hiding this comment.
The design of the constructor here is that options can be fed in intentionally; this should be connected to either env vars or CLI params in bin/cavalcade instead.
Problem
The Runner's worker count is hardcoded to
4and there is no way to change it.bin/cavalcadeaccepts no options beyond a WordPress path,Runner::instance()callsnew static()with no arguments, and nothing in the codebase reads the environment. The only way to run fewer workers today is to patch the file or bypass the singleton with a custom entry script.Each worker spawns a full WordPress process, so this value is what effectively sets the Runner's peak memory use. On a memory-constrained host that matters a lot: 4 concurrent workers can exceed available RAM, at which point the host starts swapping.
We hit this on a small ECS container instance. The Cavalcade task was driving ~2,000 page-sized writes per second to a swapfile on the instance's EBS volume — effectively all of that instance's write IOPS, enough to exhaust its EBS I/O credit balance and leave the CPU sitting at 80% iowait. Cron throughput from 4 workers rather than 2 was worth far less than the I/O it was costing, but there was no way to make that trade.
Proposed solution
Read the default for
max_workersfrom aCAVALCADE_MAX_WORKERSenvironment variable so a deployment can match the worker count to the host it runs on.Design decisions worth reviewing:
bin/cavalcade's argument handling or theRunner::instance()signature, both of which are public surface.DEFAULT_MAX_WORKERSconstant. Deployments that set nothing behave exactly as before.new Runner( [ 'max_workers' => N ] )keeps working and the env var acts only as the default.STDERR. Being strict here is deliberate rather than defensive padding: an unvalidated0,-1orabcwould leavemax_workersat a value the limit check never matches, and the Runner would spawn workers without bound. Silently ignoring a bad setting is much safer than that.===to>=. Now that the value can come from outside, exact equality is fragile — a numeric string would never matchcount()and the limit would never fire.>=is equivalent when the type is right and safe when it isn't.Testing
Verified by constructing the Runner directly across a range of values:
CAVALCADE_MAX_WORKERS4""41,2,81,2,80,-3,abc,3.54, warning on STDERR2+new Runner( [ 'max_workers' => 7 ] )7(explicit option wins)Unrelated, but worth flagging
I could not run
vendor/bin/phpcsagainst this change, because the repo's dev toolchain no longer installs:composer.jsonrequireshumanmade/coding-standards: 1.0.0butcomposer.lockpinsv0.7.0, socomposer installfails outright.dealerdirect/phpcodesniffer-composer-installer ^0.6.0, which requirescomposer-plugin-api ^1.0and PHP^5.3|^7— i.e. Composer 1 and PHP ≤ 7.CI is also Travis (
travis-ci.org, shut down in 2021) pinned tocomposer self-update --1, so nothing is currently linting this repo. All pre-existing and out of scope here, but probably wants its own issue. Style in this change was matched to the surrounding file by hand.