1

I am working on Kafka Streams application with following topology:

private final Initializer<Set<String>> eventInitializer = () -> new HashSet<>();

final StreamsBuilder streamBuilder = new StreamsBuilder();

    final KStream<String, AggQuantityByPrimeValue> eventStreams = streamBuilder.stream("testTopic",
            Consumed.with(Serdes.String(), **valueSerde**));

    final  KStream<String, Value> filteredStreams = eventStreams
                .filter((key,clientRecord)->recordValidator.isAllowedByRules(clientRecord));

    final KGroupedStream<Integer, Value> groupedStreams = filteredStreams.groupBy(
        (key, transactionEntry) -> transactionEntry.getNodeid(),
        Serialized.with(Serdes.Integer(), **valueSerde**));

    /* Hopping window */
    final TimeWindowedKStream<Integer, Value> windowedGroupStreams = groupedStreams
        .windowedBy(TimeWindows.of(Duration.ofSeconds(30)).advanceBy(Duration.ofSeconds(25))
            .grace(Duration.ofSeconds(0)));

    /* Aggregating the events */
    final KStream<Windowed<Integer>, Set<String>> suppressedStreams = windowedGroupStreams
        .aggregate(eventInitializer, countAggregator, Materialized.as("counts-aggregate")
        .suppress(Suppressed.untilWindowCloses(Suppressed.BufferConfig.unbounded())
                .withName("suppress-window")
            .toStream();

    suppressedStreams.foreach((windowed, value) -> eventProcessor.publish(windowed.key(), value)); 

    return new KafkaStreams(streamBuilder.build(), config.getKafkaConfigForStreams());

I am observing that intermittently few events are getting dropped during/after windowing. For example:

  • All records can be seen/printed in isAllowedByRules() method, which are valid(allowed by filters) and consumed by the stream.
  • But when printing the events in countAggregator, I can see few events are not coming through it.

Current configurations for streams:

Properties streamsConfig = new Properties();
streamsConfig.put(StreamsConfig.APPLICATION_ID_CONFIG,"kafka-app-id"
streamsConfig.put(StreamsConfig.BOOTSTRAP_SERVERS_CONFIG, <bootstraps-server>); 
streamsConfig.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "latest");
streamsConfig.put(StreamsConfig.COMMIT_INTERVAL_MS_CONFIG, 30000);
streamsConfig.put(StreamsConfig.NUM_STREAM_THREADS_CONFIG, 5);
streamsConfig.put(ConsumerConfig.HEARTBEAT_INTERVAL_MS_CONFIG, 10000);
streamsConfig.put(ConsumerConfig.SESSION_TIMEOUT_MS_CONFIG, 30000);
streamsConfig.put(ConsumerConfig.FETCH_MAX_BYTES_CONFIG, 10485760);
streamsConfig.put(ProducerConfig.MAX_REQUEST_SIZE_CONFIG, 10485760);
streamsConfig.put(ConsumerConfig.MAX_PARTITION_FETCH_BYTES_CONFIG, 10485760);
/*For window buffering across all threads*/
streamsConfig.put(StreamsConfig.CACHE_MAX_BYTES_BUFFERING_CONFIG, 52428800);

streamsConfig.put(StreamsConfig.DEFAULT_KEY_SERDE_CLASS_CONFIG, Serdes.Integer().getClass().getName());
streamsConfig.put(StreamsConfig.DEFAULT_VALUE_SERDE_CLASS_CONFIG, **customSerdesForSet**);

Initially, I was using tumbling window but I found that mostly at the end of window few events were getting lost so I changed to hopping window (better to duplicate than lost). Then dropped events became zero. But today again after almost 4 days I saw few dropped events and there is one pattern among them, that they are late by almost a minute compared to other events which were produced together. But then expectation is that these late events should come in any of the future windows but that didn't happen. Correct me here if my understanding is not right.

Also as I have mentioned in the topic, on restart of streams (gracefully) I could see few events getting lost again at aggregation step though processed by isAllowedByRules() method.

I have searched a lot on stack overflow and other sites, but couldn't find the root cause of this behaviour. Is it something related to some configuration that I am missing/not correctly setting or could be due to some other reason?

Matthias J. Sax
  • 59,682
  • 7
  • 117
  • 137
  • Any chance transactionEntry.getNodeid() can return null ( thus explain some drops) ? – Yannick Mar 05 '20 at 09:44
  • `But then expectation is that these late events should come in any of the future windows but that didn't happen` -- as mentioned in the answer below from @Yannick this does not happen by default as by default event-time semantics are used. Maybe changing the `timestamp.extractor` to `WallclockTimestampExtractor` solves your issue. – Matthias J. Sax Mar 10 '20 at 04:27
  • Hi @MatthiasJ.Sax stream thanks for your reply. It was working fine for a long time but then again I could see that messages are getting dropped. I have changed `timestamp.extractor` to `WallclockTimestampExtractor` after your suggestion. Also I have added grace period. Current configuration is: Kafka window time : 30s Window advanceBy : 25s Grace period: 60s Commit Interval: 100s Is it possible to get the stream time so I can compare message timestamp with stream time and check if it is really late! It might be happening due to out-of-order events also.Is there any way to find them? – Rohan Kapoor Dec 17 '20 at 12:48
  • Atm, stream-time is not accessible. We have a KIP an will hopefully add this feature in AK 2.8 release: https://cwiki.apache.org/confluence/display/KAFKA/KIP-622%3A+Add+currentSystemTimeMs+and+currentStreamTimeMs+to+ProcessorContext – Matthias J. Sax Dec 17 '20 at 23:31
  • Okay, thank you @MatthiasJ.Sax for the information. – Rohan Kapoor Jan 04 '21 at 11:28

1 Answers1

1

From my understanding, you have a empty grace period :

 /* Hopping window */
...
            .grace(Duration.ofSeconds(0))

So your window is closed without permitting any late arrivals.

Then regarding your sub question : But then expectation is that these late events should come in any of the future windows but that didn't happen. Correct me here if my understanding is not right.

Maybe you're mixing event time and processing time. Your record will be categorized as 'late' if the timestamp of the record ( added by the producer at produce time, or by the brokers when arriving in the cluster if not set by producer) is outside your current window.

Here is a example with 2 records '*'.

Their event time (et1 and et2) fit in the window :

 |    window       |
 t1                t2
 |      *    *     |
       et1  et2          

But, processing time of et2 (pt2) is in fact as follows :

 |    window       |
 t1                t2
 |      *          |   *
       pt1            pt2

Here the window is a slice of time between t1 and t2 (processing time) et1 and et2 are respectively event time of the 2 records '*'. et1 and et2 are timestamps set in the records themselves. in this example , et1 and et2 are between t1 and t2, et2 have been received after the window closure, as your grace period is 0, it will be skipped.

Might be a explanation

Yannick
  • 1,240
  • 2
  • 13
  • 25
  • Just a minor comment: a record is not necessary _late_ if it's after the window end-time -- it's only _late_ if it's after the grace-period -- otherwise it's just _out-of-order_. – Matthias J. Sax Mar 10 '20 at 04:28