0

I am currently trying to override the KafkaTemplate to force a synchronous send method similar to the suggestion here.

Here is the code:

public class SyncKafkaTemplate<K, V> extends KafkaTemplate<K, V> {

    public SyncKafkaTemplate(ProducerFactory<K, V> producerFactory) {
        super(producerFactory);
    }

    @Override
    public ListenableFuture<SendResult<K, V>> send(ProducerRecord<K, V> record) {
        ListenableFuture<SendResult<K, V>> future = super.send(record);

        try {
            SendResult<K, V> result = future.get();

            if (!result.getRecordMetadata().hasOffset()) {
                throw new RuntimeException("Produce request failed to return valid offset");
            }
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }

        return future;
    }
}

Is the .hasOffset() check here necessary to ensure the message was produced successfully? Or will an exception always be thrown by the send request if a valid offset isn't returned to the Producer?

I've been trying to understand the internal source code for the KafkaTemplate but it's unclear to me if an exception would always be thrown by the .get() call. I've also been searching the internet for any reason a negative offset would be returned but can't find anything relevant.

When running without the bad offset check, very rarely a message would be dropped by my service. It would show no errors on the producer side, but would never be committed to a partition on the server. I've added the offset check now, but without a way to replicate the dropped messages I don't know if this is a real solution.

Gary Russell
  • 166,535
  • 14
  • 146
  • 179
Oak
  • 36
  • 6

1 Answers1

0

You should call the overloaded get() method with a timeout, just in case something might block forever (it won't in general, but it is always good practice to use a timeout when getting future results).

Best practice is not to catch Exception when getting a future result, because you might be eating an interrupt exception which might cause other upstream problems.

Something like this...

try {
    SendResult<K, V> result = future.get(10, TimeUnit.SECONDS);
}
catch (TimeoutExeption e) {
    throw new SomeRuntimeException("Timed out", e);
}
catch (InterruptedException e) {
    Thread.currentThread().interrupt();
    throw new SomeRuntimeException("Interrupted", e);
}
catch (ExecutionException e) {
    Exception actualException = e.getCause();
    ...
}

There is no RecordMetadata if the future is failed exceptionally.

According to the logic in RecordMetadata, it is possible to get a false result there, but I don't know what conditions that might occur under.

/**
 * Indicates whether the record metadata includes the offset.
 * @return true if the offset is included in the metadata, false otherwise.
 */
public boolean hasOffset() {
    return this.offset != ProduceResponse.INVALID_OFFSET;
}

However this is internal to the Kafka Producer client (or even the broker itself), so even harder to find out what those conditions might be.

You might want to try asking on one of the Kafka mailing lists.

Gary Russell
  • 166,535
  • 14
  • 146
  • 179
  • I just took a quick look at the `kafka-clients` and it looks like INVALID_OFFSET is returned by the broker. I somehow doubt that it will ever reach your application code. It is probably used by the client to generate an exception which would then propagate up to the future that you are getting. But, I can't say for certain. – Gary Russell Jun 05 '23 at 21:07