Skip to content

feat(amber): carry cache-reuse status as a metrics flag - #6729

Open
Xiao-zhen-Liu wants to merge 3 commits into
apache:mainfrom
Xiao-zhen-Liu:cache-state-stats
Open

feat(amber): carry cache-reuse status as a metrics flag#6729
Xiao-zhen-Liu wants to merge 3 commits into
apache:mainfrom
Xiao-zhen-Liu:cache-state-stats

Conversation

@Xiao-zhen-Liu

@Xiao-zhen-Liu Xiao-zhen-Liu commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

What changes were proposed in this PR?

This PR was reworked after the discussion in #5880. The old version added a new CACHE_REUSED state; the review threads below refer to that version. The discussion concluded that a reused operator should just report COMPLETED, because every place that checks state treats completed and reused the same way. What still needs to travel is one bit: whether the operator's results came from the cache.

So the PR now adds only that bit:

  • A reused_from_cache boolean on OperatorMetrics. A reused operator still reports COMPLETED.
  • A logical operator counts as reused only when all of its physical operators are (aggregateMetrics).
  • The statistics websocket event and the TS OperatorStatistics type carry the flag to the frontend. No UI changes here; that is Add cache panel and canvas display to the workflow editor #5886.

Nothing sets the flag yet. The producer comes with #5884, so with an empty cache the engine behaves exactly like main.

Any related issues, documentation, discussions?

Part of #5881. Design discussion: #5880. Related: #5883 and #5884.

How was this PR tested?

New unit tests in ExecutionUtilsSpec cover the all-physical-operators rule and the flag staying false when nothing sets it. Existing specs pass unchanged, scalafmt is clean, and the frontend production build passes.

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

Generated-by: Claude (Claude Code)

@github-actions

github-actions Bot commented Jul 22, 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: @Yicong-Huang, @aglinxinyuan, @mengw15
    You can notify them by mentioning @Yicong-Huang, @aglinxinyuan, @mengw15 in a comment.

@Xiao-zhen-Liu

Copy link
Copy Markdown
Contributor Author

@carloea2 this is PR 2 of the operator output port result cache (MVP): the completed-from-cache operator state (CACHE_REUSED) plus cached-region statistics handling (#5883). Would appreciate your eyes on it. It lands dormant, with the empty-cache == main safety property held.

@Xiao-zhen-Liu

Copy link
Copy Markdown
Contributor Author

@Yicong-Huang would you be able to review this one? (I couldn't add you via the reviewer field from a fork PR.) It's the cache state + stats slice of the operator output port result cache (#5883, under #5881), lands dormant with the empty-cache == main safety property.

@github-actions

github-actions Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

⚠️ Benchmark changes need a look

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

Compared against main 561cd0e 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 435 0.266 22,023/29,483/29,483 us 🔴 +10.6% / 🔴 +84.4%
🟢 bs=100 sw=10 sl=64 936 0.571 107,907/119,798/119,798 us 🟢 -14.2% / 🔴 +11.7%
🔴 bs=1000 sw=10 sl=64 1,070 0.653 929,387/1,037,529/1,037,529 us 🔴 +8.7% / ⚪ within ±5%
Baseline details

Latest main 561cd0e from same runner

config metric PR latest main 7d avg Δ latest Δ 7d
bs=10 sw=10 sl=64 throughput 435 tuples/sec 464 tuples/sec 779.07 tuples/sec -6.3% -44.2%
bs=10 sw=10 sl=64 MB/s 0.266 MB/s 0.283 MB/s 0.476 MB/s -6.0% -44.1%
bs=10 sw=10 sl=64 p50 22,023 us 19,905 us 12,818 us +10.6% +71.8%
bs=10 sw=10 sl=64 p95 29,483 us 31,437 us 15,986 us -6.2% +84.4%
bs=10 sw=10 sl=64 p99 29,483 us 31,437 us 19,339 us -6.2% +52.5%
bs=100 sw=10 sl=64 throughput 936 tuples/sec 929 tuples/sec 1,011 tuples/sec +0.8% -7.4%
bs=100 sw=10 sl=64 MB/s 0.571 MB/s 0.567 MB/s 0.617 MB/s +0.7% -7.5%
bs=100 sw=10 sl=64 p50 107,907 us 103,950 us 100,965 us +3.8% +6.9%
bs=100 sw=10 sl=64 p95 119,798 us 139,621 us 107,295 us -14.2% +11.7%
bs=100 sw=10 sl=64 p99 119,798 us 139,621 us 115,531 us -14.2% +3.7%
bs=1000 sw=10 sl=64 throughput 1,070 tuples/sec 1,098 tuples/sec 1,049 tuples/sec -2.6% +2.0%
bs=1000 sw=10 sl=64 MB/s 0.653 MB/s 0.67 MB/s 0.64 MB/s -2.5% +2.0%
bs=1000 sw=10 sl=64 p50 929,387 us 912,518 us 978,248 us +1.8% -5.0%
bs=1000 sw=10 sl=64 p95 1,037,529 us 954,624 us 1,021,881 us +8.7% +1.5%
bs=1000 sw=10 sl=64 p99 1,037,529 us 954,624 us 1,050,075 us +8.7% -1.2%
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,459.38,200,128000,435,0.266,22023.19,29482.65,29482.65
1,100,10,64,20,2137.15,2000,1280000,936,0.571,107907.12,119798.31,119798.31
2,1000,10,64,20,18692.97,20000,12800000,1070,0.653,929386.78,1037528.51,1037528.51

Comment thread amber/src/main/scala/org/apache/texera/amber/engine/common/Utils.scala Outdated
@Yicong-Huang

Copy link
Copy Markdown
Contributor

I will have to check in on weekend.

@github-actions github-actions Bot added the frontend Changes related to the frontend GUI label Jul 24, 2026
@codecov-commenter

codecov-commenter commented Jul 24, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 90.12%. Comparing base (133da7b) to head (a48b223).
⚠️ Report is 91 commits behind head on main.

Additional details and impacted files
@@             Coverage Diff              @@
##               main    #6729      +/-   ##
============================================
+ Coverage     86.80%   90.12%   +3.31%     
- Complexity     4226     4429     +203     
============================================
  Files          1173     1173              
  Lines         46865    47131     +266     
  Branches       5231     5286      +55     
============================================
+ Hits          40682    42477    +1795     
+ Misses         4457     2979    -1478     
+ Partials       1726     1675      -51     
Flag Coverage Δ *Carryforward flag
access-control-service 70.00% <ø> (ø) Carriedforward from 82f92a0
agent-service 89.01% <ø> (ø) Carriedforward from 82f92a0
amber 87.46% <100.00%> (+5.33%) ⬆️
computing-unit-managing-service 60.38% <ø> (ø) Carriedforward from 82f92a0
config-service 65.97% <ø> (ø) Carriedforward from 82f92a0
file-service 69.05% <ø> (ø) Carriedforward from 82f92a0
frontend 92.59% <ø> (+3.11%) ⬆️
notebook-migration-service 78.89% <ø> (ø) Carriedforward from 82f92a0
pyamber 97.52% <ø> (ø) Carriedforward from 82f92a0
workflow-compiling-service 26.31% <ø> (ø) Carriedforward from 82f92a0

*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.

@Xiao-zhen-Liu

Copy link
Copy Markdown
Contributor Author

I will have to check in on weekend.

@Yicong-Huang Can you review this PR? Thanks.

@Yicong-Huang Yicong-Huang 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.

🔴 7 must-fix · 4 advisory · 1 polish — the new state's necessity is never established anywhere in the feature's paper trail, and its fan-out produced most of the rest of these findings, including a frontend build break.

Design & architecture (3)

  • controlreturns.proto:129 — does this enum value need to exist? Region and workflow level already derive COMPLETED for a cached region (must-fix, see inline)
  • Utils.scala:171 — status byte 6 has no producer, so the dashboard mirror added here is dead code (must-fix, see inline)
  • execute-workflow.interface.ts:162 — the OperatorState mirror, the side that does produce this state, was not updated (must-fix, see inline)

If the first one lands as "reuse COMPLETED", the next four findings and both simplifications disappear — worth settling the direction before fixing them.

Correctness (3)

  • execute-workflow.interface.ts:162CacheReused is in no ExecutionStateInfo variant; TS2322 fails the frontend build on all three platforms (must-fix, see inline)
  • ExecutionStatsService.scala:120inMap/outMap bypass the non-negative guard, so -1 reaches the canvas port label (must-fix, see inline)
  • Closes #5883 covers two of that issue's three scope bullets. The third — the zero-worker guard that still triggers the final result update — has no counterpart in the diff, and none of ExecutionResultService.scala:367, ExecutionStatsService.scala:218-219, ExecutionRuntimeService.scala:78 handles CACHE_REUSED. Either add the guard or downgrade the keyword to Part of and keep the issue open (must-fix)

Simplifications (2)

  • ExecutionUtils.scala:153-1 is a bare literal inside TupleMetrics.count's legal range; name the constant if the sentinel survives (advisory, see inline)
  • ExecutionUtils.scala:103 — eight same-typed positional parameters, with a precondition the compiler cannot enforce (advisory, see inline)

Conventions (3)

  • Retitle → "feat(amber): add cache-reused state and unknown stats markers" — the current subject is 82 characters against AGENTS.md's ~60-char guidance. feat and the amber scope are both right (advisory)
  • Description: "How was this PR tested?" lists only the two Scala specs and scalafmtCheckAll, but four frontend files changed and the frontend is where CI is red (must-fix)
  • Utils.scala:160 tells the next person to update ngbd-modal-workflow-executions.component.ts, which no longer exists; the real mirrors are workflow-execution-history.component.ts and workflow-executions-entry.ts (advisory)

Polish: 1 quick touch-up (see inline comments).

Verification trace

Traced state derivation at all three levels to check whether a cached region needs a distinct value. OperatorExecution.getState rolls worker states up through aggregateStates, but RegionExecution.getState (RegionExecution.scala:122-134) is a pure function of port completion returning only COMPLETED or RUNNING, and WorkflowExecution.getState (:161) is regionStates.forall(_ == COMPLETED). So CACHE_REUSED cannot appear above the operator level by construction — which is also why status byte 6 has no writer: ExecutionStateStore.scala:44 is the only setStatus(maptoStatusCode(...)) call site, and its input comes only from WorkflowExecution.getState or StartWorkflowHandler.scala:49,52.

Separately confirmed the empty-cache safety claim branch by branch: with cachedState = None every added cachedState.contains(s) term is false, and hasUnknown is false for non-negative counts, so both new paths are the identity. That part holds on the Scala side.

Comment thread frontend/src/app/workspace/types/execute-workflow.interface.ts Outdated
Comment thread frontend/src/app/workspace/types/execute-workflow.interface.ts Outdated
Comment thread amber/src/main/scala/org/apache/texera/amber/engine/common/Utils.scala Outdated
Comment thread amber/src/main/scala/org/apache/texera/web/service/ExecutionStatsService.scala Outdated

@carloea2 carloea2 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.

Please fix the open issues before merge. The frontend does not build, and cache reused operators are not handled in the operator state or port counts.

@Yicong-Huang Yicong-Huang 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.

🔴 0 resolved · 12 open · 5 new (5 new = 0 newly introduced · 5 late catches)

No commits since my last review, so all twelve findings there still stand and @carloea2's changes-requested still applies. This round adds five late catches, and two of them correct recommendations I made last time.

Design & architecture (2)

  • ExecutionUtils.scala:54 — nothing can put CACHE_REUSED into this input: a cached region has zero workers, so the operator rolls up to UNINITIALIZED (must-fix, see inline)
  • Utils.scala:171 — correction: keep this case. maptoStatusCode has a second, operator-level writer I missed (advisory, see inline)

Correctness (1)

  • execute-workflow.interface.ts:162 — correction: the one-line fix I proposed last round breaks exhaustiveGuard and does not compile (must-fix, see inline)

Conventions (1)

  • Description: the diff changes a rendered icon and colour (the cyan database glyph at workflow-execution-history.component.ts:356) with no before/after screenshot (advisory)

Polish: 1 quick touch-up (see inline comments).

Verification trace

Traced whether anything can produce the two new shapes. aggregateMetrics (ExecutionUtils.scala:46-55) consumes OperatorMetrics built by OperatorExecution.getStats (:92-105), whose state comes from getState (:73-84) — and that call passes no cachedState, while WorkerState (statistics.proto:32-40) has no cached member to pass. With zero workers, the shape #5883 describes for a skipped region, workerStates is empty, so aggregateStates returns UNINITIALIZED from its states.isEmpty branch; getStats also emits no port mappings at all, so nothing can carry -1. Both new branches are unreachable from the case they were built for.

Separately re-walked maptoStatusCode's call graph, which is where my earlier comment went wrong: ExecutionStateStore.scala:44 is workflow-level and cannot reach 6, but ExecutionStatsService.scala:294 is operator-level and can.

Comment thread frontend/src/app/workspace/types/execute-workflow.interface.ts Outdated
Comment thread amber/src/main/scala/org/apache/texera/amber/engine/common/Utils.scala Outdated
Add a reused_from_cache boolean to OperatorMetrics so a later scheduler
PR can report an operator whose results were reused from the operator
port cache. Per the direction settled in apache#5880, reuse is provenance of
a completed operator, not a distinct state: a reused operator reports
COMPLETED, and this flag carries the distinction to the frontend for
display.

- executionruntimestate.proto: bool reused_from_cache on OperatorMetrics.
- aggregateMetrics: a logical operator is reused only when every one of
  its physical operators is.
- OperatorAggregatedMetrics and the statistics event carry the flag; the
  TS OperatorStatistics interface gains the matching optional field.

Nothing sets the flag until the producer lands with apache#5884, so with an
empty cache the engine behaves identically to before.
@Xiao-zhen-Liu Xiao-zhen-Liu changed the title feat(amber): add cache-reused operator state and cached-region statistics handling feat(amber): carry cache-reuse status as a metrics flag Aug 11, 2026

@Yicong-Huang Yicong-Huang 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.

🟡 12 resolved · 0 open · 4 new (4 new = 4 newly introduced · 0 late catches)

The rework does what #5880 concluded, and all twelve prior findings are verified gone from the tree rather than just claimed fixed — 43 lines where the old revision needed eight synchronized surfaces. Nothing below blocks merge.

Simplifications (1)

  • ExecutionStatsService.scala:128computeStatsDiff's identity rebuild at :243-256 silently drops the new field (advisory, see inline)

Conventions (2)

  • The websocket leg is untested: 0% patch coverage on :128, and TexeraWebSocketEventSpec's fixture (:115-127) omits the field, so the spec pinning this wire contract misses it (advisory)
  • No linked issue in the sidebar (0 linked, 7 mentions). Dropping Closes was right; a non-closing link, or a checklist entry on #5881, restores the trace (advisory)

Polish: 1 quick touch-up (see inline comments).

Verification trace

Traced whether the new field survives each consumer of OperatorMetrics. On the UI path it does: statsStore.withOperatorInfo(evt.operatorMetrics) (:192) stores the metrics unmodified, so :128 reads a live value. On the persistence path it does not — computeStatsDiff (:243-256) rebuilds from fields 1 and 2 only, invisible today because runtimeStatisticsSchema has no such column.

Confirmed the dormancy claim too: the only writers are the spec helper and the forall rollup, and a primitive Boolean is never dropped under NON_ABSENT, so every payload carries reusedFromCache: false with no frontend reader. The mirror is complete — the proto sits outside frontend-proto-gen.sh's set, making execute-workflow.interface.ts:85 the required hand-mirror — and no CACHE_REUSED token survives in the tree.

Review follow-ups on apache#6729:

- computeStatsDiff rebuilt OperatorMetrics from its first two fields, an
  identity transform that silently reset reused_from_cache on the
  persistence path. Return the combined map as is.
- Set the flag non-default in TexeraWebSocketEventSpec's fixture so the
  symmetric round trip pins it on the wire.
- Reword a test comment that relied on PR-history vocabulary and drop a
  duplicate assertion.

@Yicong-Huang Yicong-Huang 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.

🟡 3 resolved · 1 open · 2 new (2 new = 0 newly introduced · 2 late catches)

Last round's two substantive findings are verified gone from the tree, not just claimed. The two below predate that review and are my own misses. Nothing blocks merge.

Correctness (1)

  • ExecutionUtils.scala:84 — the forall input is narrower than "every physical operator"; asking whether partial reuse is representable (advisory, see inline)

Simplifications (1)

  • OperatorStatisticsUpdateEvent.scala:35 — the only one of twelve DTO fields with a default argument, and it is never taken (advisory, see inline)

Conventions (1)

  • Still open: no linked issue in the sidebar (0 linked, 7 mentions). A non-closing link, or a checklist entry on #5881, restores the trace (advisory)
Verification trace

Re-traced the field's survival now that the rebuild is gone. computeStatsDiff (:240) returns the combined map directly, so storeRuntimeStatistics (:243-270) newly receives metrics retaining field 3 — harmless, since it projects 11 named values into runtimeStatisticsSchema and never enumerates the message. The websocket leg is now pinned: the round trip asserts full case-class equality (TexeraWebSocketEventSpec:262-267) on a fixture with the field non-default, so a NON_ABSENT drop would change the value read back and fail.

On the rollup I traced the input set rather than the call: aggregateMetrics' sole caller (WorkflowExecution.scala:94-110) flattens getAllRegionExecutions and groups by logicalOpId, so the set is the physical operators that currently have a region execution. Also confirmed the mirror is complete — the proto sits outside frontend-proto-gen.sh's set, making execute-workflow.interface.ts:85 the required hand-mirror — the TS ? matches the sibling optionals resetStatus relies on, and no CACHE_REUSED token survives in the tree.

Review follow-ups on apache#6729:

- OperatorAggregatedMetrics.reusedFromCache loses its default so a new
  construction site must decide the flag explicitly instead of silently
  sending false, the same shape as the deleted computeStatsDiff rebuild.
- The aggregateMetrics comment claimed "every physical operator"; the
  input holds the operators that currently have a region execution, so
  it now says so and names the shared transient window.
@Xiao-zhen-Liu
Xiao-zhen-Liu added this pull request to the merge queue Aug 18, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to no response for status checks Aug 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

engine frontend Changes related to the frontend GUI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants