Day 32: Log Producers - Publishing Events to Message Queues
What We’re Building Today
High-throughput log producer service handling 50,000+ events/second with configurable batching
Multi-source log collection from REST APIs, file watchers, and streaming endpoints
Partitioned message publishing with intelligent routing based on log severity and source
Producer reliability patterns including acknowledgments, retries, and circuit breakers
Why This Matters: The Producer Problem at Scale
When Netflix processes 500 billion events per day, they don’t write each log directly to storage. When Uber tracks 14 million trips daily across 10,000+ cities, they don’t block user requests waiting for analytics pipelines. The producer pattern solves a fundamental distributed systems challenge: decoupling event generation from event processing.
Your application servers should never wait for downstream analytics, ML pipelines, or compliance logging to complete. Producers create an asynchronous boundary that absorbs traffic spikes, maintains service availability during downstream failures, and enables independent scaling of collection versus processing infrastructure. This isn’t just about queuing—it’s about building systems where components can fail, scale, and evolve independently while maintaining end-to-end reliability guarantees.
System Design Deep Dive: Producer Architecture Patterns
1. Producer Reliability Models
Message brokers offer three fundamental reliability guarantees, each with distinct trade-offs:
At-most-once delivery (fire-and-forget): Producer sends messages without waiting for acknowledgment. Latency is minimal (sub-millisecond), throughput is maximized, but messages can be lost during network failures or broker crashes. LinkedIn uses this for non-critical telemetry where data loss is acceptable but latency isn’t.
At-least-once delivery: Producer waits for broker acknowledgment and retries on failure. Messages are guaranteed to arrive but may be duplicated during retry scenarios. Uber’s event streaming infrastructure operates primarily in this mode, relying on downstream consumer idempotency to handle duplicates. This provides 99.99% delivery reliability while maintaining throughput.
Exactly-once semantics: Producer and broker coordinate with idempotent producers and transactional commits. Kafka achieves this through producer IDs and sequence numbers. Twitter’s advertising pipeline requires exactly-once to prevent duplicate charge events. The overhead is significant—20-30% throughput reduction—but eliminates duplicate processing cost.
The architectural insight: Choose reliability based on business impact, not technical preference. Non-critical metrics can tolerate 0.1% loss. Financial transactions cannot.
2. Partitioning Strategy and Message Ordering
Message ordering exists only within partitions, not across them. Your partitioning strategy determines both scalability and consistency guarantees.
Key-based partitioning: Hash the partition key (user ID, request ID, session ID) to route related events to the same partition. Netflix uses this to maintain user session ordering—all events for a given user flow through one partition, preserving playback sequence while allowing parallel processing of different users. The trade-off is hotkey problems: popular users create imbalanced partitions.
Round-robin partitioning: Distribute messages evenly across partitions without ordering guarantees. Google’s distributed tracing uses this for span collection where ordering doesn’t matter but even distribution maximizes throughput. This achieves perfect load balancing but breaks causality.
Custom partitioning: Pinterest implements geography-based partitioning where logs from the same region cluster together. This enables regional data residency compliance and reduces cross-AZ network costs. The complexity is managing partition count changes during scaling.
Anti-pattern: Using timestamp-based partitioning creates thundering herd problems. All events from a given second route to one partition, creating wild load imbalance. Always partition on a distributed dimension (user, tenant, source) rather than a concentrated one (time, priority).
3. Batching and Buffering Strategy
Individual message sends are expensive—each requires network round-trip, serialization, and broker indexing. Batching amortizes these costs across multiple messages.
Kafka producers buffer messages in memory and send batches based on size (16KB default) or time (100ms default) thresholds. Datadog’s agent batches up to 1000 log entries per request, reducing API calls by 99% while maintaining sub-second latency. The architectural tension is latency versus efficiency: larger batches improve throughput (10x gains typical) but increase end-to-end latency.
Adaptive batching: Confluent’s enterprise Kafka adjusts batch size based on throughput. During high load, batches fill quickly (size-triggered). During low load, time triggers prevent messages from stalling. This balances throughput and latency automatically.
Compression: Enable LZ4 or Snappy compression on batches, not individual messages. Airbnb sees 70% reduction in network bandwidth with minimal CPU overhead. Compression ratios improve with batch size—another reason to batch.
The hidden cost: Memory usage scales with batch size and partition count. With 1000 partitions and 16KB buffers, you’re allocating 16MB just for send buffers. Monitor JVM heap carefully.
4. Backpressure and Flow Control
Producers generate data faster than consumers can process during traffic spikes. Without backpressure, you overwhelm brokers with messages, exhaust memory, and cascade failures.
Buffer-based backpressure: Block producer when send buffer exceeds threshold (Kafka’s buffer.memory setting). This creates natural pushback to log-generating threads. Stripe’s payment processing uses this—API requests block when event publishing falls behind, preventing data loss at the cost of increased request latency.
Circuit breaker pattern: Stop sending to failed brokers and fail fast. Resilience4j circuit breakers detect when brokers are down (5 failures in 10 seconds) and immediately reject new sends rather than timing out. The system degrades gracefully—logs are dropped but service remains responsive. Netflix’s edge services use this to prevent cascading failures.
Rate limiting: Token bucket algorithms cap maximum send rate per producer. Cloudflare enforces per-customer rate limits to prevent noisy neighbors from overwhelming shared infrastructure. This ensures fair resource allocation but requires careful capacity planning.
Production lesson from Amazon: They limit producer rate to 70% of broker capacity, reserving 30% headroom for traffic spikes. This prevents saturation while maximizing utilization.
5. Schema Evolution and Serialization
Log formats evolve: new fields get added, old fields deprecated. Your producer strategy determines whether this breaks consumers.
Schema Registry pattern: Confluent’s approach stores Avro/Protobuf/JSON schemas centrally. Producers register schemas, consumers validate compatibility. LinkedIn’s 1000+ data teams publish events independently while maintaining backward compatibility. Forward compatibility allows new fields without consumer updates. Backward compatibility allows field removal without producer updates.
Versioned topics: Pinterest creates new topics for breaking changes (logs.v1, logs.v2). Both versions run concurrently during migration. This doubles infrastructure costs temporarily but enables zero-downtime schema changes.
JSON vs Avro: JSON is flexible but verbose (4x size overhead). Avro is compact and validates schemas but requires registry infrastructure. Uber migrated from JSON to Avro and reduced network bandwidth 70% while gaining schema validation.
Github Link:
https://github.com/sysdr/sdc-java/tree/main/day32/log-producer-systemImplementation Walkthrough: Building the Producer Service
Our implementation creates a Spring Boot microservice that collects logs from multiple sources (REST API, file watcher, application events) and publishes them to Kafka with production-grade reliability.
Core Producer Service: KafkaProducerService wraps Spring Kafka’s KafkaTemplate with retry logic, metrics, and circuit breaker integration. We implement three send methods: sendFireAndForget() for high-throughput non-critical logs, sendSync() for critical events requiring acknowledgment, and sendAsync() with callback handling for balanced reliability and throughput.
Partition Strategy: LogPartitioner implements custom partitioning based on log level (ERROR logs go to partition 0 for priority processing) and source hash (evenly distributing logs from each service). This ensures critical logs are processed first while maintaining even load distribution.
Batching Configuration: Producer configs set batch.size=32768 (32KB) and linger.ms=100 to optimize throughput. With compression.type=snappy, we achieve 60% size reduction with minimal CPU overhead. The buffer.memory=67108864 (64MB) setting allows buffering during brief broker unavailability.
Error Handling: Resilience4j CircuitBreaker wraps send operations with a 50% failure threshold over 10 calls. When broker failures exceed this threshold, the circuit opens for 60 seconds, failing fast and preventing thread exhaustion. RetryTemplate handles transient failures with exponential backoff (100ms, 200ms, 400ms) before giving up.
Metrics and Monitoring: Micrometer integration exposes producer metrics: messages sent per second, batch size distribution, send latency percentiles (p50, p95, p99), and error rates by type. Custom metrics track buffer utilization and circuit breaker state changes.
The REST controller accepts log events via POST requests with rate limiting (100 req/sec per IP) to prevent abuse. Each request is validated, enriched with metadata (timestamp, hostname, correlation ID), and sent asynchronously to Kafka. The API returns immediately—a 202 Accepted status—without waiting for Kafka acknowledgment, maintaining sub-5ms response times.
Working Code Demo:
Production Considerations
Performance Characteristics: This implementation sustains 50,000 events/second on a 4-core instance with 8GB RAM, limited primarily by network bandwidth (1Gbps). Latency breakdown: REST processing (2ms), serialization (1ms), batching wait (0-100ms average 50ms), network transmission (5ms), broker acknowledgment (10ms). End-to-end p99 latency: 150ms.
Failure Scenarios: Broker unavailability triggers circuit breaker after 5 failures, rejecting new sends and alerting operators. During partial cluster failure (1 of 3 brokers down), automatic leader election redirects traffic within 30 seconds. Network partitions cause producer buffering up to 64MB (approximately 60 seconds of traffic) before blocking application threads.
Monitoring Strategy: Alert on producer buffer utilization >80% (indicates slow brokers or insufficient capacity), circuit breaker state changes (indicates broker health issues), p99 send latency >500ms (indicates performance degradation), and error rate >0.1% (indicates configuration or infrastructure problems). Grafana dashboards visualize these metrics with 5-second resolution.
Capacity Planning: With 50KB average log size and 50,000 events/second, we generate 2.5GB/second of raw data (175MB/second with Snappy compression). Plan for 3x peak capacity: 525MB/second or 45TB/day. Each producer instance handles 10,000 events/second, requiring 5 instances for 50K target plus 2 for redundancy.
Scale Connection: FAANG Producer Patterns
Netflix’s edge gateway collects 500 billion events daily from 200 million subscribers using producer patterns identical to this implementation: asynchronous fire-and-forget for most telemetry, batched sends with compression, and partitioning by user ID to maintain session ordering. Their innovation is adaptive buffering—producers automatically increase batch size during high-load periods, achieving 10x throughput improvement.
Uber’s event streaming infrastructure processes 14 million trips daily with exactly-once producers for financial events (fare calculations, charges) and at-least-once for operational metrics (GPS coordinates, ETA updates). They partition by city to enable regional processing and regulatory compliance. Their lesson: reliability requirements vary by event type within the same system.
Next Steps
Tomorrow: Building consumer services that process logs from queues with parallel workers, error handling, and exactly-once processing semantics.


