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

Gaming on AWS - 1. AWS로 글로벌 게임 런칭하기 - 장르별 아키텍처 중심
Gaming on AWS - 1. AWS로 글로벌 게임 런칭하기 - 장르별 아키텍처 중심Gaming on AWS - 1. AWS로 글로벌 게임 런칭하기 - 장르별 아키텍처 중심
Gaming on AWS - 1. AWS로 글로벌 게임 런칭하기 - 장르별 아키텍처 중심Amazon Web Services Korea
 
카카오 광고 플랫폼 MSA 적용 사례 및 API Gateway와 인증 구현에 대한 소개
카카오 광고 플랫폼 MSA 적용 사례 및 API Gateway와 인증 구현에 대한 소개카카오 광고 플랫폼 MSA 적용 사례 및 API Gateway와 인증 구현에 대한 소개
카카오 광고 플랫폼 MSA 적용 사례 및 API Gateway와 인증 구현에 대한 소개if kakao
 
React Js Simplified
React Js SimplifiedReact Js Simplified
React Js SimplifiedSunil Yadav
 
State management in react applications (Statecharts)
State management in react applications (Statecharts)State management in react applications (Statecharts)
State management in react applications (Statecharts)Tomáš Drenčák
 
Apache kafka 모니터링을 위한 Metrics 이해 및 최적화 방안
Apache kafka 모니터링을 위한 Metrics 이해 및 최적화 방안Apache kafka 모니터링을 위한 Metrics 이해 및 최적화 방안
Apache kafka 모니터링을 위한 Metrics 이해 및 최적화 방안SANG WON PARK
 
[C++ Korea 3rd Seminar] 새 C++은 새 Visual Studio에, 좌충우돌 마이그레이션 이야기
[C++ Korea 3rd Seminar] 새 C++은 새 Visual Studio에, 좌충우돌 마이그레이션 이야기[C++ Korea 3rd Seminar] 새 C++은 새 Visual Studio에, 좌충우돌 마이그레이션 이야기
[C++ Korea 3rd Seminar] 새 C++은 새 Visual Studio에, 좌충우돌 마이그레이션 이야기Chris Ohk
 
[부스트캠퍼세미나]김재원_presentation-oop
[부스트캠퍼세미나]김재원_presentation-oop[부스트캠퍼세미나]김재원_presentation-oop
[부스트캠퍼세미나]김재원_presentation-oopCONNECT FOUNDATION
 
Introduction to Kafka Cruise Control
Introduction to Kafka Cruise ControlIntroduction to Kafka Cruise Control
Introduction to Kafka Cruise ControlJiangjie Qin
 
홍성우, 게임 서버의 목차 - 시작부터 출시까지, NDC2019
홍성우, 게임 서버의 목차 - 시작부터 출시까지, NDC2019홍성우, 게임 서버의 목차 - 시작부터 출시까지, NDC2019
홍성우, 게임 서버의 목차 - 시작부터 출시까지, NDC2019devCAT Studio, NEXON
 
Vert.X: Microservices Were Never So Easy (Clement Escoffier)
Vert.X: Microservices Were Never So Easy (Clement Escoffier)Vert.X: Microservices Were Never So Easy (Clement Escoffier)
Vert.X: Microservices Were Never So Easy (Clement Escoffier)Red Hat Developers
 
React + Redux Introduction
React + Redux IntroductionReact + Redux Introduction
React + Redux IntroductionNikolaus Graf
 
자바에서 null을 안전하게 다루는 방법
자바에서 null을 안전하게 다루는 방법자바에서 null을 안전하게 다루는 방법
자바에서 null을 안전하게 다루는 방법Sungchul Park
 
[LetSwift 2023] 객체지향-함수형 아키텍처 직접 만들기
[LetSwift 2023] 객체지향-함수형 아키텍처 직접 만들기[LetSwift 2023] 객체지향-함수형 아키텍처 직접 만들기
[LetSwift 2023] 객체지향-함수형 아키텍처 직접 만들기Moonbeom KWON
 
오딘: 발할라 라이징 MMORPG의 성능 최적화 사례 공유 [카카오게임즈 - 레벨 300] - 발표자: 김문권, 팀장, 라이온하트 스튜디오...
오딘: 발할라 라이징 MMORPG의 성능 최적화 사례 공유 [카카오게임즈 - 레벨 300] - 발표자: 김문권, 팀장, 라이온하트 스튜디오...오딘: 발할라 라이징 MMORPG의 성능 최적화 사례 공유 [카카오게임즈 - 레벨 300] - 발표자: 김문권, 팀장, 라이온하트 스튜디오...
오딘: 발할라 라이징 MMORPG의 성능 최적화 사례 공유 [카카오게임즈 - 레벨 300] - 발표자: 김문권, 팀장, 라이온하트 스튜디오...Amazon Web Services Korea
 
Amazon EKS로 간단한 웹 애플리케이션 구축하기 - 김주영 (AWS) :: AWS Community Day Online 2021
Amazon EKS로 간단한 웹 애플리케이션 구축하기 - 김주영 (AWS) :: AWS Community Day Online 2021Amazon EKS로 간단한 웹 애플리케이션 구축하기 - 김주영 (AWS) :: AWS Community Day Online 2021
Amazon EKS로 간단한 웹 애플리케이션 구축하기 - 김주영 (AWS) :: AWS Community Day Online 2021AWSKRUG - AWS한국사용자모임
 

What's hot (20)

Gaming on AWS - 1. AWS로 글로벌 게임 런칭하기 - 장르별 아키텍처 중심
Gaming on AWS - 1. AWS로 글로벌 게임 런칭하기 - 장르별 아키텍처 중심Gaming on AWS - 1. AWS로 글로벌 게임 런칭하기 - 장르별 아키텍처 중심
Gaming on AWS - 1. AWS로 글로벌 게임 런칭하기 - 장르별 아키텍처 중심
 
카카오 광고 플랫폼 MSA 적용 사례 및 API Gateway와 인증 구현에 대한 소개
카카오 광고 플랫폼 MSA 적용 사례 및 API Gateway와 인증 구현에 대한 소개카카오 광고 플랫폼 MSA 적용 사례 및 API Gateway와 인증 구현에 대한 소개
카카오 광고 플랫폼 MSA 적용 사례 및 API Gateway와 인증 구현에 대한 소개
 
ReactJS
ReactJSReactJS
ReactJS
 
React Js Simplified
React Js SimplifiedReact Js Simplified
React Js Simplified
 
State management in react applications (Statecharts)
State management in react applications (Statecharts)State management in react applications (Statecharts)
State management in react applications (Statecharts)
 
React Hooks
React HooksReact Hooks
React Hooks
 
Apache kafka 모니터링을 위한 Metrics 이해 및 최적화 방안
Apache kafka 모니터링을 위한 Metrics 이해 및 최적화 방안Apache kafka 모니터링을 위한 Metrics 이해 및 최적화 방안
Apache kafka 모니터링을 위한 Metrics 이해 및 최적화 방안
 
[C++ Korea 3rd Seminar] 새 C++은 새 Visual Studio에, 좌충우돌 마이그레이션 이야기
[C++ Korea 3rd Seminar] 새 C++은 새 Visual Studio에, 좌충우돌 마이그레이션 이야기[C++ Korea 3rd Seminar] 새 C++은 새 Visual Studio에, 좌충우돌 마이그레이션 이야기
[C++ Korea 3rd Seminar] 새 C++은 새 Visual Studio에, 좌충우돌 마이그레이션 이야기
 
[부스트캠퍼세미나]김재원_presentation-oop
[부스트캠퍼세미나]김재원_presentation-oop[부스트캠퍼세미나]김재원_presentation-oop
[부스트캠퍼세미나]김재원_presentation-oop
 
Introduction to Kafka Cruise Control
Introduction to Kafka Cruise ControlIntroduction to Kafka Cruise Control
Introduction to Kafka Cruise Control
 
홍성우, 게임 서버의 목차 - 시작부터 출시까지, NDC2019
홍성우, 게임 서버의 목차 - 시작부터 출시까지, NDC2019홍성우, 게임 서버의 목차 - 시작부터 출시까지, NDC2019
홍성우, 게임 서버의 목차 - 시작부터 출시까지, NDC2019
 
Intro to Three.js
Intro to Three.jsIntro to Three.js
Intro to Three.js
 
Vert.X: Microservices Were Never So Easy (Clement Escoffier)
Vert.X: Microservices Were Never So Easy (Clement Escoffier)Vert.X: Microservices Were Never So Easy (Clement Escoffier)
Vert.X: Microservices Were Never So Easy (Clement Escoffier)
 
React + Redux Introduction
React + Redux IntroductionReact + Redux Introduction
React + Redux Introduction
 
자바에서 null을 안전하게 다루는 방법
자바에서 null을 안전하게 다루는 방법자바에서 null을 안전하게 다루는 방법
자바에서 null을 안전하게 다루는 방법
 
[LetSwift 2023] 객체지향-함수형 아키텍처 직접 만들기
[LetSwift 2023] 객체지향-함수형 아키텍처 직접 만들기[LetSwift 2023] 객체지향-함수형 아키텍처 직접 만들기
[LetSwift 2023] 객체지향-함수형 아키텍처 직접 만들기
 
오딘: 발할라 라이징 MMORPG의 성능 최적화 사례 공유 [카카오게임즈 - 레벨 300] - 발표자: 김문권, 팀장, 라이온하트 스튜디오...
오딘: 발할라 라이징 MMORPG의 성능 최적화 사례 공유 [카카오게임즈 - 레벨 300] - 발표자: 김문권, 팀장, 라이온하트 스튜디오...오딘: 발할라 라이징 MMORPG의 성능 최적화 사례 공유 [카카오게임즈 - 레벨 300] - 발표자: 김문권, 팀장, 라이온하트 스튜디오...
오딘: 발할라 라이징 MMORPG의 성능 최적화 사례 공유 [카카오게임즈 - 레벨 300] - 발표자: 김문권, 팀장, 라이온하트 스튜디오...
 
Amazon EKS로 간단한 웹 애플리케이션 구축하기 - 김주영 (AWS) :: AWS Community Day Online 2021
Amazon EKS로 간단한 웹 애플리케이션 구축하기 - 김주영 (AWS) :: AWS Community Day Online 2021Amazon EKS로 간단한 웹 애플리케이션 구축하기 - 김주영 (AWS) :: AWS Community Day Online 2021
Amazon EKS로 간단한 웹 애플리케이션 구축하기 - 김주영 (AWS) :: AWS Community Day Online 2021
 
React workshop
React workshopReact workshop
React workshop
 
ReactJs
ReactJsReactJs
ReactJs
 

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

Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024BookNet Canada
 
My INSURER PTE LTD - Insurtech Innovation Award 2024
My INSURER PTE LTD - Insurtech Innovation Award 2024My INSURER PTE LTD - Insurtech Innovation Award 2024
My INSURER PTE LTD - Insurtech Innovation Award 2024The Digital Insurer
 
Human Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR SystemsHuman Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR SystemsMark Billinghurst
 
Key Features Of Token Development (1).pptx
Key  Features Of Token  Development (1).pptxKey  Features Of Token  Development (1).pptx
Key Features Of Token Development (1).pptxLBM Solutions
 
Science&tech:THE INFORMATION AGE STS.pdf
Science&tech:THE INFORMATION AGE STS.pdfScience&tech:THE INFORMATION AGE STS.pdf
Science&tech:THE INFORMATION AGE STS.pdfjimielynbastida
 
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...Patryk Bandurski
 
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
"Federated learning: out of reach no matter how close",Oleksandr LapshynFwdays
 
AI as an Interface for Commercial Buildings
AI as an Interface for Commercial BuildingsAI as an Interface for Commercial Buildings
AI as an Interface for Commercial BuildingsMemoori
 
SQL Database Design For Developers at php[tek] 2024
SQL Database Design For Developers at php[tek] 2024SQL Database Design For Developers at php[tek] 2024
SQL Database Design For Developers at php[tek] 2024Scott Keck-Warren
 
Streamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupStreamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupFlorian Wilhelm
 
CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):comworks
 
Artificial intelligence in the post-deep learning era
Artificial intelligence in the post-deep learning eraArtificial intelligence in the post-deep learning era
Artificial intelligence in the post-deep learning eraDeakin University
 
New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024
New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024
New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024BookNet Canada
 
Benefits Of Flutter Compared To Other Frameworks
Benefits Of Flutter Compared To Other FrameworksBenefits Of Flutter Compared To Other Frameworks
Benefits Of Flutter Compared To Other FrameworksSoftradix Technologies
 
Unlocking the Potential of the Cloud for IBM Power Systems
Unlocking the Potential of the Cloud for IBM Power SystemsUnlocking the Potential of the Cloud for IBM Power Systems
Unlocking the Potential of the Cloud for IBM Power SystemsPrecisely
 
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)Bun (KitWorks Team Study 노별마루 발표 2024.4.22)
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)Wonjun Hwang
 
Beyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
Beyond Boundaries: Leveraging No-Code Solutions for Industry InnovationBeyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
Beyond Boundaries: Leveraging No-Code Solutions for Industry InnovationSafe Software
 
Scanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsScanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsRizwan Syed
 

Recently uploaded (20)

Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
 
My INSURER PTE LTD - Insurtech Innovation Award 2024
My INSURER PTE LTD - Insurtech Innovation Award 2024My INSURER PTE LTD - Insurtech Innovation Award 2024
My INSURER PTE LTD - Insurtech Innovation Award 2024
 
Human Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR SystemsHuman Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR Systems
 
Key Features Of Token Development (1).pptx
Key  Features Of Token  Development (1).pptxKey  Features Of Token  Development (1).pptx
Key Features Of Token Development (1).pptx
 
Science&tech:THE INFORMATION AGE STS.pdf
Science&tech:THE INFORMATION AGE STS.pdfScience&tech:THE INFORMATION AGE STS.pdf
Science&tech:THE INFORMATION AGE STS.pdf
 
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
 
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
 
AI as an Interface for Commercial Buildings
AI as an Interface for Commercial BuildingsAI as an Interface for Commercial Buildings
AI as an Interface for Commercial Buildings
 
SQL Database Design For Developers at php[tek] 2024
SQL Database Design For Developers at php[tek] 2024SQL Database Design For Developers at php[tek] 2024
SQL Database Design For Developers at php[tek] 2024
 
Streamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupStreamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project Setup
 
The transition to renewables in India.pdf
The transition to renewables in India.pdfThe transition to renewables in India.pdf
The transition to renewables in India.pdf
 
CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):
 
Artificial intelligence in the post-deep learning era
Artificial intelligence in the post-deep learning eraArtificial intelligence in the post-deep learning era
Artificial intelligence in the post-deep learning era
 
New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024
New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024
New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024
 
Benefits Of Flutter Compared To Other Frameworks
Benefits Of Flutter Compared To Other FrameworksBenefits Of Flutter Compared To Other Frameworks
Benefits Of Flutter Compared To Other Frameworks
 
Unlocking the Potential of the Cloud for IBM Power Systems
Unlocking the Potential of the Cloud for IBM Power SystemsUnlocking the Potential of the Cloud for IBM Power Systems
Unlocking the Potential of the Cloud for IBM Power Systems
 
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)Bun (KitWorks Team Study 노별마루 발표 2024.4.22)
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)
 
E-Vehicle_Hacking_by_Parul Sharma_null_owasp.pptx
E-Vehicle_Hacking_by_Parul Sharma_null_owasp.pptxE-Vehicle_Hacking_by_Parul Sharma_null_owasp.pptx
E-Vehicle_Hacking_by_Parul Sharma_null_owasp.pptx
 
Beyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
Beyond Boundaries: Leveraging No-Code Solutions for Industry InnovationBeyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
Beyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
 
Scanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsScanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL Certs
 

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