@@ -573,26 +573,69 @@ func (t *TriggerDefault) String() string {
573573 return "Default"
574574}
575575
576- // TriggerAfterProcessingTime fires once after a specified amount of processing time
577- // has passed since an element was first seen.
578- type TriggerAfterProcessingTime struct {
576+ // TimestampTransform is the engine's representation of a processing time transform.
577+ type TimestampTransform struct {
579578 Delay time.Duration
580579 AlignToPeriod time.Duration
581580 AlignToOffset time.Duration
582581}
583582
584- func (t * TriggerAfterProcessingTime ) onElement (input triggerInput , state * StateData ) {}
583+ // TriggerAfterProcessingTime fires once after a specified amount of processing time
584+ // has passed since an element was first seen.
585+ // Uses the extra state field to track if the time has been reached.
586+ type TriggerAfterProcessingTime struct {
587+ Transforms []TimestampTransform
588+ }
589+
590+ func (t * TriggerAfterProcessingTime ) onElement (input triggerInput , state * StateData ) {
591+ ts := state .getTriggerState (t )
592+ if ts .finished {
593+ return
594+ }
595+
596+ if ts .extra == nil {
597+ ts .extra = mtime .Now ()
598+ }
599+
600+ state .setTriggerState (t , ts )
601+ }
602+
603+ func (t * TriggerAfterProcessingTime ) applyTimestampTransforms (start mtime.Time ) mtime.Time {
604+ ret := start
605+ for _ , transform := range t .Transforms {
606+ ret = ret + mtime .Time (transform .Delay / time .Millisecond )
607+ if transform .AlignToPeriod > 0 {
608+ // Formula from https://cloud.google.com/blog/products/data-analytics/windowing-and-triggering-in-apache-beam
609+ // timestamp - (timestamp % period) + period
610+ // And with an offset, we adjust before and after.
611+ tsMs := ret
612+ periodMs := mtime .Time (transform .AlignToPeriod / time .Millisecond )
613+ offsetMs := mtime .Time (transform .AlignToOffset / time .Millisecond )
614+
615+ adjustedMs := tsMs - offsetMs
616+ alignedMs := adjustedMs - (adjustedMs % periodMs ) + periodMs + offsetMs
617+ ret = alignedMs
618+ }
619+ }
620+ return ret
621+ }
585622
586623func (t * TriggerAfterProcessingTime ) shouldFire (state * StateData ) bool {
587- return false
624+ ts := state .getTriggerState (t )
625+ if ts .extra == nil {
626+ return false
627+ }
628+ startTime := ts .extra .(mtime.Time )
629+ firingTime := t .applyTimestampTransforms (startTime )
630+ return mtime .Now () > firingTime
588631}
589632
590633func (t * TriggerAfterProcessingTime ) onFire (state * StateData ) {}
591634
592- func (t * TriggerAfterProcessingTime ) reset (state * StateData ) {}
635+ func (t * TriggerAfterProcessingTime ) reset (state * StateData ) {
636+ delete (state .Trigger , t )
637+ }
593638
594639func (t * TriggerAfterProcessingTime ) String () string {
595- return fmt .Sprintf ("AfterProcessingTime[Delay: %v, AlignToPeriod: %v, AlignToOffset: %v ]" , t .Delay , t . AlignToPeriod , t . AlignToOffset )
640+ return fmt .Sprintf ("AfterProcessingTime[%v ]" , t .Transforms )
596641}
597-
598- // TODO https://github.com/apache/beam/issues/31438 Handle TriggerAfterProcessingTime
0 commit comments