Migrating from Apache Kafka to StreamFlow

    Table of Contents

    1. Why Migrate?
    2. Compatibility
    3. Migration Plan
    4. Migration by Language
    5. Kafka Connect → StreamFlow Connectors
    6. Kafka Streams → Agent Graphs
    7. Migration Checklist
    8. FAQ

    1. Why Migrate?

    AspectApache KafkaStreamFlow
    Consumer throughput462K msg/s743K msg/s (+61%)
    Producer throughput447K msg/s358K msg/s (80%, gap = pd-ssd)
    Native engine (SDK embedded)N/A3.8M evt/s
    Built-in AI AgentsNo (requires Kafka Streams + external service)Yes (LLM, rules, MCP tools)
    Agent GraphsNoYes (fluent DSL, conditional routing)
    Elastic partitioningNo (fixed partitions)Yes (16384 slots, auto-rebalance)
    Geo-replicationMirrorMaker (separate tool)Built-in (active-passive + active-active)
    io_uringNo (mmap + page cache)Yes (SQPOLL, DMA buffers)
    DeploymentZooKeeper or KRaft (complex)Single binary (built-in Raft)

    2. Compatibility

    StreamFlow implements the Kafka wire protocol (TCP, port 9092). Your existing applications connect without any code change:

    ┌──────────────────────┐        ┌──────────────────────┐
    │  Your application    │        │  Your application    │
    │  KafkaProducer       │        │  KafkaProducer       │  ← SAME CODE
    │  KafkaConsumer       │        │  KafkaConsumer       │
    │  bootstrap=kafka:9092│        │  bootstrap=sf:9092   │  ← JUST CHANGE THE ADDRESS
    └──────────┬───────────┘        └──────────┬───────────┘
               │                               │
         Apache Kafka                    StreamFlow

    What works without changes

    Kafka FeatureSupportedNotes
    KafkaProducer (Java, Python, Go, Node.js)All Kafka libs
    KafkaConsumer with consumer groupsRebalancing, offset commit
    acks=0, acks=1, acks=allSame semantics
    batch.size, linger.msSame configs
    compression.type (snappy, lz4, zstd)Transparent decompression
    Topic auto-creationConfigurable
    kafka-console-producer.shStandard CLI tools
    kafka-console-consumer.sh
    kafka-producer-perf-test.sh
    kafka-consumer-perf-test.sh
    Idempotent producerInitProducerId supported
    SASL PLAIN authentication

    What is not (yet) supported

    Kafka FeatureStatusStreamFlow Alternative
    Kafka Transactionsacks=all + Raft replication
    Kafka StreamsAgent graphs (more powerful)
    Kafka ConnectSource/Sink Connector SDK (19 built-in)
    Schema RegistryHeaders + SDK validation
    Exactly-once semantics (EOS)PartialIdempotent producer + at-least-once consumer
    ACL via kafka-acls.shREST API + TopicAclPolicy
    Log compactionBuilt-in LogCompactor

    3. Migration Plan

    Phase 1: Parallel Testing (1 day)

    Run StreamFlow alongside Kafka without touching production.

    bash
    1# 1. Start StreamFlow (single node for testing)
    2./scripts/start-streamflow.sh
    3
    4# 2. Create the same topic as in Kafka
    5kafka-topics.sh --create --topic orders \
    6  --partitions 16 --bootstrap-server localhost:9092
    7
    8# 3. Run the same benchmark as Kafka
    9kafka-producer-perf-test.sh --topic orders \
    10  --num-records 1000000 --record-size 256 \
    11  --throughput -1 \
    12  --producer-props bootstrap.servers=localhost:9092 \
    13  batch.size=65536 linger.ms=5 acks=1
    14
    15kafka-consumer-perf-test.sh --topic orders \
    16  --messages 1000000 --broker-list localhost:9092 --timeout 60000
    17
    18# 4. Compare results with Kafka

    Phase 2: Dual-Write (1-2 weeks)

    Your producers write to both clusters. Consumers read from StreamFlow.

    Java
    1// Dual-write producer (during migration)
    2public class DualWriteProducer {
    3    private final KafkaProducer<String, byte[]> kafkaProducer;   // old
    4    private final StreamFlowProducer streamFlowProducer;          // new
    5
    6    public void send(String topic, String key, byte[] value) {
    7        // Write to both
    8        kafkaProducer.send(new ProducerRecord<>(topic, key, value));
    9        streamFlowProducer.send(topic, key, value);
    10    }
    11}

    Verify data parity:

    bash
    1# Count events in each system
    2kafka-run-class.sh kafka.tools.GetOffsetShell \
    3  --broker-list kafka:9092 --topic orders
    4# vs
    5kafka-run-class.sh kafka.tools.GetOffsetShell \
    6  --broker-list streamflow:9092 --topic orders

    Phase 3: Switchover (1 day)

    bash
    1# 1. Stop Kafka consumers
    2# 2. Change bootstrap.servers in your app config
    3sed -i 's/kafka-cluster:9092/streamflow-cluster:9092/' application.yml
    4
    5# 3. Redeploy apps
    6kubectl rollout restart deployment/my-app
    7
    8# 4. Verify everything works
    9curl http://streamflow:8080/metrics | grep events_published

    Phase 4: Decommission Kafka (after 1 month)

    bash
    1# Once StreamFlow is stable in production
    2docker compose -f kafka-cluster.yml down
    3# or
    4kubectl delete statefulset kafka

    4. Migration by Language

    Java (KafkaProducer/KafkaConsumer)

    Before (Kafka):

    Java
    1Properties props = new Properties();
    2props.put("bootstrap.servers", "kafka-cluster:9092");
    3props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
    4props.put("value.serializer", "org.apache.kafka.common.serialization.ByteArraySerializer");
    5KafkaProducer<String, byte[]> producer = new KafkaProducer<>(props);
    6producer.send(new ProducerRecord<>("orders", "key-1", payload));

    After (StreamFlow SDK):

    Java
    1StreamFlowClient client = StreamFlowClient.remote("streamflow-cluster:9092");
    2StreamFlowProducer producer = client.producer();
    3producer.send("orders", "key-1", payload);

    Or without changing code at all:

    Java
    1// ONLY CHANGE: the cluster address
    2props.put("bootstrap.servers", "streamflow-cluster:9092");
    3// The rest of the KafkaProducer code works as-is

    Python (confluent-kafka)

    Before:

    python
    1producer = Producer({'bootstrap.servers': 'kafka-cluster:9092'})

    After:

    python
    1producer = Producer({'bootstrap.servers': 'streamflow-cluster:9092'})
    2# That's it. Same code, same lib.

    Node.js (KafkaJS)

    Before:

    javascript
    1const kafka = new Kafka({ brokers: ['kafka-cluster:9092'] });

    After:

    javascript
    1const kafka = new Kafka({ brokers: ['streamflow-cluster:9092'] });

    Go (kafka-go)

    Before:

    go
    1writer := &kafka.Writer{Addr: kafka.TCP("kafka-cluster:9092"), Topic: "orders"}

    After:

    go
    1writer := &kafka.Writer{Addr: kafka.TCP("streamflow-cluster:9092"), Topic: "orders"}

    5. Kafka Connect → StreamFlow Connectors

    Kafka Connect (before)

    JSON
    1{
    2  "name": "jdbc-source",
    3  "config": {
    4    "connector.class": "io.confluent.connect.jdbc.JdbcSourceConnector",
    5    "connection.url": "jdbc:postgresql://db:5432/shop",
    6    "table.whitelist": "orders",
    7    "mode": "incrementing",
    8    "incrementing.column.name": "id",
    9    "topic.prefix": "cdc-"
    10  }
    11}

    StreamFlow Connector (after)

    Properties
    1# connect-source.properties
    2source.jdbc-orders.type=jdbc
    3source.jdbc-orders.topic=cdc-orders
    4source.jdbc-orders.connection.url=jdbc:postgresql://db:5432/shop
    5source.jdbc-orders.table=orders
    6source.jdbc-orders.mode=incrementing
    7source.jdbc-orders.incrementing.column=id
    8source.jdbc-orders.poll.interval.ms=1000

    Connector Mapping

    Kafka ConnectStreamFlowConfig type
    JdbcSourceConnectorJdbcSourceConnectorjdbc
    JdbcSinkConnectorJdbcSinkConnectorjdbc
    FileStreamSourceConnectorHttpSourceConnectorhttp
    ElasticsearchSinkConnectorElasticsearchSinkConnectorelasticsearch
    MongoSinkConnectorMongoDbSinkConnectormongodb
    S3SinkConnector❌ (not yet)
    DebeziumSourceConnectorCdcSourceConnectorcdc

    6. Kafka Streams → Agent Graphs

    Kafka Streams (before)

    Java
    1StreamsBuilder builder = new StreamsBuilder();
    2
    3builder.stream("transactions")
    4    .filter((key, value) -> value.getAmount() > 10000)
    5    .mapValues(tx -> new FraudCheck(tx))
    6    .to("fraud-alerts");
    7
    8KafkaStreams streams = new KafkaStreams(builder.build(), props);
    9streams.start();

    StreamFlow Agent Graph (after)

    Java
    1CompiledGraph graph = StreamflowGraph.define("fraud-detection")
    2    .start("filter")
    3        .agent("amount-filter")
    4        .prompt("Filter: keep only transactions with amount > 10000. " +
    5               "Return the transaction unchanged or null to drop.")
    6    .then("analyze")
    7        .agent("fraud-analyzer")
    8        .prompt("Analyze this high-value transaction for fraud indicators. " +
    9               "Return {fraud: true/false, confidence: 0-1, reason: '...'}")
    10    .end("alert")
    11        .agent("alert-generator")
    12        .prompt("Generate a fraud alert for the security team. " +
    13               "Include transaction details and risk assessment.")
    14    .compile();
    15
    16GraphRunner runner = new GraphRunner();
    17runner.register(graph);
    StreamFlow advantage: Agents use AI reasoning (a language model) instead of imperative code. A Kafka Streams filter does if amount > 10000. A StreamFlow agent can analyze context, customer history, and fraud patterns, in natural language.

    7. Migration Checklist

    Phase 1: Test

    • Install StreamFlow (1 node)
    • Run benchmark with kafka-producer-perf-test
    • Compare results with Kafka
    • Test consumer groups

    Phase 2: Dual-write

    • Configure dual-write in producers
    • Switch consumers to StreamFlow
    • Verify data parity (offsets, counts)
    • Monitor for 1-2 weeks

    Phase 3: Switchover

    • Change bootstrap.servers in all apps
    • Redeploy
    • Verify /metrics (throughput, latency, lag)
    • Configure Prometheus alerts

    Phase 4: Decommission Kafka

    • Wait 1 month of stability
    • Export offsets if needed
    • Shut down Kafka cluster
    • Delete ZooKeeper/KRaft volumes

    Bonus: Enable StreamFlow features

    • Create AI agents (Agent Guide)
    • Configure geo-replication
    • Enable elastic partitioning
    • Migrate Kafka Connect to StreamFlow connectors
    • Replace Kafka Streams with Agent Graphs

    8. FAQ

    Q: Do I need to change my code?

    R: No. Just change bootstrap.servers. All Kafka libs (Java, Python, Go, Node.js) work.

    Q: What about my Kafka offsets?

    R: StreamFlow offsets start from 0. Use auto.offset.reset=earliest to re-read from the beginning, or latest to read only new messages.

    Q: Do my consumer groups work?

    R: Yes. Rebalancing, offset commit, and session timeout work just like Kafka.

    Q: Performance comparison?

    R: Consumer 61% faster than Kafka. Producer at 80% of Kafka on pd-ssd (gap due to network disk latency, not code). On local NVMe, StreamFlow should match or exceed Kafka.

    Q: What about durability?

    R: With acks=all, data is replicated via Raft across 3 nodes before ACK. Same guarantee as Kafka with replication.factor=3.

    Q: Can I go back to Kafka?

    R: Yes. During the dual-write phase, both clusters have the same data. Just change bootstrap.servers back.

    StreamFlow© 2026 StreamFlow, Built for real-time AI at scale.