refactor(etl): упрощена проверка готовности Kafka

- Зачем:
  - верхние смещения можно получить тем же административным клиентом без одноразового Consumer и фиктивной группы.
- Что:
  - чтение хвоста переведено на AdminClient.list_offsets с OffsetSpec.latest.
  - назначение system.one названо рядом с запросом, а документация и запись проверок сокращены и переставлены по смыслу.
- Проверка:
  - make config-test; make lint.
  - живые проверки готового и отсутствующего разделов Kafka; полный etl_pipeline.
This commit is contained in:
ddadmin committed 2026-08-27 11:17:43 +03:00
1 parent 12a5c109b2
commit 61e662c19c
4 files changed
+27 -30

No files matched your search

+20 -23
View File
@@ -53,9 +53,9 @@ WORLD_POSITION = "world_position"
def _kafka_readiness() -> tuple[bool, str]:
"""Получить подтвержденные и верхние смещения обоих разделов событий."""
from confluent_kafka import Consumer, ConsumerGroupTopicPartitions, TopicPartition
from confluent_kafka.admin import AdminClient
"""Проверить, дочитала ли группа оба раздела событий."""
from confluent_kafka import ConsumerGroupTopicPartitions, TopicPartition
from confluent_kafka.admin import AdminClient, OffsetSpec
admin = AdminClient({"bootstrap.servers": KAFKA_BOOTSTRAP_SERVERS})
groups = admin.list_consumer_groups(request_timeout=10).result()
@@ -78,27 +78,22 @@ def _kafka_readiness() -> tuple[bool, str]:
high_watermarks: dict[int, int] = {}
watermark_errors: dict[int, str] = {}
consumer = Consumer(
# OffsetSpec.latest возвращает следующее смещение после хвоста; сверено
# через Context7 и на установленном confluent-kafka 2.15.0.
latest = admin.list_offsets(
{
"bootstrap.servers": KAFKA_BOOTSTRAP_SERVERS,
# Клиент требует group.id, но без подписки и poll() в группу не
# входит: здесь он только запрашивает верхние смещения топика.
"group.id": "etl-readiness",
"enable.auto.commit": False,
}
TopicPartition(HITS_TOPIC, partition): OffsetSpec.latest()
for partition in HITS_PARTITIONS
},
request_timeout=10,
)
try:
for partition in HITS_PARTITIONS:
try:
bounds = consumer.get_watermark_offsets(
TopicPartition(HITS_TOPIC, partition), timeout=10
)
if bounds is not None and bounds[1] >= 0:
high_watermarks[partition] = bounds[1]
except Exception as error:
watermark_errors[partition] = str(error)
finally:
consumer.close()
for topic_partition, future in latest.items():
try:
offset = future.result().offset
if offset >= 0:
high_watermarks[topic_partition.partition] = offset
except Exception as error:
watermark_errors[topic_partition.partition] = str(error)
all_known = group_exists
total_lag = 0
@@ -139,7 +134,7 @@ def _kafka_readiness() -> tuple[bool, str]:
def _delivery_queue_readiness() -> tuple[bool, str]:
"""Получить число недоставленных файлов событий на каждой ноде."""
"""Проверить, пуста ли очередь доставки событий на каждой ноде."""
query = f"""
SELECT
node,
@@ -153,6 +148,8 @@ def _delivery_queue_readiness() -> tuple[bool, str]:
) AS last_exception
FROM
(
-- У пустой очереди нет строк; system.one оставляет обе ноды
-- видимыми и в зеленом опросе.
SELECT
hostName() AS node,
toUInt64(0) AS queued_files,