Kyle Ahn
01/21/2023, 9:31 PMEXACTLY_ONCE checkpointing mode (k8s Flink operator).
When I change the job’s state to suspended to manually and change back to running. It seems that the raw data sink, which does not involve TumblingEventTimeWindows + ProcessWindowFunction , do not write duplicate records, but the processed data seem to either miss some records or write duplicate records.
The two sinks are sharing the same sink. How is this happening?Kyle Ahn
01/21/2023, 9:32 PMKyle Ahn
01/21/2023, 9:33 PMEventTimeTrigger with AWS Kinesis data streams. The checkpointing interval is every 10 seconds.Kyle Ahn
01/21/2023, 9:35 PMProcessWindowFunction. I see that the records received in the ProcessWindowFunction step are less than the records sent downstream.Kyle Ahn
01/21/2023, 10:27 PMFileSink gaurantees exactly once write. Is this not true?Ahmed Hamdy
01/22/2023, 7:12 PMKyle Ahn
01/22/2023, 11:04 PMKyle Ahn
01/23/2023, 7:00 AMMartijn Visser
01/23/2023, 10:07 AMKyle Ahn
01/23/2023, 5:46 PMinprogress and pending files have any impacts. This matters because I am using detecting files uploaded to S3 bucket and relay these parquet files to another pipeline.Martijn Visser
01/23/2023, 5:47 PMMartijn Visser
01/23/2023, 5:47 PMKyle Ahn
01/23/2023, 5:51 PMinprogress files have an impact here, it only makes sense to me if both raw data bucket and processed data bucket have some missing and duplicate records. How should I understand this behaviour?Martijn Visser
01/23/2023, 6:02 PMKyle Ahn
01/23/2023, 6:05 PMWindowProcessFunction, and should we not see duplicate records in the Sink bucket, not missing records?Kyle Ahn
01/23/2023, 7:40 PMKyle Ahn
01/23/2023, 9:10 PMWindowProcessFunction, do I have to manually implement the logic for what records were in buffer (not triggered yet) to restore manually?Kyle Ahn
01/23/2023, 11:45 PMTumblingProcessingTimeWindows + ProcessingTimeTrigger to solve the issue.Kyle Ahn
01/24/2023, 12:24 AMTumblingEventTimeWindows + EventTimeTrigger with Kinesis data stream , I observe that each window is only triggered when events whose timestamp belongs to the next window arrive.
This works great, until I suspend the Flink job manager manually by setting state: suspended .
In the checkpoint files, I see that the buffered events, that were not triggered since the events of the next window didn’t arrive, were correctly stored.
Upon state restoration, however, I see that the restored events from the last successful checkpoint are just buffered, and never triggered. The restoration reads the correct number of records from the Kinesis source, but it just never triggers the buffered events restored from the checkpoint.
Is this expected?
If needed, I can reproduce a quick example, and share?Martijn Visser
01/24/2023, 10:20 AMHong Teoh
01/24/2023, 11:01 AMRaw Sink does not have this problem, whereas the Processed Data sink which has a window before the sink has this issue.
I can think of two places where this error might be (am happy to replicate this issue + help investigate this further, since it seems like quite a nasty failure mode):
1. Kinesis Consumer’s watermarking generation has bug
◦ We can help root cause if this is the issue / mitigate this by using the new watermark generation rather than the one built into the Kinesis Consumer
2. Window Operator has bug when restoring from state
◦ This seems unlikely. I haven’t looked, but I would be surprised if we don’t have tests validating this behavior!
Couple of clarifications:
• Which Flink version are you using?
• To test (1), we can try switching out the Watermarking Strategy on consumer
WatermarkStrategy<> s = WatermarkStrategy
.<>forMonotonousTimestamps()
.withTimestampAssigner((event,timestamp) -> {
return timestamp;
})
.withIdleness(Duration.ofSeconds(15));
env.addSource(consumer)
.assignTimestampsAndWatermarks(s)Hong Teoh
01/24/2023, 11:07 AMwithIdleness settingKyle Ahn
01/24/2023, 11:21 AMwithIdleness in this case, and it is set to 1ms.
env.addSource(
new FlinkKinesisConsumer<>(
streamName,
new EventDeserializationSchema(),
inputProperties
)
)
.assignTimestampsAndWatermarks(
WatermarkStrategy
.<Event>forBoundedOutOfOrderness(
Duration.ofSeconds(0))
.withIdleness(Duration.ofMillis(1))) // This is to avoid the watermark to be stuck
.uid("event_watermarking_strategy_" + APP_VERSION)
.name("event_watermarking_strategy_" + APP_VERSION)
.keyBy(EventUtils::getDynamicPartitionKey)
.uid(name)
.name(name)
.window(TumblingEventTimeWindows.of(Time.seconds(10)))
.trigger(EventTimeTrigger.create())
.process(new PredictionEventProcessWindowFunction(
inputProperties.get("ServiceUri").toString()))
.uid("prediction_event_process_window_function_" + APP_VERSION)
.name("prediction_event_process_window_function_" + APP_VERSION)
.forBulkFormat(
new Path(path),
AvroParquetWriters.forReflectRecord(Event.class))
.withBucketAssigner(new KeyAndDateBucketAssigner(
"'year='yyyy'/month='MM'/day='dd'/hour='HH/"))
.withRollingPolicy(OnCheckpointRollingPolicy.build())
.withOutputFileConfig(DateAndIdOutputFileConfig.configBuilder()
.withDateFormat("yyyy-MM-dd-HH-mm-ss")
.withPartSuffix(".parquet")
.build())
.build()
.uid("sink_to_datalake_processed_zone_" + APP_VERSION)
.name("sink_to_datalake_processed_zone_" + APP_VERSION)
.setParallelism(2)
.uid("set_parallelism_to_1_after_sink_to_datalake_processed_zone_" + APP_VERSION)
.name("set_parallelism_to_1_after_sink_to_datalake_processed_zone_" + APP_VERSION);;Hong Teoh
01/24/2023, 11:22 AMKyle Ahn
01/24/2023, 11:22 AMforMonotonousTimestamps to rule out that the Kinesis data stream does not have a bug.
Could you please clarify what the expected output should be? It should just be monotonously increasing timestamps, correct?Kyle Ahn
01/24/2023, 11:23 AMKyle Ahn
01/24/2023, 11:24 AMforMonotonousTimestamp watermark strategy, too, btw. However, the buffered records were never triggered.
So, I am doubting whether the Kinesis data stream has a bug in generating the watermarks. Though, I am using Kinesis data stream as is, meaning that I am not altering any behaviour of how Kinesis data stream is generating the watermarks. Thus, I am not so clear how there could be this critical bug.Hong Teoh
01/24/2023, 11:35 AMFlinkKinesisConsumer<> consumer = new FlinkKinesisConsumer<>(...);
consumer.setPeriodicWatermarkAssigner(...)
This was the old method of creating watermarks where the Source itself had to generate the watermarks, but more recently it has been abstracted away and sources should use assignTimestampsAndWatermarks() (which is what you are doing!)
Hmm… forMonotonousTimestamps vs BoundedOutOfOrderness should not be the root cause, but it would be worth testing if easy.
Yes, forMonotonousTimestamps just assumes that the records are in order and will advance the watermarks to time T when record with event time T comes in
forBoundedOutOfOrderness means it will emit watermarks to time T - duration when record with event time T comes in… Given you are using Duration(0) it is basically the same as forMonotonousTimestamps() 😆Kyle Ahn
01/24/2023, 11:41 AMjob.state: suspended. Then, I went to the S3 buckets of the raw sink and processed sink paths, and verified that the raw sink has 2 parquet files, each containing 1 record. On the other hand, the processed sink path contained a single parquet file with 1 record.
While the Flink job was down, I sent 2 more records.
Next, I restored the job by setting job.state: running . Then, I sent 1 more record to the kinesis data stream. The raw sink correctly received 3 records in total. 2 records that were sent while Flink was suspended, and 1 record that was sent after the state was restored. On the other hand, the processed sink step only received 1 record, which is the latest record sent after the state was restored.
So, I did manually verify the monotonously increasing event time. But, I can still reproduce this issue with a very simple example.Kyle Ahn
01/24/2023, 11:42 AMHong Teoh
01/24/2023, 11:55 AM.print() to output the events to std out / map() with logKyle Ahn
01/24/2023, 12:00 PMÁW˝Torg.apache.flink.api.common.typeutils.base.StringSerializer$StringSerializerSnapshotcorg.apache.flink.streaming.api.windowing.windows.TimeWindow$Serializer$TimeWindowSerializerSnapshot _timer_state/event_window-timersVALUE_SERIALIZER@org.apache.flink.streaming.api.operators.TimerSerializerSnapshot
ÁW˝Torg.apache.flink.api.common.typeutils.base.StringSerializer$StringSerializerSnapshotcorg.apache.flink.streaming.api.windowing.windows.TimeWindow$Serializer$TimeWindowSerializerSnapshothij$1ab4dec8-411f-44c8-9e97-c39617c85d9a(k
…fl°<s3://ggg-staging-flink-state-store/eventlogger-pipeline-v0-1/checkpoints/00000000000000000000000000000000/chk-1362/ab2e447b-1305-44e4-897f-d79fe0c0b44eıTorg.apache.flink.api.common.typeutils.base.StringSerializer$StringSerializerSnapshotwindow-contentsKEYED_STATE_TYPELISTVALUE_SERIALIZERAorg.apache.flink.api.common.typeutils.base.ListSerializerSnapshot>Hong Teoh
01/24/2023, 12:00 PMHong Teoh
01/24/2023, 12:01 PMHong Teoh
01/24/2023, 12:01 PMKyle Ahn
01/24/2023, 12:01 PMKyle Ahn
01/24/2023, 12:01 PMKyle Ahn
01/24/2023, 12:01 PMHong Teoh
01/24/2023, 12:03 PMHong Teoh
01/24/2023, 12:04 PMKyle Ahn
01/24/2023, 12:05 PMHong Teoh
01/25/2023, 6:13 PMvar stream = env.addSource(
new FlinkKinesisConsumer<>(
"<streamname>",
new SimpleStringSchema(),
inputStreamConfig
)
).assignTimestampsAndWatermarks(WatermarkStrategy.forMonotonousTimestamps())
.keyBy(String::hashCode)
.window(TumblingEventTimeWindows.of(Time.minutes(1)))
.process(new ProcessWindowFunction<String, Object, Integer, TimeWindow>() {
@Override
public void process(Integer integer, ProcessWindowFunction<String, Object, Integer, TimeWindow>.Context context, Iterable<String> iterable, Collector<Object> collector) throws Exception {
iterable.forEach(collector::collect);
}
})
.print();Hong Teoh
01/25/2023, 6:13 PMKyle Ahn
01/25/2023, 6:14 PMHong Teoh
01/25/2023, 6:15 PMHong Teoh
01/25/2023, 6:15 PMHong Teoh
01/25/2023, 6:16 PMKyle Ahn
01/25/2023, 6:48 PMKyle Ahn
01/25/2023, 6:48 PM