Skip to content

feat(amber): record each result port's output mode and row count - #9049

Open
mengw15 wants to merge 2 commits into
apache:mainfrom
mengw15:feat/8994-persist-port-output-mode-count
Open

mengw15 wants to merge 2 commits into
apache:mainfrom
mengw15:feat/8994-persist-port-output-mode-count

Conversation

@mengw15

@mengw15 mengw15 commented Oct 20, 2026

Copy link
Copy Markdown
Contributor

What changes were proposed in this PR?

Records two facts per result port in operator_port_executions, so a finished execution's results can be listed without the computing unit (#5363):

  • output_mode (new output_mode_enum): whether the port's result is a paginated table or a visualization. Today only the engine's physical plan knows this, and it lives in the CU. The engine already looks up the output port when it creates the port's storage, so OperatorPortResultUriAvailable now carries the port's declared mode, and the row is inserted with it.
  • result_count: the number of rows, written where result_size is written today, from the count the result store already tracks — no extra table reads. Visualization (SINGLE_SNAPSHOT) ports skip that path and keep a null count; they are never paged.

Schema change: sql/updates/52.sql (idempotent), texera_ddl.sql, and a changelog.xml entry. Existing rows keep both columns null. Nothing reads the columns yet; that is #8995.

Any related issues, documentation, discussions?

Closes #8994. Part of #5363, proposed in #8945.

How was this PR tested?

  • ExecutionResultServiceSpec: the mode carried by the event is persisted; every output mode maps to its stored form and an unrecognized one to null; the result store's count is recorded for a table port, and no count for a SINGLE_SNAPSHOT port.
  • WorkflowExecutionsResourceSpec: updateResultCount sets the column on the matching row.
  • Removed the mode and count writes and confirmed exactly the two corresponding tests fail.
  • Applied 52.sql twice to a local database (the second run is a no-op), and ran the full texera_ddl.sql into a scratch database.

Was this PR authored or co-authored using generative AI tooling?

Generated-by: Claude Code (claude-opus-5-5)

@mengw15
mengw15 requested a balanced review from Copilot October 20, 2026 20:23

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Adds persistence of result-port metadata (declared output mode and row count) into operator_port_executions so completed workflow results can be listed without consulting the computing unit’s physical plan.

Changes:

  • Introduces output_mode_enum, plus output_mode and result_count columns for operator_port_executions (DDL + Liquibase update).
  • Extends OperatorPortResultUriAvailable to carry OutputMode and persists it when storing result URIs.
  • Records table-result row counts into RESULT_COUNT and adds/updates unit tests around mode/count behavior.
File Description
sql/​updates/​52.sql Adds idempotent migration for enum + new columns
sql/​texera_ddl.sql Updates baseline schema with new enum + columns
sql/​changelog.xml Registers Liquibase changeSet for update 52
amber/​src/​main/​scala/​org/​apache/​texera/​web/​service/​ExecutionResultService.scala Persists output mode; writes result_count via resource update
amber/​src/​main/​scala/​org/​apache/​texera/​web/​resource/​dashboard/​user/​workflow/​WorkflowExecutionsResource.scala Adds updateResultCount DB update helper
amber/​src/​main/​scala/​org/​apache/​texera/​amber/​engine/​architecture/​scheduling/​RegionExecutionManager.scala Includes output mode in result-URI event
amber/​src/​main/​scala/​org/​apache/​texera/​amber/​engine/​architecture/​coordinator/​ClientEvent.scala Extends event payload to include output mode
amber/​src/​test/​scala/​org/​apache/​texera/​web/​service/​ExecutionResultServiceSpec.scala Adds coverage for output mode mapping and result_count persistence rules
amber/​src/​test/​scala/​org/​apache/​texera/​web/​resource/​dashboard/​user/​workflow/​WorkflowExecutionsResourceSpec.scala Adds test for updateResultCount behavior

Review effort: Lite

Comment thread sql/updates/52.sql
Comment thread amber/src/main/scala/org/apache/texera/web/service/ExecutionResultService.scala Outdated
@mengw15
mengw15 requested a balanced review from Copilot October 20, 2026 22:31

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

2 open issues
3 resolved since last review

Review effort: Lite

Comment thread sql/updates/52.sql
-- liquibase runner in sql/docker-compose.yml strips `SET search_path` out of these files before
-- applying them, while bin/local-dev.sh keeps it.

\c texera_db

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keeping it for consistency: 51 of the 52 scripts in sql/updates have it, and both runners strip it before executing (see #9049 (comment)).

Comment on lines +423 to +434
def updateResultCount(
eid: ExecutionIdentity,
globalPortId: GlobalPortIdentity,
count: Long
): Unit = {
context
.update(OPERATOR_PORT_EXECUTIONS)
.set(OPERATOR_PORT_EXECUTIONS.RESULT_COUNT, java.lang.Long.valueOf(count))
.where(OPERATOR_PORT_EXECUTIONS.WORKFLOW_EXECUTION_ID.eq(eid.id.toInt))
.and(OPERATOR_PORT_EXECUTIONS.GLOBAL_PORT_ID.eq(globalPortId.serializeAsString))
.execute()
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keeping Long, like updateResultSize beside it: the insert already leaves result_count null, and the only caller always has a count.

@github-actions github-actions Bot added feature engine ddl-change Changes to the TexeraDB DDL labels Oct 20, 2026
@github-actions

github-actions Bot commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

Automated Reviewer Suggestions

Based on the git blame history of the changed files, we recommend the following reviewers:

  • Contributors with relevant context: @kz930, @yrenat, @Yicong-Huang
    You can notify them by mentioning @kz930, @yrenat, @Yicong-Huang in a comment.

operator_port_executions gains output_mode and result_count, so a finished
execution's results can be listed without the computing unit: the mode lives
only in the engine's physical plan, and the count otherwise needs each table
opened. OperatorPortResultUriAvailable carries the port's declared mode, and
the count is written where result_size already is, from the result store's
own count.

Closes apache#8994
- Schema-qualify the type and table in 52.sql: the liquibase runner
  strips `SET search_path` before applying update scripts.
- Return Option from toOutputModeEnum instead of null.
- Reword updateResultCount's Scaladoc to name the table it writes.
@mengw15
mengw15 force-pushed the feat/8994-persist-port-output-mode-count branch from 6305d59 to 9e316d6 Compare October 26, 2026 19:06
@mengw15
mengw15 requested review from bobbai00 and kunwp1 October 26, 2026 19:06
@codecov-commenter

codecov-commenter commented Oct 26, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 92.85714% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 91.81%. Comparing base (1299992) to head (9e316d6).
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
...he/texera/web/service/ExecutionResultService.scala 83.33% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff            @@
##               main    #9049   +/-   ##
=========================================
  Coverage     91.81%   91.81%           
  Complexity     5069     5069           
=========================================
  Files          1271     1271           
  Lines         55208    55221   +13     
  Branches       6987     6991    +4     
=========================================
+ Hits          50688    50700   +12     
  Misses         2800     2800           
- Partials       1720     1721    +1     
Flag Coverage Δ *Carryforward flag
access-control-service 77.44% <ø> (ø)
agent-service 99.16% <ø> (ø) Carriedforward from 1299992
amber 85.97% <92.85%> (+<0.01%) ⬆️
computing-unit-managing-service 61.84% <ø> (ø)
config-service 87.50% <ø> (ø)
file-service 81.92% <ø> (ø)
frontend 96.50% <ø> (ø) Carriedforward from 1299992
notebook-migration-service 83.77% <ø> (ø)
pyamber 98.54% <ø> (ø) Carriedforward from 1299992
workflow-compiling-service 72.61% <ø> (ø)

*This pull request uses carry forward flags. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Benchmark changes need a look

🟢 4 better · 🔴 6 worse · ⚪ 5 noise (<±5%) · 0 without baseline

Compared against main 1299992 benchmarked on this same runner, so the delta is largely free of cross-runner hardware noise. The "7d avg" column still reflects the gh-pages dashboard. Treat <±5% as noise unless repeated.

Dashboard · Run

config throughput MB/s latency max Δ latest / 7d
🔴 bs=10 sw=10 sl=64 645 0.394 15,359/20,238/20,238 us 🔴 +26.2% / 🔴 +65.5%
🔴 bs=100 sw=10 sl=64 1,281 0.782 78,832/92,925/92,925 us 🔴 +12.5% / 🟢 -12.9%
⚪ bs=1000 sw=10 sl=64 1,543 0.942 646,062/725,515/725,515 us ⚪ within ±5% / 🟢 -17.2%
Baseline details

Latest main 1299992 from same runner

config metric PR latest main 7d avg Δ latest Δ 7d
bs=10 sw=10 sl=64 throughput 645 tuples/sec 682 tuples/sec 1,019 tuples/sec -5.4% -36.7%
bs=10 sw=10 sl=64 MB/s 0.394 MB/s 0.416 MB/s 0.622 MB/s -5.3% -36.7%
bs=10 sw=10 sl=64 p50 15,359 us 12,173 us 10,144 us +26.2% +51.4%
bs=10 sw=10 sl=64 p95 20,238 us 23,786 us 12,225 us -14.9% +65.5%
bs=10 sw=10 sl=64 p99 20,238 us 23,786 us 15,527 us -14.9% +30.3%
bs=100 sw=10 sl=64 throughput 1,281 tuples/sec 1,410 tuples/sec 1,371 tuples/sec -9.1% -6.6%
bs=100 sw=10 sl=64 MB/s 0.782 MB/s 0.861 MB/s 0.837 MB/s -9.2% -6.6%
bs=100 sw=10 sl=64 p50 78,832 us 70,102 us 79,910 us +12.5% -1.3%
bs=100 sw=10 sl=64 p95 92,925 us 98,341 us 87,155 us -5.5% +6.6%
bs=100 sw=10 sl=64 p99 92,925 us 98,341 us 106,643 us -5.5% -12.9%
bs=1000 sw=10 sl=64 throughput 1,543 tuples/sec 1,515 tuples/sec 1,415 tuples/sec +1.8% +9.0%
bs=1000 sw=10 sl=64 MB/s 0.942 MB/s 0.925 MB/s 0.864 MB/s +1.8% +9.0%
bs=1000 sw=10 sl=64 p50 646,062 us 647,810 us 780,407 us -0.3% -17.2%
bs=1000 sw=10 sl=64 p95 725,515 us 741,454 us 817,814 us -2.1% -11.3%
bs=1000 sw=10 sl=64 p99 725,515 us 741,454 us 848,683 us -2.1% -14.5%
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,309.99,200,128000,645,0.394,15358.89,20237.60,20237.60
1,100,10,64,20,1561.18,2000,1280000,1281,0.782,78832.38,92925.37,92925.37
2,1000,10,64,20,12959.21,20000,12800000,1543,0.942,646061.68,725515.37,725515.37

@kunwp1 kunwp1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

Comment on lines +464 to +468
WorkflowExecutionsResource.updateResultCount(
executionId,
globalPortIdOption.get,
info.tupleCount
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

info.tupleCount belongs to the operator, not to this port. onResultUpdate builds the result store keyed by logical operator from every result URI, so when an operator has two result ports (Split, If), whichever URI comes last wins. That means this row (port 0) can get the other port's count, and its result_size, which comes from port 0's own document, then disagrees with it. It's also an Int (getCount.toInt) going into a BIGINT column. That matches the existing TODO, but since the description says "per result port", it may be worth either covering them or saying so.

Comment on lines +423 to +434
def updateResultCount(
eid: ExecutionIdentity,
globalPortId: GlobalPortIdentity,
count: Long
): Unit = {
context
.update(OPERATOR_PORT_EXECUTIONS)
.set(OPERATOR_PORT_EXECUTIONS.RESULT_COUNT, java.lang.Long.valueOf(count))
.where(OPERATOR_PORT_EXECUTIONS.WORKFLOW_EXECUTION_ID.eq(eid.id.toInt))
.and(OPERATOR_PORT_EXECUTIONS.GLOBAL_PORT_ID.eq(globalPortId.serializeAsString))
.execute()
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This and updateResultSize update the same row and they run on every result-update tick. One method setting both columns would save a round trip per operator per tick and keep size and count written together.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ddl-change Changes to the TexeraDB DDL engine feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Persist each result port's output mode and row count

4 participants