Перейти к содержанию
Learning Platform
Продвинутый
25 минут
scaling tasks performance architecture

Масштабирование коннектора: Правда о tasks.max

Один из самых распространенных мифов в мире Debezium: “Чтобы увеличить пропускную способность PostgreSQL коннектора, установите tasks.max=4”. Это неправда, и попытки так сделать не дадут никакого эффекта.

В этом уроке мы разберем, почему PostgreSQL коннектор принципиально ограничен одной задачей, и изучим реальные стратегии масштабирования CDC pipeline.

Миф: tasks.max увеличивает throughput

Myth: “Мой PostgreSQL коннектор медленный. Установлю tasks.max=4 для параллелизма.”

Reality: PostgreSQL коннектор Debezium ВСЕГДА использует только 1 task, независимо от значения tasks.max. Это не баг — это архитектурное ограничение.

МИФ: tasks.max=4
PostgreSQL
Task 1
Task 2
Task 3
Task 4
Ожидание: 4x throughput
РЕАЛЬНОСТЬ
PostgreSQL WAL
Task 1
(ЕДИНСТВЕННЫЙ)
Kafka
tasks.max = 4 ИГНОРИРУЕТСЯ

Доказательство

Создайте коннектор с tasks.max: "4" и проверьте количество tasks:

# Конфигурация с tasks.max=4
curl -X POST http://localhost:8083/connectors \
  -H "Content-Type: application/json" \
  -d '{
    "name": "inventory-connector",
    "config": {
      "connector.class": "io.debezium.connector.postgresql.PostgresConnector",
      "database.hostname": "postgres",
      "database.port": "5432",
      "database.user": "postgres",
      "database.password": "postgres",
      "database.dbname": "inventory",
      "topic.prefix": "inventory",
      "tasks.max": "4"
    }
  }'

# Проверить количество tasks
curl http://localhost:8083/connectors/inventory-connector/status | jq '.tasks | length'
# Результат: 1  (не 4!)

Почему PostgreSQL коннектор single-task?

Причина кроется в архитектуре PostgreSQL WAL (Write-Ahead Log).

WAL — последовательный журнал

PostgreSQL WAL - последовательный журнал
Event 1LSN: 0/1000
Event 2LSN: 0/1001
Event 3LSN: 0/1002
Event 4LSN: 0/1003
Event 5LSN: 0/1004
Replication Slot
position: 0/1002
Читает последовательно от restart_lsn. Одна позиция = один reader.
Порядок имеет значение
События обрабатываются строго по LSN
Одна позиция
Slot отслеживает один restart_lsn
Транзакционность
Атомарная обработка транзакций

WAL — это последовательный журнал транзакций. Logical decoding читает его строго по порядку от позиции restart_lsn. Невозможно распараллелить чтение, потому что:

  1. Порядок имеет значение: События должны обрабатываться в порядке их записи в WAL
  2. Одна позиция: Replication slot отслеживает одну позицию, не несколько
  3. Транзакционная целостность: Все изменения одной транзакции должны обрабатываться атомарно

Сравнение с другими коннекторами

КоннекторПоддержка tasks.maxПричина
PostgreSQLНет (только 1)WAL последовательный, одна точка чтения
MySQLНет (только 1)Binlog последовательный
MongoDBДа (множество)Каждый shard = отдельный поток
SQL ServerДа (несколько)Каждая БД = отдельный capture instance
OracleНет (только 1)LogMiner читает последовательно

Архитектурная причина

Архитектура Debezium PostgreSQL Connector
PostgreSQL
WAL Stream
Logical Decoding
Debezium Connector (Single Task)
WAL Reader
(1 поток - BOTTLENECK)
Events
Internal Queue
(8192 events)
Batch
Kafka Writer
(1 поток)
ProducerRecord
Kafka
Topic Partitions
WAL Reader - единственный bottleneck. Все остальное масштабируется.
Проверка знаний
Почему PostgreSQL коннектор Debezium принципиально ограничен одной task, даже если установить tasks.max = 10?
Ответ
WAL — последовательный журнал транзакций с единственной точкой чтения (restart_lsn). Replication slot отслеживает одну позицию, и все изменения одной транзакции должны обрабатываться атомарно. Параллельное чтение нарушило бы порядок событий и транзакционную целостность.

Реальные стратегии масштабирования

Раз tasks.max не работает, что делать? Есть три основных подхода.

Стратегия 1: Множественные коннекторы

Когда использовать: Разные наборы таблиц имеют независимых потребителей.

Стратегия 1: Множественные коннекторы

Разделение таблиц по доменам с независимыми коннекторами

PostgreSQL
orders
order_items
products
inventory
customers
addresses
Connectors
orders-connector
task=1
inventory-connector
task=1
customers-connector
task=1
Kafka Topics
orders.public.*
inventory.public.*
customers.public.*
Преимущества
  • Независимые replication slots
  • Изоляция failures
  • Per-domain monitoring
Недостатки
  • Больше WAL retention (N slots)
  • Больше ресурсов Connect
  • Сложнее управлять

Конфигурация:

// orders-connector
{
  "name": "orders-connector",
  "config": {
    "connector.class": "io.debezium.connector.postgresql.PostgresConnector",
    "database.hostname": "postgres",
    "database.dbname": "inventory",
    "topic.prefix": "orders",
    "table.include.list": "public.orders,public.order_items",
    "slot.name": "debezium_orders"
  }
}

// inventory-connector
{
  "name": "inventory-connector",
  "config": {
    "connector.class": "io.debezium.connector.postgresql.PostgresConnector",
    "database.hostname": "postgres",
    "database.dbname": "inventory",
    "topic.prefix": "inventory",
    "table.include.list": "public.products,public.inventory",
    "slot.name": "debezium_inventory"
  }
}

Преимущества:

  • Независимые replication slots
  • Независимые offsets
  • Изоляция failures

Недостатки:

  • Каждый коннектор создает отдельный slot (больше WAL retention)
  • Больше ресурсов Kafka Connect
  • Сложнее управлять

Стратегия 2: Downstream параллелизация

Когда использовать: Один коннектор, но нужна параллельная обработка.

Стратегия 2: Downstream параллелизация

Параллелизм на стороне consumer через Kafka partitions

PostgreSQL
orders table
Single Connector
task=1
orders topic
8 partitions
Consumer 1
p: 0-1
Consumer 2
p: 2-3
Consumer 3
p: 4-5
Consumer 4
p: 6-7
Как это работает:
1.Debezium пишет события в Kafka topic
2.Topic имеет N партиций (key = primary key)
3.N consumer instances читают параллельно
4.Ordering гарантирован per-key в партиции
Преимущества
  • Один коннектор, один slot
  • Параллелизм на consumer
  • Ordering per-key гарантирован
Недостатки
  • Debezium остается single-threaded
  • Bottleneck в WAL reader
  • Требует partition-aware consumers

Как работает:

  1. Debezium пишет события в Kafka topic
  2. Topic имеет N партиций (key = primary key таблицы)
  3. N consumer instances читают параллельно
  4. События одного ключа всегда в одной партиции (ordering гарантирован)

Конфигурация Debezium:

{
  "name": "inventory-connector",
  "config": {
    "connector.class": "io.debezium.connector.postgresql.PostgresConnector",
    "topic.prefix": "inventory",

    "transforms": "route",
    "transforms.route.type": "org.apache.kafka.connect.transforms.RegexRouter",
    "transforms.route.regex": ".*",
    "transforms.route.replacement": "inventory.cdc.events"
  }
}

Создание топика с партициями:

kafka-topics --create \
  --bootstrap-server kafka:9092 \
  --topic inventory.cdc.events \
  --partitions 8 \
  --replication-factor 1

Преимущества:

  • Один коннектор, один slot
  • Параллелизм на стороне consumer
  • Ordering гарантирован per-key

Недостатки:

  • Debezium все еще single-threaded
  • Bottleneck в WAL reader

Стратегия 3: Performance Tuning

Когда использовать: Максимизировать throughput одного коннектора.

{
  "name": "high-throughput-connector",
  "config": {
    "connector.class": "io.debezium.connector.postgresql.PostgresConnector",
    "database.hostname": "postgres",
    "database.dbname": "inventory",
    "topic.prefix": "inventory",

    "max.queue.size": "16384",
    "max.batch.size": "4096",
    "poll.interval.ms": "500",

    "snapshot.fetch.size": "10240",

    "producer.override.batch.size": "131072",
    "producer.override.linger.ms": "10",
    "producer.override.compression.type": "lz4"
  }
}

Параметры tuning:

ПараметрDefaultTunedЭффект
max.queue.size819216384Больше буфер между WAL и Kafka
max.batch.size20484096Больше events per commit
poll.interval.ms1000500Чаще poll (меньше latency)
producer.override.batch.size16384131072Больше batch к Kafka
producer.override.linger.ms010Подождать для batch
producer.override.compression.typenonelz4Сжатие (меньше I/O)

Performance Ceiling

Максимальная пропускная способность одного PostgreSQL коннектора:

~7,000 events/second (под оптимальными условиями)

Это примерное значение, зависящее от:

  • Размера events (больше = медленнее)
  • Network latency между Connect и Kafka
  • Disk I/O на PostgreSQL
  • Transforms complexity

Если нужно больше 7K events/sec:

  • Используйте множественные коннекторы
  • Архивируйте старые данные
  • Рассмотрите партиционирование PostgreSQL

Decision Framework: Выбор стратегии

Decision Framework: Выбор стратегии масштабирования
Нужно больше throughput?
Текущий vs целевой
Меньше 7K/sec
Стратегия 3:
Performance Tuning
Достаточно?
Да
Done
Нет
~7K/sec или больше
Таблицы независимы?
Да
Стратегия 1:
Multiple Connectors
Нет
Нужен ordering
per-key?
Да
Стратегия 2:
Downstream
Нет
Комбинация:
Multiple + DS
Performance ceiling: ~7,000 events/sec per PostgreSQL connector
Проверка знаний
Какое преимущество дает downstream параллелизация (Kafka partitions + consumer group) по сравнению с созданием дополнительных коннекторов?
Ответ
Downstream параллелизация использует один коннектор и один replication slot, минимизируя WAL retention overhead. Параллелизм достигается на стороне consumer через партиции Kafka. Ordering per-key гарантирован, так как события одного primary key попадают в одну партицию.

Anti-Patterns

Anti-Pattern 1: tasks.max больше 1

{
  "tasks.max": "4"  // БЕСПОЛЕЗНО для PostgreSQL!
}

Проблема: Игнорируется. Вы думаете, что масштабировали, но ничего не изменилось.

Решение: Не тратьте время. Используйте реальные стратегии.

Anti-Pattern 2: Все таблицы в одном коннекторе

{
  "table.include.list": "public.table1,public.table2,...,public.table100"
}

Проблема:

  • Один slot для всех таблиц
  • Failure одной таблицы блокирует все
  • Трудно мониторить per-table lag

Решение: Группируйте таблицы по домену/SLO.

Anti-Pattern 3: Слишком маленький max.queue.size

{
  "max.queue.size": "1024"  // Слишком мало!
}

Проблема: Частые flush к Kafka, высокий overhead.

Решение: Минимум 8192, лучше 16384 для high-throughput.

Мониторинг throughput

Ключевые метрики

# Events per second (rate over 5 minutes)
curl -s http://localhost:9404/metrics | \
  grep debezium_metrics_TotalNumberOfEventsSeen

# Queue utilization
curl -s http://localhost:9404/metrics | \
  grep -E "QueueRemainingCapacity|QueueTotalCapacity"

PromQL запросы:

# Events per second
rate(debezium_metrics_TotalNumberOfEventsSeen{connector="inventory-connector"}[5m])

# Queue utilization %
100 * (1 - (
  debezium_metrics_QueueRemainingCapacity /
  debezium_metrics_QueueTotalCapacity
))

Когда беспокоиться

МетрикаWarningCriticalДействие
Events/secПадение более 20%Падение более 50%Проверить WAL reader
Queue utilБолее 80%Более 95%Kafka bottleneck
LagБолее 5sБолее 30sCapacity review

Lab: Measure Connector Throughput

Цель

Измерить текущий throughput и определить bottleneck.

Шаги

1. Создать нагрузку на БД:

docker exec -it postgres psql -U postgres -d inventory -c "
-- Создать таблицу для теста
CREATE TABLE IF NOT EXISTS load_test (
    id SERIAL PRIMARY KEY,
    data TEXT,
    created_at TIMESTAMP DEFAULT NOW()
);

-- Генерировать 10000 записей
INSERT INTO load_test (data)
SELECT md5(random()::text)
FROM generate_series(1, 10000);
"

2. Наблюдать throughput в Prometheus:

# Открыть Prometheus UI
open http://localhost:9090

# Query: rate of events
rate(debezium_metrics_TotalNumberOfEventsSeen[1m])

3. Проверить queue utilization:

100 * (1 - (
  debezium_metrics_QueueRemainingCapacity /
  debezium_metrics_QueueTotalCapacity
))

4. Определить bottleneck:

  • Queue util высокий (более 80%): Kafka write bottleneck
  • Queue util низкий, но throughput низкий: WAL read bottleneck

Ожидаемый результат

  • Видите пик throughput при INSERT
  • Queue utilization временно растет
  • После INSERT throughput падает к baseline

Ключевые выводы

  1. tasks.max НЕ работает для PostgreSQL — коннектор всегда использует 1 task

  2. Причина: WAL — последовательный журнал, один reader

  3. Реальные стратегии:

    • Множественные коннекторы (разные наборы таблиц)
    • Downstream параллелизация (Kafka partitions + consumers)
    • Performance tuning (queue size, batch size, compression)
  4. Performance ceiling: ~7,000 events/sec per connector

  5. Monitoring: rate(TotalNumberOfEventsSeen), Queue utilization, Lag

Production Insight: Не теряйте время на tasks.max. Если throughput критичен — разделяйте на множественные коннекторы и используйте downstream parallelization. Один коннектор имеет жесткий потолок.

Проверьте понимание

Результат: 0 из 0
Концептуальный
Вопрос 1 из 4. Установка tasks.max = 4 для PostgreSQL source-коннектора Debezium увеличивает пропускную способность в 4 раза за счет параллельного чтения WAL.

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс