This is an automated email from the ASF dual-hosted git repository.

github-merge-queue[bot] pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/texera.git


The following commit(s) were added to refs/heads/main by this push:
     new ddfe28469b test(amber): cover CostBasedScheduleGenerator greedy search 
paths (#6157)
ddfe28469b is described below

commit ddfe28469bc164937bbf24488b4f84c9c91b6871
Author: Xinyuan Lin <[email protected]>
AuthorDate: Mon Jul 6 22:43:30 2026 -0700

    test(amber): cover CostBasedScheduleGenerator greedy search paths (#6157)
    
    ### What changes were proposed in this PR?
    
    Extends `CostBasedScheduleGeneratorSpec` to cover the greedy
    (`globalSearch = false`) search paths, which the existing tests never
    exercised (they only ever call `bottomUpSearch`/`topDownSearch` with
    `globalSearch = true`). Reusing the spec's existing pure-JVM harness
    (the 4-op `csv → filter → join → filter` workflow), the new tests call
    `bottomUpSearch()` and `topDownSearch()` with default args (greedy) and
    assert a schedulable result (non-empty region DAG, finite cost, chosen
    state ⊆ physical links, and fewer states explored than global search).
    
    No production code changed.
    
    ### Any related issues, documentation, discussions?
    
    Coverage gaps identified from the Codecov report for `apache/texera`
    (amber module coverage push toward 80%).
    
    ### How was this PR tested?
    
    ```
    sbt "WorkflowExecutionService/testOnly *CostBasedScheduleGeneratorSpec"
    ```
    
    All 11 pass. `scalafmt` and `scalafixAll --check` are clean.
    
    ### Was this PR authored or co-authored using generative AI tooling?
    
    Generated-by: Claude Code (Opus 4.8 [1M context])
    
    ---------
    
    Co-authored-by: Claude Opus 4.8 <[email protected]>
---
 .../CostBasedScheduleGeneratorSpec.scala           | 125 +++++++++++++++++++++
 1 file changed, 125 insertions(+)

diff --git 
a/amber/src/test/scala/org/apache/texera/amber/engine/architecture/scheduling/CostBasedScheduleGeneratorSpec.scala
 
b/amber/src/test/scala/org/apache/texera/amber/engine/architecture/scheduling/CostBasedScheduleGeneratorSpec.scala
index c9c2843acd..73f80c1b3c 100644
--- 
a/amber/src/test/scala/org/apache/texera/amber/engine/architecture/scheduling/CostBasedScheduleGeneratorSpec.scala
+++ 
b/amber/src/test/scala/org/apache/texera/amber/engine/architecture/scheduling/CostBasedScheduleGeneratorSpec.scala
@@ -552,4 +552,129 @@ class CostBasedScheduleGeneratorSpec extends AnyFlatSpec 
with MockFactory {
     )
   }
 
+  "CostBasedRegionPlanGenerator" should "finish bottom-up greedy search 
(globalSearch=false) in csv->->filter->join->filter2 workflow" in {
+    val headerlessCsvOpDesc1 = TestOperators.headerlessSmallCsvScanOpDesc()
+    val keywordOpDesc = TestOperators.keywordSearchOpDesc("column-1", "Asia")
+    val joinOpDesc = TestOperators.joinOpDesc("column-1", "column-1")
+    val keywordOpDesc2 = TestOperators.keywordSearchOpDesc("column-1", "Asia")
+    val workflow = buildWorkflow(
+      List(
+        headerlessCsvOpDesc1,
+        keywordOpDesc,
+        joinOpDesc,
+        keywordOpDesc2
+      ),
+      List(
+        LogicalLink(
+          headerlessCsvOpDesc1.operatorIdentifier,
+          PortIdentity(),
+          joinOpDesc.operatorIdentifier,
+          PortIdentity()
+        ),
+        LogicalLink(
+          headerlessCsvOpDesc1.operatorIdentifier,
+          PortIdentity(),
+          keywordOpDesc.operatorIdentifier,
+          PortIdentity()
+        ),
+        LogicalLink(
+          keywordOpDesc.operatorIdentifier,
+          PortIdentity(),
+          joinOpDesc.operatorIdentifier,
+          PortIdentity(1)
+        ),
+        LogicalLink(
+          joinOpDesc.operatorIdentifier,
+          PortIdentity(),
+          keywordOpDesc2.operatorIdentifier,
+          PortIdentity()
+        )
+      ),
+      new WorkflowContext()
+    )
+
+    val scheduleGenerator = new CostBasedScheduleGenerator(
+      workflow.context,
+      workflow.physicalPlan,
+      COORDINATOR
+    )
+
+    // Greedy search (globalSearch = false): at each schedulable/unschedulable 
state the frontier keeps only
+    // the single lowest-cost neighbor, driving the greedy branch 
(filteredNeighborStates.nonEmpty + minBy).
+    val greedyResult = scheduleGenerator.bottomUpSearch(globalSearch = false)
+
+    // A schedulable plan should have been found: the region DAG is non-empty 
and the cost is finite.
+    assert(greedyResult.regionDAG.vertexSet().asScala.nonEmpty)
+    assert(greedyResult.cost < Double.PositiveInfinity)
+
+    // The greedy search enqueues at most one neighbor per explored state, and 
each bottom-up transition materializes
+    // one more edge, so the number of states it explores is bounded linearly 
by the number of physical links. This is
+    // a guaranteed property of greedy search, unlike a comparison against 
global search whose explored count depends on
+    // early-stop pruning and queue ordering.
+    assert(greedyResult.numStatesExplored <= 
scheduleGenerator.physicalPlan.links.size + 1)
+
+    // The chosen state is a set of materialized non-blocking edges, all of 
which must be links of the physical plan.
+    assert(greedyResult.state.subsetOf(scheduleGenerator.physicalPlan.links))
+  }
+
+  "CostBasedRegionPlanGenerator" should "finish top-down greedy search 
(globalSearch=false) in csv->->filter->join->filter2 workflow" in {
+    val headerlessCsvOpDesc1 = TestOperators.headerlessSmallCsvScanOpDesc()
+    val keywordOpDesc = TestOperators.keywordSearchOpDesc("column-1", "Asia")
+    val joinOpDesc = TestOperators.joinOpDesc("column-1", "column-1")
+    val keywordOpDesc2 = TestOperators.keywordSearchOpDesc("column-1", "Asia")
+    val workflow = buildWorkflow(
+      List(
+        headerlessCsvOpDesc1,
+        keywordOpDesc,
+        joinOpDesc,
+        keywordOpDesc2
+      ),
+      List(
+        LogicalLink(
+          headerlessCsvOpDesc1.operatorIdentifier,
+          PortIdentity(),
+          joinOpDesc.operatorIdentifier,
+          PortIdentity()
+        ),
+        LogicalLink(
+          headerlessCsvOpDesc1.operatorIdentifier,
+          PortIdentity(),
+          keywordOpDesc.operatorIdentifier,
+          PortIdentity()
+        ),
+        LogicalLink(
+          keywordOpDesc.operatorIdentifier,
+          PortIdentity(),
+          joinOpDesc.operatorIdentifier,
+          PortIdentity(1)
+        ),
+        LogicalLink(
+          joinOpDesc.operatorIdentifier,
+          PortIdentity(),
+          keywordOpDesc2.operatorIdentifier,
+          PortIdentity()
+        )
+      ),
+      new WorkflowContext()
+    )
+
+    val scheduleGenerator = new CostBasedScheduleGenerator(
+      workflow.context,
+      workflow.physicalPlan,
+      COORDINATOR
+    )
+
+    // Greedy search (globalSearch = false): starting from the fully 
materialized seed state, each transition
+    // keeps only the single lowest-cost neighbor, driving the greedy branch 
(unvisitedNeighborStates.nonEmpty + minBy)
+    // over both the schedulable (Left) and unschedulable-intermediate (Right) 
legs.
+    val greedyResult = scheduleGenerator.topDownSearch(globalSearch = false)
+
+    // A schedulable plan should have been found: the region DAG is non-empty 
and the cost is finite.
+    assert(greedyResult.regionDAG.vertexSet().asScala.nonEmpty)
+    assert(greedyResult.cost < Double.PositiveInfinity)
+
+    // The chosen state is a set of materialized non-blocking edges, all of 
which must be links of the physical plan.
+    assert(greedyResult.state.subsetOf(scheduleGenerator.physicalPlan.links))
+  }
+
 }

Reply via email to