librdkafka clib

Apache Kafka client library for PascalAI. Produce and consume messages, consumer groups, delivery reports, topic metadata, partition management — all via the battle-tested librdkafka C library used by every major language's Kafka binding.

Version 1.0.0
Type clib
System Requirement librdkafka-dev
ppm install librdkafka
CLib package — system dependency required

Ubuntu/Debian: sudo apt install librdkafka-dev  •  macOS: brew install librdkafka

Quick Start

Producer

Create a producer, send keyed messages for consistent partition routing, then flush and destroy.

uses librdkafka;

var prod := NewProducer('broker1:9092,broker2:9092');

{ Send messages with a key for partitioning }
ProduceKeyed(prod, 'orders', 'user-42', '{"id":1001,"total":99.90}', -1);
ProduceKeyed(prod, 'orders', 'user-17', '{"id":1002,"total":14.50}', -1);

{ Wait for all deliveries }
Flush(prod, 5000);
DestroyProducer(prod);

Consumer Loop

Create a consumer, subscribe to one or more topics, then poll in a loop and commit offsets after processing.

uses librdkafka;

var cons := NewConsumer('broker1:9092', 'my-group');
Subscribe(cons, ['orders', 'events']);

while True do
begin
  var msg := Poll(cons, 1000);
  if not msg.IsError then
  begin
    WriteLn('Topic: ' + msg.Topic + '  Key: ' + msg.Key);
    WriteLn('Value: ' + msg.Value);
    CommitMessage(cons, msg);
  end;
end;

Producer API

FunctionDescription
NewProducer(brokers)Create a producer connected to the given comma-separated broker list. Returns a producer handle.
NewProducerEx(brokers, config)Create a producer with an extended configuration record (acks, compression, linger.ms, etc.).
DestroyProducer(prod)Release all resources held by the producer. Call after Flush to ensure no messages are lost.
Produce(prod, topic, value, partition)Enqueue a message with no key. Pass -1 as partition to use automatic partitioning.
ProduceKeyed(prod, topic, key, value, partition)Enqueue a message with an explicit key. Messages with the same key are routed to the same partition.
ProduceBatch(prod, topic, messages)Enqueue a List<TKafkaMessage> in a single call. More efficient than looping over Produce.
Flush(prod, timeoutMs)Block until all enqueued messages are delivered or the timeout expires. Returns the number of messages still in the queue.
OutQueueLen(prod)Return the current number of messages waiting in the producer queue (not yet delivered).
PollProducer(prod, timeoutMs)Serve delivery-report callbacks without blocking. Call periodically in high-throughput loops.
GetDeliveryReports(prod)Return a List<TDeliveryReport> of completed deliveries since the last call. Each report contains topic, partition, offset, key, and error status.

Consumer API

FunctionDescription
NewConsumer(brokers, groupId)Create a consumer joined to the specified consumer group. Handles automatic partition rebalancing.
NewConsumerEx(brokers, groupId, config)Create a consumer with extended configuration (auto.offset.reset, session.timeout.ms, etc.).
DestroyConsumer(cons)Close the consumer, commit pending offsets, and release all resources.
Subscribe(cons, topics)Subscribe to a list of topics with automatic partition assignment managed by the broker.
Unsubscribe(cons)Remove all topic subscriptions from this consumer.
Assign(cons, partitions)Manually assign a specific list of TTopicPartition records (bypasses group rebalancing).
Poll(cons, timeoutMs)Fetch the next message, waiting up to timeoutMs milliseconds. Returns a TKafkaMessage; check msg.IsError and msg.IsTimeout.
PollBatch(cons, maxMessages, timeoutMs)Fetch up to maxMessages in one call. Returns a List<TKafkaMessage>.
CommitSync(cons)Synchronously commit the current partition offsets for all assigned partitions.
CommitMessage(cons, msg)Commit the offset of a specific message. Preferred over CommitSync for per-message acknowledgement.
SeekPartition(cons, topic, partition, offset)Seek a specific partition to a given offset. Use -2 for earliest, -1 for latest.
PausePartitions(cons, partitions)Pause fetching from a list of TTopicPartition records without leaving the consumer group.
ResumePartitions(cons, partitions)Resume fetching from previously paused partitions.
GetAssignment(cons)Return the current partition assignment as a List<TTopicPartition>.

Admin / Metadata

FunctionDescription
ListTopics(handle, timeoutMs)Return a List<TTopicInfo> describing all topics visible to this producer or consumer handle. Each record includes name, partition count, and replication factor.
GetPartitionCount(handle, topic)Return the number of partitions for the given topic as an Integer. Useful for manual partition key routing.
GetWatermarks(handle, topic, partition)Return a TWatermarks record with Low (earliest available) and High (latest) offsets for the specified topic-partition.
uses librdkafka;

var prod := NewProducer('broker1:9092');

{ Inspect cluster topology }
var topics := ListTopics(prod, 5000);
for t in topics do
  WriteLn(t.Name + ' — ' + IntToStr(t.PartitionCount) + ' partitions');

{ Check how much data is waiting on a partition }
var wm := GetWatermarks(prod, 'orders', 0);
WriteLn('orders[0] lag window: ' + IntToStr(wm.High - wm.Low));

DestroyProducer(prod);

Key Concepts

Topics & Partitions A topic is a named, ordered log. Topics are split into partitions for parallelism. Each partition is an independent ordered sequence of records with monotonically increasing offsets. Producers route messages to partitions by key hash, explicit assignment, or round-robin.
Consumer Groups Consumers sharing a groupId form a consumer group. The broker assigns each partition to exactly one member of the group, distributing load automatically. When members join or leave, a rebalance redistributes partitions. Use Assign to opt out of group management.
Delivery Reports Kafka delivery is asynchronous. After a message is acknowledged by the broker (or fails permanently), librdkafka fires a delivery report. Use GetDeliveryReports or PollProducer to retrieve them. Each report carries the final offset, partition, and any error code.
Offset Management An offset is a consumer's position within a partition. Committing an offset tells the broker "I have processed up to here." Use CommitMessage for at-least-once semantics (commit after processing) or CommitSync to commit the entire current position. Seek to replay or skip messages.

Delivery Reports

Poll delivery reports after producing to confirm each message reached the broker and capture its final offset and partition assignment.

uses librdkafka;

var prod := NewProducer('broker1:9092,broker2:9092');

{ Enqueue a batch of messages }
ProduceKeyed(prod, 'payments', 'txn-001', '{"amount":250.00}', -1);
ProduceKeyed(prod, 'payments', 'txn-002', '{"amount":75.50}', -1);
ProduceKeyed(prod, 'payments', 'txn-003', '{"amount":1200.00}', -1);

{ Drive the event loop until all messages are delivered }
while OutQueueLen(prod) > 0 do
begin
  PollProducer(prod, 100);
  var reports := GetDeliveryReports(prod);
  for r in reports do
  begin
    if r.Error = '' then
      WriteLn('Delivered key=' + r.Key +
              ' partition=' + IntToStr(r.Partition) +
              ' offset=' + IntToStr(r.Offset))
    else
      WriteLn('FAILED key=' + r.Key + ' error: ' + r.Error);
  end;
end;

DestroyProducer(prod);

Batch Consumer with Manual Commit

Use PollBatch and commit only after the entire batch is processed for higher throughput with controlled at-least-once guarantees.

uses librdkafka;

var cons := NewConsumer('broker1:9092', 'analytics-group');
Subscribe(cons, ['payments']);

while True do
begin
  var batch := PollBatch(cons, 100, 500);
  if batch.Count > 0 then
  begin
    WriteLn('Processing ' + IntToStr(batch.Count) + ' messages');
    for msg in batch do
    begin
      if not msg.IsError then
        ProcessPayment(msg.Value);
    end;
    { Commit the entire batch in one round-trip }
    CommitSync(cons);
  end;
end;

Partition Seek & Replay

Rewind a partition to reprocess historical messages or skip ahead past a known bad range.

uses librdkafka;

var cons := NewConsumer('broker1:9092', 'replay-group');

{ Manually assign partition 0 of 'events' }
Assign(cons, [TopicPartition('events', 0)]);

{ Seek to a specific offset to replay from there }
SeekPartition(cons, 'events', 0, 8000);

var msg := Poll(cons, 2000);
WriteLn('Replaying from offset: ' + IntToStr(msg.Offset));

{ Check available range }
var wm := GetWatermarks(cons, 'events', 0);
WriteLn('Available range: [' + IntToStr(wm.Low) + ', ' + IntToStr(wm.High) + ']');

DestroyConsumer(cons);