Topic design, partition strategies, consumer group patterns, exactly-once processing, and dead letter queue handling.
npx skills add https://github.com/vibeeval/vibecosystem --skill kafka-patterns
Event streaming patterns for Apache Kafka in distributed systems.
# Topic naming convention: <domain>.<entity>.<event-type>
# Examples:
# orders.order.created
# payments.payment.completed
# inventory.stock.updated
# Topic configuration
topics:
orders.order.created:
partitions: 12 # Match expected consumer parallelism
replication-factor: 3 # Survive 2 broker failures
retention.ms: 604800000 # 7 days
cleanup.policy: delete
orders.order.changelog:
partitions: 12
replication-factor: 3
retention.ms: -1 # Infinite retention (compacted)
cleanup.policy: compact # Keep latest value per key
min.compaction.lag.ms: 3600000 # 1h before compacting
import { Kafka, Partitioners, CompressionTypes } from 'kafkajs'
const kafka = new Kafka({
clientId: 'order-service',
brokers: process.env.KAFKA_BROKERS!.split(','),
})
const producer = kafka.producer({
idempotent: true, // Exactly-once producer
maxInFlightRequests: 5, // Max parallel requests
createPartitioner: Partitioners.DefaultPartitioner,
})
await producer.connect()
// Key-based partitioning: same key always goes to same partition (ordering)
async function publishOrderEvent(order: Order, eventType: string): Promise<void> {
await producer.send({
topic: `orders.order.${eventType}`,
compression: CompressionTypes.LZ4,
messages: [{
key: order.id, // Orders for same ID → same partition → ordered
value: JSON.stringify({
eventId: crypto.randomUUID(), // Idempotency key
eventType,
timestamp: new Date().toISOString(),
data: order,
}),
headers: {
'content-type': 'application/json',
'source': 'order-service',
'correlation-id': order.correlationId,
},
}],
})
}
// Batch publishing for throughput
async function publishBatch(events: OrderEvent[]): Promise<void> {
await producer.sendBatch({
topicMessages: [{
topic: 'orders.order.created',
messages: events.map(e => ({
key: e.orderId,
value: JSON.stringify(e),
})),
}],
})
}
const consumer = kafka.consumer({
groupId: 'payment-processor', // Consumer group: shared topic consumption
sessionTimeout: 30000,
heartbeatInterval: 3000,
maxBytesPerPartition: 1048576, // 1MB per partition per fetch
retry: { retries: 5 },
})
await consumer.connect()
await consumer.subscribe({
topics: ['orders.order.created'],
fromBeginning: false, // Start from latest offset
})
await consumer.run({
autoCommit: false, // Manual commit for exactly-once
eachBatchAutoResolve: false,
eachBatch: async ({ batch, resolveOffset, commitOffsetsIfNecessary, heartbeat }) => {
for (const message of batch.messages) {
try {
const event = JSON.parse(message.value!.toString())
// Idempotency check: skip already processed events
if (await isAlreadyProcessed(event.eventId)) {
resolveOffset(message.offset)
continue
}
await processOrderPayment(event.data)
await markAsProcessed(event.eventId)
resolveOffset(message.offset)
await commitOffsetsIfNecessary()
await heartbeat()
} catch (err) {
console.error(`Failed to process message at offset ${message.offset}:`, err)
// Send to DLQ instead of blocking the partition
await sendToDeadLetterQueue(message, err as Error)
resolveOffset(message.offset)
}
}
},
})
const DLQ_TOPIC = 'orders.order.created.dlq'
async function sendToDeadLetterQueue(
originalMessage: KafkaMessage,
error: Error
): Promise<void> {
await producer.send({
topic: DLQ_TOPIC,
messages: [{
key: originalMessage.key,
value: originalMessage.value,
headers: {
...originalMessage.headers,
'dlq-reason': error.message,
'dlq-timestamp': new Date().toISOString(),
'dlq-original-topic': 'orders.order.created',
'dlq-retry-count': '0',
},
}],
})
}
// DLQ consumer: retry or alert
async function processDLQ(): Promise<void> {
const dlqConsumer = kafka.consumer({ groupId: 'dlq-processor' })
await dlqConsumer.subscribe({ topics: [DLQ_TOPIC] })
await dlqConsumer.run({
eachMessage: async ({ message }) => {
const retryCount = parseInt(
message.headers?.['dlq-retry-count']?.toString() ?? '0'
)
if (retryCount >= 3) {
// Max retries exceeded: alert ops team
await alertOps({
topic: DLQ_TOPIC,
key: message.key?.toString(),
reason: message.headers?.['dlq-reason']?.toString(),
retries: retryCount,
})
return
}
// Retry with incremented count
try {
const event = JSON.parse(message.value!.toString())
await processOrderPayment(event.data)
} catch (err) {
// Re-enqueue with incremented retry count
await producer.send({
topic: DLQ_TOPIC,
messages: [{
key: message.key,
value: message.value,
headers: {
...message.headers,
'dlq-retry-count': String(retryCount + 1),
},
}],
})
}
},
})
}
// Custom partitioner: route by region for data locality
const regionalPartitioner = () => ({
partition: ({ topic, partitionMetadata, message }) => {
const region = message.headers?.['region']?.toString() ?? 'default'
const regionMap: Record<string, number> = {
'us-east': 0, 'us-west': 1,
'eu-west': 2, 'eu-east': 3,
'ap-southeast': 4,
}
const partition = regionMap[region]
if (partition !== undefined && partition < partitionMetadata.length) {
return partition
}
// Fallback: hash the key
const numPartitions = partitionMetadata.length
const hash = murmurHash(message.key?.toString() ?? '')
return Math.abs(hash) % numPartitions
}
})
Integration with protocols.io API for managing scientific protocols. This skill should be used when working with protocols.io to search, create, update, or publish protocols; manage protocol steps and materials; handle discussions and comments; organize workspaces; upload and manage files; or integrate protocols.io functionality into workflows. Applicable for protocol discovery, collaborative protocol development, experiment tracking, lab protocol management, and scientific documentation.
Analyzes job descriptions and generates tailored resumes that highlight relevant experience, skills, and achievements to maximize interview chances
Generate Excalidraw diagrams from natural language descriptions. Use when asked to "create a diagram", "make a flowchart", "visualize a process", "draw a system architecture", "create a mind map", or "generate an Excalidraw file". Supports flowcharts, relationship diagrams, mind maps, and system architecture diagrams. Outputs .excalidraw JSON files that can be opened directly in Excalidraw.
Build and distribute Expo development clients locally or via TestFlight
Use when you have a written implementation plan to execute in a separate session with review checkpoints
Data structure for annotated matrices in single-cell analysis. Use when working with .h5ad files or integrating with the scverse ecosystem. This is the data format skill—for analysis workflows use scanpy; for probabilistic models use scvi-tools; for population-scale queries use cellxgene-census.
Benchling R&D platform integration. Access registry (DNA, proteins), inventory, ELN entries, workflows via API, build Benchling Apps, query Data Warehouse, for lab data management automation.
Comprehensive molecular biology toolkit. Use for sequence manipulation, file parsing (FASTA/GenBank/PDB), phylogenetics, and programmatic NCBI/PubMed access (Bio.Entrez). Best for batch processing, custom bioinformatics pipelines, BLAST automation. For quick lookups use gget; for multi-service integration use bioservices.
Take vibeeval/kafka-patterns from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.