Repository navigation
fix(amber): compare dashboard workflow uids by value - #8647
Conversation
Automated Reviewer SuggestionsBased on the
|
Backport auto-label reportThis
|
|
| config | throughput | MB/s | latency | max Δ latest / 7d | |
|---|---|---|---|---|---|
| 🔴 | bs=10 sw=10 sl=64 | 562 | 0.343 | 17,369/22,425/22,425 us | 🟢 -28.8% / 🔴 +57.6% |
| 🔴 | bs=100 sw=10 sl=64 | 1,184 | 0.722 | 84,653/100,965/100,965 us | 🔴 +8.7% / ⚪ within ±5% |
| ⚪ | bs=1000 sw=10 sl=64 | 1,462 | 0.892 | 680,144/773,186/773,186 us | ⚪ within ±5% / 🟢 +23.7% |
Baseline details
Latest main 98c6337 from same runner
| config | metric | PR | latest main | 7d avg | Δ latest | Δ 7d |
|---|---|---|---|---|---|---|
| bs=10 sw=10 sl=64 | throughput | 562 tuples/sec | 565 tuples/sec | 879.16 tuples/sec | -0.5% | -36.1% |
| bs=10 sw=10 sl=64 | MB/s | 0.343 MB/s | 0.345 MB/s | 0.537 MB/s | -0.6% | -36.1% |
| bs=10 sw=10 sl=64 | p50 | 17,369 us | 15,504 us | 11,504 us | +12.0% | +51.0% |
| bs=10 sw=10 sl=64 | p95 | 22,425 us | 31,492 us | 14,234 us | -28.8% | +57.6% |
| bs=10 sw=10 sl=64 | p99 | 22,425 us | 31,492 us | 17,338 us | -28.8% | +29.3% |
| bs=100 sw=10 sl=64 | throughput | 1,184 tuples/sec | 1,251 tuples/sec | 1,147 tuples/sec | -5.4% | +3.2% |
| bs=100 sw=10 sl=64 | MB/s | 0.722 MB/s | 0.763 MB/s | 0.7 MB/s | -5.4% | +3.1% |
| bs=100 sw=10 sl=64 | p50 | 84,653 us | 77,881 us | 88,987 us | +8.7% | -4.9% |
| bs=100 sw=10 sl=64 | p95 | 100,965 us | 108,246 us | 96,473 us | -6.7% | +4.7% |
| bs=100 sw=10 sl=64 | p99 | 100,965 us | 108,246 us | 102,791 us | -6.7% | -1.8% |
| bs=1000 sw=10 sl=64 | throughput | 1,462 tuples/sec | 1,473 tuples/sec | 1,182 tuples/sec | -0.7% | +23.7% |
| bs=1000 sw=10 sl=64 | MB/s | 0.892 MB/s | 0.899 MB/s | 0.721 MB/s | -0.8% | +23.7% |
| bs=1000 sw=10 sl=64 | p50 | 680,144 us | 681,645 us | 872,751 us | -0.2% | -22.1% |
| bs=1000 sw=10 sl=64 | p95 | 773,186 us | 747,981 us | 910,151 us | +3.4% | -15.0% |
| bs=1000 sw=10 sl=64 | p99 | 773,186 us | 747,981 us | 941,479 us | +3.4% | -17.9% |
Raw CSV
config_idx,batch_size,schema_width,string_len,num_batches,total_ms,total_tuples,total_bytes,tuples_per_sec,mb_per_sec,lat_p50_us,lat_p95_us,lat_p99_us
0,10,10,64,20,355.86,200,128000,562,0.343,17368.72,22425.14,22425.14
1,100,10,64,20,1689.56,2000,1280000,1184,0.722,84652.56,100965.15,100965.15
2,1000,10,64,20,13679.26,20000,12800000,1462,0.892,680144.17,773185.97,773185.97
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #8647 +/- ##
============================================
- Coverage 91.87% 91.87% -0.01%
+ Complexity 5049 5048 -1
============================================
Files 1265 1265
Lines 54915 54915
Branches 6940 6940
============================================
- Hits 50453 50452 -1
Misses 2769 2769
- Partials 1693 1694 +1
*This pull request uses carry forward flags. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Replace the `AnyRef.eq` identity check in `WorkflowResource.mapWorkflowEntries` with value equality, and extend `HubResourceSpec` with `isOwner` cases on both sides of the `java.lang.Integer` cache boundary and with the two null-uid negatives.
4c0e721 to
e5c2ed8
Compare
|
/request-review @aglinxinyuan |
There was a problem hiding this comment.
Warning
Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.
Copilot review overview
3 open issues
What changed in this PR
Fixes incorrect isOwner computation for dashboard workflows by switching from reference equality (eq) to value equality (==) when comparing boxed java.lang.Integer UIDs, and adds regression tests that cover both sides of the JVM Integer cache boundary and null-ownership edge cases.
Changes:
- Update
WorkflowResource.mapWorkflowEntriesto compare owner UID and caller UID by value (==) instead of reference (eq). - Add
HubResourceSpeccoverage forisOwnerwith cached vs uncached Integer values (42, 127, 128, 9001) and for workflows lacking an ownership row (null owner UID).
| File | Description |
|---|---|
| amber/src/main/scala/org/apache/texera/web/resource/dashboard/user/workflow/WorkflowResource.scala | Fixes isOwner calculation by using value equality for boxed Integer UID comparison. |
| amber/src/test/scala/org/apache/texera/web/resource/dashboard/hub/HubResourceSpec.scala | Adds regression tests ensuring isOwner is correctly computed across Integer cache boundary and null owner UID scenarios. |
Review effort: Lite
…workflow/list isOwner Collapse the isOwner expression in WorkflowResource.mapWorkflowEntries to `uid != null && ownerUid == uid` and read the owner uid once per row for both isOwner and ownerId. Add a WorkflowResourceSpec case that lists workflows through retrieveWorkflowsBySessionUser for an owner with uid 1001 and asserts isOwner for the owner and for a read-only user. In HubResourceSpec, merge the two no-ownership-row cases into one, reuse the id returned by seedWorkflow, and shorten the isOwner comments to describe the default Integer autoboxing cache.


What changes were proposed in this PR?
WorkflowResource.mapWorkflowEntriessetsDashboardWorkflow.isOwnerby comparing the record's owner uid with the caller's uid. It did so withgetUid.eq(uid). Both operands arejava.lang.Integer, and Scala'seqis reference identity, so the comparison only succeeded when both boxes were the same object. Two independently autoboxed values are the same object only inside the defaultjava.lang.Integercache, which spans -128 to 127. This PR changes the comparison to==, which compares by value and is null-safe on the left operand. The owner uid is now read once per row and feeds bothisOwnerandownerId, and the flag is a single expression,uid != null && ownerUid == uid.Why this is a bug.
isOwneranswers "is the caller this workflow's owner". For every uid above 127 the answer came backfalsefor the owner's own request, while theownerIdin the same response equalled the caller's uid. The cutoff is a JVM boxing detail, not a property of the data, so the field is correct on a fresh database with small uids and silently wrong once a deployment has more than 127 accounts.WorkflowSearchQueryBuilder.toEntryImplalready computed the same flag with==, so the two implementations disagreed for the same user and the same workflow..equalswould also compare by value, butbaseWorkflowSelect()LEFT JOINsWORKFLOW_OF_USER, so the record's uid can be null and.equalswould throw;==is the null-safe form and matches the search builder.What the flag reaches today. Two endpoints go through
mapWorkflowEntries:GET /workflow/listand the hub'sgetTops, viaHubResource.fetchDashboardWorkflowsByWids. No current frontend code readsisOwnerfrom either of them. The dashboard listing that does gate affordances onisOwneris served byGET /dashboard/search, which uses the search builder and was already correct, and the hub landing page renders its own card that does not read the flag. So this PR changes API responses rather than any rendered behavior, and no screenshots apply. It is still a defect in a published REST response and a contradiction between three code paths that answer the same question.One consequence worth stating plainly.
HubResource.getTopshas no@Authparameter and takesuidas a query parameter, so the uid it reflects intoisOwneris caller-asserted. Before this change a caller passing someone else's uid only gotisOwner: trueback for uids inside the Integer cache; afterwards it works for any uid. This widens an existing weakness rather than opening a new one, and it is not privilege escalation: nothing reads that field, anddeleteWorkflowindependently re-checks ownership against the authenticated session user.isOwnerfrom that endpoint must not be treated as an authorization signal. The endpoint's missing authentication is out of scope here and belongs in its own issue.The test changes cover both endpoints that go through
mapWorkflowEntries.WorkflowResourceSpecgains one case for the authenticatedGET /workflow/listpath,set isOwner for an owner whose uid is outside the Integer cache. The suite's existing users have uids 1 and 2, inside the cache, so it could not have caught this. The new case creates an owner with uid 1001, lists throughretrieveWorkflowsBySessionUser, and assertsisOwneristruewithownerId1001 for the owner andfalsefor a user who only holds READ access to the same workflow.HubResourceSpec, whose uids already sit outside the Integer cache, gains four positiveisOwnercases and one merged negative, and drops a staleNOTEcomment that documented this bug as deliberately unasserted:set isOwner for the owner's own request when the uid is outside the Integer cache(uid 9001) is the regression proper;set isOwner for the owner's own request when the uid is inside the Integer cache(uid 42) pins that small uids, which were already correct, stay correct;set isOwner for the highest uid inside the Integer cache (127)andset isOwner for the lowest uid outside the Integer cache (128)localise the break to a single increment rather than to the gap between 42 and 9001; andclear isOwner for a workflow with no ownership row, for an authenticated and an anonymous callerasserts that a null record uid yieldsfalsewithout throwing, and that both uids being null at once also yieldsfalse, where Scala's==reports two nulls as equal and only theuid != nullguard keeps the answerfalse.The pre-existing negative for a different uid and for an anonymous caller is unchanged.
Any related issues, documentation, discussions?
Closes #8646
Two neighbouring defects were found while working on this and are deliberately not addressed here, since each is a different function and a different failure mode:
mapWorkflowEntriesdereferences a null privilege for a workflow with noworkflow_user_accessrow, andWorkflowSearchQueryBuilder.toEntryImpllacks theuid != nullguard that this file has, so an anonymouspublicSearchcaller would reportisOwner: truefor a workflow with no ownership row.How was this PR tested?
Unit tests, run against the embedded PostgreSQL that
MockTexeraDBprovides, so no external database is needed.Results:
HubResourceSpec: 69/69 passed andWorkflowResourceSpec: 81/81 passed;mapWorkflowEntriesor assert on anisOwnerflag: 190/190 passed;scalafmtCheckAll: passed; andscalafixAll --check: passed.Both new behaviours were confirmed to fail without the corresponding source text, not merely to pass with it. Replacing
ownerUid == uidwithownerUid.eq(uid)fails exactly three cases, the uid 9001 and uid 128 cases inHubResourceSpecand the uid 1001 case inWorkflowResourceSpec, each withfalse was not equal to true, while the uid 127 case still passes, which places the break at one increment. Separately, deleting theuid != nullguard while keeping==fails exactly one case, the merged no-ownership-row case inHubResourceSpec, on its anonymous-caller assertion withtrue was not equal to false; no pre-existing test catches that, which is why the case was added.Was this PR authored or co-authored using generative AI tooling?
Generated-by: Claude Code (Claude Opus 5)