CEP In Flink (3) - Extracting Matched Events
How matched events are extracted in Apache Flink’s CEP module.
Translated from Chinese with AI · Read the original
The previous post described matching and its optimizations. What happens to events after a match?
Reconstructing Event Sequences
When a state reaches Final, its match enters potentialMatches as a candidate for output. SharedBuffer traces backward from the final match, using version numbers and NodeIds, described in the previous post, to reconstruct a unique event path. Here is the code:
for (ComputationState match : potentialMatches) { // 因为要从EventId还原到具体的T类型事件,做缓存提高效率 Map<EventId, T> eventsCache = new HashMap<>();
// 从SharedBuffer中,根据match的上一个NodeId和版本不断向前提取事件 Map<String, List<T>> materializedMatch = sharedBuffer.materializeMatch(sharedBuffer.extractPatterns( match.getPreviousBufferEntry(), match.getVersion()).get(0), eventsCache);
// 释放已经匹配完成的Node sharedBuffer.releaseNode(match.getPreviousBufferEntry());}Here, Map<String, List<T>> represents the relationship between state names and their events.
In AbstractKeyedCEPPatternOperator.java, after nfa.process completes, the return type is Collection<Map<String, List<IN>>>. String is the state name and List<IN> contains the events matched by that state. The code is:
/** * Process the given event by giving it to the NFA and outputting the produced set of matched * event sequences. * * @param nfaState Our NFAState object * @param event The current event to be processed * @param timestamp The timestamp of the event */private void processEvent(NFAState nfaState, IN event, long timestamp) throws Exception { Collection<Map<String, List<IN>>> patterns = nfa.process(partialMatches, nfaState, event, timestamp, afterMatchSkipStrategy); processMatchedSequences(patterns, timestamp);}The role of processMatchedSequences is explained next.
Event Output
Consider the test in CEPITCase.java:
CEP.pattern(input, pattern).select( new PatternSelectFunction<Event, String>() {
@Override public String select(Map<String, List<Event>> pattern) { StringBuilder builder = new StringBuilder(); // 对builder的一些操作 return builder.toString(); } });processMatchedSequences directly invokes the user-defined extraction function. Flink supports several forms, including flatSelect and select.
AfterMatchSkipStrategy
Another interesting feature is AfterMatchSkipStrategy. Used well, it filters many invalid or unwanted matches.
Suppose you want to detect a W-shaped stock-price pattern:

Both acde and bcde satisfy the W pattern, but do you want both outputs? Not necessarily. AfterMatchSkipStrategy removes completed or ongoing matches according to configured pruning rules after a successful match. Its interface is:
/** * Prunes matches/partial matches based on the chosen strategy. * * @param matchesToPrune current partial matches * @param matchedResult already completed matches * @param sharedBuffer corresponding shared buffer * @throws Exception thrown if could not access the state */public void prune( Collection<ComputationState> matchesToPrune, Collection<Map<String, List<EventId>>> matchedResult, SharedBuffer<?> sharedBuffer) throws Exception { // 挑选出开始prune的EventId(或者说位置) EventId pruningId = getPruningId(matchedResult); if (pruningId != null) { List<ComputationState> discardStates = new ArrayList<>();
// 针对每一个已经匹配成功的match做检查 for (ComputationState computationState : matchesToPrune) { if (computationState.getStartEventID() != null && shouldPrune(computationState.getStartEventID(), pruningId)) { sharedBuffer.releaseNode(computationState.getPreviousBufferEntry()); discardStates.add(computationState); } } matchesToPrune.removeAll(discardStates); }}Custom AfterMatchSkipStrategies implement getPruningId and shouldPrune.
/** * Tells if the partial/completed match starting at given id should be prunned by given pruningId. * * @param startEventID starting event id of a partial/completed match * @param pruningId pruningId calculated by this strategy * @return true if the match should be pruned */protected abstract boolean shouldPrune(EventId startEventID, EventId pruningId);
/** * Retrieves event id of the pruning element from the given match based on the strategy. * * @param match match corresponding to which should the pruning happen * @return pruning event id */protected abstract EventId getPruningId(Collection<Map<String, List<EventId>>> match);Common predefined strategies include: