fix: bridge Kafka write async (v0.1.69) — MQTT callback не блокируется
This commit is contained in:
@@ -469,3 +469,77 @@ Consumer: iot-kafka-consumer-577f7ff88d-pkqd8, Running, 0 restarts
|
||||
Bridge: iot-mqtt-bridge-7dc87c46bc-tqjgz, Running, 0 restarts
|
||||
kafka-0: Running, 4 мин (перезапускался в TEST 7)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Fix: v0.1.69 — Kafka write async (2026-04-06, после тестирования)
|
||||
## Агент: GitHub Copilot (Claude Sonnet 4.6)
|
||||
|
||||
### Проблема, выявленная тестом #3
|
||||
|
||||
При load test 100 сообщений выяснилось: **27/100 доставлено**.
|
||||
|
||||
Первичная диагностика показала throughput ~1 msg/сек — я объяснил это
|
||||
"bottleneck bridge" и записал в backlog. Но пользователь указал: это не backlog,
|
||||
это архитектурная ошибка. **Между звеньями pipeline не должно быть ничего синхронного.**
|
||||
|
||||
### Анализ root cause
|
||||
|
||||
```
|
||||
MQTT callback (paho.mqtt.golang) вызывается синхронно в своём goroutine.
|
||||
Если callback долго выполняется — следующие входящие MQTT сообщения накапливаются.
|
||||
При Async=false: WriteMessages блокируется до получения ACK от Kafka (~1-10мс в норме,
|
||||
но при burst + latency spike → сотни мс → EMQX keepalive timeout = disconnect).
|
||||
```
|
||||
|
||||
Цепочка событий при burst:
|
||||
1. 100 сообщений за <100мс влетают в EMQX
|
||||
2. Bridge получает первое, вызывает WriteMessages (blocking ~1с)
|
||||
3. Пока bridge заблокирован — EMQX keepalive не получает pingresp
|
||||
4. После 30с (keepalive): EMQX разрывает соединение
|
||||
5. Сообщения QoS 0, которые не были получены bridge — испаряются
|
||||
|
||||
### Решение
|
||||
|
||||
`kafka.Writer{Async: true}` — WriteMessages возвращается немедленно, Kafka batching
|
||||
работает в фоновом goroutine внутри kafka-go. Ошибки доставки идут в `ErrorLogger`,
|
||||
который логирует без блокировки MQTT loop.
|
||||
|
||||
Почему **не** нужен отдельный channel/goroutine в handler:
|
||||
kafka-go с `Async: true` уже внутри держит буфер и горутину записи.
|
||||
Добавлять ещё один слой buffering — overengineering без причины.
|
||||
|
||||
### Что изменено в коде (v0.1.69)
|
||||
|
||||
**`iot/cmd/mqtt-bridge/main.go`:**
|
||||
```go
|
||||
// ДО (v0.1.68) — НЕПРАВИЛЬНО:
|
||||
kafkaWriter := &kafka.Writer{
|
||||
Async: false, // блокирует MQTT callback до ACK Kafka
|
||||
}
|
||||
// в handler:
|
||||
err = w.WriteMessages(ctx, ...) // блокировка ~1с/msg
|
||||
|
||||
// ПОСЛЕ (v0.1.69) — ПРАВИЛЬНО:
|
||||
kafkaWriter := &kafka.Writer{
|
||||
Async: true, // WriteMessages возвращается немедленно
|
||||
ErrorLogger: kafka.LoggerFunc(func(msg string, args ...interface{}) {
|
||||
log.Error("kafka async write error", ...) // ошибки не блокируют MQTT
|
||||
}),
|
||||
}
|
||||
// в handler:
|
||||
_ = w.WriteMessages(ctx, ...) // немедленный возврат, доставка в фоне
|
||||
```
|
||||
|
||||
### Deployment manifests
|
||||
|
||||
Оба yaml обновлены: `v0.1.68` → `v0.1.69`:
|
||||
- `deployments/k8s/iot-mqtt-bridge.yaml`
|
||||
- `deployments/k8s/iot-kafka-consumer.yaml`
|
||||
|
||||
### Что ожидаем после фикса
|
||||
|
||||
- MQTT callback завершается за <1мс (только marshal JSON + WriteMessages enqueue)
|
||||
- Bridge не теряет keepalive с EMQX при burst
|
||||
- Throughput: лимитируется сетью/Kafka, а не синхронным write (~тысячи msg/сек)
|
||||
- Load test 100 сообщений: должны дойти все 100
|
||||
|
||||
Reference in New Issue
Block a user