Back to Kafka Fundamentals: Topics, Partitions, Offsets
Q2Question 2 of 18

What is Solace PubSub+, and how does its topic routing differ from Kafka's flat topics?

Key Concepts
  • Solace PubSub+ is an enterprise message broker with hierarchical topic routing and level-aware wildcard subscriptions.
  • Solace topics are slash-separated strings attached at publish time, not pre-created broker objects.
  • The single-level wildcard (*) matches exactly one topic level; the multi-level wildcard (>) matches one or more remaining levels.
  • The Solace broker evaluates subscriptions and delivers only matching messages, so routing intelligence lives in the broker.
  • Kafka uses flat, pre-created topics where the broker does no content-based routing and consumers filter on their own.
  • Solace shifts routing CPU to the broker; Kafka shifts it to the consumer in exchange for massive throughput and replay.
  • A common enterprise pattern uses Solace for live selective routing and Kafka as the durable replayable backbone.

Definition

Solace routes messages at the broker using a hierarchical topic structure and level-aware wildcards, while Kafka uses flat topics and pushes fine-grained filtering to the consumer.

Solace PubSub+ and Kafka handle topic routing in fundamentally different ways.

  • Solace PubSub+ – an enterprise message broker that routes messages using hierarchical, slash-separated topic strings with level-aware wildcard subscriptions evaluated by the broker itself.
  • Hierarchical topic – a publish-time string like txn/IN/Mumbai/savings/large where each slash-separated level carries a routing dimension.
  • **Single-level wildcard (*)** – matches exactly one level in a Solace topic subscription.
  • Multi-level wildcard (>) – matches one or more remaining levels at the end of a Solace subscription.
  • Kafka flat topic – a pre-created, named, partitioned log where the broker does no content-based routing and consumers read entire partitions.

At National Bank of India, Solace is like a mail system where every letter has a precise hierarchical address: transactions, Mumbai branch, savings accounts, large amounts. A desk can say "give me everything from Mumbai regardless of type" and the sorting office understands the structure and delivers only matching letters. Kafka is like having a few big labelled bins: everything goes into the transactions bin, and if you only want Mumbai large transactions you must pull out every letter and check it yourself, because the bin does not understand the address structure.

Understanding the Concepts

What is Solace PubSub+?

Solace PubSub+ is an enterprise message broker whose defining feature is hierarchical topic routing with level-aware wildcard subscriptions. Unlike Kafka where topics are pre-created objects, a Solace topic is a string attached to each message at publish time. The broker evaluates the topic against all active subscriptions and delivers each message only to interested subscribers.

At National Bank of India, the trading floor uses Solace to distribute transaction events where each desk receives only the specific slice it cares about.

JAVA
package com.bank.solace;

import com.solacesystems.jcsmp.JCSMPFactory;
import com.solacesystems.jcsmp.JCSMPSession;
import com.solacesystems.jcsmp.JCSMPProperties;
import com.solacesystems.jcsmp.Topic;
import com.solacesystems.jcsmp.XMLMessageProducer;
import com.solacesystems.jcsmp.TextMessage;
import java.math.BigDecimal;

/**
 * Rajesh publishes a transaction with a hierarchical topic.
 * The broker routes it to desks whose subscriptions match.
 */
public class SolaceTransactionPublisher {

    public static void main(String[] args) throws Exception {
        JCSMPProperties properties = new JCSMPProperties();
        properties.setProperty(JCSMPProperties.HOST, "solace-broker:55555");
        properties.setProperty(JCSMPProperties.VPN_NAME, "nbi-vpn");
        properties.setProperty(JCSMPProperties.USERNAME, "rajesh");

        JCSMPSession session = JCSMPFactory.onlyInstance().createSession(properties);
        session.connect();

        XMLMessageProducer producer = session.getMessageProducer(null);

        // Hierarchical topic: txn/{country}/{city}/{accountType}/{size}
        String topicString = "txn/IN/Mumbai/savings/large";
        Topic topic = JCSMPFactory.onlyInstance().createTopic(topicString);

        TextMessage message = JCSMPFactory.onlyInstance().createMessage(TextMessage.class);
        BigDecimal amount = new BigDecimal("750000.00");
        message.setText("{\"account\":\"SAV2001\",\"amount\":" + amount + "}");

        producer.send(message, topic);
        System.out.println("Published to topic: " + topicString);
        System.out.println("Broker routes to matching subscriptions only.");

        session.closeSession();
    }
}
TEXT
Expected output:
Published to topic: txn/IN/Mumbai/savings/large
Broker routes to matching subscriptions only.

How do Solace wildcard subscriptions work?

Solace has two wildcard characters for subscriptions.

The single-level wildcard * matches exactly one topic level. A subscription to txn/IN/*/savings/* matches txn/IN/Mumbai/savings/large and txn/IN/Delhi/savings/small, but not txn/IN/Mumbai/current/large because the fourth level does not match.

The multi-level wildcard > matches one or more remaining levels and can only appear at the end. A subscription to txn/IN/Mumbai/> matches txn/IN/Mumbai/savings/large, txn/IN/Mumbai/current/small, and even deeper paths like txn/IN/Mumbai/savings/large/priority.

JAVA
package com.bank.solace;

import com.solacesystems.jcsmp.JCSMPFactory;
import com.solacesystems.jcsmp.JCSMPSession;
import com.solacesystems.jcsmp.JCSMPProperties;
import com.solacesystems.jcsmp.Topic;

/**
 * Priya subscribes to all Mumbai transactions using multi-level wildcard.
 * Pankaj subscribes to all large savings transactions using single-level wildcards.
 */
public class SolaceWildcardSubscriptions {

    public static void main(String[] args) throws Exception {
        JCSMPProperties properties = new JCSMPProperties();
        properties.setProperty(JCSMPProperties.HOST, "solace-broker:55555");
        properties.setProperty(JCSMPProperties.VPN_NAME, "nbi-vpn");
        properties.setProperty(JCSMPProperties.USERNAME, "priya");

        JCSMPSession session = JCSMPFactory.onlyInstance().createSession(properties);
        session.connect();

        // Priya: all Mumbai transactions regardless of type or size
        Topic mumbaiAll = JCSMPFactory.onlyInstance().createTopic("txn/IN/Mumbai/>");
        session.addSubscription(mumbaiAll);
        System.out.println("Priya subscribed: txn/IN/Mumbai/>");
        System.out.println("  Matches: txn/IN/Mumbai/savings/large");
        System.out.println("  Matches: txn/IN/Mumbai/current/small");
        System.out.println("  No match: txn/IN/Delhi/savings/large");
        System.out.println();

        // Pankaj: all large savings from any city in India
        Topic largeSavings = JCSMPFactory.onlyInstance().createTopic("txn/IN/*/savings/large");
        session.addSubscription(largeSavings);
        System.out.println("Pankaj subscribed: txn/IN/*/savings/large");
        System.out.println("  Matches: txn/IN/Mumbai/savings/large");
        System.out.println("  Matches: txn/IN/Delhi/savings/large");
        System.out.println("  No match: txn/IN/Mumbai/savings/small");

        session.closeSession();
    }
}
TEXT
Expected output:
Priya subscribed: txn/IN/Mumbai/>
  Matches: txn/IN/Mumbai/savings/large
  Matches: txn/IN/Mumbai/current/small
  No match: txn/IN/Delhi/savings/large

Pankaj subscribed: txn/IN/*/savings/large
  Matches: txn/IN/Mumbai/savings/large
  Matches: txn/IN/Delhi/savings/large
  No match: txn/IN/Mumbai/savings/small

How does Kafka's flat topic model work?

A Kafka topic is a flat, named, pre-created, partitioned log. The broker does not understand any structure inside the topic name or the message content. A producer writes to a topic and partition, and a consumer reads every record in its assigned partitions.

If you want only Mumbai large savings transactions from a broad bank.transactions topic, you must filter in the consumer application.

JAVA
package com.bank.kafka;

import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.apache.kafka.clients.consumer.KafkaConsumer;
import java.time.Duration;
import java.util.Collections;
import java.util.Properties;

/**
 * Kafka consumer must filter records manually.
 * The broker sends everything; the consumer decides what to keep.
 */
public class KafkaFlatTopicConsumer {

    public static void main(String[] args) {
        Properties props = new Properties();
        props.put("bootstrap.servers", "kafka-1:9092");
        props.put("group.id", "mumbai-desk");
        props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
        props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");

        try (KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props)) {
            // Subscribe to the flat topic: receive ALL transactions
            consumer.subscribe(Collections.singletonList("bank.transactions"));

            int total = 0;
            int matched = 0;

            ConsumerRecords<String, String> records = consumer.poll(Duration.ofSeconds(5));
            for (ConsumerRecord<String, String> record : records) {
                total++;
                // Manual filtering: only keep Mumbai large savings
                if (record.value().contains("Mumbai")
                    && record.value().contains("savings")
                    && record.value().contains("large")) {
                    matched++;
                    System.out.println("Matched: " + record.value());
                }
                // All other records are discarded by the consumer
            }
            System.out.println("Total received: " + total + ", matched: " + matched);
            System.out.println("Wasted network/CPU on " + (total - matched) + " irrelevant records");
        }
    }
}
TEXT
Expected output:
Matched: {"city":"Mumbai","type":"savings","size":"large","amount":750000.00}
Total received: 1000, matched: 12
Wasted network/CPU on 988 irrelevant records

How is Solace different from Kafka?

The two systems differ in where routing intelligence lives and what trade-offs each makes.

| Feature | Solace PubSub+ | Kafka |
|---|---|---|
| Topic model | Hierarchical string, publish-time | Flat, pre-created, partitioned log |
| Topic creation | No creation step, any string is valid | Must be created with partition count and RF |
| Routing | Broker evaluates wildcard subscriptions | No broker-side routing, consumer filters |
| Wildcards | * (one level), > (remaining levels) | Regex on topic names only, not content |
| Filtering CPU | In the broker | In the consumer |
| Retention and replay | Not the primary strength | Core feature: days/weeks/years |
| Throughput | Optimized for fan-out and low latency | Optimized for sequential log throughput |
| Stream processing | External processor needed | Kafka Streams, ksqlDB built-in |

JAVA
package com.bank.comparison;

public class SolaceVsKafkaRouting {
    public static void main(String[] args) {
        System.out.println("=== Routing: Where does filtering happen? ===");
        System.out.println();
        System.out.println("Solace: broker filters, consumer receives only matches");
        System.out.println("  Subscribe: txn/IN/Mumbai/>");
        System.out.println("  Receive: only Mumbai transactions");
        System.out.println("  CPU cost: in the broker");
        System.out.println();
        System.out.println("Kafka: consumer filters, receives everything");
        System.out.println("  Subscribe: bank.transactions (all records)");
        System.out.println("  Filter: if (record.contains('Mumbai'))");
        System.out.println("  CPU cost: in the consumer");
    }
}
TEXT
Expected output:
=== Routing: Where does filtering happen? ===

Solace: broker filters, consumer receives only matches
  Subscribe: txn/IN/Mumbai/>
  Receive: only Mumbai transactions
  CPU cost: in the broker

Kafka: consumer filters, receives everything
  Subscribe: bank.transactions (all records)
  Filter: if (record.contains('Mumbai'))
  CPU cost: in the consumer

What are the three ways to approximate Solace routing in Kafka?

Since Kafka does not do broker-side content routing, you have three options to get similar behavior, each with a trade-off.

Option 1: Many fine-grained topics. Create a separate Kafka topic per routing combination, like txn.in.mumbai.savings.large. This pushes routing to publish time but explodes topic count.

Option 2: One broad topic, consumer-side filtering. Publish everything to bank.transactions and let each consumer discard what it does not need. Simple but wastes network and CPU.

Option 3: Stream processing layer. Use Kafka Streams or ksqlDB to read the broad topic, branch by fields, and republish into narrower topics. Precise but adds infrastructure.

JAVA
package com.bank.kafka;

public class KafkaRoutingOptions {
    public static void main(String[] args) {
        System.out.println("Option 1: Many topics (topic proliferation)");
        System.out.println("  Topics: txn.in.mumbai.savings.large, txn.in.delhi.current.small, ...");
        System.out.println("  Problem: 3 types x 50 cities x 3 sizes = 450 topics!");
        System.out.println();
        System.out.println("Option 2: One topic, consumer filters");
        System.out.println("  Topic: bank.transactions");
        System.out.println("  Problem: every consumer downloads everything");
        System.out.println();
        System.out.println("Option 3: Stream processing layer");
        System.out.println("  Kafka Streams reads bank.transactions");
        System.out.println("  Branches by city/type/size into narrow topics");
        System.out.println("  Problem: extra infrastructure to operate");
    }
}
TEXT
Expected output:
Option 1: Many topics (topic proliferation)
  Topics: txn.in.mumbai.savings.large, txn.in.delhi.current.small, ...
  Problem: 3 types x 50 cities x 3 sizes = 450 topics!

Option 2: One topic, consumer filters
  Topic: bank.transactions
  Problem: every consumer downloads everything

Option 3: Stream processing layer
  Kafka Streams reads bank.transactions
  Branches by city/type/size into narrow topics
  Problem: extra infrastructure to operate

What are common mistakes?

Wrong: trying to use Kafka topic names as a routing hierarchy.

Some teams create topics like txn.IN.Mumbai.savings.large and use regex subscription (Pattern.compile("txn\\.IN\\.Mumbai\\..*")) to subscribe. But Kafka regex subscription resolves to full topics at subscription time, not per-message. Every matched topic delivers all its records.

JAVA
package com.bank.kafka;

public class WrongKafkaHierarchy {
    public static void main(String[] args) {
        System.out.println("WRONG: Using Kafka topic names as hierarchy");
        System.out.println("  Created: txn.IN.Mumbai.savings.large (1 topic)");
        System.out.println("  Created: txn.IN.Mumbai.savings.small (1 topic)");
        System.out.println("  Created: txn.IN.Mumbai.current.large (1 topic)");
        System.out.println("  ... 450 topics total");
        System.out.println("  Result: operational nightmare, partition explosion");
        System.out.println();
        System.out.println("CORRECT: Use Solace for fine-grained routing");
        System.out.println("  Or: use one Kafka topic + consumer-side filtering");
        System.out.println("  Or: use Kafka Streams to branch into a few key topics");
    }
}
TEXT
Expected output:
WRONG: Using Kafka topic names as hierarchy
  Created: txn.IN.Mumbai.savings.large (1 topic)
  Created: txn.IN.Mumbai.savings.small (1 topic)
  Created: txn.IN.Mumbai.current.large (1 topic)
  ... 450 topics total
  Result: operational nightmare, partition explosion

CORRECT: Use Solace for fine-grained routing
  Or: use one Kafka topic + consumer-side filtering
  Or: use Kafka Streams to branch into a few key topics

When should you use Solace vs Kafka?

Use Solace when consumers have diverse, frequently changing routing needs and you want the broker to deliver only matching messages. Use Kafka when you need durable retention, replay, stream processing, and high sequential throughput.

A common bank pattern is to use both: Solace for real-time selective routing to trading desks and Kafka as the durable event backbone.

JAVA
package com.bank.comparison;

public class WhenToUseEach {
    public static void main(String[] args) {
        System.out.println("Choose Solace when:");
        System.out.println("  - Many consumers want different slices");
        System.out.println("  - Routing needs change frequently");
        System.out.println("  - Low-latency fan-out to selective consumers");
        System.out.println("  - Multi-protocol access (JMS, MQTT, AMQP, REST)");
        System.out.println();
        System.out.println("Choose Kafka when:");
        System.out.println("  - Durable retention for days/weeks/years");
        System.out.println("  - Replay capability (reset offset)");
        System.out.println("  - Stream processing (Kafka Streams, ksqlDB)");
        System.out.println("  - Very high sequential throughput");
    }
}
TEXT
Expected output:
Choose Solace when:
  - Many consumers want different slices
  - Routing needs change frequently
  - Low-latency fan-out to selective consumers
  - Multi-protocol access (JMS, MQTT, AMQP, REST)

Choose Kafka when:
  - Durable retention for days/weeks/years
  - Replay capability (reset offset)
  - Stream processing (Kafka Streams, ksqlDB)
  - Very high sequential throughput

What should a senior engineer know?

A senior engineer understands the operational trade-offs.

  • Solace's broker-side matching uses a trie data structure keyed on topic levels. This gives near-constant matching time per level but means the broker must be sized for the subscription workload.
  • Kafka pushes filtering cost to consumers, which keeps the broker simple and fast but means every consumer downloads and scans everything.
  • Kafka's regex topic subscription resolves at subscription time to whole topics, not per-message. It is not equivalent to Solace wildcards.
  • A common architecture runs Solace for real-time fan-out and Kafka for durable retention, sometimes bridged together so events flow through both.
  • Solace supports guaranteed messaging with persistent queues, not just direct pub-sub, but its retention and replay model is different from Kafka's partitioned log.
JAVA
package com.bank.comparison;

public class SeniorSolaceKafka {
    public static void main(String[] args) {
        System.out.println("Senior nuances:");
        System.out.println("1. Solace trie-based matching: O(levels) per message");
        System.out.println("2. Kafka: no broker matching, consumer does all filtering");
        System.out.println("3. Kafka regex subscription != Solace wildcards");
        System.out.println("4. Common pattern: Solace routes, Kafka stores");
        System.out.println("5. Solace has guaranteed messaging (persistent queues)");
        System.out.println("6. Size Solace broker for subscription fan-out workload");
    }
}
TEXT
Expected output:
Senior nuances:
1. Solace trie-based matching: O(levels) per message
2. Kafka: no broker matching, consumer does all filtering
3. Kafka regex subscription != Solace wildcards
4. Common pattern: Solace routes, Kafka stores
5. Solace has guaranteed messaging (persistent queues)
6. Size Solace broker for subscription fan-out workload

Visual Overview

DIAGRAM
  Solace Hierarchical Routing:

  Publisher ──► txn/IN/Mumbai/savings/large
                        │
               ┌────────▼────────┐
               │  Solace Broker   │
               │  (trie matcher)  │
               └────────┬────────┘
                        │
          ┌─────────────┼─────────────┐
          │             │             │
     txn/IN/Mumbai/>  txn/IN/*/savings/large  txn/IN/Delhi/>
     (Priya: all      (Pankaj: all large     (no match,
      Mumbai txns)     savings across         not delivered)
                       India)

  ─────────────────────────────────────────────────

  Kafka Flat Topic:

  Publisher ──► bank.transactions [partition 0][partition 1][partition 2]
                        │
               ┌────────▼────────┐
               │  Kafka Broker    │
               │  (no filtering)  │
               └────────┬────────┘
                        │
          ┌─────────────┼─────────────┐
          │             │             │
     Consumer A      Consumer B    Consumer C
     (reads ALL,     (reads ALL,   (reads ALL,
      filters in      filters in    filters in
      app code)       app code)     app code)

Key Takeaway

Solace PubSub+ is an enterprise message broker whose defining feature is hierarchical topic routing with level-aware wildcard subscriptions. Publishers attach structured, slash-separated topic strings to each message, and subscribers express interest using wildcards: * matches exactly one level and > matches one or more remaining levels. The broker itself evaluates every message against all active subscriptions and delivers only matching messages, so the routing intelligence lives in the broker and consumers stay thin.

Kafka takes the opposite approach and keeps the broker deliberately simple. A Kafka topic is a flat, pre-created, partitioned log, the broker never interprets structure inside the topic name or the message content, and a consumer subscribes to a whole topic and reads every record in its partitions. If you want only a narrow slice, you either filter in the consumer, create many fine-grained topics, or add a stream-processing layer.

At National Bank of India, the difference was practical. Desks that needed specific slices of the live transaction feed, like all Mumbai events or all large savings transactions, used Solace subscriptions and the broker delivered exactly the matching messages. The same feed flowed into a single flat Kafka topic for seven-year compliance retention and analytics, where each consumer filtered in application code.

Remember one sentence: Solace routes at the broker with hierarchical wildcards so consumers receive only what they asked for; Kafka keeps the broker simple with flat topics and pushes filtering to the consumer in exchange for massive throughput and durable replay.

---

Share
Use ← → arrow keys to navigate