Skip to content

Commit 34c9edb

Browse files
committed
Handle pending adjustment for processing time bundle correctly.
1 parent c0774c9 commit 34c9edb

2 files changed

Lines changed: 28 additions & 21 deletions

File tree

runners/prism/java/build.gradle

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -86,9 +86,8 @@ def sickbayTests = [
8686
'org.apache.beam.sdk.metrics.MetricsTest$CommittedMetricTests.testCommittedStringSetMetrics',
8787
'org.apache.beam.sdk.metrics.MetricsTest$CommittedMetricTests.testCommittedGaugeMetrics',
8888

89-
// negative WaitGroup counter when failing bundle
90-
'org.apache.beam.sdk.transforms.GroupByKeyTest$BasicTests.testAfterProcessingTimeContinuationTriggerUsingState',
91-
'org.apache.beam.sdk.testing.TestStreamTest.testEarlyPanesOfWindow',
89+
// Instead of 42, Prism got 84, which suggests two early panes of 42 are fired.
90+
// 'org.apache.beam.sdk.transforms.GroupByKeyTest$BasicTests.testAfterProcessingTimeContinuationTriggerUsingState',
9291

9392
// A regression introduced when we use number of pending elements rather than watermark to determine
9493
// the bundle readiness of a stateless stage.

sdks/go/pkg/beam/runners/prism/internal/engine/elementmanager.go

Lines changed: 26 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -477,12 +477,15 @@ func (em *ElementManager) Bundles(ctx context.Context, upstreamCancelFn context.
477477
}
478478
}
479479
if ptimeEventsReady {
480-
bundleID, ok, reschedule := ss.startProcessingTimeBundle(em, emNow, nextBundID)
480+
bundleID, ok, reschedule, pendingAdjustment := ss.startProcessingTimeBundle(em, emNow, nextBundID)
481481
// Handle the reschedule even when there's no bundle.
482482
if reschedule {
483483
em.changedStages.insert(stageID)
484484
}
485485
if ok {
486+
if pendingAdjustment > 0 {
487+
em.addPending(pendingAdjustment)
488+
}
486489
rb := RunBundle{StageID: stageID, BundleID: bundleID, Watermark: watermark}
487490

488491
em.inprogressBundles.insert(rb.BundleID)
@@ -1218,7 +1221,7 @@ type stageKind interface {
12181221
holdsInBundle map[mtime.Time]int, panesInBundle []bundlePane, schedulable bool, pendingAdjustment int)
12191222
// buildProcessingTimeBundle handles building processing-time bundles for the stage per it's kind.
12201223
buildProcessingTimeBundle(ss *stageState, em *ElementManager, emNow mtime.Time) (toProcess elementHeap, minTs mtime.Time, newKeys set[string],
1221-
holdsInBundle map[mtime.Time]int, panesInBundle []bundlePane, schedulable bool)
1224+
holdsInBundle map[mtime.Time]int, panesInBundle []bundlePane, schedulable bool, pendingAdjustment int)
12221225
// getPaneOrDefault based on the stage state, element metadata, and bundle id.
12231226
getPaneOrDefault(ss *stageState, defaultPane typex.PaneInfo, w typex.Window, keyBytes []byte, bundID string) typex.PaneInfo
12241227
}
@@ -1983,24 +1986,24 @@ keysPerBundle:
19831986
return toProcess, minTs, newKeys, holdsInBundle, panesInBundle, stillSchedulable, accumulatingPendingAdjustment
19841987
}
19851988

1986-
func (ss *stageState) startProcessingTimeBundle(em *ElementManager, emNow mtime.Time, genBundID func() string) (string, bool, bool) {
1989+
func (ss *stageState) startProcessingTimeBundle(em *ElementManager, emNow mtime.Time, genBundID func() string) (string, bool, bool, int) {
19871990
ss.mu.Lock()
19881991
defer ss.mu.Unlock()
19891992

1990-
toProcess, minTs, newKeys, holdsInBundle, panesInBundle, stillSchedulable := ss.kind.buildProcessingTimeBundle(ss, em, emNow)
1993+
toProcess, minTs, newKeys, holdsInBundle, panesInBundle, stillSchedulable, accumulatingPendingAdjustment := ss.kind.buildProcessingTimeBundle(ss, em, emNow)
19911994

19921995
if len(toProcess) == 0 {
19931996
// If we have nothing
1994-
return "", false, stillSchedulable
1997+
return "", false, stillSchedulable, accumulatingPendingAdjustment
19951998
}
19961999
bundID := ss.makeInProgressBundle(genBundID, toProcess, minTs, newKeys, holdsInBundle, panesInBundle)
19972000
slog.Debug("started a processing time bundle", "stageID", ss.ID, "bundleID", bundID, "size", len(toProcess), "emNow", emNow)
1998-
return bundID, true, stillSchedulable
2001+
return bundID, true, stillSchedulable, accumulatingPendingAdjustment
19992002
}
20002003

20012004
// handleProcessingTimeTimer contains the common code for handling processing-time timers for aggregation stages and stateful stages.
20022005
func handleProcessingTimeTimer(ss *stageState, em *ElementManager, emNow mtime.Time,
2003-
processTimerFn func(e element, toProcess []element, holdsInBundle map[mtime.Time]int, panesInBundle []bundlePane) ([]element, []bundlePane)) (elementHeap, mtime.Time, set[string], map[mtime.Time]int, []bundlePane, bool) {
2006+
processTimerFn func(e element, toProcess []element, holdsInBundle map[mtime.Time]int, panesInBundle []bundlePane) ([]element, []bundlePane, int)) (elementHeap, mtime.Time, set[string], map[mtime.Time]int, []bundlePane, bool, int) {
20042007
// TODO: Determine if it's possible and a good idea to treat all EventTime processing as a MinTime
20052008
// Special Case for ProcessingTime handling.
20062009
// Eg. Always queue EventTime elements at minTime.
@@ -2010,6 +2013,8 @@ func handleProcessingTimeTimer(ss *stageState, em *ElementManager, emNow mtime.T
20102013

20112014
var toProcess []element
20122015
var panesInBundle []bundlePane
2016+
var pendingAdjustment int
2017+
accumulatingPendingAdjustment := 0
20132018
minTs := mtime.MaxTimestamp
20142019
holdsInBundle := map[mtime.Time]int{}
20152020

@@ -2044,7 +2049,8 @@ func handleProcessingTimeTimer(ss *stageState, em *ElementManager, emNow mtime.T
20442049
minTs = e.timestamp
20452050
}
20462051

2047-
toProcess, panesInBundle = processTimerFn(e, toProcess, holdsInBundle, panesInBundle)
2052+
toProcess, panesInBundle, pendingAdjustment = processTimerFn(e, toProcess, holdsInBundle, panesInBundle)
2053+
accumulatingPendingAdjustment += pendingAdjustment
20482054
}
20492055

20502056
nextTime = ss.processingTimeTimers.Peek()
@@ -2065,24 +2071,26 @@ func handleProcessingTimeTimer(ss *stageState, em *ElementManager, emNow mtime.T
20652071
// Add a refresh if there are still processing time events to process.
20662072
stillSchedulable := (nextTime < emNow && nextTime != mtime.MaxTimestamp || len(notYet) > 0)
20672073

2068-
return toProcess, minTs, newKeys, holdsInBundle, panesInBundle, stillSchedulable
2074+
return toProcess, minTs, newKeys, holdsInBundle, panesInBundle, stillSchedulable, accumulatingPendingAdjustment
20692075
}
20702076

20712077
// buildProcessingTimeBundle for stateful stages prepares bundles for processing-time timers
2072-
func (*statefulStageKind) buildProcessingTimeBundle(ss *stageState, em *ElementManager, emNow mtime.Time) (elementHeap, mtime.Time, set[string], map[mtime.Time]int, []bundlePane, bool) {
2073-
return handleProcessingTimeTimer(ss, em, emNow, func(e element, toProcess []element, holdsInBundle map[mtime.Time]int, panesInBundle []bundlePane) ([]element, []bundlePane) {
2078+
func (*statefulStageKind) buildProcessingTimeBundle(ss *stageState, em *ElementManager, emNow mtime.Time) (elementHeap, mtime.Time, set[string], map[mtime.Time]int, []bundlePane, bool, int) {
2079+
return handleProcessingTimeTimer(ss, em, emNow, func(e element, toProcess []element, holdsInBundle map[mtime.Time]int, panesInBundle []bundlePane) ([]element, []bundlePane, int) {
20742080
holdsInBundle[e.holdTimestamp]++
20752081
// We're going to process this timer!
20762082
toProcess = append(toProcess, e)
2077-
return toProcess, nil
2083+
return toProcess, nil, 0
20782084
})
20792085
}
20802086

20812087
// buildProcessingTimeBundle for aggregation stages prepares bundles for after-processing-time triggers
2082-
func (*aggregateStageKind) buildProcessingTimeBundle(ss *stageState, em *ElementManager, emNow mtime.Time) (elementHeap, mtime.Time, set[string], map[mtime.Time]int, []bundlePane, bool) {
2083-
return handleProcessingTimeTimer(ss, em, emNow, func(e element, toProcess []element, holdsInBundle map[mtime.Time]int, panesInBundle []bundlePane) ([]element, []bundlePane) {
2088+
func (*aggregateStageKind) buildProcessingTimeBundle(ss *stageState, em *ElementManager, emNow mtime.Time) (elementHeap, mtime.Time, set[string], map[mtime.Time]int, []bundlePane, bool, int) {
2089+
return handleProcessingTimeTimer(ss, em, emNow, func(e element, toProcess []element, holdsInBundle map[mtime.Time]int, panesInBundle []bundlePane) ([]element, []bundlePane, int) {
20842090
// Different from `buildProcessingTimeBundle` for stateful stage,
20852091
// triggers don't hold back the watermark, so no holds are in the triggered bundle.
2092+
var pendingAdjustment int
2093+
var elems []element
20862094
state := ss.state[LinkID{}][e.window][string(e.keyBytes)]
20872095
endOfWindowReached := e.window.MaxTimestamp() < ss.input
20882096
ready := ss.strat.IsTriggerReady(triggerInput{
@@ -2095,22 +2103,22 @@ func (*aggregateStageKind) buildProcessingTimeBundle(ss *stageState, em *Element
20952103
state.Pane = computeNextTriggeredPane(state.Pane, endOfWindowReached)
20962104

20972105
// We're going to process this trigger!
2098-
elems, _ := ss.buildTriggeredBundle(em, string(e.keyBytes), e.window)
2106+
elems, pendingAdjustment = ss.buildTriggeredBundle(em, string(e.keyBytes), e.window)
20992107
toProcess = append(toProcess, elems...)
21002108

21012109
ss.state[LinkID{}][e.window][string(e.keyBytes)] = state
21022110

21032111
panesInBundle = append(panesInBundle, bundlePane{})
21042112
}
21052113

2106-
return toProcess, panesInBundle
2114+
return toProcess, panesInBundle, pendingAdjustment
21072115
})
21082116
}
21092117

21102118
// buildProcessingTimeBundle for stateless stages is not supposed to be called currently
2111-
func (*ordinaryStageKind) buildProcessingTimeBundle(ss *stageState, em *ElementManager, emNow mtime.Time) (elementHeap, mtime.Time, set[string], map[mtime.Time]int, []bundlePane, bool) {
2119+
func (*ordinaryStageKind) buildProcessingTimeBundle(ss *stageState, em *ElementManager, emNow mtime.Time) (elementHeap, mtime.Time, set[string], map[mtime.Time]int, []bundlePane, bool, int) {
21122120
slog.Error("ordinary stages can't have processing time elements")
2113-
return nil, mtime.MinTimestamp, nil, nil, nil, false
2121+
return nil, mtime.MinTimestamp, nil, nil, nil, false, 0
21142122
}
21152123

21162124
// makeInProgressBundle is common code to store a set of elements as a bundle in progress.

0 commit comments

Comments
 (0)