POC: KEDA ScaledJob for queue jobs (payload stays in Redis)#41
POC: KEDA ScaledJob for queue jobs (payload stays in Redis)#41abnegate wants to merge 5 commits into
Conversation
Alternative to the env-var KubernetesJob broker: instead of inlining the payload in a per-message Job, producers enqueue to the ordinary Redis broker and a KEDA ScaledJob scales one-shot worker Jobs off the queue depth. Each worker drains the queue with the same Redis broker and exits — no custom broker, no payload on any Kubernetes object. Adds a drain worker, Dockerfile, redis + ScaledJob manifests, a kubectl-driven e2e (KedaTest) proving KEDA spawns Jobs that drain the queue, and keda-e2e.sh (kind + KEDA + redis). Verified end-to-end on kind. See servers/Keda/README.md for the comparison. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Adds a proof-of-concept for running utopia-php/queue jobs as Kubernetes Jobs via KEDA ScaledJob, keeping job payloads in the existing Redis queue (no Kubernetes involvement at enqueue time, no payload stored on K8s objects).
Changes:
- Introduces a KEDA worker image + one-shot drain worker script and a
ScaledJobmanifest for scaling off Redis list depth. - Adds an end-to-end PHPUnit test (
KedaTest) plus akind + KEDA + Redisharness script to validate the flow. - Documents the approach and trade-offs vs the env-var
KubernetesJobapproach.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/queue/tests/Queue/servers/Keda/worker.php | One-shot worker that drains Redis queue messages using the existing Redis broker. |
| packages/queue/tests/Queue/servers/Keda/README.md | Documentation for running the KEDA ScaledJob POC and trade-offs. |
| packages/queue/tests/Queue/servers/Keda/k8s.yaml | Kind-friendly namespace + Redis + KEDA ScaledJob manifest for scaling jobs from Redis depth. |
| packages/queue/tests/Queue/servers/Keda/Dockerfile | Builds the worker container image (PHP + redis extension + project code). |
| packages/queue/tests/Queue/E2E/Adapter/KedaTest.php | E2E test that enqueues directly into Redis and asserts KEDA drains the queue via Jobs. |
| packages/queue/tests/keda-e2e.sh | Harness that provisions kind + KEDA, loads the worker image, applies manifests, and runs the test. |
| packages/queue/phpunit.xml | Registers KedaTest in the e2e testsuite (skips unless KEDA_E2E=true). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Greptile SummaryThis POC introduces a KEDA
Confidence Score: 5/5Safe to merge; all changes are additive test infrastructure and a new adapter with no impact on existing adapters or production paths. All changes are purely additive — a new adapter class, new test files, and new shell scripts. The existing Redis broker, Swoole/Workerman adapters, and the rest of the queue library are untouched. The two observations raised are both robustness nits in new code with no effect on the existing test suite or production deployments. No files require special attention; both findings are in new files only. Important Files Changed
Reviews (5): Last reviewed commit: "test(queue): make KEDA setup best-effort..." | Re-trigger Greptile |
Factor the kind + KEDA + Redis + ScaledJob provisioning into tests/keda-lib.sh (keda_up/keda_down) and have both keda-e2e.sh and the package's e2e.sh use it, so `bin/monorepo test queue` (and thus CI) stands up KEDA and runs KedaTest against a real cluster instead of skipping it. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- keda-lib.sh: pin + SHA-256-verify Helm (same as kind/kubectl) instead of curl|bash of an unpinned installer; only tear down a kind cluster this run created, never a pre-existing one. - KedaTest: also assert the .failed.* list is empty — draining the main queue alone doesn't prove success since receive() pops before handling; note that enqueue() mirrors Redis::enqueue()'s envelope. - k8s.yaml: correct the scaling comment (KEDA scales N Jobs, workers batch-drain) and document the backoffLimit/processing-orphan crash-recovery caveat. - README: crash-recovery section (processing-list reaper via Publisher::retry). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Addressed in 4781c8f: greptile-apps
Copilot
|
The KEDA approach still needs one bit of library code: a consumer that drains the queue and exits (so a Job completes) instead of blocking like the Swoole/ Workerman adapters. Extract that out of the test worker into Utopia\Queue\Adapter\KubernetesJob, cover it with a bare-host unit test (KubernetesJobAdapterTest), and have the KEDA worker run it via Server. Producers are unchanged — they still enqueue with any Publisher (e.g. the Redis broker). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
keda_up ran unconditionally under set -e, so a provisioning failure (no Docker, kind download failure) aborted the whole e2e suite before phpunit, skipping the Swoole/Workerman/pool tests. Gate it: required in CI (hard fail so KEDA regressions surface), best-effort locally where KedaTest self-skips. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Alternative POC to the env-var
KubernetesJobbroker (#35), built to compare the two ways of running queue jobs as Kubernetes Jobs.Approach
The K8s-native pattern: the payload stays in the Redis queue, and KEDA scales worker Jobs off the queue depth.
Utopia\Queue\Broker\Redisbroker — no custom broker, no Kubernetes involvement at enqueue time.ScaledJobwatches the queue's Redis list length and spawns one-shot worker Jobs (up tomaxReplicaCount).tests/Queue/servers/Keda/worker.php) drains the queue with the same Redis broker (receive→ handle →commit) and exits.Notably this needs zero library code — just the existing Redis broker + a drain worker + the
ScaledJobmanifest.Proven end-to-end
tests/keda-e2e.shstands up kind + KEDA (helm) + Redis + theScaledJob, loads the worker image, and runsKedaTest, which enqueues messages and asserts KEDA spawns Jobs that drain the queue. Verified on kind (OK (1 test, 4 assertions); observed 8queue-worker-*Jobs draining the queue).bin/monorepo check queuepasses (pint + phpstan + rector).env-var
KubernetesJob(#35) vs KEDAScaledJob(this PR)ARG_MAXlimits, visible in pod spec, duplicated per Podappwrite-labs/php-k8sin the appRecommendation: KEDA is the more correct/standard path (keeps the queue a queue, no payload on etcd, no custom broker); its cost is requiring the KEDA operator in the cluster. See
tests/Queue/servers/Keda/README.md.🤖 Generated with Claude Code