SlideShare a Scribd company logo
1 of 79
1@loicmdivad @PubSapientEng@loicmdivad @PubSapientEng
Streaming Apps and Poison Pills:
handle the unexpected with Kafka Streams
28 Jul. 2020 - Online Kafka Meetup
2@loicmdivad @PubSapientEng@loicmdivad @PubSapientEng
Loïc DIVAD
Software Engineer
@loicmdivad
Technical Officer @PubSapientEng
3@loicmdivad @PubSapientEng
Streaming Apps and Poison Pills
San Francisco, CA - October 2019
Ratatouille: handle the unexpected with Kafka Streams
4@loicmdivad @PubSapientEng 4@loicmdivad @PubSapientEng
> println(sommaire)
Incoming records may be corrupted, or cannot be
handled by the serializer / deserializer. These
records are referred to as “poison pills”
1. Log and Crash
2. Skip the Corrupted
3. Sentinel Value Pattern
4. Dead Letter Queue Pattern
5@loicmdivad @PubSapientEng
Ratatouille app, a delicious use case
Streaming
APP
6@loicmdivad @PubSapientEng
Ratatouille app, a delicious use case
Streaming
APP
7@loicmdivad @PubSapientEng 7@loicmdivad @PubSapientEng
Streaming App Poison Pills
1. Log and Crash - Breakfast
2. Skip the Corrupted - Lunch
3. Sentinel Value Pattern - Drink
4. Dead Letter Queue Pattern - Dinner
8@loicmdivad @PubSapientEng
Apache Kafka Brokers / Clients
9@loicmdivad @PubSapientEng
Log and Crash
Exercise #1 - breakfast
10@loicmdivad @PubSapientEng
Really old systems receive raw bytes
directly from message queues
10100110111010101
Exercise #1 - breakfast
11@loicmdivad @PubSapientEng
Really old systems receive raw bytes
directly from message queues
With Kafka (Connect and Streams)
we’d like to continuously transform
these messages
10100110111010101
Kafka Connect
Kafka Brokers
Exercise #1 - breakfast
12@loicmdivad @PubSapientEng
Really old systems receive raw bytes
directly from message queues
With Kafka (Connect and Streams)
we’d like to continuously transform
these messages
But we need a deserializer with
special decoder to understand each
event
What happens if we get a buggy
implementation of the deserializer?
10100110111010101
Kafka Connect
Kafka Brokers
Kafka Streams
Exercise #1 - breakfast
13@loicmdivad @PubSapientEng
The Tooling Team
They will provide an appropriate deserializer
14@loicmdivad @PubSapientEng
// Exercise #1: Breakfast
sealed trait FoodOrder
case class Breakfast(lang: Lang,
fruit: Fruit,
liquid: Liquid,
pastries: Vector[Pastry] = Vector.empty) extends FoodOrder
15@loicmdivad @PubSapientEng
// Exercise #1: Breakfast
sealed trait FoodOrder
case class Breakfast(lang: Lang,
fruit: Fruit,
liquid: Liquid,
pastries: Vector[Pastry] = Vector.empty) extends FoodOrder
implicit lazy val BreakfastCodec: Codec[Breakfast] = new Codec[Breakfast] = ???
16@loicmdivad @PubSapientEng
// Exercise #1: Breakfast
sealed trait FoodOrder
case class Breakfast(lang: Lang,
fruit: Fruit,
liquid: Liquid,
pastries: Vector[Pastry] = Vector.empty) extends FoodOrder
implicit lazy val BreakfastCodec: Codec[Breakfast] = new Codec[Breakfast] = ???
class FoodOrderSerializer extends Serializer[FoodOrder] = ???
class FoodOrderDeserializer extends Deserializer[FoodOrder] = ???
17@loicmdivad @PubSapientEng
// Exercise #1: Breakfast
sealed trait FoodOrder
case class Breakfast(lang: Lang,
fruit: Fruit,
liquid: Liquid,
pastries: Vector[Pastry] = Vector.empty) extends FoodOrder
implicit lazy val BreakfastCodec: Codec[Breakfast] = new Codec[Breakfast] = ???
class FoodOrderSerializer extends Serializer[FoodOrder] = ???
class FoodOrderDeserializer extends Deserializer[FoodOrder] = ???
org.apache.kafka.common.serialization
Take
Away
18@loicmdivad @PubSapientEng@loicmdivad @PubSapientEng
19@loicmdivad @PubSapientEng
Log and Crash
2019-04-17 03:43:12 macbook-de-lolo [ERROR] (LogAndFailExceptionHandler.java:39) - Exception caught during
Deserialization, taskId: 0_0, topic: input-food-order, partition: 0, offset: 109
Exception in thread "answer-one-breakfast-0d808ce7-0ef1-44c6-808a-f594bc7fceae-StreamThread-1"
org.apache.kafka.streams.errors.StreamsException: Deserialization exception handler is set to fail upon a
deserialization error. If you would rather have the streaming pipeline continue after a deserialization error, please
set the default.deserialization.exception.handler appropriately.
at org.apache.kafka.streams.processor.internals.RecordDeserializer.deserialize(RecordDeserializer.java:80)
at org.apache.kafka.streams.processor.internals.RecordQueue.addRawRecords(RecordQueue.java:101)
at org.apache.kafka.streams.processor.internals.PartitionGroup.addRawRecords(PartitionGroup.java:124)
...
at org.apache.kafka.streams.processor.internals.StreamTask.addRecords(StreamTask.java:711)
at org.apache.kafka.streams.processor.internals.StreamThread.run(StreamThread.java:747)
Caused by: java.lang.IllegalArgumentException: dishes: Insufficient number of elements: decoded 0 but should have
decoded 268435712
at scodec.Attempt$Failure.require(Attempt.scala:108)
at fr.xebia.ldi.ratatouille.serde.BreakfastDeserializer.deserialize(BreakfastDeserializer.scala:22)
at fr.xebia.ldi.ratatouille.serde.BreakfastDeserializer.deserialize(BreakfastDeserializer.scala:15)
at org.apache.kafka.common.serialization.Deserializer.deserialize(Deserializer.java:58)
at fr.xebia.ldi.ratatouille.serde.BreakfastDeserializer.deserialize(BreakfastDeserializer.scala:15)
at org.apache.kafka.streams.processor.internals.SourceNode.deserializeValue(SourceNode.java:60)
at org.apache.kafka.streams.processor.internals.RecordDeserializer.deserialize(RecordDeserializer.java:66)
20@loicmdivad @PubSapientEng
Log and Crash
2019-04-17 03:43:12 macbook-de-lolo [ERROR] (LogAndFailExceptionHandler.java:39) - Exception caught during
Deserialization, taskId: 0_0, topic: exercise-breakfast, partition: 0, offset: 109
Exception in thread "answer-one-breakfast-0d808ce7-0ef1-44c6-808a-f594bc7fceae-StreamThread-1"
org.apache.kafka.streams.errors.StreamsException: Deserialization exception handler is set to fail upon a
deserialization error. If you would rather have the streaming pipeline continue after a deserialization error, please
set the default.deserialization.exception.handler appropriately.
at org.apache.kafka.streams.processor.internals.RecordDeserializer.deserialize(RecordDeserializer.java:80)
at org.apache.kafka.streams.processor.internals.RecordQueue.addRawRecords(RecordQueue.java:101)
at org.apache.kafka.streams.processor.internals.PartitionGroup.addRawRecords(PartitionGroup.java:124)
...
at org.apache.kafka.streams.processor.internals.StreamTask.addRecords(StreamTask.java:711)
at org.apache.kafka.streams.processor.internals.StreamThread.run(StreamThread.java:747)
Caused by: java.lang.IllegalArgumentException: dishes: Insufficient number of
elements: decoded 0 but should have decoded 268435712
at scodec.Attempt$Failure.require(Attempt.scala:108)
at fr.xebia.ldi.ratatouille.serde.BreakfastDeserializer.deserialize(BreakfastDeserializer.scala:22)
at fr.xebia.ldi.ratatouille.serde.BreakfastDeserializer.deserialize(BreakfastDeserializer.scala:15)
at org.apache.kafka.common.serialization.Deserializer.deserialize(Deserializer.java:58)
at fr.xebia.ldi.ratatouille.serde.BreakfastDeserializer.deserialize(BreakfastDeserializer.scala:15)
at org.apache.kafka.streams.processor.internals.SourceNode.deserializeValue(SourceNode.java:60)
at org.apache.kafka.streams.processor.internals.RecordDeserializer.deserialize(RecordDeserializer.java:66)
21@loicmdivad @PubSapientEng
|val frame1: Array[Byte] = Array(0x33, 0xd4, 0xfc, 0x00, 0x00, 0x00, 0x01, 0xa5)
|val frame2: Array[Byte] = Array(0x44, 0xd2, 0xfe, 0x10, 0x02, 0x03, 0x01)
22@loicmdivad @PubSapientEng
|val frame1: Array[Byte] = Array( , 0xd4, 0xfc, 0x00, 0x00, 0x00, 0x01, 0xa5)
|val frame2: Array[Byte] = Array( , 0xd2, 0xfe, 0x10, 0x02, 0x03, 0x01)
23@loicmdivad @PubSapientEng
|val frame1: Array[Byte] = Array( , 0xd4, 0xfc, 0x00, 0x00, 0x00, 0x01, 0xa5)
|val frame2: Array[Byte] = Array( , 0xd2, 0xfe, 0x10, x2, 0x03, 0x01)
|case class Meat(sausages: Int, bacons: Int, . . . )
24@loicmdivad @PubSapientEng
▼ Change consumer group
▼ Manually update my offsets
▼ Reset my streaming app and set my auto reset to
LATEST
▽ $ kafka-streams-application-reset ...
▼ Destroy the topic, no message = no poison pill
▽ $ kafka-topics --delete --topic ...
▼ My favourite <3
▽ $ confluent destroy && confluent start
Don’t Do
▼ Fill an issue and suggest a fix to the tooling team
25@loicmdivad @PubSapientEng@loicmdivad @PubSapientEng
26@loicmdivad @PubSapientEng 26@loicmdivad @PubSapientEng
Log and Crash
Like all consumers, Kafka Streams applications
deserialize messages from the broker.
The deserialization process can fail. It raises an
exception that cannot be caught by our code.
Buggy deserializers have to be fixed before the
application restarts, by default ...
27@loicmdivad @PubSapientEng
Skip the Corrupted
Exercise #2 - lunch
28@loicmdivad @PubSapientEng
// Exercise #2: Lunch
sealed trait FoodOrder
case class Lunch(name: String, price: Double, `type`: LunchType) extends FoodOrder
29@loicmdivad @PubSapientEng
// Exercise #2: Lunch
sealed trait FoodOrder
case class Lunch(name: String, price: Double, `type`: LunchType) extends FoodOrder
● starter
● main
● dessert
30@loicmdivad @PubSapientEng@loicmdivad @PubSapientEng
31@loicmdivad @PubSapientEng
Skip the Corrupted
2019-04-17 03:43:12 macbook-de-lolo [ERROR] (LogAndFailExceptionHandler.java:39) - Exception caught during
Deserialization, taskId: 0_0, topic: exercise-breakfast, partition: 0, offset: 109
Exception in thread "answer-one-breakfast-0d808ce7-0ef1-44c6-808a-f594bc7fceae-StreamThread-1"
org.apache.kafka.streams.errors.StreamsException: Deserialization exception handler is set to fail upon a
deserialization error. If you would rather have the streaming pipeline continue after a
deserialization error, please set the default.deserialization.exception.handler
appropriately.
at org.apache.kafka.streams.processor.internals.RecordDeserializer.deserialize(RecordDeserializer.java:80)
at org.apache.kafka.streams.processor.internals.RecordQueue.addRawRecords(RecordQueue.java:101)
at org.apache.kafka.streams.processor.internals.PartitionGroup.addRawRecords(PartitionGroup.java:124)
...
at org.apache.kafka.streams.processor.internals.StreamTask.addRecords(StreamTask.java:711)
at org.apache.kafka.streams.processor.internals.StreamThread.run(StreamThread.java:747)
Caused by: java.lang.IllegalArgumentException: ... decoded 0 but should have decoded 268435712
at scodec.Attempt$Failure.require(Attempt.scala:108)
at fr.xebia.ldi.ratatouille.serde.BreakfastDeserializer.deserialize(BreakfastDeserializer.scala:22)
at fr.xebia.ldi.ratatouille.serde.BreakfastDeserializer.deserialize(BreakfastDeserializer.scala:15)
at org.apache.kafka.common.serialization.Deserializer.deserialize(Deserializer.java:58)
at fr.xebia.ldi.ratatouille.serde.BreakfastDeserializer.deserialize(BreakfastDeserializer.scala:15)
at org.apache.kafka.streams.processor.internals.SourceNode.deserializeValue(SourceNode.java:60)
at org.apache.kafka.streams.processor.internals.RecordDeserializer.deserialize(RecordDeserializer.java:66)
32@loicmdivad @PubSapientEng 32@loicmdivad @PubSapientEng
public class LogAndFailExceptionHandler implements DeserializationExceptionHandler
/* ... */
public class LogAndContinueExceptionHandler implements DeserializationExceptionHandler
/* ... */
33@loicmdivad @PubSapientEng
public class LogAndFailExceptionHandler implements DeserializationExceptionHandler
/* ... */
public class LogAndContinueExceptionHandler implements DeserializationExceptionHandler
/* ... */
public interface DeserializationExceptionHandler extends Configurable {
DeserializationHandlerResponse handle(final ProcessorContext context,
final ConsumerRecord<byte[], byte[]> record,
final Exception exception);
enum DeserializationHandlerResponse {
CONTINUE(0, "CONTINUE"),
FAIL(1, "FAIL");
/* ... */
}
}
}
34@loicmdivad @PubSapientEng
public class LogAndFailExceptionHandler implements DeserializationExceptionHandler
/* ... */
public class LogAndContinueExceptionHandler implements DeserializationExceptionHandler
/* ... */
public interface DeserializationExceptionHandler extends Configurable {
DeserializationHandlerResponse handle(final ProcessorContext context,
final ConsumerRecord<byte[], byte[]> record,
final Exception exception);
enum DeserializationHandlerResponse {
CONTINUE(0, "CONTINUE"),
FAIL(1, "FAIL");
/* ... */
}
}
}
Take
Away
35@loicmdivad @PubSapientEng@loicmdivad @PubSapientEng
36@loicmdivad @PubSapientEng 36@loicmdivad @PubSapientEng
The Exception Handler in the call stack
Powered by the Flow intelliJ plugin ➞ findtheflow.io
37@loicmdivad @PubSapientEng 37@loicmdivad @PubSapientEng
Powered by the Flow intelliJ plugin ➞ findtheflow.io
The Exception Handler in the call stack
38@loicmdivad @PubSapientEng 38@loicmdivad @PubSapientEng
Powered by the Flow intelliJ plugin ➞ findtheflow.io
The Exception Handler in the call stack
39@loicmdivad @PubSapientEng 39@loicmdivad @PubSapientEng
Powered by the Flow intelliJ plugin ➞ findtheflow.io
The Exception Handler in the call stack
40@loicmdivad @PubSapientEng 40@loicmdivad @PubSapientEng
Skip the Corrupted
All exceptions thrown by deserializers are caught by
a DeserializationExceptionHandler
A handler returns Fail or Continue
You can implement your own Handler
But the two handlers provided by the library are
really basic… let’s explore other methods
41@loicmdivad @PubSapientEng 41@loicmdivad @PubSapientEng
All exceptions thrown by deserializers are caught by
a DeserializationExceptionHandler
A handler returns Fail or Continue
You can implement your own Handler
But the two handlers provided by the library are
really basic… let’s explore other methods
Skip the Corrupted
Take
Away
42@loicmdivad @PubSapientEng
Sentinel Value Pattern
Exercise #3 - drinks
43@loicmdivad @PubSapientEng
// Exercise #3: Drink
sealed trait FoodOrder
case class Drink(name: String,
quantity: Int,
`type`: DrinkType,
alcohol: Option[Double]) extends FoodOrder
44@loicmdivad @PubSapientEng
// Exercise #3: Drink
sealed trait FoodOrder
case class Drink(name: String,
quantity: Int,
`type`: DrinkType,
alcohol: Option[Double]) extends FoodOrder
● wine
● rhum
● beer
● champagne
● ...
45@loicmdivad @PubSapientEng
We need to turn the deserialization process into a
pure transformation that cannot crash
To do so, we will replace corrupted message by a
sentinel value. It’s a special-purpose record (e.g: null,
None, Json.Null, etc ...)
Sentinel Value Pattern
f: G → H
G H
46@loicmdivad @PubSapientEng
We need to turn the deserialization process into a
pure transformation that cannot crash
To do so, we will replace corrupted message by a
sentinel value. It’s a special-purpose record (e.g: null,
None, Json.Null, etc ...)
This allows downstream processors to recognize and
handle such sentinel values
Sentinel Value Pattern
f: G → H
G H
G H
47@loicmdivad @PubSapientEng
We need to turn the deserialization process into a
pure transformation that cannot crash
To do so, we will replace corrupted message by a
sentinel value. It’s a special-purpose record (e.g: null,
None, Json.Null, etc ...)
This allows downstream processors to recognize and
handle such sentinel values
With Kafka Streams this can be achieved by
implementing a Deserializer
Sentinel Value Pattern
f: G → H
G H
G H
null
48@loicmdivad @PubSapientEng
case object FoodOrderError extends FoodOrder
class FoodOrderDeserializer extends Deserializer[FoodOrder] = ???
49@loicmdivad @PubSapientEng
case object FoodOrderError extends FoodOrder
class FoodOrderDeserializer extends Deserializer[FoodOrder] = ???
class SentinelValueDeserializer extends FoodOrderDeserializer {
override def deserialize(topic: String, data: Array[Byte]): FoodOrder =
Try(super.deserialize(topic, data)).getOrElse(FoodOrderErr)
}
50@loicmdivad @PubSapientEng@loicmdivad @PubSapientEng
51@loicmdivad @PubSapientEng
class FoodOrderSentinelValueProcessor extends ValueTransformer[FoodOrder, Unit] {
var sensor: Sensor = _
var context: ProcessorContext = _
def metricName(stat: String): MetricName = ???
override def init(context: ProcessorContext): Unit = {
this.context = context
this.sensor = this.context.metrics.addSensor("sentinel-value", INFO)
sensor.add(metricName("total"), new Total())
sensor.add(metricName("rate"), new Rate(TimeUnit.SECONDS, new Count()))
}
override def transform(value: FoodOrder): Unit = sensor.record()
}
52@loicmdivad @PubSapientEng@loicmdivad @PubSapientEng
53@loicmdivad @PubSapientEng
54@loicmdivad @PubSapientEng 54@loicmdivad @PubSapientEng
Sentinel Value Pattern
By implementing a custom serde we can create a safe
Deserializer.
Downstreams now receive a sentinel value
indicating a deserialization error.
Errors can then be treated correctly, example:
monitoring the number of deserialization
errors with a custom metric
But we lost a lot of information about the error…
let’s see a last method
55@loicmdivad @PubSapientEng 55@loicmdivad @PubSapientEng
Sentinel Value Pattern
By implementing a custom serde we can create a safe
Deserializer.
Downstreams now receive a sentinel value
indicating a deserialization error.
Errors can then be treated correctly, example:
monitoring the number of deserialization
errors with a custom metric
But we lost a lot of information about the error…
let’s see a last method
Take
Away
56@loicmdivad @PubSapientEng
Dead Letter Queue Pattern
Exercise #4 - dinner
57@loicmdivad @PubSapientEng
// Exercise #4: Dinner
sealed trait FoodOrder
case class Dinner(dish: Command,
zone: String,
moment: Moment,
maybeClient: Option[Client]) extends FoodOrder
58@loicmdivad @PubSapientEng
Dead Letter Queue Pattern
In this method we will let the deserializer fail.
For each failure we will send a message to a topic
containing corrupted messages.
Each message will have the original content of the
input message (for reprocessing) and additional
meta data about the failure.
With Kafka Streams this can be achieved by
implementing a DeserializationExceptionHandler
Streaming
APP
dead letter queue
input topic output topic
59@loicmdivad @PubSapientEng
class DeadLetterQueueFoodExceptionHandler() extends DeserializationExceptionHandler {
override def handle(context: ProcessorContext,
record: ConsumerRecord[Array[Byte], Array[Byte]],
exception: Exception): DeserializationHandlerResponse = {
}
60@loicmdivad @PubSapientEng
class DeadLetterQueueFoodExceptionHandler() extends DeserializationExceptionHandler {
override def handle(context: ProcessorContext,
record: ConsumerRecord[Array[Byte], Array[Byte]],
exception: Exception): DeserializationHandlerResponse = {
val producerRecord = new ProducerRecord(topic, /*same key, value and ts,*/ headers.asJava)
producer.send(producerRecord, /* Producer Callback */ )
DeserializationHandlerResponse.CONTINUE
}
61@loicmdivad @PubSapientEng
class DeadLetterQueueFoodExceptionHandler() extends DeserializationExceptionHandler {
var topic: String = _
var producer: KafkaProducer[Array[Byte], Array[Byte]] = _
override def configure(configs: util.Map[String, _]): Unit = ???
override def handle(context: ProcessorContext,
record: ConsumerRecord[Array[Byte], Array[Byte]],
exception: Exception): DeserializationHandlerResponse = {
val producerRecord = new ProducerRecord(topic, /*same key, value and ts,*/ headers.asJava)
producer.send(producerRecord, /* Producer Callback */ )
DeserializationHandlerResponse.CONTINUE
}
62@loicmdivad @PubSapientEng
class DeadLetterQueueFoodExceptionHandler() extends DeserializationExceptionHandler {
var topic: String = _
var producer: KafkaProducer[Array[Byte], Array[Byte]] = _
override def configure(configs: util.Map[String, _]): Unit = ???
override def handle(context: ProcessorContext,
record: ConsumerRecord[Array[Byte], Array[Byte]],
exception: Exception): DeserializationHandlerResponse = {
val headers = record.headers().toArray ++ Array[Header](
new RecordHeader("processing-time", ???),
new RecordHeader("hexa-datetime", ???),
new RecordHeader("error-message", ???),
...
)
val producerRecord = new ProducerRecord(topic, /*same key, value and ts,*/ headers.asJava)
producer.send(producerRecord, /* Producer Callback */ )
DeserializationHandlerResponse.CONTINUE
}
63@loicmdivad @PubSapientEng
Fill the headers with some meta data
01061696e0016536f6d6500000005736f6d65206f
Value message to hexa
Restaurant
description
Event date and time
Food order category
64@loicmdivad @PubSapientEng
class DeadLetterQueueFoodExceptionHandler() extends DeserializationExceptionHandler {
var topic: String = _
var producer: KafkaProducer[Array[Byte], Array[Byte]] = _
override def configure(configs: util.Map[String, _]): Unit = ???
override def handle(context: ProcessorContext,
record: ConsumerRecord[Array[Byte], Array[Byte]],
exception: Exception): DeserializationHandlerResponse = {
val headers = record.headers().toArray ++ Array[Header](
new RecordHeader("processing-time", ???),
new RecordHeader("hexa-datetime", ???),
new RecordHeader("error-message", ???),
...
)
val producerRecord = new ProducerRecord(topic, /*same key, value and ts,*/ headers.asJava)
producer.send(producerRecord, /* Producer Callback */ )
DeserializationHandlerResponse.CONTINUE
}
65@loicmdivad @PubSapientEng
class DeadLetterQueueFoodExceptionHandler() extends DeserializationExceptionHandler {
var topic: String = _
var producer: KafkaProducer[Array[Byte], Array[Byte]] = _
override def configure(configs: util.Map[String, _]): Unit = ???
override def handle(context: ProcessorContext,
record: ConsumerRecord[Array[Byte], Array[Byte]],
exception: Exception): DeserializationHandlerResponse = {
val headers = record.headers().toArray ++ Array[Header](
new RecordHeader("processing-time", ???),
new RecordHeader("hexa-datetime", ???),
new RecordHeader("error-message", ???),
...
)
val producerRecord = new ProducerRecord(topic, /*same key, value and ts,*/ headers.asJava)
producer.send(producerRecord, /* Producer Callback */ )
DeserializationHandlerResponse.CONTINUE
}
Take
Away
66@loicmdivad @PubSapientEng@loicmdivad @PubSapientEng
67@loicmdivad @PubSapientEng
68@loicmdivad @PubSapientEng
414554=AET=
Australia/Sydney
69@loicmdivad @PubSapientEng 69@loicmdivad @PubSapientEng
Dead Letter Queue Pattern
You can provide your own implementation of
DeserializationExceptionHandler.
This lets you use the Producer API to write a
corrupted record directly to a quarantine topic.
Then you can manually analyse your corrupted
records
⚠Warning: This approach have side effects that are
invisible to the Kafka Streams runtime.
70@loicmdivad @PubSapientEng 70@loicmdivad @PubSapientEng
Dead Letter Queue Pattern
You can provide your own implementation of
DeserializationExceptionHandler.
This lets you use the Producer API to write a
corrupted record directly to a quarantine topic.
Then you can manually analyse your corrupted
records
⚠Warning: This approach have side effects that are
invisible to the Kafka Streams runtime.
Take
Away
71@loicmdivad @PubSapientEng
Conclusion
Exercise #NaN - take aways
72@loicmdivad @PubSapientEng 72@loicmdivad @PubSapientEng
CONFLUENT FAQ
Links
XKE-RATATOUILLE
73@loicmdivad @PubSapientEng 73@loicmdivad @PubSapientEng
Related Post
Kafka Connect Deep Dive – Error Handling and
Dead Letter Queues - by Robin Moffatt
Building Reliable Reprocessing and Dead Letter
Queues with Apache Kafka - by Ning Xia
Handling bad messages using Kafka's Streams API -
answer by Matthias J. Sax
74@loicmdivad @PubSapientEng 74@loicmdivad @PubSapientEng
Conclusion
When using Kafka, deserialization is the
responsibility of the clients.
These internal errors are not easy to catch
When it’s possible, use Avro + Schema Registry
When it’s not possible, Kafka Streams applies
techniques to deal with serde errors:
- DLQ: By extending a ExceptionHandler
- Sentinel Value: By extending a Deserializer
75@loicmdivad @PubSapientEng@loicmdivad @PubSapientEng
MERCI
(Thank you)
76@loicmdivad @PubSapientEng 76@loicmdivad @PubSapientEng
Images
Photo by rawpixel on Unsplash
Photo by João Marcelo Martins on Unsplash
Photo by Jordane Mathieu on Unsplash
Photo by Brooke Lark on Unsplash
Photo by Jakub Kapusnak on Unsplash
Photo by Melissa Walker Horn on Unsplash
Photo by Aneta Pawlik on Unsplash
77@loicmdivad @PubSapientEng 77@loicmdivad @PubSapientEng
With special thanks to
Robin M.
Sylvain L.
Giulia B.
78@loicmdivad @PubSapientEng
How the generator works?
79@loicmdivad @PubSapientEng
Pure HTML
Akka Http Server
Akka Actor System
Kafka Topic
Exercise1
Exercise2
Me, clicking
everywhere
Akka Stream
Kafka

More Related Content

What's hot

雑なMySQLパフォーマンスチューニング
雑なMySQLパフォーマンスチューニング雑なMySQLパフォーマンスチューニング
雑なMySQLパフォーマンスチューニングyoku0825
 
パケットキャプチャの勘どころ Ssmjp 201501
パケットキャプチャの勘どころ Ssmjp 201501パケットキャプチャの勘どころ Ssmjp 201501
パケットキャプチャの勘どころ Ssmjp 201501稔 小林
 
より高品質なメディアサービスを目指す ABEMA の技術進化
より高品質なメディアサービスを目指す ABEMA の技術進化より高品質なメディアサービスを目指す ABEMA の技術進化
より高品質なメディアサービスを目指す ABEMA の技術進化Yusuke Goto
 
「Atomic Design × Nuxt.js」コンポーネント毎に責務の範囲を明確にしたら幸せになった話
「Atomic Design × Nuxt.js」コンポーネント毎に責務の範囲を明確にしたら幸せになった話「Atomic Design × Nuxt.js」コンポーネント毎に責務の範囲を明確にしたら幸せになった話
「Atomic Design × Nuxt.js」コンポーネント毎に責務の範囲を明確にしたら幸せになった話gree_tech
 
劇的改善 Ci4時間から5分へ〜私がやった10のこと〜
劇的改善 Ci4時間から5分へ〜私がやった10のこと〜劇的改善 Ci4時間から5分へ〜私がやった10のこと〜
劇的改善 Ci4時間から5分へ〜私がやった10のこと〜aha_oretama
 
「なにをどこまでやれば?」OWASP SAMMが導く開発セキュリティ強化戦略
「なにをどこまでやれば?」OWASP SAMMが導く開発セキュリティ強化戦略「なにをどこまでやれば?」OWASP SAMMが導く開発セキュリティ強化戦略
「なにをどこまでやれば?」OWASP SAMMが導く開発セキュリティ強化戦略Riotaro OKADA
 
コードの自動修正によって実現する、機能開発を止めないフレームワーク移行
コードの自動修正によって実現する、機能開発を止めないフレームワーク移行コードの自動修正によって実現する、機能開発を止めないフレームワーク移行
コードの自動修正によって実現する、機能開発を止めないフレームワーク移行gree_tech
 
DockerとPodmanの比較
DockerとPodmanの比較DockerとPodmanの比較
DockerとPodmanの比較Akihiro Suda
 
LogbackからLog4j 2への移行によるアプリケーションのスループット改善 ( JJUG CCC 2021 Fall )
LogbackからLog4j 2への移行によるアプリケーションのスループット改善 ( JJUG CCC 2021 Fall ) LogbackからLog4j 2への移行によるアプリケーションのスループット改善 ( JJUG CCC 2021 Fall )
LogbackからLog4j 2への移行によるアプリケーションのスループット改善 ( JJUG CCC 2021 Fall ) Hironobu Isoda
 
Jenkins x Kubernetesが簡単だと思ったら大変だった話
Jenkins x Kubernetesが簡単だと思ったら大変だった話Jenkins x Kubernetesが簡単だと思ったら大変だった話
Jenkins x Kubernetesが簡単だと思ったら大変だった話Masaki Yamamoto
 
WebSocketのキホン
WebSocketのキホンWebSocketのキホン
WebSocketのキホンYou_Kinjoh
 
ストリーム処理を支えるキューイングシステムの選び方
ストリーム処理を支えるキューイングシステムの選び方ストリーム処理を支えるキューイングシステムの選び方
ストリーム処理を支えるキューイングシステムの選び方Yoshiyasu SAEKI
 
NGINXセミナー(基本編)~いまさら聞けないNGINXコンフィグなど基本がわかる!
NGINXセミナー(基本編)~いまさら聞けないNGINXコンフィグなど基本がわかる!NGINXセミナー(基本編)~いまさら聞けないNGINXコンフィグなど基本がわかる!
NGINXセミナー(基本編)~いまさら聞けないNGINXコンフィグなど基本がわかる!NGINX, Inc.
 
Metasploitでペネトレーションテスト
MetasploitでペネトレーションテストMetasploitでペネトレーションテスト
Metasploitでペネトレーションテストsuper_a1ice
 
Open Liberty: オープンソースになったWebSphere Liberty
Open Liberty: オープンソースになったWebSphere LibertyOpen Liberty: オープンソースになったWebSphere Liberty
Open Liberty: オープンソースになったWebSphere LibertyTakakiyo Tanaka
 
コンテナ未経験新人が学ぶコンテナ技術入門
コンテナ未経験新人が学ぶコンテナ技術入門コンテナ未経験新人が学ぶコンテナ技術入門
コンテナ未経験新人が学ぶコンテナ技術入門Kohei Tokunaga
 

What's hot (20)

雑なMySQLパフォーマンスチューニング
雑なMySQLパフォーマンスチューニング雑なMySQLパフォーマンスチューニング
雑なMySQLパフォーマンスチューニング
 
パケットキャプチャの勘どころ Ssmjp 201501
パケットキャプチャの勘どころ Ssmjp 201501パケットキャプチャの勘どころ Ssmjp 201501
パケットキャプチャの勘どころ Ssmjp 201501
 
より高品質なメディアサービスを目指す ABEMA の技術進化
より高品質なメディアサービスを目指す ABEMA の技術進化より高品質なメディアサービスを目指す ABEMA の技術進化
より高品質なメディアサービスを目指す ABEMA の技術進化
 
「Atomic Design × Nuxt.js」コンポーネント毎に責務の範囲を明確にしたら幸せになった話
「Atomic Design × Nuxt.js」コンポーネント毎に責務の範囲を明確にしたら幸せになった話「Atomic Design × Nuxt.js」コンポーネント毎に責務の範囲を明確にしたら幸せになった話
「Atomic Design × Nuxt.js」コンポーネント毎に責務の範囲を明確にしたら幸せになった話
 
Oss貢献超入門
Oss貢献超入門Oss貢献超入門
Oss貢献超入門
 
劇的改善 Ci4時間から5分へ〜私がやった10のこと〜
劇的改善 Ci4時間から5分へ〜私がやった10のこと〜劇的改善 Ci4時間から5分へ〜私がやった10のこと〜
劇的改善 Ci4時間から5分へ〜私がやった10のこと〜
 
「なにをどこまでやれば?」OWASP SAMMが導く開発セキュリティ強化戦略
「なにをどこまでやれば?」OWASP SAMMが導く開発セキュリティ強化戦略「なにをどこまでやれば?」OWASP SAMMが導く開発セキュリティ強化戦略
「なにをどこまでやれば?」OWASP SAMMが導く開発セキュリティ強化戦略
 
WebSocket / WebRTCの技術紹介
WebSocket / WebRTCの技術紹介WebSocket / WebRTCの技術紹介
WebSocket / WebRTCの技術紹介
 
コードの自動修正によって実現する、機能開発を止めないフレームワーク移行
コードの自動修正によって実現する、機能開発を止めないフレームワーク移行コードの自動修正によって実現する、機能開発を止めないフレームワーク移行
コードの自動修正によって実現する、機能開発を止めないフレームワーク移行
 
DockerとPodmanの比較
DockerとPodmanの比較DockerとPodmanの比較
DockerとPodmanの比較
 
LogbackからLog4j 2への移行によるアプリケーションのスループット改善 ( JJUG CCC 2021 Fall )
LogbackからLog4j 2への移行によるアプリケーションのスループット改善 ( JJUG CCC 2021 Fall ) LogbackからLog4j 2への移行によるアプリケーションのスループット改善 ( JJUG CCC 2021 Fall )
LogbackからLog4j 2への移行によるアプリケーションのスループット改善 ( JJUG CCC 2021 Fall )
 
Jenkins x Kubernetesが簡単だと思ったら大変だった話
Jenkins x Kubernetesが簡単だと思ったら大変だった話Jenkins x Kubernetesが簡単だと思ったら大変だった話
Jenkins x Kubernetesが簡単だと思ったら大変だった話
 
WebSocketのキホン
WebSocketのキホンWebSocketのキホン
WebSocketのキホン
 
本当に怖いパフォーマンスが悪い実装 #phpcon2013
本当に怖いパフォーマンスが悪い実装 #phpcon2013本当に怖いパフォーマンスが悪い実装 #phpcon2013
本当に怖いパフォーマンスが悪い実装 #phpcon2013
 
ストリーム処理を支えるキューイングシステムの選び方
ストリーム処理を支えるキューイングシステムの選び方ストリーム処理を支えるキューイングシステムの選び方
ストリーム処理を支えるキューイングシステムの選び方
 
NGINXセミナー(基本編)~いまさら聞けないNGINXコンフィグなど基本がわかる!
NGINXセミナー(基本編)~いまさら聞けないNGINXコンフィグなど基本がわかる!NGINXセミナー(基本編)~いまさら聞けないNGINXコンフィグなど基本がわかる!
NGINXセミナー(基本編)~いまさら聞けないNGINXコンフィグなど基本がわかる!
 
Metasploitでペネトレーションテスト
MetasploitでペネトレーションテストMetasploitでペネトレーションテスト
Metasploitでペネトレーションテスト
 
ヤフー社内でやってるMySQLチューニングセミナー大公開
ヤフー社内でやってるMySQLチューニングセミナー大公開ヤフー社内でやってるMySQLチューニングセミナー大公開
ヤフー社内でやってるMySQLチューニングセミナー大公開
 
Open Liberty: オープンソースになったWebSphere Liberty
Open Liberty: オープンソースになったWebSphere LibertyOpen Liberty: オープンソースになったWebSphere Liberty
Open Liberty: オープンソースになったWebSphere Liberty
 
コンテナ未経験新人が学ぶコンテナ技術入門
コンテナ未経験新人が学ぶコンテナ技術入門コンテナ未経験新人が学ぶコンテナ技術入門
コンテナ未経験新人が学ぶコンテナ技術入門
 

Similar to Streaming Apps and Poison Pills: handle the unexpected with Kafka Streams

Streaming Apps and Poison Pills: handle the unexpected with Kafka Streams (Lo...
Streaming Apps and Poison Pills: handle the unexpected with Kafka Streams (Lo...Streaming Apps and Poison Pills: handle the unexpected with Kafka Streams (Lo...
Streaming Apps and Poison Pills: handle the unexpected with Kafka Streams (Lo...confluent
 
Applets_Lab3Character Codes.jarMETA-INFMANIFEST.MFManife.docx
Applets_Lab3Character Codes.jarMETA-INFMANIFEST.MFManife.docxApplets_Lab3Character Codes.jarMETA-INFMANIFEST.MFManife.docx
Applets_Lab3Character Codes.jarMETA-INFMANIFEST.MFManife.docxarmitageclaire49
 
The WordPress way, the modern way: developing as if it were 2016
The WordPress way, the modern way: developing as if it were 2016The WordPress way, the modern way: developing as if it were 2016
The WordPress way, the modern way: developing as if it were 2016lucatume
 
COCOA: Communication-Efficient Coordinate Ascent
COCOA: Communication-Efficient Coordinate AscentCOCOA: Communication-Efficient Coordinate Ascent
COCOA: Communication-Efficient Coordinate Ascentjeykottalam
 
Impossible Programs
Impossible ProgramsImpossible Programs
Impossible ProgramsC4Media
 
Puppet and Software Delivery
Puppet and Software DeliveryPuppet and Software Delivery
Puppet and Software DeliveryJulien Pivotto
 

Similar to Streaming Apps and Poison Pills: handle the unexpected with Kafka Streams (8)

Streaming Apps and Poison Pills: handle the unexpected with Kafka Streams (Lo...
Streaming Apps and Poison Pills: handle the unexpected with Kafka Streams (Lo...Streaming Apps and Poison Pills: handle the unexpected with Kafka Streams (Lo...
Streaming Apps and Poison Pills: handle the unexpected with Kafka Streams (Lo...
 
Applets_Lab3Character Codes.jarMETA-INFMANIFEST.MFManife.docx
Applets_Lab3Character Codes.jarMETA-INFMANIFEST.MFManife.docxApplets_Lab3Character Codes.jarMETA-INFMANIFEST.MFManife.docx
Applets_Lab3Character Codes.jarMETA-INFMANIFEST.MFManife.docx
 
The WordPress way, the modern way: developing as if it were 2016
The WordPress way, the modern way: developing as if it were 2016The WordPress way, the modern way: developing as if it were 2016
The WordPress way, the modern way: developing as if it were 2016
 
COCOA: Communication-Efficient Coordinate Ascent
COCOA: Communication-Efficient Coordinate AscentCOCOA: Communication-Efficient Coordinate Ascent
COCOA: Communication-Efficient Coordinate Ascent
 
Impossible Programs
Impossible ProgramsImpossible Programs
Impossible Programs
 
Model-checking for efficient malware detection
Model-checking for efficient malware detectionModel-checking for efficient malware detection
Model-checking for efficient malware detection
 
Weapons for Boilerplate Destruction
Weapons for Boilerplate DestructionWeapons for Boilerplate Destruction
Weapons for Boilerplate Destruction
 
Puppet and Software Delivery
Puppet and Software DeliveryPuppet and Software Delivery
Puppet and Software Delivery
 

More from confluent

Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...
Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...
Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...confluent
 
Santander Stream Processing with Apache Flink
Santander Stream Processing with Apache FlinkSantander Stream Processing with Apache Flink
Santander Stream Processing with Apache Flinkconfluent
 
Unlocking the Power of IoT: A comprehensive approach to real-time insights
Unlocking the Power of IoT: A comprehensive approach to real-time insightsUnlocking the Power of IoT: A comprehensive approach to real-time insights
Unlocking the Power of IoT: A comprehensive approach to real-time insightsconfluent
 
Workshop híbrido: Stream Processing con Flink
Workshop híbrido: Stream Processing con FlinkWorkshop híbrido: Stream Processing con Flink
Workshop híbrido: Stream Processing con Flinkconfluent
 
Industry 4.0: Building the Unified Namespace with Confluent, HiveMQ and Spark...
Industry 4.0: Building the Unified Namespace with Confluent, HiveMQ and Spark...Industry 4.0: Building the Unified Namespace with Confluent, HiveMQ and Spark...
Industry 4.0: Building the Unified Namespace with Confluent, HiveMQ and Spark...confluent
 
AWS Immersion Day Mapfre - Confluent
AWS Immersion Day Mapfre   -   ConfluentAWS Immersion Day Mapfre   -   Confluent
AWS Immersion Day Mapfre - Confluentconfluent
 
Eventos y Microservicios - Santander TechTalk
Eventos y Microservicios - Santander TechTalkEventos y Microservicios - Santander TechTalk
Eventos y Microservicios - Santander TechTalkconfluent
 
Q&A with Confluent Experts: Navigating Networking in Confluent Cloud
Q&A with Confluent Experts: Navigating Networking in Confluent CloudQ&A with Confluent Experts: Navigating Networking in Confluent Cloud
Q&A with Confluent Experts: Navigating Networking in Confluent Cloudconfluent
 
Citi TechTalk Session 2: Kafka Deep Dive
Citi TechTalk Session 2: Kafka Deep DiveCiti TechTalk Session 2: Kafka Deep Dive
Citi TechTalk Session 2: Kafka Deep Diveconfluent
 
Build real-time streaming data pipelines to AWS with Confluent
Build real-time streaming data pipelines to AWS with ConfluentBuild real-time streaming data pipelines to AWS with Confluent
Build real-time streaming data pipelines to AWS with Confluentconfluent
 
Q&A with Confluent Professional Services: Confluent Service Mesh
Q&A with Confluent Professional Services: Confluent Service MeshQ&A with Confluent Professional Services: Confluent Service Mesh
Q&A with Confluent Professional Services: Confluent Service Meshconfluent
 
Citi Tech Talk: Event Driven Kafka Microservices
Citi Tech Talk: Event Driven Kafka MicroservicesCiti Tech Talk: Event Driven Kafka Microservices
Citi Tech Talk: Event Driven Kafka Microservicesconfluent
 
Confluent & GSI Webinars series - Session 3
Confluent & GSI Webinars series - Session 3Confluent & GSI Webinars series - Session 3
Confluent & GSI Webinars series - Session 3confluent
 
Citi Tech Talk: Messaging Modernization
Citi Tech Talk: Messaging ModernizationCiti Tech Talk: Messaging Modernization
Citi Tech Talk: Messaging Modernizationconfluent
 
Citi Tech Talk: Data Governance for streaming and real time data
Citi Tech Talk: Data Governance for streaming and real time dataCiti Tech Talk: Data Governance for streaming and real time data
Citi Tech Talk: Data Governance for streaming and real time dataconfluent
 
Confluent & GSI Webinars series: Session 2
Confluent & GSI Webinars series: Session 2Confluent & GSI Webinars series: Session 2
Confluent & GSI Webinars series: Session 2confluent
 
Data In Motion Paris 2023
Data In Motion Paris 2023Data In Motion Paris 2023
Data In Motion Paris 2023confluent
 
Confluent Partner Tech Talk with Synthesis
Confluent Partner Tech Talk with SynthesisConfluent Partner Tech Talk with Synthesis
Confluent Partner Tech Talk with Synthesisconfluent
 
The Future of Application Development - API Days - Melbourne 2023
The Future of Application Development - API Days - Melbourne 2023The Future of Application Development - API Days - Melbourne 2023
The Future of Application Development - API Days - Melbourne 2023confluent
 
The Playful Bond Between REST And Data Streams
The Playful Bond Between REST And Data StreamsThe Playful Bond Between REST And Data Streams
The Playful Bond Between REST And Data Streamsconfluent
 

More from confluent (20)

Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...
Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...
Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...
 
Santander Stream Processing with Apache Flink
Santander Stream Processing with Apache FlinkSantander Stream Processing with Apache Flink
Santander Stream Processing with Apache Flink
 
Unlocking the Power of IoT: A comprehensive approach to real-time insights
Unlocking the Power of IoT: A comprehensive approach to real-time insightsUnlocking the Power of IoT: A comprehensive approach to real-time insights
Unlocking the Power of IoT: A comprehensive approach to real-time insights
 
Workshop híbrido: Stream Processing con Flink
Workshop híbrido: Stream Processing con FlinkWorkshop híbrido: Stream Processing con Flink
Workshop híbrido: Stream Processing con Flink
 
Industry 4.0: Building the Unified Namespace with Confluent, HiveMQ and Spark...
Industry 4.0: Building the Unified Namespace with Confluent, HiveMQ and Spark...Industry 4.0: Building the Unified Namespace with Confluent, HiveMQ and Spark...
Industry 4.0: Building the Unified Namespace with Confluent, HiveMQ and Spark...
 
AWS Immersion Day Mapfre - Confluent
AWS Immersion Day Mapfre   -   ConfluentAWS Immersion Day Mapfre   -   Confluent
AWS Immersion Day Mapfre - Confluent
 
Eventos y Microservicios - Santander TechTalk
Eventos y Microservicios - Santander TechTalkEventos y Microservicios - Santander TechTalk
Eventos y Microservicios - Santander TechTalk
 
Q&A with Confluent Experts: Navigating Networking in Confluent Cloud
Q&A with Confluent Experts: Navigating Networking in Confluent CloudQ&A with Confluent Experts: Navigating Networking in Confluent Cloud
Q&A with Confluent Experts: Navigating Networking in Confluent Cloud
 
Citi TechTalk Session 2: Kafka Deep Dive
Citi TechTalk Session 2: Kafka Deep DiveCiti TechTalk Session 2: Kafka Deep Dive
Citi TechTalk Session 2: Kafka Deep Dive
 
Build real-time streaming data pipelines to AWS with Confluent
Build real-time streaming data pipelines to AWS with ConfluentBuild real-time streaming data pipelines to AWS with Confluent
Build real-time streaming data pipelines to AWS with Confluent
 
Q&A with Confluent Professional Services: Confluent Service Mesh
Q&A with Confluent Professional Services: Confluent Service MeshQ&A with Confluent Professional Services: Confluent Service Mesh
Q&A with Confluent Professional Services: Confluent Service Mesh
 
Citi Tech Talk: Event Driven Kafka Microservices
Citi Tech Talk: Event Driven Kafka MicroservicesCiti Tech Talk: Event Driven Kafka Microservices
Citi Tech Talk: Event Driven Kafka Microservices
 
Confluent & GSI Webinars series - Session 3
Confluent & GSI Webinars series - Session 3Confluent & GSI Webinars series - Session 3
Confluent & GSI Webinars series - Session 3
 
Citi Tech Talk: Messaging Modernization
Citi Tech Talk: Messaging ModernizationCiti Tech Talk: Messaging Modernization
Citi Tech Talk: Messaging Modernization
 
Citi Tech Talk: Data Governance for streaming and real time data
Citi Tech Talk: Data Governance for streaming and real time dataCiti Tech Talk: Data Governance for streaming and real time data
Citi Tech Talk: Data Governance for streaming and real time data
 
Confluent & GSI Webinars series: Session 2
Confluent & GSI Webinars series: Session 2Confluent & GSI Webinars series: Session 2
Confluent & GSI Webinars series: Session 2
 
Data In Motion Paris 2023
Data In Motion Paris 2023Data In Motion Paris 2023
Data In Motion Paris 2023
 
Confluent Partner Tech Talk with Synthesis
Confluent Partner Tech Talk with SynthesisConfluent Partner Tech Talk with Synthesis
Confluent Partner Tech Talk with Synthesis
 
The Future of Application Development - API Days - Melbourne 2023
The Future of Application Development - API Days - Melbourne 2023The Future of Application Development - API Days - Melbourne 2023
The Future of Application Development - API Days - Melbourne 2023
 
The Playful Bond Between REST And Data Streams
The Playful Bond Between REST And Data StreamsThe Playful Bond Between REST And Data Streams
The Playful Bond Between REST And Data Streams
 

Recently uploaded

ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemkeProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemkeProduct Anonymous
 
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptxHampshireHUG
 
Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)wesley chun
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationMichael W. Hawkins
 
Histor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slideHistor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slidevu2urc
 
Strategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a FresherStrategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a FresherRemote DBA Services
 
What Are The Drone Anti-jamming Systems Technology?
What Are The Drone Anti-jamming Systems Technology?What Are The Drone Anti-jamming Systems Technology?
What Are The Drone Anti-jamming Systems Technology?Antenna Manufacturer Coco
 
Real Time Object Detection Using Open CV
Real Time Object Detection Using Open CVReal Time Object Detection Using Open CV
Real Time Object Detection Using Open CVKhem
 
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...Neo4j
 
AWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of TerraformAWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of TerraformAndrey Devyatkin
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfsudhanshuwaghmare1
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)Gabriella Davis
 
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, AdobeApidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobeapidays
 
Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024The Digital Insurer
 
Advantages of Hiring UIUX Design Service Providers for Your Business
Advantages of Hiring UIUX Design Service Providers for Your BusinessAdvantages of Hiring UIUX Design Service Providers for Your Business
Advantages of Hiring UIUX Design Service Providers for Your BusinessPixlogix Infotech
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerThousandEyes
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Drew Madelung
 
GenAI Risks & Security Meetup 01052024.pdf
GenAI Risks & Security Meetup 01052024.pdfGenAI Risks & Security Meetup 01052024.pdf
GenAI Risks & Security Meetup 01052024.pdflior mazor
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonAnna Loughnan Colquhoun
 
HTML Injection Attacks: Impact and Mitigation Strategies
HTML Injection Attacks: Impact and Mitigation StrategiesHTML Injection Attacks: Impact and Mitigation Strategies
HTML Injection Attacks: Impact and Mitigation StrategiesBoston Institute of Analytics
 

Recently uploaded (20)

ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemkeProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
 
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
 
Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day Presentation
 
Histor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slideHistor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slide
 
Strategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a FresherStrategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a Fresher
 
What Are The Drone Anti-jamming Systems Technology?
What Are The Drone Anti-jamming Systems Technology?What Are The Drone Anti-jamming Systems Technology?
What Are The Drone Anti-jamming Systems Technology?
 
Real Time Object Detection Using Open CV
Real Time Object Detection Using Open CVReal Time Object Detection Using Open CV
Real Time Object Detection Using Open CV
 
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
 
AWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of TerraformAWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of Terraform
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdf
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)
 
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, AdobeApidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
 
Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024
 
Advantages of Hiring UIUX Design Service Providers for Your Business
Advantages of Hiring UIUX Design Service Providers for Your BusinessAdvantages of Hiring UIUX Design Service Providers for Your Business
Advantages of Hiring UIUX Design Service Providers for Your Business
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
 
GenAI Risks & Security Meetup 01052024.pdf
GenAI Risks & Security Meetup 01052024.pdfGenAI Risks & Security Meetup 01052024.pdf
GenAI Risks & Security Meetup 01052024.pdf
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt Robison
 
HTML Injection Attacks: Impact and Mitigation Strategies
HTML Injection Attacks: Impact and Mitigation StrategiesHTML Injection Attacks: Impact and Mitigation Strategies
HTML Injection Attacks: Impact and Mitigation Strategies
 

Streaming Apps and Poison Pills: handle the unexpected with Kafka Streams

  • 1. 1@loicmdivad @PubSapientEng@loicmdivad @PubSapientEng Streaming Apps and Poison Pills: handle the unexpected with Kafka Streams 28 Jul. 2020 - Online Kafka Meetup
  • 2. 2@loicmdivad @PubSapientEng@loicmdivad @PubSapientEng Loïc DIVAD Software Engineer @loicmdivad Technical Officer @PubSapientEng
  • 3. 3@loicmdivad @PubSapientEng Streaming Apps and Poison Pills San Francisco, CA - October 2019 Ratatouille: handle the unexpected with Kafka Streams
  • 4. 4@loicmdivad @PubSapientEng 4@loicmdivad @PubSapientEng > println(sommaire) Incoming records may be corrupted, or cannot be handled by the serializer / deserializer. These records are referred to as “poison pills” 1. Log and Crash 2. Skip the Corrupted 3. Sentinel Value Pattern 4. Dead Letter Queue Pattern
  • 5. 5@loicmdivad @PubSapientEng Ratatouille app, a delicious use case Streaming APP
  • 6. 6@loicmdivad @PubSapientEng Ratatouille app, a delicious use case Streaming APP
  • 7. 7@loicmdivad @PubSapientEng 7@loicmdivad @PubSapientEng Streaming App Poison Pills 1. Log and Crash - Breakfast 2. Skip the Corrupted - Lunch 3. Sentinel Value Pattern - Drink 4. Dead Letter Queue Pattern - Dinner
  • 9. 9@loicmdivad @PubSapientEng Log and Crash Exercise #1 - breakfast
  • 10. 10@loicmdivad @PubSapientEng Really old systems receive raw bytes directly from message queues 10100110111010101 Exercise #1 - breakfast
  • 11. 11@loicmdivad @PubSapientEng Really old systems receive raw bytes directly from message queues With Kafka (Connect and Streams) we’d like to continuously transform these messages 10100110111010101 Kafka Connect Kafka Brokers Exercise #1 - breakfast
  • 12. 12@loicmdivad @PubSapientEng Really old systems receive raw bytes directly from message queues With Kafka (Connect and Streams) we’d like to continuously transform these messages But we need a deserializer with special decoder to understand each event What happens if we get a buggy implementation of the deserializer? 10100110111010101 Kafka Connect Kafka Brokers Kafka Streams Exercise #1 - breakfast
  • 13. 13@loicmdivad @PubSapientEng The Tooling Team They will provide an appropriate deserializer
  • 14. 14@loicmdivad @PubSapientEng // Exercise #1: Breakfast sealed trait FoodOrder case class Breakfast(lang: Lang, fruit: Fruit, liquid: Liquid, pastries: Vector[Pastry] = Vector.empty) extends FoodOrder
  • 15. 15@loicmdivad @PubSapientEng // Exercise #1: Breakfast sealed trait FoodOrder case class Breakfast(lang: Lang, fruit: Fruit, liquid: Liquid, pastries: Vector[Pastry] = Vector.empty) extends FoodOrder implicit lazy val BreakfastCodec: Codec[Breakfast] = new Codec[Breakfast] = ???
  • 16. 16@loicmdivad @PubSapientEng // Exercise #1: Breakfast sealed trait FoodOrder case class Breakfast(lang: Lang, fruit: Fruit, liquid: Liquid, pastries: Vector[Pastry] = Vector.empty) extends FoodOrder implicit lazy val BreakfastCodec: Codec[Breakfast] = new Codec[Breakfast] = ??? class FoodOrderSerializer extends Serializer[FoodOrder] = ??? class FoodOrderDeserializer extends Deserializer[FoodOrder] = ???
  • 17. 17@loicmdivad @PubSapientEng // Exercise #1: Breakfast sealed trait FoodOrder case class Breakfast(lang: Lang, fruit: Fruit, liquid: Liquid, pastries: Vector[Pastry] = Vector.empty) extends FoodOrder implicit lazy val BreakfastCodec: Codec[Breakfast] = new Codec[Breakfast] = ??? class FoodOrderSerializer extends Serializer[FoodOrder] = ??? class FoodOrderDeserializer extends Deserializer[FoodOrder] = ??? org.apache.kafka.common.serialization Take Away
  • 19. 19@loicmdivad @PubSapientEng Log and Crash 2019-04-17 03:43:12 macbook-de-lolo [ERROR] (LogAndFailExceptionHandler.java:39) - Exception caught during Deserialization, taskId: 0_0, topic: input-food-order, partition: 0, offset: 109 Exception in thread "answer-one-breakfast-0d808ce7-0ef1-44c6-808a-f594bc7fceae-StreamThread-1" org.apache.kafka.streams.errors.StreamsException: Deserialization exception handler is set to fail upon a deserialization error. If you would rather have the streaming pipeline continue after a deserialization error, please set the default.deserialization.exception.handler appropriately. at org.apache.kafka.streams.processor.internals.RecordDeserializer.deserialize(RecordDeserializer.java:80) at org.apache.kafka.streams.processor.internals.RecordQueue.addRawRecords(RecordQueue.java:101) at org.apache.kafka.streams.processor.internals.PartitionGroup.addRawRecords(PartitionGroup.java:124) ... at org.apache.kafka.streams.processor.internals.StreamTask.addRecords(StreamTask.java:711) at org.apache.kafka.streams.processor.internals.StreamThread.run(StreamThread.java:747) Caused by: java.lang.IllegalArgumentException: dishes: Insufficient number of elements: decoded 0 but should have decoded 268435712 at scodec.Attempt$Failure.require(Attempt.scala:108) at fr.xebia.ldi.ratatouille.serde.BreakfastDeserializer.deserialize(BreakfastDeserializer.scala:22) at fr.xebia.ldi.ratatouille.serde.BreakfastDeserializer.deserialize(BreakfastDeserializer.scala:15) at org.apache.kafka.common.serialization.Deserializer.deserialize(Deserializer.java:58) at fr.xebia.ldi.ratatouille.serde.BreakfastDeserializer.deserialize(BreakfastDeserializer.scala:15) at org.apache.kafka.streams.processor.internals.SourceNode.deserializeValue(SourceNode.java:60) at org.apache.kafka.streams.processor.internals.RecordDeserializer.deserialize(RecordDeserializer.java:66)
  • 20. 20@loicmdivad @PubSapientEng Log and Crash 2019-04-17 03:43:12 macbook-de-lolo [ERROR] (LogAndFailExceptionHandler.java:39) - Exception caught during Deserialization, taskId: 0_0, topic: exercise-breakfast, partition: 0, offset: 109 Exception in thread "answer-one-breakfast-0d808ce7-0ef1-44c6-808a-f594bc7fceae-StreamThread-1" org.apache.kafka.streams.errors.StreamsException: Deserialization exception handler is set to fail upon a deserialization error. If you would rather have the streaming pipeline continue after a deserialization error, please set the default.deserialization.exception.handler appropriately. at org.apache.kafka.streams.processor.internals.RecordDeserializer.deserialize(RecordDeserializer.java:80) at org.apache.kafka.streams.processor.internals.RecordQueue.addRawRecords(RecordQueue.java:101) at org.apache.kafka.streams.processor.internals.PartitionGroup.addRawRecords(PartitionGroup.java:124) ... at org.apache.kafka.streams.processor.internals.StreamTask.addRecords(StreamTask.java:711) at org.apache.kafka.streams.processor.internals.StreamThread.run(StreamThread.java:747) Caused by: java.lang.IllegalArgumentException: dishes: Insufficient number of elements: decoded 0 but should have decoded 268435712 at scodec.Attempt$Failure.require(Attempt.scala:108) at fr.xebia.ldi.ratatouille.serde.BreakfastDeserializer.deserialize(BreakfastDeserializer.scala:22) at fr.xebia.ldi.ratatouille.serde.BreakfastDeserializer.deserialize(BreakfastDeserializer.scala:15) at org.apache.kafka.common.serialization.Deserializer.deserialize(Deserializer.java:58) at fr.xebia.ldi.ratatouille.serde.BreakfastDeserializer.deserialize(BreakfastDeserializer.scala:15) at org.apache.kafka.streams.processor.internals.SourceNode.deserializeValue(SourceNode.java:60) at org.apache.kafka.streams.processor.internals.RecordDeserializer.deserialize(RecordDeserializer.java:66)
  • 21. 21@loicmdivad @PubSapientEng |val frame1: Array[Byte] = Array(0x33, 0xd4, 0xfc, 0x00, 0x00, 0x00, 0x01, 0xa5) |val frame2: Array[Byte] = Array(0x44, 0xd2, 0xfe, 0x10, 0x02, 0x03, 0x01)
  • 22. 22@loicmdivad @PubSapientEng |val frame1: Array[Byte] = Array( , 0xd4, 0xfc, 0x00, 0x00, 0x00, 0x01, 0xa5) |val frame2: Array[Byte] = Array( , 0xd2, 0xfe, 0x10, 0x02, 0x03, 0x01)
  • 23. 23@loicmdivad @PubSapientEng |val frame1: Array[Byte] = Array( , 0xd4, 0xfc, 0x00, 0x00, 0x00, 0x01, 0xa5) |val frame2: Array[Byte] = Array( , 0xd2, 0xfe, 0x10, x2, 0x03, 0x01) |case class Meat(sausages: Int, bacons: Int, . . . )
  • 24. 24@loicmdivad @PubSapientEng ▼ Change consumer group ▼ Manually update my offsets ▼ Reset my streaming app and set my auto reset to LATEST ▽ $ kafka-streams-application-reset ... ▼ Destroy the topic, no message = no poison pill ▽ $ kafka-topics --delete --topic ... ▼ My favourite <3 ▽ $ confluent destroy && confluent start Don’t Do ▼ Fill an issue and suggest a fix to the tooling team
  • 26. 26@loicmdivad @PubSapientEng 26@loicmdivad @PubSapientEng Log and Crash Like all consumers, Kafka Streams applications deserialize messages from the broker. The deserialization process can fail. It raises an exception that cannot be caught by our code. Buggy deserializers have to be fixed before the application restarts, by default ...
  • 27. 27@loicmdivad @PubSapientEng Skip the Corrupted Exercise #2 - lunch
  • 28. 28@loicmdivad @PubSapientEng // Exercise #2: Lunch sealed trait FoodOrder case class Lunch(name: String, price: Double, `type`: LunchType) extends FoodOrder
  • 29. 29@loicmdivad @PubSapientEng // Exercise #2: Lunch sealed trait FoodOrder case class Lunch(name: String, price: Double, `type`: LunchType) extends FoodOrder ● starter ● main ● dessert
  • 31. 31@loicmdivad @PubSapientEng Skip the Corrupted 2019-04-17 03:43:12 macbook-de-lolo [ERROR] (LogAndFailExceptionHandler.java:39) - Exception caught during Deserialization, taskId: 0_0, topic: exercise-breakfast, partition: 0, offset: 109 Exception in thread "answer-one-breakfast-0d808ce7-0ef1-44c6-808a-f594bc7fceae-StreamThread-1" org.apache.kafka.streams.errors.StreamsException: Deserialization exception handler is set to fail upon a deserialization error. If you would rather have the streaming pipeline continue after a deserialization error, please set the default.deserialization.exception.handler appropriately. at org.apache.kafka.streams.processor.internals.RecordDeserializer.deserialize(RecordDeserializer.java:80) at org.apache.kafka.streams.processor.internals.RecordQueue.addRawRecords(RecordQueue.java:101) at org.apache.kafka.streams.processor.internals.PartitionGroup.addRawRecords(PartitionGroup.java:124) ... at org.apache.kafka.streams.processor.internals.StreamTask.addRecords(StreamTask.java:711) at org.apache.kafka.streams.processor.internals.StreamThread.run(StreamThread.java:747) Caused by: java.lang.IllegalArgumentException: ... decoded 0 but should have decoded 268435712 at scodec.Attempt$Failure.require(Attempt.scala:108) at fr.xebia.ldi.ratatouille.serde.BreakfastDeserializer.deserialize(BreakfastDeserializer.scala:22) at fr.xebia.ldi.ratatouille.serde.BreakfastDeserializer.deserialize(BreakfastDeserializer.scala:15) at org.apache.kafka.common.serialization.Deserializer.deserialize(Deserializer.java:58) at fr.xebia.ldi.ratatouille.serde.BreakfastDeserializer.deserialize(BreakfastDeserializer.scala:15) at org.apache.kafka.streams.processor.internals.SourceNode.deserializeValue(SourceNode.java:60) at org.apache.kafka.streams.processor.internals.RecordDeserializer.deserialize(RecordDeserializer.java:66)
  • 32. 32@loicmdivad @PubSapientEng 32@loicmdivad @PubSapientEng public class LogAndFailExceptionHandler implements DeserializationExceptionHandler /* ... */ public class LogAndContinueExceptionHandler implements DeserializationExceptionHandler /* ... */
  • 33. 33@loicmdivad @PubSapientEng public class LogAndFailExceptionHandler implements DeserializationExceptionHandler /* ... */ public class LogAndContinueExceptionHandler implements DeserializationExceptionHandler /* ... */ public interface DeserializationExceptionHandler extends Configurable { DeserializationHandlerResponse handle(final ProcessorContext context, final ConsumerRecord<byte[], byte[]> record, final Exception exception); enum DeserializationHandlerResponse { CONTINUE(0, "CONTINUE"), FAIL(1, "FAIL"); /* ... */ } } }
  • 34. 34@loicmdivad @PubSapientEng public class LogAndFailExceptionHandler implements DeserializationExceptionHandler /* ... */ public class LogAndContinueExceptionHandler implements DeserializationExceptionHandler /* ... */ public interface DeserializationExceptionHandler extends Configurable { DeserializationHandlerResponse handle(final ProcessorContext context, final ConsumerRecord<byte[], byte[]> record, final Exception exception); enum DeserializationHandlerResponse { CONTINUE(0, "CONTINUE"), FAIL(1, "FAIL"); /* ... */ } } } Take Away
  • 36. 36@loicmdivad @PubSapientEng 36@loicmdivad @PubSapientEng The Exception Handler in the call stack Powered by the Flow intelliJ plugin ➞ findtheflow.io
  • 37. 37@loicmdivad @PubSapientEng 37@loicmdivad @PubSapientEng Powered by the Flow intelliJ plugin ➞ findtheflow.io The Exception Handler in the call stack
  • 38. 38@loicmdivad @PubSapientEng 38@loicmdivad @PubSapientEng Powered by the Flow intelliJ plugin ➞ findtheflow.io The Exception Handler in the call stack
  • 39. 39@loicmdivad @PubSapientEng 39@loicmdivad @PubSapientEng Powered by the Flow intelliJ plugin ➞ findtheflow.io The Exception Handler in the call stack
  • 40. 40@loicmdivad @PubSapientEng 40@loicmdivad @PubSapientEng Skip the Corrupted All exceptions thrown by deserializers are caught by a DeserializationExceptionHandler A handler returns Fail or Continue You can implement your own Handler But the two handlers provided by the library are really basic… let’s explore other methods
  • 41. 41@loicmdivad @PubSapientEng 41@loicmdivad @PubSapientEng All exceptions thrown by deserializers are caught by a DeserializationExceptionHandler A handler returns Fail or Continue You can implement your own Handler But the two handlers provided by the library are really basic… let’s explore other methods Skip the Corrupted Take Away
  • 42. 42@loicmdivad @PubSapientEng Sentinel Value Pattern Exercise #3 - drinks
  • 43. 43@loicmdivad @PubSapientEng // Exercise #3: Drink sealed trait FoodOrder case class Drink(name: String, quantity: Int, `type`: DrinkType, alcohol: Option[Double]) extends FoodOrder
  • 44. 44@loicmdivad @PubSapientEng // Exercise #3: Drink sealed trait FoodOrder case class Drink(name: String, quantity: Int, `type`: DrinkType, alcohol: Option[Double]) extends FoodOrder ● wine ● rhum ● beer ● champagne ● ...
  • 45. 45@loicmdivad @PubSapientEng We need to turn the deserialization process into a pure transformation that cannot crash To do so, we will replace corrupted message by a sentinel value. It’s a special-purpose record (e.g: null, None, Json.Null, etc ...) Sentinel Value Pattern f: G → H G H
  • 46. 46@loicmdivad @PubSapientEng We need to turn the deserialization process into a pure transformation that cannot crash To do so, we will replace corrupted message by a sentinel value. It’s a special-purpose record (e.g: null, None, Json.Null, etc ...) This allows downstream processors to recognize and handle such sentinel values Sentinel Value Pattern f: G → H G H G H
  • 47. 47@loicmdivad @PubSapientEng We need to turn the deserialization process into a pure transformation that cannot crash To do so, we will replace corrupted message by a sentinel value. It’s a special-purpose record (e.g: null, None, Json.Null, etc ...) This allows downstream processors to recognize and handle such sentinel values With Kafka Streams this can be achieved by implementing a Deserializer Sentinel Value Pattern f: G → H G H G H null
  • 48. 48@loicmdivad @PubSapientEng case object FoodOrderError extends FoodOrder class FoodOrderDeserializer extends Deserializer[FoodOrder] = ???
  • 49. 49@loicmdivad @PubSapientEng case object FoodOrderError extends FoodOrder class FoodOrderDeserializer extends Deserializer[FoodOrder] = ??? class SentinelValueDeserializer extends FoodOrderDeserializer { override def deserialize(topic: String, data: Array[Byte]): FoodOrder = Try(super.deserialize(topic, data)).getOrElse(FoodOrderErr) }
  • 51. 51@loicmdivad @PubSapientEng class FoodOrderSentinelValueProcessor extends ValueTransformer[FoodOrder, Unit] { var sensor: Sensor = _ var context: ProcessorContext = _ def metricName(stat: String): MetricName = ??? override def init(context: ProcessorContext): Unit = { this.context = context this.sensor = this.context.metrics.addSensor("sentinel-value", INFO) sensor.add(metricName("total"), new Total()) sensor.add(metricName("rate"), new Rate(TimeUnit.SECONDS, new Count())) } override def transform(value: FoodOrder): Unit = sensor.record() }
  • 54. 54@loicmdivad @PubSapientEng 54@loicmdivad @PubSapientEng Sentinel Value Pattern By implementing a custom serde we can create a safe Deserializer. Downstreams now receive a sentinel value indicating a deserialization error. Errors can then be treated correctly, example: monitoring the number of deserialization errors with a custom metric But we lost a lot of information about the error… let’s see a last method
  • 55. 55@loicmdivad @PubSapientEng 55@loicmdivad @PubSapientEng Sentinel Value Pattern By implementing a custom serde we can create a safe Deserializer. Downstreams now receive a sentinel value indicating a deserialization error. Errors can then be treated correctly, example: monitoring the number of deserialization errors with a custom metric But we lost a lot of information about the error… let’s see a last method Take Away
  • 56. 56@loicmdivad @PubSapientEng Dead Letter Queue Pattern Exercise #4 - dinner
  • 57. 57@loicmdivad @PubSapientEng // Exercise #4: Dinner sealed trait FoodOrder case class Dinner(dish: Command, zone: String, moment: Moment, maybeClient: Option[Client]) extends FoodOrder
  • 58. 58@loicmdivad @PubSapientEng Dead Letter Queue Pattern In this method we will let the deserializer fail. For each failure we will send a message to a topic containing corrupted messages. Each message will have the original content of the input message (for reprocessing) and additional meta data about the failure. With Kafka Streams this can be achieved by implementing a DeserializationExceptionHandler Streaming APP dead letter queue input topic output topic
  • 59. 59@loicmdivad @PubSapientEng class DeadLetterQueueFoodExceptionHandler() extends DeserializationExceptionHandler { override def handle(context: ProcessorContext, record: ConsumerRecord[Array[Byte], Array[Byte]], exception: Exception): DeserializationHandlerResponse = { }
  • 60. 60@loicmdivad @PubSapientEng class DeadLetterQueueFoodExceptionHandler() extends DeserializationExceptionHandler { override def handle(context: ProcessorContext, record: ConsumerRecord[Array[Byte], Array[Byte]], exception: Exception): DeserializationHandlerResponse = { val producerRecord = new ProducerRecord(topic, /*same key, value and ts,*/ headers.asJava) producer.send(producerRecord, /* Producer Callback */ ) DeserializationHandlerResponse.CONTINUE }
  • 61. 61@loicmdivad @PubSapientEng class DeadLetterQueueFoodExceptionHandler() extends DeserializationExceptionHandler { var topic: String = _ var producer: KafkaProducer[Array[Byte], Array[Byte]] = _ override def configure(configs: util.Map[String, _]): Unit = ??? override def handle(context: ProcessorContext, record: ConsumerRecord[Array[Byte], Array[Byte]], exception: Exception): DeserializationHandlerResponse = { val producerRecord = new ProducerRecord(topic, /*same key, value and ts,*/ headers.asJava) producer.send(producerRecord, /* Producer Callback */ ) DeserializationHandlerResponse.CONTINUE }
  • 62. 62@loicmdivad @PubSapientEng class DeadLetterQueueFoodExceptionHandler() extends DeserializationExceptionHandler { var topic: String = _ var producer: KafkaProducer[Array[Byte], Array[Byte]] = _ override def configure(configs: util.Map[String, _]): Unit = ??? override def handle(context: ProcessorContext, record: ConsumerRecord[Array[Byte], Array[Byte]], exception: Exception): DeserializationHandlerResponse = { val headers = record.headers().toArray ++ Array[Header]( new RecordHeader("processing-time", ???), new RecordHeader("hexa-datetime", ???), new RecordHeader("error-message", ???), ... ) val producerRecord = new ProducerRecord(topic, /*same key, value and ts,*/ headers.asJava) producer.send(producerRecord, /* Producer Callback */ ) DeserializationHandlerResponse.CONTINUE }
  • 63. 63@loicmdivad @PubSapientEng Fill the headers with some meta data 01061696e0016536f6d6500000005736f6d65206f Value message to hexa Restaurant description Event date and time Food order category
  • 64. 64@loicmdivad @PubSapientEng class DeadLetterQueueFoodExceptionHandler() extends DeserializationExceptionHandler { var topic: String = _ var producer: KafkaProducer[Array[Byte], Array[Byte]] = _ override def configure(configs: util.Map[String, _]): Unit = ??? override def handle(context: ProcessorContext, record: ConsumerRecord[Array[Byte], Array[Byte]], exception: Exception): DeserializationHandlerResponse = { val headers = record.headers().toArray ++ Array[Header]( new RecordHeader("processing-time", ???), new RecordHeader("hexa-datetime", ???), new RecordHeader("error-message", ???), ... ) val producerRecord = new ProducerRecord(topic, /*same key, value and ts,*/ headers.asJava) producer.send(producerRecord, /* Producer Callback */ ) DeserializationHandlerResponse.CONTINUE }
  • 65. 65@loicmdivad @PubSapientEng class DeadLetterQueueFoodExceptionHandler() extends DeserializationExceptionHandler { var topic: String = _ var producer: KafkaProducer[Array[Byte], Array[Byte]] = _ override def configure(configs: util.Map[String, _]): Unit = ??? override def handle(context: ProcessorContext, record: ConsumerRecord[Array[Byte], Array[Byte]], exception: Exception): DeserializationHandlerResponse = { val headers = record.headers().toArray ++ Array[Header]( new RecordHeader("processing-time", ???), new RecordHeader("hexa-datetime", ???), new RecordHeader("error-message", ???), ... ) val producerRecord = new ProducerRecord(topic, /*same key, value and ts,*/ headers.asJava) producer.send(producerRecord, /* Producer Callback */ ) DeserializationHandlerResponse.CONTINUE } Take Away
  • 69. 69@loicmdivad @PubSapientEng 69@loicmdivad @PubSapientEng Dead Letter Queue Pattern You can provide your own implementation of DeserializationExceptionHandler. This lets you use the Producer API to write a corrupted record directly to a quarantine topic. Then you can manually analyse your corrupted records ⚠Warning: This approach have side effects that are invisible to the Kafka Streams runtime.
  • 70. 70@loicmdivad @PubSapientEng 70@loicmdivad @PubSapientEng Dead Letter Queue Pattern You can provide your own implementation of DeserializationExceptionHandler. This lets you use the Producer API to write a corrupted record directly to a quarantine topic. Then you can manually analyse your corrupted records ⚠Warning: This approach have side effects that are invisible to the Kafka Streams runtime. Take Away
  • 72. 72@loicmdivad @PubSapientEng 72@loicmdivad @PubSapientEng CONFLUENT FAQ Links XKE-RATATOUILLE
  • 73. 73@loicmdivad @PubSapientEng 73@loicmdivad @PubSapientEng Related Post Kafka Connect Deep Dive – Error Handling and Dead Letter Queues - by Robin Moffatt Building Reliable Reprocessing and Dead Letter Queues with Apache Kafka - by Ning Xia Handling bad messages using Kafka's Streams API - answer by Matthias J. Sax
  • 74. 74@loicmdivad @PubSapientEng 74@loicmdivad @PubSapientEng Conclusion When using Kafka, deserialization is the responsibility of the clients. These internal errors are not easy to catch When it’s possible, use Avro + Schema Registry When it’s not possible, Kafka Streams applies techniques to deal with serde errors: - DLQ: By extending a ExceptionHandler - Sentinel Value: By extending a Deserializer
  • 76. 76@loicmdivad @PubSapientEng 76@loicmdivad @PubSapientEng Images Photo by rawpixel on Unsplash Photo by João Marcelo Martins on Unsplash Photo by Jordane Mathieu on Unsplash Photo by Brooke Lark on Unsplash Photo by Jakub Kapusnak on Unsplash Photo by Melissa Walker Horn on Unsplash Photo by Aneta Pawlik on Unsplash
  • 77. 77@loicmdivad @PubSapientEng 77@loicmdivad @PubSapientEng With special thanks to Robin M. Sylvain L. Giulia B.
  • 79. 79@loicmdivad @PubSapientEng Pure HTML Akka Http Server Akka Actor System Kafka Topic Exercise1 Exercise2 Me, clicking everywhere Akka Stream Kafka