@@ -851,7 +851,7 @@ func (em *ElementManager) PersistBundle(rb RunBundle, col2Coders map[string]PCol
851851 element {
852852 window : w ,
853853 timestamp : et ,
854- pane : stage .kind .updatePane (stage , pn , w , keyBytes ),
854+ pane : stage .kind .getPaneOrDefault (stage , pn , w , keyBytes , rb . BundleID ),
855855 elmBytes : elmBytes ,
856856 keyBytes : keyBytes ,
857857 sequence : seq ,
@@ -905,6 +905,7 @@ func (em *ElementManager) PersistBundle(rb RunBundle, col2Coders map[string]PCol
905905 delete (stage .inprogressKeys , k )
906906 }
907907 delete (stage .inprogressKeysByBundle , rb .BundleID )
908+ delete (stage .bundlePanes , rb .BundleID )
908909
909910 // Adjust holds as needed.
910911 for h , c := range newHolds {
@@ -1170,12 +1171,13 @@ type stageState struct {
11701171 sideInputs map [LinkID ]map [typex.Window ][][]byte // side input data for this stage, from {tid, inputID} -> window
11711172
11721173 // Fields for stateful stages which need to be per key.
1173- pendingByKeys map [string ]* dataAndTimers // pending input elements by Key, if stateful.
1174- inprogressKeys set [string ] // all keys that are assigned to bundles.
1175- inprogressKeysByBundle map [string ]set [string ] // bundle to key assignments.
1176- state map [LinkID ]map [typex.Window ]map [string ]StateData // state data for this stage, from {tid, stateID} -> window -> userKey
1177- stateTypeLen map [LinkID ]func ([]byte ) int // map from state to a function that will produce the total length of a single value in bytes.
1178- bundlesToInject []RunBundle // bundlesToInject are triggered bundles that will be injected by the watermark loop to avoid premature pipeline termination.
1174+ pendingByKeys map [string ]* dataAndTimers // pending input elements by Key, if stateful.
1175+ inprogressKeys set [string ] // all keys that are assigned to bundles.
1176+ inprogressKeysByBundle map [string ]set [string ] // bundle to key assignments.
1177+ state map [LinkID ]map [typex.Window ]map [string ]StateData // state data for this stage, from {tid, stateID} -> window -> userKey
1178+ stateTypeLen map [LinkID ]func ([]byte ) int // map from state to a function that will produce the total length of a single value in bytes.
1179+ bundlesToInject []RunBundle // bundlesToInject are triggered bundles that will be injected by the watermark loop to avoid premature pipeline termination.
1180+ bundlePanes map [string ]map [typex.Window ]map [string ]typex.PaneInfo // PaneInfo snapshot for bundles, from BundleID -> window -> userKey
11791181
11801182 // Accounting for handling watermark holds for timers.
11811183 // We track the count of timers with the same hold, and clear it from
@@ -1187,6 +1189,13 @@ type stageState struct {
11871189 processingTimeTimers * timerHandler
11881190}
11891191
1192+ // bundlePane holds pane info for a bundle.
1193+ type bundlePane struct {
1194+ win typex.Window
1195+ key string
1196+ pane typex.PaneInfo
1197+ }
1198+
11901199// stageKind handles behavioral differences between ordinary, stateful, and aggregation stage kinds.
11911200//
11921201// kinds should be stateless, and stageState retains all state for the stage,
@@ -1195,10 +1204,11 @@ type stageKind interface {
11951204 // addPending handles adding new pending elements to the stage appropriate for the kind.
11961205 addPending (ss * stageState , em * ElementManager , newPending []element ) int
11971206 // buildEventTimeBundle handles building bundles for the stage per it's kind.
1198- buildEventTimeBundle (ss * stageState , watermark mtime.Time ) (toProcess elementHeap , minTs mtime.Time , newKeys set [string ], holdsInBundle map [mtime.Time ]int , schedulable bool , pendingAdjustment int )
1207+ buildEventTimeBundle (ss * stageState , watermark mtime.Time ) (toProcess elementHeap , minTs mtime.Time , newKeys set [string ],
1208+ holdsInBundle map [mtime.Time ]int , panesInBundle []bundlePane , schedulable bool , pendingAdjustment int )
11991209
1200- // updatePane based on the stage state.
1201- updatePane (ss * stageState , pane typex.PaneInfo , w typex.Window , keyBytes []byte ) typex.PaneInfo
1210+ // getPaneOrDefault based on the stage state, element metadata, and bundle id .
1211+ getPaneOrDefault (ss * stageState , defaultPane typex.PaneInfo , w typex.Window , keyBytes []byte , bundID string ) typex.PaneInfo
12021212}
12031213
12041214// ordinaryStageKind represents stages that have no special behavior associated with them.
@@ -1207,17 +1217,17 @@ type ordinaryStageKind struct{}
12071217
12081218func (* ordinaryStageKind ) String () string { return "OrdinaryStage" }
12091219
1210- func (* ordinaryStageKind ) updatePane (ss * stageState , pane typex.PaneInfo , w typex.Window , keyBytes []byte ) typex.PaneInfo {
1211- return pane
1220+ func (* ordinaryStageKind ) getPaneOrDefault (ss * stageState , defaultPane typex.PaneInfo , w typex.Window , keyBytes []byte , bundID string ) typex.PaneInfo {
1221+ return defaultPane
12121222}
12131223
12141224// statefulStageKind require keyed elements, and handles stages with stateful transforms, with state and timers.
12151225type statefulStageKind struct {}
12161226
12171227func (* statefulStageKind ) String () string { return "StatefulStage" }
12181228
1219- func (* statefulStageKind ) updatePane (ss * stageState , pane typex.PaneInfo , w typex.Window , keyBytes []byte ) typex.PaneInfo {
1220- return pane
1229+ func (* statefulStageKind ) getPaneOrDefault (ss * stageState , defaultPane typex.PaneInfo , w typex.Window , keyBytes []byte , bundID string ) typex.PaneInfo {
1230+ return defaultPane
12211231}
12221232
12231233// aggregateStageKind handles stages that perform aggregations over their primary inputs.
@@ -1226,9 +1236,12 @@ type aggregateStageKind struct{}
12261236
12271237func (* aggregateStageKind ) String () string { return "AggregateStage" }
12281238
1229- func (* aggregateStageKind ) updatePane (ss * stageState , pane typex.PaneInfo , w typex.Window , keyBytes []byte ) typex.PaneInfo {
1239+ func (* aggregateStageKind ) getPaneOrDefault (ss * stageState , defaultPane typex.PaneInfo , w typex.Window , keyBytes []byte , bundID string ) typex.PaneInfo {
12301240 ss .mu .Lock ()
12311241 defer ss .mu .Unlock ()
1242+ if pane , ok := ss.bundlePanes [bundID ][w ][string (keyBytes )]; ok {
1243+ return pane
1244+ }
12321245 return ss .state [LinkID {}][w ][string (keyBytes )].Pane
12331246}
12341247
@@ -1459,6 +1472,24 @@ func computeNextWatermarkPane(pane typex.PaneInfo) typex.PaneInfo {
14591472 return pane
14601473}
14611474
1475+ func (ss * stageState ) savePanes (bundID string , panesInBundle []bundlePane ) {
1476+ if len (panesInBundle ) == 0 {
1477+ return
1478+ }
1479+ if ss .bundlePanes == nil {
1480+ ss .bundlePanes = make (map [string ]map [typex.Window ]map [string ]typex.PaneInfo )
1481+ }
1482+ if ss .bundlePanes [bundID ] == nil {
1483+ ss .bundlePanes [bundID ] = make (map [typex.Window ]map [string ]typex.PaneInfo )
1484+ }
1485+ for _ , p := range panesInBundle {
1486+ if ss.bundlePanes [bundID ][p.win ] == nil {
1487+ ss.bundlePanes [bundID ][p.win ] = make (map [string ]typex.PaneInfo )
1488+ }
1489+ ss.bundlePanes [bundID ][p.win ][p .key ] = p .pane
1490+ }
1491+ }
1492+
14621493// buildTriggeredBundle must be called with the stage.mu lock held.
14631494// When in discarding mode, returns 0.
14641495// When in accumulating mode, returns the number of fired elements to maintain a correct pending count.
@@ -1502,13 +1533,23 @@ func (ss *stageState) buildTriggeredBundle(em *ElementManager, key []byte, win t
15021533 if ss .inprogressKeys == nil {
15031534 ss .inprogressKeys = set [string ]{}
15041535 }
1536+ panesInBundle := []bundlePane {
1537+ {
1538+ win : win ,
1539+ key : string (key ),
1540+ pane : ss .state [LinkID {}][win ][string (key )].Pane ,
1541+ },
1542+ }
1543+
15051544 ss .makeInProgressBundle (
15061545 func () string { return rb .BundleID },
15071546 toProcess ,
15081547 ss .input ,
15091548 singleSet (string (key )),
15101549 nil ,
1550+ panesInBundle ,
15111551 )
1552+
15121553 ss .bundlesToInject = append (ss .bundlesToInject , rb )
15131554 // Bundle is marked in progress here to prevent a race condition.
15141555 em .refreshCond .L .Lock ()
@@ -1612,26 +1653,27 @@ func (ss *stageState) startEventTimeBundle(watermark mtime.Time, genBundID func(
16121653 }()
16131654 ss .mu .Lock ()
16141655 defer ss .mu .Unlock ()
1615- toProcess , minTs , newKeys , holdsInBundle , stillSchedulable , accumulatingPendingAdjustment := ss .kind .buildEventTimeBundle (ss , watermark )
1656+ toProcess , minTs , newKeys , holdsInBundle , panesInBundle , stillSchedulable , accumulatingPendingAdjustment := ss .kind .buildEventTimeBundle (ss , watermark )
16161657
16171658 if len (toProcess ) == 0 {
16181659 // If we have nothing, there's nothing to progress.
16191660 return "" , false , stillSchedulable , accumulatingPendingAdjustment
16201661 }
16211662
1622- bundID := ss .makeInProgressBundle (genBundID , toProcess , minTs , newKeys , holdsInBundle )
1663+ bundID := ss .makeInProgressBundle (genBundID , toProcess , minTs , newKeys , holdsInBundle , panesInBundle )
1664+
16231665 return bundID , true , stillSchedulable , accumulatingPendingAdjustment
16241666}
16251667
16261668// buildEventTimeBundle for ordinary stages processes all pending elements.
1627- func (* ordinaryStageKind ) buildEventTimeBundle (ss * stageState , watermark mtime.Time ) (toProcess elementHeap , minTs mtime.Time , newKeys set [string ], holdsInBundle map [mtime.Time ]int , schedulable bool , pendingAdjustment int ) {
1669+ func (* ordinaryStageKind ) buildEventTimeBundle (ss * stageState , watermark mtime.Time ) (toProcess elementHeap , minTs mtime.Time , newKeys set [string ], holdsInBundle map [mtime.Time ]int , _ [] bundlePane , schedulable bool , pendingAdjustment int ) {
16281670 toProcess = ss .pending
16291671 ss .pending = nil
1630- return toProcess , mtime .MaxTimestamp , nil , nil , true , 0
1672+ return toProcess , mtime .MaxTimestamp , nil , nil , nil , true , 0
16311673}
16321674
16331675// buildEventTimeBundle for stateful stages, processes all elements that are before the input watermark time.
1634- func (* statefulStageKind ) buildEventTimeBundle (ss * stageState , watermark mtime.Time ) (toProcess elementHeap , _ mtime.Time , _ set [string ], _ map [mtime.Time ]int , schedulable bool , pendingAdjustment int ) {
1676+ func (* statefulStageKind ) buildEventTimeBundle (ss * stageState , watermark mtime.Time ) (toProcess elementHeap , _ mtime.Time , _ set [string ], _ map [mtime.Time ]int , _ [] bundlePane , schedulable bool , pendingAdjustment int ) {
16351677 minTs := mtime .MaxTimestamp
16361678 // TODO: Allow configurable limit of keys per bundle, and elements per key to improve parallelism.
16371679 // TODO: when we do, we need to ensure that the stage remains schedualable for bundle execution, for remaining pending elements and keys.
@@ -1715,11 +1757,11 @@ keysPerBundle:
17151757 // If we're out of data, and timers were not cleared then the watermark is accurate.
17161758 stillSchedulable := ! (len (ss .pendingByKeys ) == 0 && ! timerCleared )
17171759
1718- return toProcess , minTs , newKeys , holdsInBundle , stillSchedulable , 0
1760+ return toProcess , minTs , newKeys , holdsInBundle , nil , stillSchedulable , 0
17191761}
17201762
17211763// buildEventTimeBundle for aggregation stages, processes all elements that are within the watermark for completed windows.
1722- func (* aggregateStageKind ) buildEventTimeBundle (ss * stageState , watermark mtime.Time ) (toProcess elementHeap , _ mtime.Time , _ set [string ], _ map [mtime.Time ]int , schedulable bool , pendingAdjustment int ) {
1764+ func (* aggregateStageKind ) buildEventTimeBundle (ss * stageState , watermark mtime.Time ) (toProcess elementHeap , _ mtime.Time , _ set [string ], _ map [mtime.Time ]int , panesInBundle [] bundlePane , schedulable bool , pendingAdjustment int ) {
17231765 minTs := mtime .MaxTimestamp
17241766 // TODO: Allow configurable limit of keys per bundle, and elements per key to improve parallelism.
17251767 // TODO: when we do, we need to ensure that the stage remains schedualable for bundle execution, for remaining pending elements and keys.
@@ -1814,6 +1856,13 @@ keysPerBundle:
18141856 }
18151857 ss .state [LinkID {}][elm .window ][string (elm .keyBytes )] = state
18161858
1859+ // Save latest PaneInfo for this window + key pair. It will be used in PersistBundle.
1860+ panesInBundle = append (panesInBundle , bundlePane {
1861+ win : elm .window ,
1862+ key : string (elm .keyBytes ),
1863+ pane : ss .state [LinkID {}][elm .window ][string (elm .keyBytes )].Pane ,
1864+ })
1865+
18171866 // The pane is already correct for this key + window + firing.
18181867 if ss .strat .Accumulating && ! state .Pane .IsLast {
18191868 // If this isn't the last pane, then we must add the element back to the pending store for subsequent firings.
@@ -1835,7 +1884,7 @@ keysPerBundle:
18351884 // If this is an aggregate, we need a watermark change in order to reschedule
18361885 stillSchedulable := false
18371886
1838- return toProcess , minTs , newKeys , holdsInBundle , stillSchedulable , accumulatingPendingAdjustment
1887+ return toProcess , minTs , newKeys , holdsInBundle , panesInBundle , stillSchedulable , accumulatingPendingAdjustment
18391888}
18401889
18411890func (ss * stageState ) startProcessingTimeBundle (em * ElementManager , emNow mtime.Time , genBundID func () string ) (string , bool , bool ) {
@@ -1910,14 +1959,14 @@ func (ss *stageState) startProcessingTimeBundle(em *ElementManager, emNow mtime.
19101959 // If we have nothing
19111960 return "" , false , stillSchedulable
19121961 }
1913- bundID := ss .makeInProgressBundle (genBundID , toProcess , minTs , newKeys , holdsInBundle )
1962+ bundID := ss .makeInProgressBundle (genBundID , toProcess , minTs , newKeys , holdsInBundle , nil )
19141963 return bundID , true , stillSchedulable
19151964}
19161965
19171966// makeInProgressBundle is common code to store a set of elements as a bundle in progress.
19181967//
19191968// Callers must hold the stage lock.
1920- func (ss * stageState ) makeInProgressBundle (genBundID func () string , toProcess []element , minTs mtime.Time , newKeys set [string ], holdsInBundle map [mtime.Time ]int ) string {
1969+ func (ss * stageState ) makeInProgressBundle (genBundID func () string , toProcess []element , minTs mtime.Time , newKeys set [string ], holdsInBundle map [mtime.Time ]int , panesInBundle [] bundlePane ) string {
19211970 // Catch the ordinary case for the minimum timestamp.
19221971 if toProcess [0 ].timestamp < minTs {
19231972 minTs = toProcess [0 ].timestamp
@@ -1941,6 +1990,9 @@ func (ss *stageState) makeInProgressBundle(genBundID func() string, toProcess []
19411990 ss .inprogressKeysByBundle [bundID ] = newKeys
19421991 ss .inprogressKeys .merge (newKeys )
19431992 ss .inprogressHoldsByBundle [bundID ] = holdsInBundle
1993+
1994+ // Save latest PaneInfo for PersistBundle
1995+ ss .savePanes (bundID , panesInBundle )
19441996 return bundID
19451997}
19461998
@@ -2156,6 +2208,7 @@ func (ss *stageState) createOnWindowExpirationBundles(newOut mtime.Time, em *Ele
21562208 wm ,
21572209 usedKeys ,
21582210 map [mtime.Time ]int {wm : 1 },
2211+ nil ,
21592212 )
21602213 ss .expiryWindowsByBundles [rb .BundleID ] = win
21612214
0 commit comments