Successfully reported this slideshow.
Your SlideShare is downloading. ×

Flink Forward Berlin 2017: Matt Zimmer - Custom, Complex Windows at Scale Using Apache Flink

Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad

Check these out next

1 of 110 Ad

Flink Forward Berlin 2017: Matt Zimmer - Custom, Complex Windows at Scale Using Apache Flink

The windowing capabilities offered by most stream processing engines are limited to aligned windows of a fixed duration. However, many real-world event processing use cases don’t fit this rigid structure, resulting in awkward processing pipelines. There haven’t been good alternatives, until recently that is. Apache Flink offers a rich Window API that supports implementing unaligned windows of varying duration. In this talk, Matt Zimmer will discuss using this API at Netflix to aggregate events into windows customized along varying definitions of a session. He will talk about implementation details such as: * Handling out-of-order events * Limiting state build-up while aggregating a subset of events from an event stream * Periodically emitting early results * Creating windows bounded by a type of event Attendees will leave this talk with practical techniques and knowledge to implement their own custom windows in Apache Flink.

The windowing capabilities offered by most stream processing engines are limited to aligned windows of a fixed duration. However, many real-world event processing use cases don’t fit this rigid structure, resulting in awkward processing pipelines. There haven’t been good alternatives, until recently that is. Apache Flink offers a rich Window API that supports implementing unaligned windows of varying duration. In this talk, Matt Zimmer will discuss using this API at Netflix to aggregate events into windows customized along varying definitions of a session. He will talk about implementation details such as: * Handling out-of-order events * Limiting state build-up while aggregating a subset of events from an event stream * Periodically emitting early results * Creating windows bounded by a type of event Attendees will leave this talk with practical techniques and knowledge to implement their own custom windows in Apache Flink.

Advertisement
Advertisement

More Related Content

Slideshows for you (20)

Similar to Flink Forward Berlin 2017: Matt Zimmer - Custom, Complex Windows at Scale Using Apache Flink (20)

Advertisement

More from Flink Forward (20)

Recently uploaded (20)

Advertisement

Flink Forward Berlin 2017: Matt Zimmer - Custom, Complex Windows at Scale Using Apache Flink

  1. 1. Matt Zimmer Flink Forward Berlin 12 September | 2017 Custom, Complex Windows at Scale Using Apache Flink @zimmermatt
  2. 2. Agenda. ● Motivating Use Cases. ● Window Requirements. ● The Solution (Conceptual). ● Event Processing Flow. ● Apache Flink Window API Walk-Through. ● The Solution (Detail). ● Pitfalls to Watch Out For. ● Alternative Implementations. ● Questions. @zimmermatt
  3. 3. ● Motivating Use Cases. ● Window Requirements. ● The Solution (Conceptual). ● Event Processing Flow. ● Apache Flink Window API Walk-Through. ● The Solution in (Detail). ● Pitfalls to Watch Out For. ● Alternative Implementations. ● Questions. @zimmermatt
  4. 4. @zimmermatt
  5. 5. @zimmermatt Use Case: Most Simple.
  6. 6. @zimmermatt Use Case: Most Simple.
  7. 7. @zimmermatt Use Case: More Complex.
  8. 8. @zimmermatt Use Case: More Complex.
  9. 9. @zimmermatt Use Case: Most Complex.
  10. 10. @zimmermatt Use Case: Most Complex.
  11. 11. ● Events ○ Millions per second ○ 100s billions per day ● Data Flowing In ○ 10s of GiB per second ○ Low (single digit) PiB per day ● State ○ 10s of TiB Targeted Scale. @zimmermatt
  12. 12. ● Motivating Use Cases. ● Window Requirements. ● The Solution (Conceptual). ● Event Processing Flow. ● Apache Flink Window API Walk-Through. ● The Solution in (Detail). ● Pitfalls to Watch Out For. ● Alternative Implementations. ● Questions. @zimmermatt
  13. 13. ● Unaligned windows ● Bounded by event type ● Handle out of order events ● Emit early results ● Capture relevant events; ignore the rest Window Requirements. @zimmermatt
  14. 14. Can we use a standard window type? @zimmermatt
  15. 15. @zimmermatt Tumbling Window?
  16. 16. @zimmermatt Sliding Window?
  17. 17. @zimmermatt Sliding Window?
  18. 18. @zimmermatt Sliding Window?
  19. 19. @zimmermatt Sliding Window?
  20. 20. @zimmermatt Sliding Window?
  21. 21. @zimmermatt Apache Beam Session Window?
  22. 22. @zimmermatt Apache Beam Session Window?
  23. 23. @zimmermatt Apache Beam Session Window?
  24. 24. @zimmermatt Apache Beam Session Window?
  25. 25. @zimmermatt Apache Beam Session Window?
  26. 26. @zimmermatt Apache Beam Session Window?
  27. 27. @zimmermatt Apache Beam Session Window?
  28. 28. ● Motivating Use Cases. ● Window Requirements. ● The Solution (Conceptual). ● Event Processing Flow. ● Apache Flink Window API Walk-Through. ● The Solution in (Detail). ● Pitfalls to Watch Out For. ● Alternative Implementations. ● Questions. @zimmermatt
  29. 29. ● Unaligned windows ● Bounded by event type ● Handle out of order events ● Emit early results ● Capture relevant events; ignore the rest @zimmermatt Window Requirements Redux.
  30. 30. The solution at 10,000 feet. @zimmermatt
  31. 31. The solution at 3,048 feet meters. @zimmermatt x
  32. 32. @zimmermatt The Solution (Conceptual). Time User A User B User C t4 t3 t1 t2 t5 t3 t1 t2 t5
  33. 33. @zimmermatt Time User A User B User C t4 t3 t1 t2 t5 t3 t1 t2 t5 The Solution (Conceptual).
  34. 34. @zimmermatt The Solution (Conceptual). Time User A t4 t3 t1 t2 t5
  35. 35. @zimmermatt The Solution (Conceptual). Time User A t4 t3 t1 t2 t5
  36. 36. @zimmermatt The Solution (Conceptual). Time User A t4 t3 t1 t2 t5
  37. 37. @zimmermatt The Solution (Conceptual). Time User A t4 t3 t1 t2 t5 Watermark
  38. 38. @zimmermatt The Solution (Conceptual). Time User A t4 t3 t1 t2 t5 Watermark
  39. 39. @zimmermatt The Solution (Conceptual). Time User A t4 t3 t1 t2 t5 Watermark
  40. 40. @zimmermatt The Solution (Conceptual). Time User A t4 t3 t1 t2 t5 Watermark
  41. 41. @zimmermatt The Solution (Conceptual). Time User A t4 t3 t1 t2 t5 Watermark
  42. 42. @zimmermatt The Solution (Conceptual). Time User A t4 t3 t1 t2 t5 Watermark
  43. 43. @zimmermatt The Solution (Conceptual). Time User A t4 t3 t1 t2 t5 Watermark
  44. 44. @zimmermatt The Solution (Conceptual). Time User A t4 t3 t1 t2 t5 Watermark
  45. 45. @zimmermatt The Solution (Conceptual). Time User A t4 t3 t1 t2 t5 Watermark
  46. 46. ● Motivating Use Cases. ● Window Requirements. ● The Solution (Conceptual). ● Event Processing Flow. ● Apache Flink Window API Walk-Through. ● The Solution in (Detail). ● Pitfalls to Watch Out For. ● Alternative Implementations. ● Questions. @zimmermatt
  47. 47. 1. Window assigner. @zimmermatt Event processing flow.
  48. 48. 1. Window assigner. a. Assign Event to Window(s) (assignWindows). @zimmermatt Event processing flow.
  49. 49. 1. Window assigner. a. Assign Event to Window(s) (assignWindows). b. Merge Windows (mergeWindows). @zimmermatt Event processing flow.
  50. 50. 1. Window assigner. a. Assign Event to Window(s) (assignWindows). b. Merge Windows (mergeWindows). 2. Trigger Handlers. @zimmermatt Event processing flow.
  51. 51. 1. Window assigner. a. Assign Event to Window(s) (assignWindows). b. Merge Windows (mergeWindows). 2. Trigger Handlers. a. Element (onElement). @zimmermatt Event processing flow.
  52. 52. 1. Window assigner. a. Assign Event to Window(s) (assignWindows). b. Merge Windows (mergeWindows). 2. Trigger Handlers. a. Element (onElement). b. Merge (onMerge). @zimmermatt Event processing flow.
  53. 53. 1. Window assigner. a. Assign Event to Window(s) (assignWindows). b. Merge Windows (mergeWindows). 2. Trigger Handlers. a. Element (onElement). b. Merge (onMerge). 3. Trigger Timers. @zimmermatt Event processing flow.
  54. 54. 1. Window assigner. a. Assign Event to Window(s) (assignWindows). b. Merge Windows (mergeWindows). 2. Trigger Handlers. a. Element (onElement). b. Merge (onMerge). 3. Trigger Timers. a. Processing Time (onProcessingTime). @zimmermatt Event processing flow.
  55. 55. 1. Window assigner. a. Assign Event to Window(s) (assignWindows). b. Merge Windows (mergeWindows). 2. Trigger Handlers. a. Element (onElement). b. Merge (onMerge). 3. Trigger Timers. a. Processing Time (onProcessingTime). b. Event Time (onEventTime). @zimmermatt Event processing flow.
  56. 56. 1. Window assigner. a. Assign Event to Window(s) (assignWindows). b. Merge Windows (mergeWindows). 2. Trigger Handlers. a. Element (onElement). b. Merge (onMerge). 3. Trigger Timers. a. Processing Time (onProcessingTime). b. Event Time (onEventTime). 4. Evictor. @zimmermatt Event processing flow.
  57. 57. 1. Window assigner. a. Assign Event to Window(s) (assignWindows). b. Merge Windows (mergeWindows). 2. Trigger Handlers. a. Element (onElement). b. Merge (onMerge). 3. Trigger Timers. a. Processing Time (onProcessingTime). b. Event Time (onEventTime). 4. Evictor. a. Before (evictBefore). @zimmermatt Event processing flow.
  58. 58. 1. Window assigner. a. Assign Event to Window(s) (assignWindows). b. Merge Windows (mergeWindows). 2. Trigger Handlers. a. Element (onElement). b. Merge (onMerge). 3. Trigger Timers. a. Processing Time (onProcessingTime). b. Event Time (onEventTime). 4. Evictor. a. Before (evictBefore). b. Evaluate Window (WindowFunction#apply). @zimmermatt Event processing flow.
  59. 59. 1. Window assigner. a. Assign Event to Window(s) (assignWindows). b. Merge Windows (mergeWindows). 2. Trigger Handlers. a. Element (onElement). b. Merge (onMerge). 3. Trigger Timers. a. Processing Time (onProcessingTime). b. Event Time (onEventTime). 4. Evictor. a. Before (evictBefore). b. Evaluate Window (WindowFunction#apply). c. After (evictAfter). @zimmermatt Event processing flow.
  60. 60. ● Motivating Use Cases. ● Window Requirements. ● The Solution (Conceptual). ● Event Processing Flow. ● Apache Flink Window API Walk-Through. ● The Solution in (Detail). ● Pitfalls to Watch Out For. ● Alternative Implementations. ● Questions. @zimmermatt
  61. 61. @zimmermatt Window API: Window. package org.apache.flink.streaming.api.windowing.windows; public abstract class Window { public abstract long maxTimestamp(); } * If you implement Window, you’ll need to provide a TypeSerializer implementation for it.
  62. 62. package org.apache.flink.streaming.api.windowing.assigners; public abstract class WindowAssigner<T, W extends Window> implements Serializable { public abstract Collection<W> assignWindows(T element, long timestamp, WindowAssignerContext context); public abstract Trigger<T, W> getDefaultTrigger(StreamExecutionEnvironment env); public abstract TypeSerializer<W> getWindowSerializer(ExecutionConfig executionConfig); public abstract boolean isEventTime(); public abstract static class WindowAssignerContext { public abstract long getCurrentProcessingTime(); } } @zimmermatt Window API: WindowAssigner.
  63. 63. @zimmermatt Window API: MergingWindowAssigner. package org.apache.flink.streaming.api.windowing.assigners; public abstract class MergingWindowAssigner<T, W extends Window> extends WindowAssigner<T, W> { public abstract void mergeWindows(Collection<W> windows, MergeCallback<W> callback); public interface MergeCallback<W> { void merge(Collection<W> toBeMerged, W mergeResult); } }
  64. 64. @zimmermatt Window API: Trigger. package org.apache.flink.streaming.api.windowing.triggers; public abstract class Trigger<T, W extends Window> implements Serializable { ... public abstract TriggerResult onElement(T element, long timestamp, W window, TriggerContext ctx) throws Exception; public boolean canMerge() { return false; } public void onMerge(W window, OnMergeContext ctx) throws Exception { throws by default } ... }
  65. 65. @zimmermatt Window API: Trigger. package org.apache.flink.streaming.api.windowing.triggers; public abstract class Trigger<T, W extends Window> implements Serializable { ... public abstract TriggerResult onProcessingTime(long time, W window, TriggerContext ctx) throws Exception; public abstract TriggerResult onEventTime(long time, W window, TriggerContext ctx) throws Exception; ... }
  66. 66. @zimmermatt Window API: Trigger. package org.apache.flink.streaming.api.windowing.triggers; public abstract class Trigger<T, W extends Window> implements Serializable { ... public abstract void clear(W window, TriggerContext ctx) throws Exception; public interface TriggerContext { ... } public interface OnMergeContext extends TriggerContext { ... } ... }
  67. 67. @zimmermatt Window API: Trigger. package org.apache.flink.streaming.api.windowing.triggers; public abstract class Trigger<T, W extends Window> implements Serializable { ... public interface TriggerContext { long getCurrentProcessingTime(); MetricGroup getMetricGroup(); long getCurrentWatermark(); void registerProcessingTimeTimer(long time); void registerEventTimeTimer(long time); void deleteProcessingTimeTimer(long time); void deleteEventTimeTimer(long time); <S extends State> S getPartitionedState(StateDescriptor<S, ?> stateDescriptor); } public interface OnMergeContext extends TriggerContext { <S extends MergingState<?, ?>> void mergePartitionedState(StateDescriptor<S, ?> stateDescriptor); } }
  68. 68. @zimmermatt package org.apache.flink.streaming.api.windowing.evictors; public interface Evictor<T, W extends Window> extends Serializable { void evictBefore(Iterable<TimestampedValue<T>> elements, int size, W window, EvictorContext evictorContext); void evictAfter(Iterable<TimestampedValue<T>> elements, int size, W window, EvictorContext evictorContext); ... } Window API: Evictor.
  69. 69. @zimmermatt package org.apache.flink.streaming.api.windowing.evictors; public interface Evictor<T, W extends Window> extends Serializable { ... interface EvictorContext { long getCurrentProcessingTime(); MetricGroup getMetricGroup(); long getCurrentWatermark(); } } Window API: Evictor.
  70. 70. The solution in detail. @zimmermatt
  71. 71. @zimmermatt Custom Window: WindowAssigner. public class CustomWindowAssigner<E extends CustomEvent> extends MergingWindowAssigner<E, CustomWindow<E>> { ... @Override public Collection<CustomWindow<E>> assignWindows(E element, long timestamp, WindowAssignerContext context) { return Collections.singletonList(new CustomWindow<>(element, timeoutDuration)); } ... }
  72. 72. @zimmermatt Custom Window: WindowAssigner. public class CustomWindowAssigner<E extends CustomEvent> extends MergingWindowAssigner<E, CustomWindow<E>> { ... @Override public void mergeWindows(Collection<CustomWindow<E>> mergeCandidates, MergeCallback<CustomWindow<E>> mergeCallback) { final CustomWindow<E> sessionWindow = calculateSessionWindow(mergeCandidates); final Collection<CustomWindow<E>> inWindow = filterWithinWindow(mergeCandidates); // MergeCallback#merge implementation expects 2 or more. if (inWindow.size() > 1) { mergeCallback.merge(inWindow, sessionWindow); } } ... }
  73. 73. @zimmermatt Custom Window: Window. public class CustomWindow<E extends CustomEvent> extends Window { ... @Override public long maxTimestamp() { return maxTimestamp; } ... }
  74. 74. @zimmermatt Custom Window: Window. public class CustomWindow<E extends CustomEvent> extends Window { ... @Override public boolean equals(Object o) { // Important: equals implementation must compare using “value” semantics } @Override public int hashCode() { // Important: same for hashCode implementation } ... }
  75. 75. @zimmermatt Custom Window: Window. public class CustomWindow<E extends CustomEvent> extends Window { ... public static class Serializer<T extends CustomEvent> extends TypeSerializer<CustomWindow<T>> { ... } ... }
  76. 76. @zimmermatt Custom Window: Window. public class CustomWindow<E extends CustomEvent> extends Window { ... public static class Serializer<T extends CustomEvent> extends TypeSerializer<CustomWindow<T>> { @Override public boolean isImmutableType() { return true; } ... } ... }
  77. 77. public class CustomWindow<E extends CustomEvent> extends Window { ... public static class Serializer<T extends CustomEvent> extends TypeSerializer<CustomWindow<T>> { ... @Override public TypeSerializer<CustomWindow<T>> duplicate() { return this; } @Override public CustomWindow<T> createInstance() { return null; } @Override public CustomWindow<T> copy(CustomWindow<T> from) { return from; } @Override public CustomWindow<T> copy(CustomWindow<T> from, CustomWindow<T> reuse) { return from; } @Override public int getLength() { return -1; } } ... } @zimmermatt Custom Window: Window.
  78. 78. @zimmermatt public class CustomWindow<E extends CustomEvent> extends Window { ... public static class Serializer<T extends CustomEvent> extends TypeSerializer<CustomWindow<T>> { ... public void serialize(CustomWindow<T> record, DataOutputView target) throws IOException { serializeStartEvent(record, target); target.writeLong(record.getDuration().toMillis()); target.writeBoolean(record.evaluate()); final boolean hasEndEventData = record.getEndEventData() != null; target.writeBoolean(hasEndEventData); if (hasEndEventData) serializeEndEvent(record, target); } } ... } Custom Window: Window.
  79. 79. @zimmermatt public class CustomWindow<E extends CustomEvent> extends Window { ... public static class Serializer<T extends CustomEvent> extends TypeSerializer<CustomWindow<T>> { ... @Override public CustomWindow<T> deserialize(DataInputView source) throws IOException { final T startEvent = deserializeStartEvent(source); final Duration duration = Duration.ofMillis(source.readLong()); final boolean evaluate = source.readBoolean(); final boolean hasEndEventData = source.readBoolean(); final T endEvent = hasEndEventData ? deserializeEndEvent(source) : null; return new CustomWindow<>(startEvent, duration, endEvent, evaluate); } } ... } Custom Window: Window.
  80. 80. @zimmermatt public class CustomWindow<E extends CustomEvent> extends Window { ... public static class Serializer<T extends CustomEvent> extends TypeSerializer<CustomWindow<T>> { ... @Override public CustomWindow<T> deserialize(CustomWindow<T> reuse, DataInputView source) throws IOException { return reuse != null ? reuse : deserialize(source); } } ... } Custom Window: Window.
  81. 81. @zimmermatt public class CustomWindow<E extends CustomEvent> extends Window { ... public static class Serializer<T extends CustomEvent> extends TypeSerializer<CustomWindow<T>> { ... @Override public void copy(DataInputView source, DataOutputView target) throws IOException { // slightly less efficient, but more maintainable CustomWindow<T> deserializedWindow = deserialize(source); serialize(deserializedWindow, target); } } ... } Custom Window: Window.
  82. 82. @zimmermatt public class CustomWindow<E extends CustomEvent> extends Window { ... public static class Serializer<T extends CustomEvent> extends TypeSerializer<CustomWindow<T>> { ... @Override public boolean equals(Object obj) { return obj instanceof Serializer; } @Override public boolean canEqual(Object obj) { return obj instanceof Serializer; } @Override public int hashCode() { return 0; } } ... } Custom Window: Window.
  83. 83. @zimmermatt public class CustomWindow<E extends CustomEvent> extends Window { ... public static class Serializer<T extends CustomEvent> extends TypeSerializer<CustomWindow<T>> { ... @Override public TypeSerializerConfigSnapshot snapshotConfiguration() { ... } @Override public CompatibilityResult<CustomWindow<T>> ensureCompatibility( TypeSerializerConfigSnapshot configSnapshot) { return CompatibilityResult.requiresMigration(); } private static class CustomWindowSerializerConfigSnapshot extends TypeSerializerConfigSnapshot { ... } } ... } Custom Window: Window.
  84. 84. @zimmermatt Custom Window: Window. public class CustomWindow<E extends CustomEvent> extends Window { … public EventWindow(@Nonnull D primaryEventData, @Nonnull Duration timeoutDuration, D endEventData, boolean evaluate) { ... this.endTimestamp = endEventData != null ? endEventData.getTimestamp() : maxTimestamp; ... } ... public boolean evaluate() { return evaluate; } public Instant startTimestamp() { return primaryEventData.getTimestamp(); } public Instant endTimestamp() { return endTimestamp; } } ... }
  85. 85. @zimmermatt Custom Window: WindowAssigner. public class CustomWindowAssigner<E extends CustomEvent> extends MergingWindowAssigner<E, CustomWindow<E>> { ... private CustomWindow<E> calculateSessionWindow(Collection<CustomWindow<E>> mergeCandidates) { CustomWindow<E> startEventWindow = findStartEventWindow(mergeCandidates); if (startEventWindow != null) { // valid window … } else { // exploratory window ... } } ... }
  86. 86. @zimmermatt Custom Window: WindowAssigner. if (startEventWindow != null) { // valid window CustomWindow<E> endEvent = findEndEventWindow(mergeCandidates); // can return null return new CustomWindow<>(startEventWindow.getEvent, timeoutDuration, endEvent, true); // fire (send this one to the WindowFunction) } else { // exploratory window ... }
  87. 87. @zimmermatt Custom Window: WindowAssigner. if (startEventWindow != null) { // valid window ... } else { // exploratory window CustomWindow<E> window = findClosestToMidpointByStartTime(mergeCandidates); return new CustomWindow(window.getEvent, exploratoryDuration, false) // just purge without firing }
  88. 88. @zimmermatt Custom Window: WindowAssigner. public class CustomWindowAssigner<E extends CustomEvent> extends MergingWindowAssigner<E, CustomWindow<E>> { ... @Override public void mergeWindows(Collection<CustomWindow<E>> mergeCandidates, MergeCallback<CustomWindow<E>> mergeCallback) { final CustomWindow<E> sessionWindow = calculateSessionWindow(mergeCandidates); final Collection<CustomWindow<E>> inWindow = filterWithinWindow(mergeCandidates); // MergeCallback#merge implementation expects 2 or more. if (inWindow.size() > 1) { mergeCallback.merge(inWindow, sessionWindow); } } ... }
  89. 89. @zimmermatt Time User A t4 t3 t1 t2 t5 Watermark
  90. 90. @zimmermatt Time User A t4 t3 t1 t2 t5 Watermark
  91. 91. @zimmermatt Time User A t4 t3 t1 t2 t5 Watermark
  92. 92. public class CustomWindowTrigger<E extends CustomEvent> extends Trigger<E, CustomWindow<E>> { ... @Override public boolean canMerge() { return true; } @Override public void onMerge(CustomWindow<E> window, OnMergeContext onMergeContext) throws Exception { onMergeContext.registerEventTimeTimer(window.endTimestamp().toEpochMilli()); } ... } @zimmermatt Custom Window: Trigger.
  93. 93. @zimmermatt Custom Window: Trigger. public class CustomWindowTrigger<E extends CustomEvent> extends Trigger<E, CustomWindow<E>> { ... @Override public TriggerResult onElement(E element, long timestamp, CustomWindow<E> window, TriggerContext triggerContext) throws Exception { final TriggerResult triggerResult; final ValueState<Boolean> windowClosedState = triggerContext.getPartitionedState(windowClosedDescriptor); final long endTimestamp = window.endTimestamp().toEpochMilli(); if (triggerContext.getCurrentWatermark() >= endTimestamp) { triggerResult = windowClosedState.value() ? TriggerResult.CONTINUE : triggerWindow(triggerContext, windowClosedState, window); } else { ... } return triggerResult; } ... }
  94. 94. @zimmermatt Custom Window: Trigger. public class CustomWindowTrigger<E extends CustomEvent> extends Trigger<E, CustomWindow<E>> { ... private TriggerResult triggerWindow(TriggerContext triggerContext, ValueState<Boolean> windowClosedState, CustomWindow<E> window) throws IOException { windowClosedState.update(Boolean.TRUE); removeEarlyFiringTimer(triggerContext); return window.evaluate() ? TriggerResult.FIRE_AND_PURGE : TriggerResult.PURGE; } private void removeEarlyFiringTimer(TriggerContext triggerContext) throws IOException { final ValueState<Long> earlyFiringState = triggerContext.getPartitionedState(earlyFiringDescriptor); if (earlyFiringState.value() > 0) { triggerContext.deleteProcessingTimeTimer(earlyFiringState.value()); // set to -1L to differentiate from the default value earlyFiringState.update(-1L); } } ... }
  95. 95. @zimmermatt Custom Window: Trigger. public class CustomWindowTrigger<E extends CustomEvent> extends Trigger<E, CustomWindow<E>> { ... @Override public TriggerResult onElement(E element, long timestamp, CustomWindow<E> window, TriggerContext triggerContext) throws Exception { final TriggerResult triggerResult; final long endTimestamp = window.endTimestamp().toEpochMilli(); final ValueState<Boolean> windowClosedState = triggerContext.getPartitionedState(windowClosedDescriptor); if ... } else { windowClosedState.update(Boolean.FALSE); triggerResult = TriggerResult.CONTINUE; triggerContext.registerEventTimeTimer(endTimestamp); registerEarlyFiringTimerIfNecessary(window, triggerContext); } return triggerResult; } ... }
  96. 96. @zimmermatt Custom Window: Trigger. public class CustomWindowTrigger<E extends CustomEvent> extends Trigger<E, CustomWindow<E>> { ... private void registerEarlyFiringTimerIfNecessary(CustomWindow<E> window, TriggerContext triggerContext) throws IOException { if (!window.evaluate() || earlyFiringInterval.toMillis() < 1) return; final ValueState<Long> earlyFiringState = triggerContext.getPartitionedState(earlyFiringDescriptor); if (earlyFiringState.value() == Long.MIN_VALUE) { final Long newEarlyFiringTimestamp = System.currentTimeMillis() + earlyFiringInterval.toMillis(); if (newEarlyFiringTimestamp < window.endTimestamp().toEpochMilli()) { triggerContext.registerProcessingTimeTimer(newEarlyFiringTimestamp); earlyFiringState.update(newEarlyFiringTimestamp); } } } ... }
  97. 97. @zimmermatt final ValueState<Long> earlyFiringState = triggerContext.getPartitionedState(earlyFiringDescriptor); if (earlyFiringState.value() == Long.MIN_VALUE) { final Long newEarlyFiringTimestamp = System.currentTimeMillis() + earlyFiringInterval.toMillis(); if (newEarlyFiringTimestamp < window.endTimestamp().toEpochMilli()) { triggerContext.registerProcessingTimeTimer(newEarlyFiringTimestamp); earlyFiringState.update(newEarlyFiringTimestamp); } Custom Window: Trigger.
  98. 98. @zimmermatt Custom Window: Trigger. public class CustomWindowTrigger<E extends CustomEvent> extends Trigger<E, CustomWindow<E>> { ... @Override public TriggerResult onEventTime(long time, CustomWindow<E> window, TriggerContext triggerContext) throws Exception { if (time != window.endTimestamp().toEpochMilli()) { return TriggerResult.CONTINUE; } final ValueState<Boolean> windowClosedState = triggerContext.getPartitionedState(windowClosedDescriptor); if (windowClosedState.value()) { return TriggerResult.CONTINUE; } return triggerWindow(triggerContext, windowClosedState, window); } ... }
  99. 99. @zimmermatt Custom Window: Trigger. public class CustomWindowTrigger<E extends CustomEvent> extends Trigger<E, CustomWindow<E>> { ... @Override public TriggerResult onEventTime(long time, CustomWindow<E> window, TriggerContext triggerContext) throws Exception { if (time != window.endTimestamp().toEpochMilli()) { return TriggerResult.CONTINUE; } final ValueState<Boolean> windowClosedState = triggerContext.getPartitionedState(windowClosedDescriptor); if (windowClosedState.value()) { return TriggerResult.CONTINUE; } return triggerWindow(triggerContext, windowClosedState, window); } ... }
  100. 100. @zimmermatt Custom Window: Trigger. public class CustomWindowTrigger<E extends CustomEvent> extends Trigger<E, CustomWindow<E>> { ... @Override public TriggerResult onProcessingTime(long time, CustomWindow<E> window, TriggerContext triggerContext) throws Exception { TriggerResult triggerResult = TriggerResult.CONTINUE; if (window.evaluate()) { ... } return triggerResult; } ... }
  101. 101. @zimmermatt Custom Window: Trigger. if (window.evaluate()) { // Update early firing final ValueState<Long> earlyFiringState = triggerContext.getPartitionedState(earlyFiringDescriptor); final Long newEarlyFiringTimestamp = earlyFiringState.value() + earlyFiringInterval.toMillis(); if (newEarlyFiringTimestamp < window.endTimestamp().toEpochMilli()) { triggerContext.registerProcessingTimeTimer(newEarlyFiringTimestamp); earlyFiringState.update(newEarlyFiringTimestamp); } triggerResult = TriggerResult.FIRE; } return triggerResult;
  102. 102. @zimmermatt ● Motivating Use Cases. ● Window Requirements. ● The Solution (Conceptual). ● Event Processing Flow. ● Apache Flink Window API Walk-Through. ● The Solution (Detail). ● Pitfalls to Watch Out For. ● Alternative Implementations. ● Questions.
  103. 103. Pitfall: @zimmermatt Window equals / hashCode.
  104. 104. Pitfall: @zimmermatt Metrics and Logs.
  105. 105. Pitfall: @zimmermatt Event Design.
  106. 106. Pitfall: @zimmermatt Large State.
  107. 107. @zimmermatt ● Motivating Use Cases. ● Window Requirements. ● The Solution (Conceptual). ● Event Processing Flow. ● Apache Flink Window API Walk-Through. ● The Solution (Detail). ● Pitfalls to Watch Out For. ● Alternative Implementations. ● Questions.
  108. 108. Alternative: @zimmermatt ProcessFunction
  109. 109. @zimmermatt ● Motivating Use Cases. ● Window Requirements. ● The Solution (Conceptual). ● Event Processing Flow. ● Apache Flink Window API Walk-Through. ● The Solution (Detail). ● Pitfalls to Watch Out For. ● Alternative Implementations. ● Questions.
  110. 110. Thank You! @zimmermatt

×