diff options
Diffstat (limited to 'core/operator/keyed_process.go')
| -rw-r--r-- | core/operator/keyed_process.go | 81 |
1 files changed, 81 insertions, 0 deletions
diff --git a/core/operator/keyed_process.go b/core/operator/keyed_process.go new file mode 100644 index 0000000..1d3c86c --- /dev/null +++ b/core/operator/keyed_process.go | |||
| @@ -0,0 +1,81 @@ | |||
| 1 | package operator | ||
| 2 | |||
| 3 | import ( | ||
| 4 | "context" | ||
| 5 | "flux/core" | ||
| 6 | "fmt" | ||
| 7 | ) | ||
| 8 | |||
| 9 | type KeyedProcesser[IN, OUT any] interface { | ||
| 10 | Open(ctx context.Context) error | ||
| 11 | ProcessElement(ctx context.Context, record core.StreamRecord[IN], out Emitter[OUT]) error | ||
| 12 | Close(ctx context.Context) error | ||
| 13 | OnTimer(ctx context.Context, timestamp int64, out Emitter[OUT]) error | ||
| 14 | } | ||
| 15 | |||
| 16 | type KeyProcessOperator[IN, OUT any] struct { | ||
| 17 | userFunc KeyedProcesser[IN, OUT] | ||
| 18 | timeService core.InternalTimerService | ||
| 19 | backend core.StateBackend | ||
| 20 | } | ||
| 21 | |||
| 22 | func NewKeyProcessOperator[IN, OUT any](userFunc KeyedProcesser[IN, OUT]) *KeyProcessOperator[IN, OUT] { | ||
| 23 | return &KeyProcessOperator[IN, OUT]{ | ||
| 24 | userFunc: userFunc, | ||
| 25 | timeService: core.NewTimerService(), | ||
| 26 | } | ||
| 27 | } | ||
| 28 | |||
| 29 | func (k *KeyProcessOperator[IN, OUT]) Open(ctx context.Context) error { | ||
| 30 | // init backend | ||
| 31 | be, ok := core.ExtractStateBackend(ctx) | ||
| 32 | if !ok { | ||
| 33 | return fmt.Errorf("state backend not found") | ||
| 34 | } | ||
| 35 | |||
| 36 | k.backend = be | ||
| 37 | // inject the timer to context for user use | ||
| 38 | ctx = core.InjectTimerService(ctx, k.timeService) | ||
| 39 | return k.userFunc.Open(ctx) | ||
| 40 | |||
| 41 | } | ||
| 42 | |||
| 43 | func (k *KeyProcessOperator[IN, OUT]) Process(ctx context.Context, record core.StreamRecord[IN], out Emitter[OUT]) error { | ||
| 44 | |||
| 45 | switch record.Type { | ||
| 46 | case core.RecordData: | ||
| 47 | if err := k.backend.SetCurrentKey(record.Key); err != nil { | ||
| 48 | return err | ||
| 49 | } | ||
| 50 | |||
| 51 | return k.userFunc.ProcessElement(ctx, record, out) | ||
| 52 | case core.RecordWatermark: | ||
| 53 | // advance the timer | ||
| 54 | triggerTimerQueue := k.timeService.AdvanceWatermark(record.Timestamp) | ||
| 55 | |||
| 56 | for _, t := range triggerTimerQueue { | ||
| 57 | |||
| 58 | if err := k.backend.SetCurrentKey(t.Key); err != nil { | ||
| 59 | return err | ||
| 60 | } | ||
| 61 | if err := k.userFunc.OnTimer(ctx, t.Timestamp, out); err != nil { | ||
| 62 | return err | ||
| 63 | } | ||
| 64 | } | ||
| 65 | return out.Emit(ctx, core.StreamRecord[OUT]{ | ||
| 66 | Type: core.RecordWatermark, | ||
| 67 | Timestamp: record.Timestamp, | ||
| 68 | }) | ||
| 69 | default: | ||
| 70 | return out.Emit(ctx, core.StreamRecord[OUT]{ | ||
| 71 | Timestamp: record.Timestamp, | ||
| 72 | BarrierID: record.BarrierID, | ||
| 73 | Key: record.Key, | ||
| 74 | }) | ||
| 75 | } | ||
| 76 | |||
| 77 | } | ||
| 78 | |||
| 79 | func (k *KeyProcessOperator[IN, OUT]) Close(ctx context.Context) error { | ||
| 80 | return k.userFunc.Close(ctx) | ||
| 81 | } | ||
