Puneet Duggal
07/27/2023, 6:13 AMpublic class NFAStateReaderTest {
private static final String NFA_STATE_NAME = "nfaStateName";
@Test
public void testNfaStateReader() throws Exception {
StreamExecutionEnvironment environment = StreamExecutionEnvironment.getExecutionEnvironment();
SavepointReader savepointReader =
SavepointReader.read(environment, "file:///opt/flink/savepoints/savepoint-093404-9bc0a38654df", new FsStateBackend("file:///abc"));
DataStream<NFAStateOutput> stream = savepointReader.readKeyedState(OperatorIdentifier.forUid(SELECT_PATTERN_EVENTS), new NFAStateReaderTest.NFAStateReaderFunction());
stream.print();
environment.execute();
}
static class NFAStateReaderFunction extends KeyedStateReaderFunction<DynamicTuple, NFAStateOutput> {
private ValueState<NFAState> computationStates;
private static Long danglingNfaCount = 0L;
private static Long newNfaCount = 0L;
private static Long minTimestamp = Long.MAX_VALUE;
private static Long minKeyForCurrentNfa = Long.MAX_VALUE;
private static Long minKeyForDanglingNfa = Long.MAX_VALUE;
private static Long maxKeyForDanglingNfa = Long.MIN_VALUE;
private static Long maxKeyForCurrentNfa = Long.MIN_VALUE;
@Override
public void open(Configuration parameters) {
computationStates = getRuntimeContext().getState(new ValueStateDescriptor<>(NFA_STATE_NAME, new NFAStateSerializer()));
}
@Override
public void readKey(DynamicTuple key, Context ctx, Collector<NFAStateOutput> out) throws Exception {
NFAState nfaState = computationStates.value();
System.out.println("**************************************************");
if (Objects.requireNonNull(nfaState.getPartialMatches().peek()).getStartTimestamp() != -1) {
minTimestamp = Math.min(minTimestamp, nfaState.getPartialMatches().peek().getStartTimestamp());
minKeyForCurrentNfa = Math.min(minKeyForCurrentNfa, Long.parseLong(key.getTuple().getField(0)));
maxKeyForCurrentNfa = Math.max(maxKeyForCurrentNfa, Long.parseLong(key.getTuple().getField(0)));
newNfaCount++;
} else {
danglingNfaCount++;
minKeyForDanglingNfa = Math.min(minKeyForDanglingNfa, Long.parseLong(key.getTuple().getField(0)));
maxKeyForDanglingNfa = Math.max(maxKeyForDanglingNfa, Long.parseLong(key.getTuple().getField(0)));
}
NFAStateOutput nfaStateOutput =
new NFAStateOutput(
danglingNfaCount,
minTimestamp,
newNfaCount,
minKeyForCurrentNfa,
maxKeyForCurrentNfa,
minKeyForDanglingNfa,
maxKeyForDanglingNfa);
out.collect(nfaStateOutput);
}
}
@Data
@NoArgsConstructor
@AllArgsConstructor
static class NFAStateOutput implements Serializable {
private Long danglingNfaCount;
private Long minTimestamp;
private Long newNfaCount;
private Long minKeyForCurrentNfa;
private Long maxKeyForCurrentNfa;
private Long minKeyForDanglingNfa;
private Long maxKeyForDanglingNfa;
}
}
As an output it printed nfaStateOutput for each key but since all the attributes in nfaStateOutput are aggregates, hence finalOutput printed was
NFAStateReaderTest.NFAStateOutput(danglingNfaCount=34391, minTimestamp=1690359951958, newNfaCount=3000, minKeyForCurrentNfa=6244230, maxKeyForCurrentNfa=6247229, minKeyForDanglingNfa=629818, maxKeyForDanglingNfa=6244229)
As we can see, checkpoint is storing approximately 34391 dangling states (for keys which have expired (matched or timed out) ) whereas there are only 3000 active keys (for which there are partial matches which are eligible for further pattern sequence matching) which is expected since throughput is 10 events per second which amounts to 3000 unique keys in 5 mins.
Questions:
Hence, I am curious about the reasoning behind this design choice, specifically why the starting state remains in the partial matches queue for all keys, even those that have either timed out or completed their matches. Additionally, I am wondering what the implications would be if we were to delete this starting state assuming that
1. it is the only state left in the partial match queue.
2. completed match queue in nfa state is empty.Martijn Visser
07/27/2023, 6:16 AMPuneet Duggal
07/27/2023, 6:16 AMPuneet Duggal
07/27/2023, 7:32 AMPuneet Duggal
08/04/2023, 11:02 AM