A Kafka poison-pill head-of-line block problem - #790
Conversation
We should put this on a timer (ideally where we wait for a specific status) so that way we can run it automatically. I can't accept a problem if it requires manual intervention to run it as we need to get our evals fully autonomous. |
You are right. I fixed it. Now there is no manual wait. Now, I used poll for the readiness check instead of manual wait. |
There was a problem hiding this comment.
@TheDeadcoder Thanks for the effort! This is a really interesting fault. The poison-pill HOL block is a great real-world scenario that's clearly distinct from the existing kafka_queue_problems. The oracle design is solid (outcome-based, not prescriptive). I left some inline comments below. Please ping me when you've addressed them.
Feel free to ping us if you have any questions or confusions!
|
/validate-problem kafka_poison_pill_hol_block |
🧪 Problem ValidationStatus: 🟡 Running for Deploying app → injecting fault → checking oracle → recovering → checking oracle. Live workflow run · commit |
|
@TheDeadcoder works great now! Thanks for addressing my comments. |
|
@TheDeadcoder I assume you are from the UIUC+ program? We are going to start the project very soon. Could you join the SREGym Slack? We are going to mostly use that for discussion. |
|
@tianyin I added him. |
|
:-O I searched "Nazmus" and failed -_- And you are going with "Pial". So I should use the last name to address colleagues in Bangladesh, right? |
|
@tianyin hehe. It differs from person to person what they prefer :3 |
* oracle * kafka_poison_pill_hol_block * fix - validation * poll for autonomous * [fix] label dropped, Rolling-update race remove, rewritten Data-loss check * fix ruff import order --------- Co-authored-by: Saad Mohammad Rafid Pial <saadmrp222@gmail.com>
* oracle * kafka_poison_pill_hol_block * fix - validation * poll for autonomous * [fix] label dropped, Rolling-update race remove, rewritten Data-loss check * fix ruff import order --------- Co-authored-by: Saad Mohammad Rafid Pial <saadmrp222@gmail.com>
Summary
This PR adds
kafka_poison_pill_hol_block, which is a Kafka poison-pill head-of-line block problemA common production incident for Kafka-based microservice platforms is poison pill. It is a single malformed record that a consumer cannot deserialize. Because order is preserved within a partition, that one record blocks every valid message behind it. The offset is never committed. Meanwhile the lag grows, and yet every pod still reports healthy. Real outages have been attributed to this, including a malformed record that blocked a payment pipeline for around 45 minutes. So far, I could not find fault of this kind in SREGym. A poison pill lives in the Kafka log, outside the control plane, so a restarted consumer replays the bad record. This PR adds that scenario.
Relevant References:
Conduktor - Dead Letter Topics: Handling Poison Pills
Redpanda - Reliable message processing with a dead letter queue
Lydtech Consulting - Kafka Poison Pill
Code changes
3 files are added and one is edited:
sregym/generators/fault/inject_kafka.py- fault injector. A self-contained producer and consumer are deployed into theastronomy-shopnamespace, and one poison record is seeded into a dedicated topic at offset 20.sregym/conductor/oracles/data_plane_progress.py-DataPlaneProgressOracle, the mitigation oracle. Data-plane progress is graded from the consumer's logs.sregym/conductor/problems/kafka_poison_pill_hol_block.py- the problem definition.sregym/conductor/problems/registry.py- one import and one registry entry (seeregistry.patch.md).After the problem starts and the diagnosis stage opens, an interval of about 1 to 2 minutes should be allowed before investigation begins, so the stalled consumer and the growing lag are clearly visible.
Validation
Expected behaviour
While the fault is active, the consumer logs repeat
unprocessable record at offset=20, the committed offset stays at 20, and lag grows, while every pod remainsRunningandReady. Diagnosis is scored by the existing LLM judge. The mitigation oracle prints its checks and ends with either a pass or a clear failure reason (offset not advanced past poison record,data loss: valid records skipped, ornot restart-resistant). Results are written to thecli.pyresults panel, or to the run CSV whenmain.pyis used. Inspection commands used here are:Mitigation
The expected fix is to move the consumer group past the poison record without dropping the valid messages queued behind it. Since an offset reset requires the group to have no active members, the consumer is stopped first, the group offset is reset to 21 (one past the poison), and the consumer is started again:
kubectl scale deployment/orders-validator -n astronomy-shop --replicas=0 # now need to wait ~60s so the broker evicts the stopped consumer and the group goes inactive kubectl run kafka-admin --rm --restart=Never -n astronomy-shop \ --image=apache/kafka:3.9.0 -- \ /opt/kafka/bin/kafka-consumer-groups.sh --bootstrap-server kafka:9092 \ --group orders-validator --topic orders-fulfillment \ --reset-offsets --to-offset 21 --execute kubectl scale deployment/orders-validator -n astronomy-shop --replicas=1The consumer resumes at offset 21, drains the backlog, and keeps pace with the producer. A plain restart is rejected, since the same offset is re-read and the stall returns. A reset to latest is rejected, since the valid backlog is skipped and counted as data loss.