"Have you ever had your stateful Kafka Streams app killed by Kubernetes with the termination reason ""OOMKilled""? Even if you did set up JVM heap limit, the pod still got killed? This is likely due to your RocksDB off-heap memory usage. This talk will explore ways of diagnosing the problem, including:
- analysis of heap and off-heap memory;
- diving into RocksDB metrics;
- looking at k8s java pod measurements.
It will also show a possible solution to the problem with RocksDB and pod memory tuning. Attendants will get hands-on experience of live debugging Kafka Streams app together with useful tips and tricks on its production usage in Kubernetes. As a Streaming Platform Owner at Raiffeisen Bank and a former Data Engineer with 5+ years of experience, I will share some insights on building scalable and reliable Kafka Streams stateful applications."
6. How to check if it is OOMKilled?
6
kubectl describe pod your-pod-name -n your-namespace
Name: your-pod-name
...
Containers:
app:
...
Last State: Terminated
Reason: OOMKilled
Exit Code: 137
19. Kafka Streams app memory
19
JVM Heap + RocksDB
Confluent article about Kafka Streams memory tunning:
https://docs.confluent.io/platform/current/streams/developer-guide/memory-mgmt.html
State Store
Kafka Streams
App
put(key, val)
20. Kafka Streams app memory
20
JVM Heap + RocksDB
Kafka Streams
App
CachingKeyValueStore
- ThreadCache context.cache()
Indexes
bloom
filters
block
cache
OS page
cache
memtable
State Store - RocksDB
! These items size
depends on number of
unique keys
JVM Heap
Not controlled by JVM
RocksDB memory details: https://github.com/facebook/rocksdb/wiki/Memory-usage-in-RocksDB
put(key, val)
native put(key, val)
21. How to fix unbounded RocksDB memory usage?
21
Application.java
properties.put(StreamsConfig.ROCKSDB_CONFIG_SETTER_CLASS_CONFIG,
âyour.package.BoundedMemoryRocksDBConfig");
your.package.BoundedMemoryRocksDBConfig.java
@Override
public void setConfig(..., Options options, ...) {
BlockBasedTableConfig tableConfig = options.tableFormatConfig();
Cache cache = new LRUCache(computeTotalRocksDbMem(), -1, false);
tableConfig.setBlockCache(cache);
tableConfig.setCacheIndexAndFilterBlocks(true);
options.setWriteBufferManager(writeBufferManager);
...
}
22. How to fix unbounded RocksDB memory usage?
22
Application.java
properties.put(StreamsConfig.ROCKSDB_CONFIG_SETTER_CLASS_CONFIG,
âyour.package.BoundedMemoryRocksDBConfig");
your.package.BoundedMemoryRocksDBConfig.java
@Override
public void setConfig(..., Options options, ...) {
BlockBasedTableConfig tableConfig = options.tableFormatConfig();
Cache cache = new LRUCache(computeTotalRocksDbMem(), -1, false);
tableConfig.setBlockCache(cache);
tableConfig.setCacheIndexAndFilterBlocks(true);
options.setWriteBufferManager(writeBufferManager);
...
}
23. How to fix unbounded RocksDB memory usage?
23
Application.java
properties.put(StreamsConfig.ROCKSDB_CONFIG_SETTER_CLASS_CONFIG,
âyour.package.BoundedMemoryRocksDBConfig");
your.package.BoundedMemoryRocksDBConfig.java
@Override
public void setConfig(..., Options options, ...) {
BlockBasedTableConfig tableConfig = options.tableFormatConfig();
Cache cache = new LRUCache(computeTotalRocksDbMem(), -1, false);
tableConfig.setBlockCache(cache);
tableConfig.setCacheIndexAndFilterBlocks(true);
}
Pay attention to the number of stores and partitions:
https://docs.confluent.io/platform/current/streams/developer-guide/memory-mgmt.html#rocksdb
38. Links and materials
38
âą How JVM analyze memory inside Docker containerâ
https://merikan.com/2019/04/jvm-in-a-container/#java-10
âą How to use jcmd to analyze non-heap memoryâ
https://www.baeldung.com/native-memory-tracking-in-jvm
âą Why use container_memory_working_set_bytes instead of container_memory_usage_bytes
https://blog.freshtracks.io/a-deep-dive-into-kubernetes-metrics-part-3-container-resource-metrics-361c5ee46e6
âą How RocksDB store works with Kafka Streams:
https://www.confluent.io/blog/how-to-tune-rocksdb-kafka-streams-state-stores-performance/
âą Linux memory controller for cgroups
https://lwn.net/Articles/432224/