Repository navigation
feat(workflow): freeze a pinned version as the public copy - #7853
yangzhang75 wants to merge 1 commit into
Conversation
Automated Reviewer SuggestionsBased on the
|
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #7853 +/- ##
============================================
- Coverage 92.60% 92.59% -0.02%
- Complexity 5044 5049 +5
============================================
Files 1252 1253 +1
Lines 53578 53644 +66
Branches 6671 6691 +20
============================================
+ Hits 49615 49670 +55
- Misses 2318 2320 +2
- Partials 1645 1654 +9
*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:
|
|
| config | throughput | MB/s | latency | max Δ latest / 7d | |
|---|---|---|---|---|---|
| 🔴 | bs=10 sw=10 sl=64 | 369 | 0.225 | 25,852/35,051/35,051 us | 🔴 -13.1% / 🔴 +159.3% |
| 🔴 | bs=100 sw=10 sl=64 | 766 | 0.468 | 127,304/172,229/172,229 us | 🔴 +9.0% / 🔴 +83.6% |
| ⚪ | bs=1000 sw=10 sl=64 | 902 | 0.551 | 1,096,399/1,235,078/1,235,078 us | ⚪ within ±5% / 🔴 +36.5% |
Baseline details
Latest main b43d465 from same runner
| config | metric | PR | latest main | 7d avg | Δ latest | Δ 7d |
|---|---|---|---|---|---|---|
| bs=10 sw=10 sl=64 | throughput | 369 tuples/sec | 424 tuples/sec | 963.37 tuples/sec | -13.0% | -61.7% |
| bs=10 sw=10 sl=64 | MB/s | 0.225 MB/s | 0.259 MB/s | 0.588 MB/s | -13.1% | -61.7% |
| bs=10 sw=10 sl=64 | p50 | 25,852 us | 23,355 us | 10,998 us | +10.7% | +135.1% |
| bs=10 sw=10 sl=64 | p95 | 35,051 us | 34,965 us | 13,515 us | +0.2% | +159.3% |
| bs=10 sw=10 sl=64 | p99 | 35,051 us | 34,965 us | 16,850 us | +0.2% | +108.0% |
| bs=100 sw=10 sl=64 | throughput | 766 tuples/sec | 781 tuples/sec | 1,235 tuples/sec | -1.9% | -38.0% |
| bs=100 sw=10 sl=64 | MB/s | 0.468 MB/s | 0.477 MB/s | 0.754 MB/s | -1.9% | -37.9% |
| bs=100 sw=10 sl=64 | p50 | 127,304 us | 125,285 us | 87,833 us | +1.6% | +44.9% |
| bs=100 sw=10 sl=64 | p95 | 172,229 us | 157,963 us | 93,795 us | +9.0% | +83.6% |
| bs=100 sw=10 sl=64 | p99 | 172,229 us | 157,963 us | 103,718 us | +9.0% | +66.1% |
| bs=1000 sw=10 sl=64 | throughput | 902 tuples/sec | 913 tuples/sec | 1,273 tuples/sec | -1.2% | -29.1% |
| bs=1000 sw=10 sl=64 | MB/s | 0.551 MB/s | 0.557 MB/s | 0.777 MB/s | -1.1% | -29.1% |
| bs=1000 sw=10 sl=64 | p50 | 1,096,399 us | 1,089,421 us | 861,707 us | +0.6% | +27.2% |
| bs=1000 sw=10 sl=64 | p95 | 1,235,078 us | 1,213,710 us | 904,523 us | +1.8% | +36.5% |
| bs=1000 sw=10 sl=64 | p99 | 1,235,078 us | 1,213,710 us | 931,473 us | +1.8% | +32.6% |
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,541.48,200,128000,369,0.225,25851.61,35051.28,35051.28
1,100,10,64,20,2609.43,2000,1280000,766,0.468,127303.58,172228.55,172228.55
2,1000,10,64,20,22167.25,20000,12800000,902,0.551,1096398.67,1235078.38,1235078.38a3c9eee to
a70c881
Compare
3f61548 to
fdd8454
Compare
|
/request-review: @mengw15 |
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.
Pull request overview
Adds a publish “pinning” state for workflows so public content can either follow the author’s latest edits or freeze to an explicitly pinned snapshot, with new API endpoints and a dedicated service to prevent accidental clobbering of publish-related columns.
Changes:
- Introduces
WorkflowPublishServiceimplementing publish/pin/unpin/unpublish/status state transitions. - Adds
/pin/{wid}(POST/DELETE) and/publish-status/{wid}(GET) endpoints and tightens write paths to update only owned columns. - Adds a comprehensive
WorkflowPublishSpeccovering state machine behavior and interleaving/race scenarios.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| amber/src/main/scala/org/apache/texera/web/resource/dashboard/user/workflow/WorkflowResource.scala | Adds pin/status endpoints and replaces read-modify-write updates with single-field UPDATEs to avoid clobbering publish state. |
| amber/src/main/scala/org/apache/texera/web/resource/dashboard/user/workflow/WorkflowPublishService.scala | New service owning publish/pin state transitions and drift detection vs pinned copy. |
| amber/src/test/scala/org/apache/texera/web/resource/dashboard/user/workflow/WorkflowPublishSpec.scala | New test suite validating pinning semantics, drift detection, access guards, and update interleavings. |
Suppressed comments (1)
amber/src/main/scala/org/apache/texera/web/resource/dashboard/user/workflow/WorkflowResource.scala:1
- This update path silently succeeds when the workflow row doesn’t exist (UPDATE returns 0), whereas the previous DAO fetch would fail fast. That can lead to endpoints returning 200/204 even though nothing was updated (e.g., if a workflow is deleted between access check and update). Capture the
execute()row count and throwNotFoundExceptionwhen it is 0.
/*
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
fca87a2 to
f67e00c
Compare
f67e00c to
a289afd
Compare
a289afd to
8f3ea74
Compare
| .set(WORKFLOW.PUBLISHED_DEFAULT_VIEW, null.asInstanceOf[DefaultViewEnum]) | ||
| val statement = | ||
| if (alsoUnpublish) cleared.set(WORKFLOW.IS_PUBLIC, java.lang.Boolean.FALSE) else cleared | ||
| statement.where(WORKFLOW.WID.eq(wid)).execute() |
There was a problem hiding this comment.
Also fixed, and it is the mirror of the previous one — thanks.
clearPin now carries the condition in its own statement for the unpin path (WHERE wid = ? AND is_public), and unpin tells a missing workflow from a private one the same way pinLatest does. Unpublishing deliberately keeps no such condition: taking down what is already down is a no-op, not an error.
Covered by an interleaving test that mirrors the pin one — a /private committed between the guard and the update now answers 400 and leaves the workflow down, instead of reporting "unpinned, following latest" about something no longer on show.
8f3ea74 to
0eb7eca
Compare
A public workflow follows the author's latest content, as publishing has
always done. This adds the other state: the author pins the version they
have now, and the public copy stops moving until they pin again.
`is_public` stays the on/off switch; `published_content` is the pin, NULL
while following. `WorkflowPublishService` owns the two states, and three
endpoints expose them: POST and DELETE `/workflow/pin/{wid}` to pin and
unpin, GET `/workflow/publish-status/{wid}` for what the author is shown.
Publishing and unpublishing move through the same service, so unpublishing
drops the pin rather than leaving a private workflow carrying one.
Two paths are narrowed so a pin can hold. A save wrote the whole row back,
so a publish landing while a save was in flight was silently rolled back,
and a request body could set the publish columns itself; saves now write
only name, description and content. Creating a workflow clears the publish
columns for the same reason.
Nothing reads the pinned copy yet: every workflow is in the following
state it is in today, and nothing on screen changes.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
0eb7eca to
2d8f489
Compare
|
/request-review: @aglinxinyuan |



What changes were proposed in this PR?
A public workflow follows the author's latest content, as publishing has always done: every
save reaches the Hub immediately. This adds the other state — the author pins the version
they have now, and what the public sees stops moving until they pin again.
WorkflowPublishServiceowns the two states and the moves between them:publish,pinLatest,unpin,unpublish,statusOf.is_publicstays the on/off switch;published_contentis the pin, NULL while following.view the workflow opens in — because a copy that froze only its graph would still advertise
a title nobody published. The default view matters because a form's definition rides inside
the content: serving the author's live preference over a frozen graph would open a form on a
copy that has none.
published_content = contentand so on) rather than from a workflow read a moment earlier, so there is no window in which the
author's next save lands and the pin freezes the version before it -- which would have left them
reading "you have unpublished changes" the instant after they pinned.
POST /workflow/pin/{wid}DELETE /workflow/pin/{wid}GET /workflow/publish-status/{wid}Publishing and unpublishing move through the same service, so unpublishing drops the pin
rather than leaving a private workflow carrying one. Re-publishing starts in the following
state: coming back should not silently put old public content on show again.
One writer for the publish columns. Two paths could still write them by accident, because
each read a whole row (or took a whole
Workflowfrom the client) and wrote it all back:earlier, so anything landing in between was silently rewritten to what that read had seen:
a publish undone, a pin dropped, or — worst — a workflow the author had just unpublished
put back on public show under its frozen copy. Each now writes only its own column.
published copy of its own choosing.
A plain save was the third, and fix(workflow): a plain save no longer clobbers is_public #8498 has since narrowed it on main for its own reasons,
so nothing is needed here — the tests below still pin the behaviour from this feature's side.
Together with
/set-default-view, which already wrote only its column, every endpoint nowwrites what it owns and nothing else: outside creation,
WorkflowPublishServiceis the onlywriter of
is_publicand the frozen copy.Nothing on screen changes. No read path consults the pinned copy yet and there is no UI:
every workflow stays in the following state it is in today. The endpoints answer, and nothing
calls them.
Any related issues, documentation, discussions?
Closes #7938
Part of #7828. Design discussion: #7128. Schema: #7851.
How was this PR tested?
32 cases in
WorkflowPublishSpec:re-publish does not resurrect the previous pin;
and that each of them moving afterwards is reported as an unpublished change;
rename all leave the publish state alone;
landing while a pin is in flight is the version that gets pinned. These three drive the
interleaving off the statement itself rather than off a thread, so the ordering is the same on
every run;
drift rather than throwing, a workflow with no description pins like any other,
unpinning one that is already following is a no-op rather than an error, and publishing one that
is already public leaves its pin alone.
hasUnpublishedChangescompares the two copies as parsed JSON rather than as strings — thesame graph can come back with its keys in another order, and reporting that as an edit is an
alarm the author cannot clear. One case covers it.
The narrowed write paths were checked by mutation: restoring
is_publicto the save statementturns three cases red, one of them on the CHECK constraint itself, and restoring the
read-modify-write to the rename path turns its two interleaving cases red — one showing the
publish reverted, the other showing the unpublished workflow back on public show. Restoring the
read-then-write to the pin turns the third red, showing the pin frozen one save behind.
Was this PR authored or co-authored using generative AI tooling?
Generated-by: Claude Code (claude-opus-5)