16
16
17
17
package com .google .cloud .dataflow .sdk .transforms .windowing ;
18
18
19
+ import static com .google .common .base .Preconditions .checkArgument ;
20
+
19
21
import com .google .cloud .dataflow .sdk .annotations .Experimental ;
20
22
import com .google .cloud .dataflow .sdk .util .ExecutableTrigger ;
21
- import com .google .common .base .Preconditions ;
22
23
23
24
import org .joda .time .Instant ;
24
25
25
26
import java .util .Arrays ;
26
- import java .util .Iterator ;
27
27
import java .util .List ;
28
28
29
29
/**
@@ -50,7 +50,7 @@ public class AfterEach<W extends BoundedWindow> extends Trigger<W> {
50
50
51
51
private AfterEach (List <Trigger <W >> subTriggers ) {
52
52
super (subTriggers );
53
- Preconditions . checkArgument (subTriggers .size () > 1 );
53
+ checkArgument (subTriggers .size () > 1 );
54
54
}
55
55
56
56
/**
@@ -61,94 +61,75 @@ public static <W extends BoundedWindow> Trigger<W> inOrder(Trigger<W>... trigger
61
61
return new AfterEach <W >(Arrays .<Trigger <W >>asList (triggers ));
62
62
}
63
63
64
- private TriggerResult result ( TriggerContext c , TriggerResult subResult )
65
- throws Exception {
66
- if (subResult . isFire ()) {
67
- return c . trigger (). areAllSubtriggersFinished ()
68
- ? TriggerResult . FIRE_AND_FINISH : TriggerResult . FIRE ;
64
+ @ Override
65
+ public void onElement ( OnElementContext c ) throws Exception {
66
+ if (! c . trigger (). isMerging ()) {
67
+ // If merges are not possible, we need only run the first unfinished subtrigger
68
+ c . trigger (). firstUnfinishedSubTrigger (). invokeOnElement ( c ) ;
69
69
} else {
70
- return TriggerResult .CONTINUE ;
70
+ // If merges are possible, we need to run all subtriggers in parallel
71
+ for (ExecutableTrigger <W > subTrigger : c .trigger ().subTriggers ()) {
72
+ // Even if the subTrigger is done, it may be revived via merging and must have
73
+ // adequate state.
74
+ subTrigger .invokeOnElement (c );
75
+ }
71
76
}
72
77
}
73
78
74
79
@ Override
75
- public TriggerResult onElement (OnElementContext c ) throws Exception {
76
- Iterator <ExecutableTrigger <W >> iterator = c .trigger ().unfinishedSubTriggers ().iterator ();
77
-
78
- // If all the sub-triggers have finished, we should have already finished, so we know there is
79
- // at least one unfinished trigger.
80
- TriggerResult firstResult = iterator .next ().invokeElement (c );
81
-
82
- // If onMerge might be called, we need to make sure we have proper state for future triggers.
83
- if (c .trigger ().isMerging ()) {
84
- if (firstResult .isFire ()) {
85
- // If we're firing, clear out all of the later subtriggers, since we don't want to pollute
86
- // their state.
87
- resetRemaining (c , iterator );
80
+ public void onMerge (OnMergeContext context ) throws Exception {
81
+ // If merging makes a subtrigger no-longer-finished, it will automatically
82
+ // begin participating in shouldFire and onFire appropriately.
83
+
84
+ // All the following triggers are retroactively "not started" but that is
85
+ // also automatic because they are cleared whenever this trigger
86
+ // fires.
87
+ boolean priorTriggersAllFinished = true ;
88
+ for (ExecutableTrigger <W > subTrigger : context .trigger ().subTriggers ()) {
89
+ if (priorTriggersAllFinished ) {
90
+ subTrigger .invokeOnMerge (context );
91
+ priorTriggersAllFinished &= context .forTrigger (subTrigger ).trigger ().isFinished ();
88
92
} else {
89
- // Otherwise, iterate over all of them to build up some state.
90
- while (iterator .hasNext ()) {
91
- iterator .next ().invokeElement (c );
92
- }
93
+ subTrigger .invokeClear (context );
93
94
}
94
95
}
95
-
96
- return result (c , firstResult );
96
+ updateFinishedState (context );
97
97
}
98
98
99
99
@ Override
100
- public MergeResult onMerge (OnMergeContext c ) throws Exception {
101
- // Iterate over the sub-triggers to identify the "current" sub-trigger.
102
- Iterator <ExecutableTrigger <W >> iterator = c .trigger ().subTriggers ().iterator ();
103
- while (iterator .hasNext ()) {
104
- ExecutableTrigger <W > subTrigger = iterator .next ();
105
-
106
- MergeResult mergeResult = subTrigger .invokeMerge (c );
107
-
108
- if (MergeResult .CONTINUE .equals (mergeResult )) {
109
- resetRemaining (c , iterator );
110
- return MergeResult .CONTINUE ;
111
- } else if (MergeResult .FIRE .equals (mergeResult )) {
112
- resetRemaining (c , iterator );
113
- return MergeResult .FIRE ;
114
- } else if (MergeResult .FIRE_AND_FINISH .equals (mergeResult )) {
115
- resetRemaining (c , iterator );
116
- return c .trigger ().areAllSubtriggersFinished ()
117
- ? MergeResult .FIRE_AND_FINISH : MergeResult .FIRE ;
118
- }
119
- }
120
-
121
- // If we get here, all the merges indicated they were finished, which means there was at least
122
- // one merged window in which the triggers had all already finished. Given that, this AfterEach
123
- // would have already finished in that window as well. Since the window was still in the window
124
- // set for merging, we can return FINISHED (because we were finished in that window) and we also
125
- // know that there must be another trigger (parent or sibling) which hasn't finished yet, which
126
- // will FIRE, CONTINUE, or FIRE_AND_FINISH.
127
- return MergeResult .ALREADY_FINISHED ;
100
+ public Instant getWatermarkThatGuaranteesFiring (W window ) {
101
+ // This trigger will fire at least once when the first trigger in the sequence
102
+ // fires at least once.
103
+ return subTriggers .get (0 ).getWatermarkThatGuaranteesFiring (window );
128
104
}
129
105
130
- private void resetRemaining (
131
- TriggerContext c , Iterator <ExecutableTrigger <W >> triggers ) throws Exception {
132
- while (triggers .hasNext ()) {
133
- c .forTrigger (triggers .next ()).trigger ().resetTree ();
134
- }
106
+ @ Override
107
+ public Trigger <W > getContinuationTrigger (List <Trigger <W >> continuationTriggers ) {
108
+ return Repeatedly .forever (new AfterFirst <W >(continuationTriggers ));
135
109
}
136
110
137
111
@ Override
138
- public TriggerResult onTimer ( OnTimerContext c ) throws Exception {
139
- // Only deliver to the currently active subtrigger
140
- return result ( c , c . trigger (). firstUnfinishedSubTrigger (). invokeTimer ( c ) );
112
+ public boolean shouldFire ( Trigger < W >. TriggerContext context ) throws Exception {
113
+ ExecutableTrigger < W > firstUnfinished = context . trigger (). firstUnfinishedSubTrigger ();
114
+ return firstUnfinished . invokeShouldFire ( context );
141
115
}
142
116
143
117
@ Override
144
- public Instant getWatermarkThatGuaranteesFiring (W window ) {
145
- // This trigger will fire at least once when the first trigger in the sequence
146
- // fires at least once.
147
- return subTriggers .get (0 ).getWatermarkThatGuaranteesFiring (window );
118
+ public void onFire (Trigger <W >.TriggerContext context ) throws Exception {
119
+ context .trigger ().firstUnfinishedSubTrigger ().invokeOnFire (context );
120
+
121
+ // Reset all subtriggers if in a merging context; any may be revived by merging so they are
122
+ // all run in parallel for each pending pane.
123
+ if (context .trigger ().isMerging ()) {
124
+ for (ExecutableTrigger <W > subTrigger : context .trigger ().subTriggers ()) {
125
+ subTrigger .invokeClear (context );
126
+ }
127
+ }
128
+
129
+ updateFinishedState (context );
148
130
}
149
131
150
- @ Override
151
- public Trigger <W > getContinuationTrigger (List <Trigger <W >> continuationTriggers ) {
152
- return Repeatedly .forever (new AfterFirst <W >(continuationTriggers ));
132
+ private void updateFinishedState (TriggerContext context ) {
133
+ context .trigger ().setFinished (context .trigger ().firstUnfinishedSubTrigger () == null );
153
134
}
154
135
}
0 commit comments