Commit dd77ff5
authored
fix(job): name the queue row when a persisted jobClass cannot be resolved (#3358)
* fix(job): name the queue row when a persisted jobClass cannot be resolved
`enqueue()` persists `GetMetadata(this).name` into `wheels_jobs.jobClass`, and the drain
re-instantiates with `CreateObject("component", jobRow.jobClass)`. A string produced by engine
metadata is stored and later resolved as a component path, so the round trip is only safe if
that string keeps the casing of the file on disk — component paths are case-sensitive on Linux
and not on macOS or Windows. That is a bug shape which passes in development and fails on a
production redeploy, on rows the old instance wrote and the new one drains.
I could NOT reproduce a casing drift. Probed on Lucee 7: `GetMetadata().name` comes back
canonical (`...jobs.ProbeJob`) even when the component is instantiated through a lowercase
path, so Lucee derives it from the file rather than echoing what the caller typed. That
explains why this has never bitten — it is a property of the engine, not luck. It is also not
a guarantee across the other four engines, and the issue is explicit that it is unverified
there.
So rather than guess at a normalisation fix for a drift that may not exist, this pins the
invariant as a test and improves the failure when it does not hold.
JobClassRoundTripSpec asserts, case-sensitively, that the metadata name's last segment equals
the .cfc file name; that the name is identical however the component was instantiated (a
case-insensitive filesystem resolves both spellings, so an engine that echoed the caller's
path would persist whatever casing was typed); and that a job re-instantiates from its own
persisted name. Those run on every engine × database leg, so lucee6, adobe2023, adobe2025 and
boxlang each answer the open question directly instead of being assumed safe.
$instantiateJobClass() replaces the bare CreateObject on both processing paths
(Job.$processJob and JobWorker.$executeJob) and throws Wheels.JobClassNotFound naming the
class, the queue row id, and the three real causes — casing, rename, delete. The issue's point
is that the raw error is `component not found` for a class that plainly exists, which sends
investigators to mappings and deployment; the fix is to describe the actual shape of the
problem, a string read out of a queue row.
It also throws Wheels.InvalidJobClass when the path resolves to a component with no perform().
That narrows but does NOT close the database-string-to-CreateObject shape the issue flags —
the real guarantee is still that only $enqueueJob writes that column, and the docstring says
so rather than implying the check is a security boundary.
JobWorker.$scheduleRetry's CreateObject is deliberately left alone: it is a best-effort backoff
lookup already wrapped in a try/catch that falls back to defaults, and it must not start
throwing.
Red-first, with Job.cfc and JobWorker.cfc reverted: 4734 pass / 2 fail / 1 error. The three
invariant specs pass on Lucee 7 — they are meant to, on an engine that holds the invariant.
Verification, lucee7 + sqlite, full core suite:
develop ab901cf 4732 pass / 0 fail / 0 error
this branch 4737 pass / 0 fail / 0 error
Exactly +5, the new specs.
Closes #3351
Signed-off-by: Peter Amiri <peter@alurium.com>
* fix(test): hoist the job-bridge receiver out of Adobe's parser path
The compat matrix run for this branch reported tests="0" on EVERY database for adobe2023 and
adobe2025 while lucee6, lucee7 and boxlang were all clean at +5. That is the compile-error
signature: one bad spec file zeroes the entire engine leg, because the core suite compiles
via directory="wheels.tests.specs".
From the adobe2023 artifact, not inferred:
coldfusion.compiler.CFMLParserBase$MissingNameException:
Invalid construct: Either argument or name is missing.
snippet: describe("Tests that the persisted jobClass round-trips", () => {
TEMPLATE: .../specs/jobs/JobClassRoundTripSpec.cfc LINE 17 TYPE SYNTAX
JobClassRoundTripSpec called `(new wheels.Job()).$instantiateJobClass(...)` — a parenthesized
`new` in RECEIVER position. Adobe rejects it and, as cross-engine invariant 16 warns, blames
the enclosing describe() line rather than the offending statement, so it reads like a broken
test-block signature.
Hoisting the instance to a variable is the fix. That form has 22 existing spec files as
precedent — `adapter.$getType(type = "boolean")` in CockroachDBUnitSpec is the same shape,
variable receiver with named arguments — and those files compile on the Adobe legs today.
CLAUDE.md invariant 16 is widened rather than left to be rediscovered. It documented only the
`application`-scope zero-argument form on Adobe 2025; this is a second shape in the same
MissingNameException family, it fails on Adobe 2023 as well, and named arguments do not save
it because the receiver is what the parser chokes on. Both are now written up as 16a/16b, with
the note that `(new X()).method()` is fine in application code that only runs on Lucee — it
appears in this file's own Background Jobs examples — and fatal in the core spec suite, which
compiles on all five engines.
Local Adobe verification was NOT possible: the adobe2023 container will not start on this
machine (`runc create failed ... error during container init`, reported as `engine-down` by
tools/test-matrix.sh), from both a worktree and the main checkout. So this rests on the
artifact root cause plus the in-repo precedent above, and the re-dispatched matrix is the
check.
lucee7 + sqlite, full core suite: 4737 pass / 0 fail / 0 error, unchanged from before the
hoist.
Refs #3351
Signed-off-by: Peter Amiri <peter@alurium.com>
* test(job): assert the round-trip property both engine families satisfy
The re-dispatched matrix confirmed the parser fix — adobe2023 compiles again, +5 tests on
every database — but surfaced a second, more interesting problem: one new ERROR per database
on Adobe.
Error | reports the same metadata name however the component was instantiated
| Could not find the ColdFusion component or interface
wheels.tests._assets.jobs.probejob.
Adobe's component resolver is CASE-SENSITIVE independently of the filesystem. A miscased path
does not resolve on macOS either, where the filesystem happily would. My spec instantiated
through a deliberately lowercase path to prove the engine does not echo the caller's casing
back into the persisted name — an assumption that only holds where the miscased path is
constructible at all.
So the spec was failing on Adobe for a reason *safer* than the one it was testing. Two engine
families close the same hole differently:
Lucee / BoxLang a miscased path RESOLVES, but the metadata name comes back canonical
Adobe a miscased path does not resolve at all — nothing can be persisted
because nothing can be constructed
The spec now asserts the property both satisfy: a caller's miscasing cannot reach
`wheels_jobs.jobClass`. Following cross-engine invariant 11, the flag lives on a struct set in
the try rather than a local set in the catch, so it survives on BoxLang.
This is the answer to the open question in the issue, which is the reason these specs run on
every leg instead of being assumed: the round trip is safe on all five engines, for two
different reasons, neither of which was verified before.
lucee7 + sqlite, full core suite: 4737 pass / 0 fail / 0 error.
Refs #3351
Signed-off-by: Peter Amiri <peter@alurium.com>
---------
Signed-off-by: Peter Amiri <peter@alurium.com>1 parent e8d094f commit dd77ff5
5 files changed
Lines changed: 182 additions & 3 deletions
File tree
- changelog.d
- vendor/wheels
- tests/specs/jobs
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
54 | 54 | | |
55 | 55 | | |
56 | 56 | | |
57 | | - | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
58 | 70 | | |
59 | 71 | | |
60 | 72 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
268 | 268 | | |
269 | 269 | | |
270 | 270 | | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
| 294 | + | |
| 295 | + | |
| 296 | + | |
| 297 | + | |
| 298 | + | |
| 299 | + | |
| 300 | + | |
| 301 | + | |
| 302 | + | |
| 303 | + | |
| 304 | + | |
| 305 | + | |
| 306 | + | |
| 307 | + | |
| 308 | + | |
| 309 | + | |
| 310 | + | |
| 311 | + | |
| 312 | + | |
271 | 313 | | |
272 | 314 | | |
273 | 315 | | |
| |||
315 | 357 | | |
316 | 358 | | |
317 | 359 | | |
318 | | - | |
| 360 | + | |
319 | 361 | | |
320 | 362 | | |
321 | 363 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
449 | 449 | | |
450 | 450 | | |
451 | 451 | | |
452 | | - | |
| 452 | + | |
| 453 | + | |
| 454 | + | |
| 455 | + | |
| 456 | + | |
| 457 | + | |
453 | 458 | | |
454 | 459 | | |
455 | 460 | | |
| |||
Lines changed: 118 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
0 commit comments